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"
86 #include "jscntxtinlines.h"
91 SetArgsPrivateNative(JSObject
*argsobj
, ArgsPrivateNative
*apn
)
93 JS_ASSERT(argsobj
->isArguments());
94 uintptr_t p
= (uintptr_t) apn
;
95 argsobj
->setPrivate((void*) (p
| 2));
99 js_GetArgsValue(JSContext
*cx
, JSStackFrame
*fp
, Value
*vp
)
103 if (fp
->flags
& JSFRAME_OVERRIDE_ARGS
) {
104 JS_ASSERT(fp
->callobj
);
105 jsid id
= ATOM_TO_JSID(cx
->runtime
->atomState
.argumentsAtom
);
106 return fp
->callobj
->getProperty(cx
, id
, vp
);
108 argsobj
= js_GetArgsObject(cx
, fp
);
111 vp
->setObject(*argsobj
);
116 js_GetArgsProperty(JSContext
*cx
, JSStackFrame
*fp
, jsid id
, Value
*vp
)
118 if (fp
->flags
& JSFRAME_OVERRIDE_ARGS
) {
119 JS_ASSERT(fp
->callobj
);
121 jsid argumentsid
= ATOM_TO_JSID(cx
->runtime
->atomState
.argumentsAtom
);
123 if (!fp
->callobj
->getProperty(cx
, argumentsid
, &v
))
127 if (v
.isPrimitive()) {
128 obj
= js_ValueToNonNullObject(cx
, v
);
134 return obj
->getProperty(cx
, id
, vp
);
138 if (JSID_IS_INT(id
)) {
139 uint32 arg
= uint32(JSID_TO_INT(id
));
140 JSObject
*argsobj
= fp
->argsobj
;
141 if (arg
< fp
->argc
) {
143 if (argsobj
->getArgsElement(arg
).isMagic(JS_ARGS_HOLE
))
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 (JSID_IS_ATOM(id
, cx
->runtime
->atomState
.lengthAtom
)) {
164 JSObject
*argsobj
= fp
->argsobj
;
165 if (argsobj
&& argsobj
->isArgsLengthOverridden())
166 return argsobj
->getProperty(cx
, id
, vp
);
167 vp
->setInt32(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
, PrivateValue(NULL
));
185 argsobj
->setArgsCallee(ObjectOrNullValue(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
, Value
*args
)
199 uint32 argc
= argsobj
->getArgsLength();
200 for (uint32 i
= 0; i
!= argc
; ++i
) {
201 if (!argsobj
->getArgsElement(i
).isMagic(JS_ARGS_HOLE
))
202 argsobj
->setArgsElement(i
, args
[i
]);
207 js_GetArgsObject(JSContext
*cx
, JSStackFrame
*fp
)
210 * We must be in a function activation; the function must be lightweight
211 * or else fp must have a variable object.
214 JS_ASSERT_IF(fp
->fun
->flags
& JSFUN_HEAVYWEIGHT
,
215 fp
->varobj(cx
->containingCallStack(fp
)));
217 /* Skip eval and debugger frames. */
218 while (fp
->flags
& JSFRAME_SPECIAL
)
221 /* Create an arguments object for fp only if it lacks one. */
222 JSObject
*argsobj
= fp
->argsobj
;
226 /* Compute the arguments object's parent slot from fp's scope chain. */
227 JSObject
*global
= fp
->scopeChain
->getGlobal();
228 argsobj
= NewArguments(cx
, global
, fp
->argc
, &fp
->argv
[-2].toObject());
232 /* Link the new object to fp so it can get actual argument values. */
233 argsobj
->setPrivate(fp
);
234 fp
->argsobj
= argsobj
;
239 js_PutArgsObject(JSContext
*cx
, JSStackFrame
*fp
)
241 JSObject
*argsobj
= fp
->argsobj
;
242 JS_ASSERT(argsobj
->getPrivate() == fp
);
243 PutArguments(cx
, argsobj
, fp
->argv
);
244 argsobj
->setPrivate(NULL
);
249 * Traced versions of js_GetArgsObject and js_PutArgsObject.
253 JSObject
* JS_FASTCALL
254 js_Arguments(JSContext
*cx
, JSObject
*parent
, uint32 argc
, JSObject
*callee
,
255 double *argv
, ArgsPrivateNative
*apn
)
257 JSObject
*argsobj
= NewArguments(cx
, parent
, argc
, callee
);
261 SetArgsPrivateNative(argsobj
, apn
);
266 JS_DEFINE_CALLINFO_6(extern, OBJECT
, js_Arguments
, CONTEXT
, OBJECT
, UINT32
, OBJECT
,
267 DOUBLEPTR
, APNPTR
, 0, nanojit::ACC_STORE_ANY
)
269 /* FIXME change the return type to void. */
271 js_PutArguments(JSContext
*cx
, JSObject
*argsobj
, Value
*args
)
273 JS_ASSERT(GetArgsPrivateNative(argsobj
));
274 PutArguments(cx
, argsobj
, args
);
275 argsobj
->setPrivate(NULL
);
279 JS_DEFINE_CALLINFO_3(extern, BOOL
, js_PutArguments
, CONTEXT
, OBJECT
, VALUEPTR
, 0,
280 nanojit::ACC_STORE_ANY
)
283 args_delProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
285 JS_ASSERT(obj
->isArguments());
287 if (JSID_IS_INT(id
)) {
288 uintN arg
= uintN(JSID_TO_INT(id
));
289 if (arg
< obj
->getArgsLength())
290 obj
->setArgsElement(arg
, MagicValue(JS_ARGS_HOLE
));
291 } else if (JSID_IS_ATOM(id
, cx
->runtime
->atomState
.lengthAtom
)) {
292 obj
->setArgsLengthOverridden();
293 } else if (JSID_IS_ATOM(id
, cx
->runtime
->atomState
.calleeAtom
)) {
294 obj
->setArgsCallee(MagicValue(JS_ARGS_HOLE
));
299 static JS_REQUIRES_STACK JSObject
*
300 WrapEscapingClosure(JSContext
*cx
, JSStackFrame
*fp
, JSObject
*funobj
, JSFunction
*fun
)
302 JS_ASSERT(GET_FUNCTION_PRIVATE(cx
, funobj
) == fun
);
303 JS_ASSERT(fun
->optimizedClosure());
304 JS_ASSERT(!fun
->u
.i
.wrapper
);
307 * We do not attempt to reify Call and Block objects on demand for outer
308 * scopes. This could be done (see the "v8" patch in bug 494235) but it is
309 * fragile in the face of ongoing compile-time optimization. Instead, the
310 * _DBG* opcodes used by wrappers created here must cope with unresolved
311 * upvars and throw them as reference errors. Caveat debuggers!
313 JSObject
*scopeChain
= js_GetScopeChain(cx
, fp
);
318 * We must wrap funobj with a JSFunction, so use NewObjectWithGivenProto.
319 * This helper has a special case for js_FunctionClass, triggered here and
320 * also possibly via the JS_NewObjectForGivenProto and JS_NewObject APIs.
322 JSObject
*wfunobj
= NewObjectWithGivenProto(cx
, &js_FunctionClass
,
326 AutoObjectRooter
tvr(cx
, wfunobj
);
328 JSFunction
*wfun
= (JSFunction
*) wfunobj
;
329 wfunobj
->setPrivate(wfun
);
331 wfun
->flags
= fun
->flags
| JSFUN_HEAVYWEIGHT
;
333 wfun
->u
.i
.nupvars
= 0;
334 wfun
->u
.i
.skipmin
= fun
->u
.i
.skipmin
;
335 wfun
->u
.i
.wrapper
= true;
336 wfun
->u
.i
.script
= NULL
;
337 wfun
->u
.i
.names
.taggedAtom
= NULL
;
338 wfun
->atom
= fun
->atom
;
340 if (fun
->hasLocalNames()) {
341 void *mark
= JS_ARENA_MARK(&cx
->tempPool
);
342 jsuword
*names
= js_GetLocalNameArray(cx
, fun
, &cx
->tempPool
);
347 for (uintN i
= 0, n
= fun
->countLocalNames(); i
!= n
; i
++) {
348 jsuword name
= names
[i
];
349 JSAtom
*atom
= JS_LOCAL_NAME_TO_ATOM(name
);
350 JSLocalKind localKind
= (i
< fun
->nargs
)
352 : (i
< fun
->countArgsAndVars())
353 ? (JS_LOCAL_NAME_IS_CONST(name
)
358 ok
= js_AddLocal(cx
, wfun
, atom
, localKind
);
363 JS_ARENA_RELEASE(&cx
->tempPool
, mark
);
366 JS_ASSERT(wfun
->nargs
== fun
->nargs
);
367 JS_ASSERT(wfun
->u
.i
.nvars
== fun
->u
.i
.nvars
);
368 JS_ASSERT(wfun
->u
.i
.nupvars
== fun
->u
.i
.nupvars
);
369 js_FreezeLocalNames(cx
, wfun
);
372 JSScript
*script
= fun
->u
.i
.script
;
373 jssrcnote
*snbase
= script
->notes();
374 jssrcnote
*sn
= snbase
;
375 while (!SN_IS_TERMINATOR(sn
))
377 uintN nsrcnotes
= (sn
- snbase
) + 1;
379 /* NB: GC must not occur before wscript is homed in wfun->u.i.script. */
380 JSScript
*wscript
= js_NewScript(cx
, script
->length
, nsrcnotes
,
381 script
->atomMap
.length
,
382 (script
->objectsOffset
!= 0)
383 ? script
->objects()->length
386 (script
->regexpsOffset
!= 0)
387 ? script
->regexps()->length
389 (script
->trynotesOffset
!= 0)
390 ? script
->trynotes()->length
392 (script
->constOffset
!= 0)
393 ? script
->consts()->length
395 (script
->globalsOffset
!= 0)
396 ? script
->globals()->length
401 memcpy(wscript
->code
, script
->code
, script
->length
);
402 wscript
->main
= wscript
->code
+ (script
->main
- script
->code
);
404 memcpy(wscript
->notes(), snbase
, nsrcnotes
* sizeof(jssrcnote
));
405 memcpy(wscript
->atomMap
.vector
, script
->atomMap
.vector
,
406 wscript
->atomMap
.length
* sizeof(JSAtom
*));
407 if (script
->objectsOffset
!= 0) {
408 memcpy(wscript
->objects()->vector
, script
->objects()->vector
,
409 wscript
->objects()->length
* sizeof(JSObject
*));
411 if (script
->regexpsOffset
!= 0) {
412 memcpy(wscript
->regexps()->vector
, script
->regexps()->vector
,
413 wscript
->regexps()->length
* sizeof(JSObject
*));
415 if (script
->trynotesOffset
!= 0) {
416 memcpy(wscript
->trynotes()->vector
, script
->trynotes()->vector
,
417 wscript
->trynotes()->length
* sizeof(JSTryNote
));
419 if (script
->globalsOffset
!= 0) {
420 memcpy(wscript
->globals()->vector
, script
->globals()->vector
,
421 wscript
->globals()->length
* sizeof(GlobalSlotArray::Entry
));
424 if (wfun
->u
.i
.nupvars
!= 0) {
425 JS_ASSERT(wfun
->u
.i
.nupvars
== wscript
->upvars()->length
);
426 memcpy(wscript
->upvars()->vector
, script
->upvars()->vector
,
427 wfun
->u
.i
.nupvars
* sizeof(uint32
));
430 jsbytecode
*pc
= wscript
->code
;
431 while (*pc
!= JSOP_STOP
) {
432 /* XYZZYbe should copy JSOP_TRAP? */
433 JSOp op
= js_GetOpcode(cx
, wscript
, pc
);
434 const JSCodeSpec
*cs
= &js_CodeSpec
[op
];
435 ptrdiff_t oplen
= cs
->length
;
437 oplen
= js_GetVariableBytecodeLength(pc
);
440 * Rewrite JSOP_{GET,CALL}DSLOT as JSOP_{GET,CALL}UPVAR_DBG for the
441 * case where fun is an escaping flat closure. This works because the
442 * UPVAR and DSLOT ops by design have the same format: an upvar index
446 case JSOP_GETUPVAR
: *pc
= JSOP_GETUPVAR_DBG
; break;
447 case JSOP_CALLUPVAR
: *pc
= JSOP_CALLUPVAR_DBG
; break;
448 case JSOP_GETDSLOT
: *pc
= JSOP_GETUPVAR_DBG
; break;
449 case JSOP_CALLDSLOT
: *pc
= JSOP_CALLUPVAR_DBG
; break;
450 case JSOP_DEFFUN_FC
: *pc
= JSOP_DEFFUN_DBGFC
; break;
451 case JSOP_DEFLOCALFUN_FC
: *pc
= JSOP_DEFLOCALFUN_DBGFC
; break;
452 case JSOP_LAMBDA_FC
: *pc
= JSOP_LAMBDA_DBGFC
; break;
459 * Fill in the rest of wscript. This means if you add members to JSScript
460 * you must update this code. FIXME: factor into JSScript::clone method.
462 wscript
->noScriptRval
= script
->noScriptRval
;
463 wscript
->savedCallerFun
= script
->savedCallerFun
;
464 wscript
->hasSharps
= script
->hasSharps
;
465 wscript
->strictModeCode
= script
->strictModeCode
;
466 wscript
->version
= script
->version
;
467 wscript
->nfixed
= script
->nfixed
;
468 wscript
->filename
= script
->filename
;
469 wscript
->lineno
= script
->lineno
;
470 wscript
->nslots
= script
->nslots
;
471 wscript
->staticLevel
= script
->staticLevel
;
472 wscript
->principals
= script
->principals
;
473 if (wscript
->principals
)
474 JSPRINCIPALS_HOLD(cx
, wscript
->principals
);
475 #ifdef CHECK_SCRIPT_OWNER
476 wscript
->owner
= script
->owner
;
479 /* Deoptimize wfun from FUN_{FLAT,NULL}_CLOSURE to FUN_INTERPRETED. */
480 FUN_SET_KIND(wfun
, JSFUN_INTERPRETED
);
481 wfun
->u
.i
.script
= wscript
;
486 ArgGetter(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
488 if (!InstanceOf(cx
, obj
, &js_ArgumentsClass
, NULL
))
491 if (JSID_IS_INT(id
)) {
493 * arg can exceed the number of arguments if a script changed the
494 * prototype to point to another Arguments object with a bigger argc.
496 uintN arg
= uintN(JSID_TO_INT(id
));
497 if (arg
< obj
->getArgsLength()) {
499 ArgsPrivateNative
*argp
= GetArgsPrivateNative(obj
);
501 ExternNativeToValue(cx
, *vp
, argp
->typemap()[arg
], &argp
->argv
[arg
]);
506 JSStackFrame
*fp
= (JSStackFrame
*) obj
->getPrivate();
510 const Value
&v
= obj
->getArgsElement(arg
);
511 if (!v
.isMagic(JS_ARGS_HOLE
))
515 } else if (JSID_IS_ATOM(id
, cx
->runtime
->atomState
.lengthAtom
)) {
516 if (!obj
->isArgsLengthOverridden())
517 vp
->setInt32(obj
->getArgsLength());
519 JS_ASSERT(JSID_IS_ATOM(id
, cx
->runtime
->atomState
.calleeAtom
));
520 const Value
&v
= obj
->getArgsCallee();
521 if (!v
.isMagic(JS_ARGS_HOLE
)) {
523 * If this function or one in it needs upvars that reach above it
524 * in the scope chain, it must not be a null closure (it could be a
525 * flat closure, or an unoptimized closure -- the latter itself not
526 * necessarily heavyweight). Rather than wrap here, we simply throw
527 * to reduce code size and tell debugger users the truth instead of
528 * passing off a fibbing wrapper.
530 if (GET_FUNCTION_PRIVATE(cx
, &v
.toObject())->needsWrapper()) {
531 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
532 JSMSG_OPTIMIZED_CLOSURE_LEAK
);
542 ArgSetter(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
545 // To be able to set a property here on trace, we would have to make
546 // sure any updates also get written back to the trace native stack.
547 // For simplicity, we just leave trace, since this is presumably not
548 // a common operation.
549 if (JS_ON_TRACE(cx
)) {
555 if (!InstanceOf(cx
, obj
, &js_ArgumentsClass
, NULL
))
558 if (JSID_IS_INT(id
)) {
559 uintN arg
= uintN(JSID_TO_INT(id
));
560 if (arg
< obj
->getArgsLength()) {
561 JSStackFrame
*fp
= (JSStackFrame
*) obj
->getPrivate();
568 JS_ASSERT(JSID_IS_ATOM(id
, cx
->runtime
->atomState
.lengthAtom
) ||
569 JSID_IS_ATOM(id
, cx
->runtime
->atomState
.calleeAtom
));
573 * For simplicity we use delete/set to replace the property with one
574 * backed by the default Object getter and setter. Note the we rely on
575 * args_delete to clear the corresponding reserved slot so the GC can
578 AutoValueRooter
tvr(cx
);
579 return js_DeleteProperty(cx
, obj
, id
, tvr
.addr()) &&
580 js_SetProperty(cx
, obj
, id
, vp
);
584 args_resolve(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN flags
,
587 JS_ASSERT(obj
->isArguments());
591 if (JSID_IS_INT(id
)) {
592 uint32 arg
= uint32(JSID_TO_INT(id
));
593 if (arg
< obj
->getArgsLength() && !obj
->getArgsElement(arg
).isMagic(JS_ARGS_HOLE
))
595 } else if (JSID_IS_ATOM(id
, cx
->runtime
->atomState
.lengthAtom
)) {
596 if (!obj
->isArgsLengthOverridden())
598 } else if (JSID_IS_ATOM(id
, cx
->runtime
->atomState
.calleeAtom
)) {
599 if (!obj
->getArgsCallee().isMagic(JS_ARGS_HOLE
))
605 * XXX ECMA specs DontEnum even for indexed properties, contrary to
606 * other array-like objects.
608 Value tmp
= UndefinedValue();
609 if (!js_DefineProperty(cx
, obj
, id
, &tmp
, ArgGetter
, ArgSetter
, JSPROP_SHARED
))
617 args_enumerate(JSContext
*cx
, JSObject
*obj
)
619 JS_ASSERT(obj
->isArguments());
622 * Trigger reflection in args_resolve using a series of js_LookupProperty
625 int argc
= int(obj
->getArgsLength());
626 for (int i
= -2; i
!= argc
; i
++) {
628 ? ATOM_TO_JSID(cx
->runtime
->atomState
.lengthAtom
)
630 ? ATOM_TO_JSID(cx
->runtime
->atomState
.calleeAtom
)
635 if (!js_LookupProperty(cx
, obj
, id
, &pobj
, &prop
))
638 /* prop is null when the property was deleted. */
640 pobj
->dropProperty(cx
, prop
);
645 #if JS_HAS_GENERATORS
647 * If a generator's arguments or call object escapes, and the generator frame
648 * is not executing, the generator object needs to be marked because it is not
649 * otherwise reachable. An executing generator is rooted by its invocation. To
650 * distinguish the two cases (which imply different access paths to the
651 * generator object), we use the JSFRAME_FLOATING_GENERATOR flag, which is only
652 * set on the JSStackFrame kept in the generator object's JSGenerator.
655 args_or_call_trace(JSTracer
*trc
, JSObject
*obj
)
657 if (obj
->isArguments()) {
658 if (GetArgsPrivateNative(obj
))
661 JS_ASSERT(obj
->getClass() == &js_CallClass
);
664 JSStackFrame
*fp
= (JSStackFrame
*) obj
->getPrivate();
665 if (fp
&& fp
->isFloatingGenerator()) {
666 JSObject
*obj
= js_FloatingFrameToGenerator(fp
)->obj
;
667 JS_CALL_OBJECT_TRACER(trc
, obj
, "generator object");
671 # define args_or_call_trace NULL
675 * The Arguments class is not initialized via JS_InitClass, because arguments
676 * objects have the initial value of Object.prototype as their [[Prototype]].
677 * However, Object.prototype.toString.call(arguments) === "[object Arguments]"
678 * per ES5 (although not ES3), so its class name is "Arguments" rather than
681 * The JSClass functions below collaborate to lazily reflect and synchronize
682 * actual argument values, argument count, and callee function object stored
683 * in a JSStackFrame with their corresponding property values in the frame's
686 Class js_ArgumentsClass
= {
688 JSCLASS_HAS_PRIVATE
| JSCLASS_NEW_RESOLVE
|
689 JSCLASS_HAS_RESERVED_SLOTS(JSObject::ARGS_FIXED_RESERVED_SLOTS
) |
690 JSCLASS_MARK_IS_TRACE
| JSCLASS_HAS_CACHED_PROTO(JSProto_Object
),
691 PropertyStub
, args_delProperty
,
692 PropertyStub
, PropertyStub
,
693 args_enumerate
, (JSResolveOp
) args_resolve
,
698 JS_CLASS_TRACE(args_or_call_trace
), NULL
701 const uint32 JSSLOT_CALLEE
= JSSLOT_PRIVATE
+ 1;
702 const uint32 JSSLOT_CALL_ARGUMENTS
= JSSLOT_PRIVATE
+ 2;
703 const uint32 CALL_CLASS_FIXED_RESERVED_SLOTS
= 2;
706 * A Declarative Environment object stores its active JSStackFrame pointer in
707 * its private slot, just as Call and Arguments objects do.
709 Class js_DeclEnvClass
= {
711 JSCLASS_HAS_PRIVATE
| JSCLASS_HAS_CACHED_PROTO(JSProto_Object
),
712 PropertyStub
, PropertyStub
, PropertyStub
, PropertyStub
,
713 EnumerateStub
, ResolveStub
, ConvertStub
, NULL
,
714 JSCLASS_NO_OPTIONAL_MEMBERS
718 CheckForEscapingClosure(JSContext
*cx
, JSObject
*obj
, Value
*vp
)
720 JS_ASSERT(obj
->getClass() == &js_CallClass
||
721 obj
->getClass() == &js_DeclEnvClass
);
723 const Value
&v
= *vp
;
726 if (IsFunctionObject(v
, &funobj
)) {
727 JSFunction
*fun
= GET_FUNCTION_PRIVATE(cx
, funobj
);
730 * Any escaping null or flat closure that reaches above itself or
731 * contains nested functions that reach above it must be wrapped.
732 * We can wrap only when this Call or Declarative Environment obj
733 * still has an active stack frame associated with it.
735 if (fun
->needsWrapper()) {
738 JSStackFrame
*fp
= (JSStackFrame
*) obj
->getPrivate();
740 JSObject
*wrapper
= WrapEscapingClosure(cx
, fp
, funobj
, fun
);
743 vp
->setObject(*wrapper
);
747 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
748 JSMSG_OPTIMIZED_CLOSURE_LEAK
);
756 CalleeGetter(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
758 return CheckForEscapingClosure(cx
, obj
, vp
);
762 NewCallObject(JSContext
*cx
, JSFunction
*fun
, JSObject
*scopeChain
)
764 JSObject
*callobj
= js_NewGCObject(cx
);
768 /* Init immediately to avoid GC seeing a half-init'ed object. */
769 callobj
->init(&js_CallClass
, NULL
, scopeChain
, PrivateValue(NULL
));
770 callobj
->map
= cx
->runtime
->emptyCallScope
->hold();
772 /* This must come after callobj->map has been set. */
773 if (!js_EnsureReservedSlots(cx
, callobj
, fun
->countArgsAndVars()))
778 static inline JSObject
*
779 NewDeclEnvObject(JSContext
*cx
, JSStackFrame
*fp
)
781 JSObject
*envobj
= js_NewGCObject(cx
);
785 /* Init immediately to avoid GC seeing a half-init'ed object. */
786 envobj
->init(&js_DeclEnvClass
, NULL
, fp
->scopeChain
, PrivateValue(fp
));
787 envobj
->map
= cx
->runtime
->emptyDeclEnvScope
->hold();
792 js_GetCallObject(JSContext
*cx
, JSStackFrame
*fp
)
796 /* Create a call object for fp only if it lacks one. */
798 callobj
= fp
->callobj
;
803 /* A call object should be a frame's outermost scope chain element. */
804 Class
*classp
= fp
->scopeChain
->getClass();
805 if (classp
== &js_WithClass
|| classp
== &js_BlockClass
)
806 JS_ASSERT(fp
->scopeChain
->getPrivate() != js_FloatingFrameIfGenerator(cx
, fp
));
807 else if (classp
== &js_CallClass
)
808 JS_ASSERT(fp
->scopeChain
->getPrivate() != fp
);
812 * Create the call object, using the frame's enclosing scope as its
813 * parent, and link the call to its stack frame. For a named function
814 * expression Call's parent points to an environment object holding
817 JSAtom
*lambdaName
= (fp
->fun
->flags
& JSFUN_LAMBDA
) ? fp
->fun
->atom
: NULL
;
819 JSObject
*envobj
= NewDeclEnvObject(cx
, fp
);
823 /* Root envobj before js_DefineNativeProperty (-> JSClass.addProperty). */
824 fp
->scopeChain
= envobj
;
826 if (!js_DefineNativeProperty(cx
, fp
->scopeChain
, ATOM_TO_JSID(lambdaName
),
829 JSPROP_PERMANENT
| JSPROP_READONLY
,
835 callobj
= NewCallObject(cx
, fp
->fun
, fp
->scopeChain
);
839 callobj
->setPrivate(fp
);
841 JS_ASSERT(fp
->fun
== GET_FUNCTION_PRIVATE(cx
, fp
->callee()));
842 callobj
->setSlot(JSSLOT_CALLEE
, fp
->calleeValue());
843 fp
->callobj
= callobj
;
846 * Push callobj on the top of the scope chain, and make it the
849 fp
->scopeChain
= callobj
;
853 JSObject
* JS_FASTCALL
854 js_CreateCallObjectOnTrace(JSContext
*cx
, JSFunction
*fun
, JSObject
*callee
, JSObject
*scopeChain
)
856 JS_ASSERT(!js_IsNamedLambda(fun
));
857 JSObject
*callobj
= NewCallObject(cx
, fun
, scopeChain
);
860 callobj
->setSlot(JSSLOT_CALLEE
, ObjectValue(*callee
));
864 JS_DEFINE_CALLINFO_4(extern, OBJECT
, js_CreateCallObjectOnTrace
, CONTEXT
, FUNCTION
, OBJECT
, OBJECT
,
865 0, nanojit::ACC_STORE_ANY
)
868 js_GetCallObjectFunction(JSObject
*obj
)
870 JS_ASSERT(obj
->getClass() == &js_CallClass
);
871 const Value
&v
= obj
->getSlot(JSSLOT_CALLEE
);
872 if (v
.isUndefined()) {
873 /* Newborn or prototype object. */
876 JS_ASSERT(v
.isObject());
877 return GET_FUNCTION_PRIVATE(cx
, &v
.toObject());
881 CopyValuesToCallObject(JSObject
*callobj
, int nargs
, Value
*argv
, int nvars
, Value
*slots
)
883 memcpy(callobj
->dslots
, argv
, nargs
* sizeof(Value
));
884 memcpy(callobj
->dslots
+ nargs
, slots
, nvars
* sizeof(Value
));
888 js_PutCallObject(JSContext
*cx
, JSStackFrame
*fp
)
890 JSObject
*callobj
= fp
->callobj
;
893 /* Get the arguments object to snapshot fp's actual argument values. */
895 if (!(fp
->flags
& JSFRAME_OVERRIDE_ARGS
))
896 callobj
->setSlot(JSSLOT_CALL_ARGUMENTS
, ObjectOrNullValue(fp
->argsobj
));
897 js_PutArgsObject(cx
, fp
);
900 JSFunction
*fun
= fp
->fun
;
901 JS_ASSERT(fun
== js_GetCallObjectFunction(callobj
));
902 uintN n
= fun
->countArgsAndVars();
905 * Since for a call object all fixed slots happen to be taken, we can copy
906 * arguments and variables straight into JSObject.dslots.
908 JS_STATIC_ASSERT(JS_INITIAL_NSLOTS
- JSSLOT_PRIVATE
==
909 1 + CALL_CLASS_FIXED_RESERVED_SLOTS
);
911 JS_ASSERT(callobj
->numSlots() >= JS_INITIAL_NSLOTS
+ n
);
912 n
+= JS_INITIAL_NSLOTS
;
913 CopyValuesToCallObject(callobj
, fun
->nargs
, fp
->argv
, fun
->u
.i
.nvars
, fp
->slots());
916 /* Clear private pointers to fp, which is about to go away (js_Invoke). */
917 if (js_IsNamedLambda(fun
)) {
918 JSObject
*env
= callobj
->getParent();
920 JS_ASSERT(env
->getClass() == &js_DeclEnvClass
);
921 JS_ASSERT(env
->getPrivate() == fp
);
922 env
->setPrivate(NULL
);
925 callobj
->setPrivate(NULL
);
930 js_PutCallObjectOnTrace(JSContext
*cx
, JSObject
*scopeChain
, uint32 nargs
, Value
*argv
,
931 uint32 nvars
, Value
*slots
)
933 JS_ASSERT(scopeChain
->hasClass(&js_CallClass
));
934 JS_ASSERT(!scopeChain
->getPrivate());
936 uintN n
= nargs
+ nvars
;
938 CopyValuesToCallObject(scopeChain
, nargs
, argv
, nvars
, slots
);
943 JS_DEFINE_CALLINFO_6(extern, BOOL
, js_PutCallObjectOnTrace
, CONTEXT
, OBJECT
, UINT32
, VALUEPTR
,
944 UINT32
, VALUEPTR
, 0, nanojit::ACC_STORE_ANY
)
947 call_enumerate(JSContext
*cx
, JSObject
*obj
)
958 fun
= js_GetCallObjectFunction(obj
);
959 n
= fun
? fun
->countArgsAndVars() : 0;
963 mark
= JS_ARENA_MARK(&cx
->tempPool
);
965 MUST_FLOW_THROUGH("out");
966 names
= js_GetLocalNameArray(cx
, fun
, &cx
->tempPool
);
972 for (i
= 0; i
!= n
; ++i
) {
973 name
= JS_LOCAL_NAME_TO_ATOM(names
[i
]);
978 * Trigger reflection by looking up the name of the argument or
981 ok
= js_LookupProperty(cx
, obj
, ATOM_TO_JSID(name
), &pobj
, &prop
);
986 * The call object will always have a property corresponding to the
987 * argument or variable name because call_resolve creates the property
988 * using JSPROP_PERMANENT.
991 JS_ASSERT(pobj
== obj
);
992 pobj
->dropProperty(cx
, prop
);
997 JS_ARENA_RELEASE(&cx
->tempPool
, mark
);
1001 enum JSCallPropertyKind
{
1009 CallPropertyOp(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
,
1010 JSCallPropertyKind kind
, JSBool setter
= false)
1012 JS_ASSERT(obj
->getClass() == &js_CallClass
);
1015 if (kind
!= JSCPK_ARGUMENTS
) {
1016 JS_ASSERT((int16
) JSID_TO_INT(id
) == JSID_TO_INT(id
));
1017 i
= (uint16
) JSID_TO_INT(id
);
1021 if (kind
== JSCPK_UPVAR
) {
1022 JSObject
*callee
= &obj
->getSlot(JSSLOT_CALLEE
).toObject();
1025 JSFunction
*callee_fun
= (JSFunction
*) callee
->getPrivate();
1026 JS_ASSERT(FUN_FLAT_CLOSURE(callee_fun
));
1027 JS_ASSERT(i
< callee_fun
->u
.i
.nupvars
);
1030 array
= callee
->dslots
;
1032 JSFunction
*fun
= js_GetCallObjectFunction(obj
);
1033 JS_ASSERT_IF(kind
== JSCPK_ARG
, i
< fun
->nargs
);
1034 JS_ASSERT_IF(kind
== JSCPK_VAR
, i
< fun
->u
.i
.nvars
);
1036 JSStackFrame
*fp
= (JSStackFrame
*) obj
->getPrivate();
1038 if (kind
== JSCPK_ARGUMENTS
) {
1041 fp
->flags
|= JSFRAME_OVERRIDE_ARGS
;
1042 obj
->setSlot(JSSLOT_CALL_ARGUMENTS
, *vp
);
1044 if (fp
&& !(fp
->flags
& JSFRAME_OVERRIDE_ARGS
)) {
1047 argsobj
= js_GetArgsObject(cx
, fp
);
1050 vp
->setObject(*argsobj
);
1052 *vp
= obj
->getSlot(JSSLOT_CALL_ARGUMENTS
);
1059 i
+= CALL_CLASS_FIXED_RESERVED_SLOTS
;
1060 if (kind
== JSCPK_VAR
)
1063 JS_ASSERT(kind
== JSCPK_ARG
);
1065 ? JS_SetReservedSlot(cx
, obj
, i
, Jsvalify(*vp
))
1066 : JS_GetReservedSlot(cx
, obj
, i
, Jsvalify(vp
));
1069 if (kind
== JSCPK_ARG
) {
1072 JS_ASSERT(kind
== JSCPK_VAR
);
1073 array
= fp
->slots();
1078 GC_POKE(cx
, array
[i
]);
1087 GetCallArguments(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
1089 return CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_ARGUMENTS
);
1093 SetCallArguments(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
1095 return CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_ARGUMENTS
, true);
1099 js_GetCallArg(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
1101 return CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_ARG
);
1105 SetCallArg(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
1107 return CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_ARG
, true);
1111 GetFlatUpvar(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
1113 return CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_UPVAR
);
1117 SetFlatUpvar(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
1119 return CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_UPVAR
, true);
1123 js_GetCallVar(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
1125 return CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_VAR
);
1129 js_GetCallVarChecked(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
1131 if (!CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_VAR
))
1134 return CheckForEscapingClosure(cx
, obj
, vp
);
1138 SetCallVar(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
1140 return CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_VAR
, true);
1145 js_SetCallArg(JSContext
*cx
, JSObject
*obj
, jsid slotid
, ValueArgType arg
)
1147 Value argcopy
= ValueArgToConstRef(arg
);
1148 return CallPropertyOp(cx
, obj
, slotid
, &argcopy
, JSCPK_ARG
, true);
1150 JS_DEFINE_CALLINFO_4(extern, BOOL
, js_SetCallArg
, CONTEXT
, OBJECT
, JSID
, VALUE
, 0,
1151 nanojit::ACC_STORE_ANY
)
1154 js_SetCallVar(JSContext
*cx
, JSObject
*obj
, jsid slotid
, ValueArgType arg
)
1156 Value argcopy
= ValueArgToConstRef(arg
);
1157 return CallPropertyOp(cx
, obj
, slotid
, &argcopy
, JSCPK_VAR
, true);
1159 JS_DEFINE_CALLINFO_4(extern, BOOL
, js_SetCallVar
, CONTEXT
, OBJECT
, JSID
, VALUE
, 0,
1160 nanojit::ACC_STORE_ANY
)
1164 call_resolve(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN flags
,
1168 JSLocalKind localKind
;
1169 PropertyOp getter
, setter
;
1172 JS_ASSERT(obj
->getClass() == &js_CallClass
);
1173 JS_ASSERT(!obj
->getProto());
1175 if (!JSID_IS_ATOM(id
))
1178 const Value
&callee
= obj
->getSlot(JSSLOT_CALLEE
);
1179 if (callee
.isUndefined())
1181 fun
= GET_FUNCTION_PRIVATE(cx
, &callee
.toObject());
1184 * Check whether the id refers to a formal parameter, local variable or
1185 * the arguments special name.
1187 * We define all such names using JSDNP_DONT_PURGE to avoid an expensive
1188 * shape invalidation in js_DefineNativeProperty. If such an id happens to
1189 * shadow a global or upvar of the same name, any inner functions can
1190 * never access the outer binding. Thus it cannot invalidate any property
1191 * cache entries or derived trace guards for the outer binding. See also
1192 * comments in js_PurgeScopeChainHelper from jsobj.cpp.
1194 localKind
= js_LookupLocal(cx
, fun
, JSID_TO_ATOM(id
), &slot
);
1195 if (localKind
!= JSLOCAL_NONE
) {
1196 JS_ASSERT((uint16
) slot
== slot
);
1199 * We follow 10.2.3 of ECMA 262 v3 and make argument and variable
1200 * properties of the Call objects enumerable.
1202 attrs
= JSPROP_ENUMERATE
| JSPROP_PERMANENT
| JSPROP_SHARED
;
1203 if (localKind
== JSLOCAL_ARG
) {
1204 JS_ASSERT(slot
< fun
->nargs
);
1205 getter
= js_GetCallArg
;
1206 setter
= SetCallArg
;
1208 JSCallPropertyKind cpkind
;
1209 if (localKind
== JSLOCAL_UPVAR
) {
1210 if (!FUN_FLAT_CLOSURE(fun
))
1212 getter
= GetFlatUpvar
;
1213 setter
= SetFlatUpvar
;
1214 cpkind
= JSCPK_UPVAR
;
1216 JS_ASSERT(localKind
== JSLOCAL_VAR
|| localKind
== JSLOCAL_CONST
);
1217 JS_ASSERT(slot
< fun
->u
.i
.nvars
);
1218 getter
= js_GetCallVar
;
1219 setter
= SetCallVar
;
1221 if (localKind
== JSLOCAL_CONST
)
1222 attrs
|= JSPROP_READONLY
;
1226 * Use js_GetCallVarChecked if the local's value is a null closure.
1227 * This way we penalize performance only slightly on first use of a
1228 * null closure, not on every use.
1231 if (!CallPropertyOp(cx
, obj
, INT_TO_JSID((int16
)slot
), &v
, cpkind
))
1234 if (IsFunctionObject(v
, &funobj
) &&
1235 GET_FUNCTION_PRIVATE(cx
, funobj
)->needsWrapper()) {
1236 getter
= js_GetCallVarChecked
;
1239 if (!js_DefineNativeProperty(cx
, obj
, id
, UndefinedValue(), getter
, setter
,
1240 attrs
, JSScopeProperty::HAS_SHORTID
, (int16
) slot
,
1241 NULL
, JSDNP_DONT_PURGE
)) {
1249 * Resolve arguments so that we never store a particular Call object's
1250 * arguments object reference in a Call prototype's |arguments| slot.
1252 if (JSID_IS_ATOM(id
, cx
->runtime
->atomState
.argumentsAtom
)) {
1253 if (!js_DefineNativeProperty(cx
, obj
, id
, UndefinedValue(),
1254 GetCallArguments
, SetCallArguments
,
1255 JSPROP_PERMANENT
| JSPROP_SHARED
,
1256 0, 0, NULL
, JSDNP_DONT_PURGE
)) {
1263 /* Control flow reaches here only if id was not resolved. */
1267 JS_PUBLIC_DATA(Class
) js_CallClass
= {
1269 JSCLASS_HAS_PRIVATE
|
1270 JSCLASS_HAS_RESERVED_SLOTS(CALL_CLASS_FIXED_RESERVED_SLOTS
) |
1271 JSCLASS_NEW_RESOLVE
| JSCLASS_IS_ANONYMOUS
| JSCLASS_MARK_IS_TRACE
,
1272 PropertyStub
, PropertyStub
,
1273 PropertyStub
, PropertyStub
,
1274 call_enumerate
, (JSResolveOp
)call_resolve
,
1279 JS_CLASS_TRACE(args_or_call_trace
), NULL
1282 /* Generic function tinyids. */
1284 FUN_ARGUMENTS
= -1, /* predefined arguments local variable */
1285 FUN_LENGTH
= -2, /* number of actual args, arity if inactive */
1286 FUN_ARITY
= -3, /* number of formal parameters; desired argc */
1287 FUN_NAME
= -4, /* function name, "" if anonymous */
1288 FUN_CALLER
= -5 /* Function.prototype.caller, backward compat */
1292 fun_getProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
1294 if (!JSID_IS_INT(id
))
1297 jsint slot
= JSID_TO_INT(id
);
1300 * Loop because getter and setter can be delegated from another class,
1301 * but loop only for FUN_LENGTH because we must pretend that f.length
1302 * is in each function instance f, per ECMA-262, instead of only in the
1303 * Function.prototype object (we use JSPROP_PERMANENT with JSPROP_SHARED
1304 * to make it appear so).
1306 * This code couples tightly to the attributes for lazy_function_props[]
1307 * initializers above, and to js_SetProperty and js_HasOwnProperty.
1309 * It's important to allow delegating objects, even though they inherit
1310 * this getter (fun_getProperty), to override arguments, arity, caller,
1311 * and name. If we didn't return early for slot != FUN_LENGTH, we would
1312 * clobber *vp with the native property value, instead of letting script
1313 * override that value in delegating objects.
1315 * Note how that clobbering is what simulates JSPROP_READONLY for all of
1316 * the non-standard properties when the directly addressed object (obj)
1317 * is a function object (i.e., when this loop does not iterate).
1320 while (!(fun
= (JSFunction
*)
1321 GetInstancePrivate(cx
, obj
, &js_FunctionClass
, NULL
))) {
1322 if (slot
!= FUN_LENGTH
)
1324 obj
= obj
->getProto();
1329 /* Find fun's top-most activation record. */
1331 for (fp
= js_GetTopStackFrame(cx
);
1332 fp
&& (fp
->fun
!= fun
|| (fp
->flags
& JSFRAME_SPECIAL
));
1339 /* Warn if strict about f.arguments or equivalent unqualified uses. */
1340 if (!JS_ReportErrorFlagsAndNumber(cx
,
1341 JSREPORT_WARNING
| JSREPORT_STRICT
,
1342 js_GetErrorMessage
, NULL
,
1343 JSMSG_DEPRECATED_USAGE
,
1344 js_arguments_str
)) {
1348 if (!js_GetArgsValue(cx
, fp
, vp
))
1357 vp
->setInt32(fun
->nargs
);
1361 vp
->setString(fun
->atom
? ATOM_TO_STRING(fun
->atom
)
1362 : cx
->runtime
->emptyString
);
1366 if (fp
&& fp
->down
&& fp
->down
->fun
) {
1367 JSFunction
*caller
= fp
->down
->fun
;
1369 * See equivalent condition in args_getProperty for ARGS_CALLEE,
1370 * but here we do not want to throw, since this escape can happen
1371 * via foo.caller alone, without any debugger or indirect eval. And
1372 * it seems foo.caller is still used on the Web.
1374 if (caller
->needsWrapper()) {
1375 JSObject
*wrapper
= WrapEscapingClosure(cx
, fp
->down
, FUN_OBJECT(caller
), caller
);
1378 vp
->setObject(*wrapper
);
1382 JS_ASSERT(fp
->down
->argv
);
1383 *vp
= fp
->down
->calleeValue();
1388 /* Censor the caller if it is from another compartment. */
1389 if (vp
->isObject()) {
1390 if (vp
->toObject().getCompartment(cx
) != cx
->compartment
)
1396 /* XXX fun[0] and fun.arguments[0] are equivalent. */
1397 if (fp
&& fp
->fun
&& (uintN
)slot
< fp
->fun
->nargs
)
1398 *vp
= fp
->argv
[slot
];
1405 struct LazyFunctionProp
{
1411 /* NB: no sentinel at the end -- use JS_ARRAY_LENGTH to bound loops. */
1412 static LazyFunctionProp lazy_function_props
[] = {
1413 {ATOM_OFFSET(arguments
), FUN_ARGUMENTS
, JSPROP_PERMANENT
},
1414 {ATOM_OFFSET(arity
), FUN_ARITY
, JSPROP_PERMANENT
},
1415 {ATOM_OFFSET(caller
), FUN_CALLER
, JSPROP_PERMANENT
},
1416 {ATOM_OFFSET(name
), FUN_NAME
, JSPROP_PERMANENT
},
1420 fun_enumerate(JSContext
*cx
, JSObject
*obj
)
1422 JS_ASSERT(obj
->isFunction());
1425 jsid id
= ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
);
1426 if (!JS_LookupPropertyById(cx
, obj
, id
, &v
))
1428 id
= ATOM_TO_JSID(cx
->runtime
->atomState
.lengthAtom
);
1429 if (!JS_LookupPropertyById(cx
, obj
, id
, &v
))
1432 for (uintN i
= 0; i
< JS_ARRAY_LENGTH(lazy_function_props
); i
++) {
1433 LazyFunctionProp
&lfp
= lazy_function_props
[i
];
1434 id
= ATOM_TO_JSID(OFFSET_TO_ATOM(cx
->runtime
, lfp
.atomOffset
));
1435 if (!JS_LookupPropertyById(cx
, obj
, id
, &v
))
1443 fun_resolve(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN flags
,
1446 if (!JSID_IS_ATOM(id
))
1449 JSFunction
*fun
= GET_FUNCTION_PRIVATE(cx
, obj
);
1452 * No need to reflect fun.prototype in 'fun.prototype = ... '. Assert that
1453 * fun is not a compiler-created function object, which must never leak to
1454 * script or embedding code and then be mutated.
1456 if ((flags
& JSRESOLVE_ASSIGNING
) && !JSID_IS_ATOM(id
, cx
->runtime
->atomState
.lengthAtom
)) {
1457 JS_ASSERT(!IsInternalFunctionObject(obj
));
1462 * Ok, check whether id is 'prototype' and bootstrap the function object's
1463 * prototype property.
1465 JSAtom
*atom
= cx
->runtime
->atomState
.classPrototypeAtom
;
1466 if (id
== ATOM_TO_JSID(atom
)) {
1467 JS_ASSERT(!IsInternalFunctionObject(obj
));
1470 * Beware of the wacky case of a user function named Object -- trying
1471 * to find a prototype for that will recur back here _ad perniciem_.
1473 if (fun
->atom
== CLASS_ATOM(cx
, Object
))
1477 * Make the prototype object an instance of Object with the same parent
1478 * as the function object itself.
1480 JSObject
*parent
= obj
->getParent();
1482 if (!js_GetClassPrototype(cx
, parent
, JSProto_Object
, &proto
))
1484 proto
= NewNativeClassInstance(cx
, &js_ObjectClass
, proto
, parent
);
1489 * ECMA (15.3.5.2) says that constructor.prototype is DontDelete for
1490 * user-defined functions, but DontEnum | ReadOnly | DontDelete for
1491 * native "system" constructors such as Object or Function. So lazily
1492 * set the former here in fun_resolve, but eagerly define the latter
1493 * in JS_InitClass, with the right attributes.
1495 if (!js_SetClassPrototype(cx
, obj
, proto
, JSPROP_PERMANENT
))
1502 atom
= cx
->runtime
->atomState
.lengthAtom
;
1503 if (id
== ATOM_TO_JSID(atom
)) {
1504 JS_ASSERT(!IsInternalFunctionObject(obj
));
1505 if (!js_DefineNativeProperty(cx
, obj
, ATOM_TO_JSID(atom
), Int32Value(fun
->nargs
),
1506 PropertyStub
, PropertyStub
,
1507 JSPROP_PERMANENT
| JSPROP_READONLY
, 0, 0, NULL
)) {
1514 for (uintN i
= 0; i
< JS_ARRAY_LENGTH(lazy_function_props
); i
++) {
1515 LazyFunctionProp
*lfp
= &lazy_function_props
[i
];
1517 atom
= OFFSET_TO_ATOM(cx
->runtime
, lfp
->atomOffset
);
1518 if (id
== ATOM_TO_JSID(atom
)) {
1519 JS_ASSERT(!IsInternalFunctionObject(obj
));
1521 if (!js_DefineNativeProperty(cx
, obj
,
1522 ATOM_TO_JSID(atom
), UndefinedValue(),
1523 fun_getProperty
, PropertyStub
,
1524 lfp
->attrs
, JSScopeProperty::HAS_SHORTID
,
1525 lfp
->tinyid
, NULL
)) {
1538 /* XXX store parent and proto, if defined */
1540 js_XDRFunctionObject(JSXDRState
*xdr
, JSObject
**objp
)
1544 uint32 firstword
; /* flag telling whether fun->atom is non-null,
1545 plus for fun->u.i.skipmin, fun->u.i.wrapper,
1546 and 14 bits reserved for future use */
1547 uintN nargs
, nvars
, nupvars
, n
;
1548 uint32 localsword
; /* word for argument and variable counts */
1549 uint32 flagsword
; /* word for fun->u.i.nupvars and fun->flags */
1552 if (xdr
->mode
== JSXDR_ENCODE
) {
1553 fun
= GET_FUNCTION_PRIVATE(cx
, *objp
);
1554 if (!FUN_INTERPRETED(fun
)) {
1555 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1556 JSMSG_NOT_SCRIPTED_FUNCTION
,
1557 JS_GetFunctionName(fun
));
1560 if (fun
->u
.i
.wrapper
) {
1561 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1562 JSMSG_XDR_CLOSURE_WRAPPER
,
1563 JS_GetFunctionName(fun
));
1566 JS_ASSERT((fun
->u
.i
.wrapper
& ~1U) == 0);
1567 firstword
= (fun
->u
.i
.skipmin
<< 2) | (fun
->u
.i
.wrapper
<< 1) | !!fun
->atom
;
1569 nvars
= fun
->u
.i
.nvars
;
1570 nupvars
= fun
->u
.i
.nupvars
;
1571 localsword
= (nargs
<< 16) | nvars
;
1572 flagsword
= (nupvars
<< 16) | fun
->flags
;
1574 fun
= js_NewFunction(cx
, NULL
, NULL
, 0, JSFUN_INTERPRETED
, NULL
, NULL
);
1577 FUN_OBJECT(fun
)->clearParent();
1578 FUN_OBJECT(fun
)->clearProto();
1580 nvars
= nargs
= nupvars
= 0; /* quell GCC uninitialized warning */
1584 AutoObjectRooter
tvr(cx
, FUN_OBJECT(fun
));
1586 if (!JS_XDRUint32(xdr
, &firstword
))
1588 if ((firstword
& 1U) && !js_XDRAtom(xdr
, &fun
->atom
))
1590 if (!JS_XDRUint32(xdr
, &localsword
) ||
1591 !JS_XDRUint32(xdr
, &flagsword
)) {
1595 if (xdr
->mode
== JSXDR_DECODE
) {
1596 nargs
= localsword
>> 16;
1597 nvars
= uint16(localsword
);
1598 JS_ASSERT((flagsword
& JSFUN_KINDMASK
) >= JSFUN_INTERPRETED
);
1599 nupvars
= flagsword
>> 16;
1600 fun
->flags
= uint16(flagsword
);
1601 fun
->u
.i
.skipmin
= uint16(firstword
>> 2);
1602 fun
->u
.i
.wrapper
= JSPackedBool((firstword
>> 1) & 1);
1605 /* do arguments and local vars */
1606 n
= nargs
+ nvars
+ nupvars
;
1614 JSLocalKind localKind
;
1617 mark
= JS_ARENA_MARK(&xdr
->cx
->tempPool
);
1620 * From this point the control must flow via the label release_mark.
1622 * To xdr the names we prefix the names with a bitmap descriptor and
1623 * then xdr the names as strings. For argument names (indexes below
1624 * nargs) the corresponding bit in the bitmap is unset when the name
1625 * is null. Such null names are not encoded or decoded. For variable
1626 * names (indexes starting from nargs) bitmap's bit is set when the
1627 * name is declared as const, not as ordinary var.
1629 MUST_FLOW_THROUGH("release_mark");
1630 bitmapLength
= JS_HOWMANY(n
, JS_BITS_PER_UINT32
);
1631 JS_ARENA_ALLOCATE_CAST(bitmap
, uint32
*, &xdr
->cx
->tempPool
,
1632 bitmapLength
* sizeof *bitmap
);
1634 js_ReportOutOfScriptQuota(xdr
->cx
);
1638 if (xdr
->mode
== JSXDR_ENCODE
) {
1639 names
= js_GetLocalNameArray(xdr
->cx
, fun
, &xdr
->cx
->tempPool
);
1644 PodZero(bitmap
, bitmapLength
);
1645 for (i
= 0; i
!= n
; ++i
) {
1647 ? JS_LOCAL_NAME_TO_ATOM(names
[i
]) != NULL
1648 : JS_LOCAL_NAME_IS_CONST(names
[i
])) {
1649 bitmap
[i
>> JS_BITS_PER_UINT32_LOG2
] |=
1650 JS_BIT(i
& (JS_BITS_PER_UINT32
- 1));
1656 names
= NULL
; /* quell GCC uninitialized warning */
1659 for (i
= 0; i
!= bitmapLength
; ++i
) {
1660 ok
= !!JS_XDRUint32(xdr
, &bitmap
[i
]);
1664 for (i
= 0; i
!= n
; ++i
) {
1666 !(bitmap
[i
>> JS_BITS_PER_UINT32_LOG2
] &
1667 JS_BIT(i
& (JS_BITS_PER_UINT32
- 1)))) {
1668 if (xdr
->mode
== JSXDR_DECODE
) {
1669 ok
= !!js_AddLocal(xdr
->cx
, fun
, NULL
, JSLOCAL_ARG
);
1673 JS_ASSERT(!JS_LOCAL_NAME_TO_ATOM(names
[i
]));
1677 if (xdr
->mode
== JSXDR_ENCODE
)
1678 name
= JS_LOCAL_NAME_TO_ATOM(names
[i
]);
1679 ok
= !!js_XDRAtom(xdr
, &name
);
1682 if (xdr
->mode
== JSXDR_DECODE
) {
1683 localKind
= (i
< nargs
)
1685 : (i
< nargs
+ nvars
)
1686 ? (bitmap
[i
>> JS_BITS_PER_UINT32_LOG2
] &
1687 JS_BIT(i
& (JS_BITS_PER_UINT32
- 1))
1691 ok
= !!js_AddLocal(xdr
->cx
, fun
, name
, localKind
);
1698 JS_ARENA_RELEASE(&xdr
->cx
->tempPool
, mark
);
1702 if (xdr
->mode
== JSXDR_DECODE
)
1703 js_FreezeLocalNames(cx
, fun
);
1706 if (!js_XDRScript(xdr
, &fun
->u
.i
.script
, false, NULL
))
1709 if (xdr
->mode
== JSXDR_DECODE
) {
1710 *objp
= FUN_OBJECT(fun
);
1711 if (fun
->u
.i
.script
!= JSScript::emptyScript()) {
1712 #ifdef CHECK_SCRIPT_OWNER
1713 fun
->u
.i
.script
->owner
= NULL
;
1715 js_CallNewScriptHook(cx
, fun
->u
.i
.script
, fun
);
1722 #else /* !JS_HAS_XDR */
1724 #define js_XDRFunctionObject NULL
1726 #endif /* !JS_HAS_XDR */
1729 * [[HasInstance]] internal method for Function objects: fetch the .prototype
1730 * property of its 'this' parameter, and walks the prototype chain of v (only
1731 * if v is an object) returning true if .prototype is found.
1734 fun_hasInstance(JSContext
*cx
, JSObject
*obj
, const Value
*v
, JSBool
*bp
)
1736 jsid id
= ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
);
1738 if (!obj
->getProperty(cx
, id
, &pval
))
1741 if (pval
.isPrimitive()) {
1743 * Throw a runtime error if instanceof is called on a function that
1744 * has a non-object as its .prototype value.
1746 js_ReportValueError(cx
, JSMSG_BAD_PROTOTYPE
, -1, ObjectValue(*obj
), NULL
);
1750 *bp
= js_IsDelegate(cx
, &pval
.toObject(), *v
);
1755 TraceLocalNames(JSTracer
*trc
, JSFunction
*fun
);
1758 DestroyLocalNames(JSContext
*cx
, JSFunction
*fun
);
1761 fun_trace(JSTracer
*trc
, JSObject
*obj
)
1763 /* A newborn function object may have a not yet initialized private slot. */
1764 JSFunction
*fun
= (JSFunction
*) obj
->getPrivate();
1768 if (FUN_OBJECT(fun
) != obj
) {
1769 /* obj is cloned function object, trace the original. */
1770 JS_CALL_TRACER(trc
, FUN_OBJECT(fun
), JSTRACE_OBJECT
, "private");
1774 JS_CALL_STRING_TRACER(trc
, ATOM_TO_STRING(fun
->atom
), "atom");
1775 if (FUN_INTERPRETED(fun
)) {
1776 if (fun
->u
.i
.script
)
1777 js_TraceScript(trc
, fun
->u
.i
.script
);
1778 TraceLocalNames(trc
, fun
);
1783 fun_finalize(JSContext
*cx
, JSObject
*obj
)
1785 /* Ignore newborn and cloned function objects. */
1786 JSFunction
*fun
= (JSFunction
*) obj
->getPrivate();
1787 if (!fun
|| FUN_OBJECT(fun
) != obj
)
1791 * Null-check of u.i.script is required since the parser sets interpreted
1794 if (FUN_INTERPRETED(fun
)) {
1795 if (fun
->u
.i
.script
)
1796 js_DestroyScript(cx
, fun
->u
.i
.script
);
1797 DestroyLocalNames(cx
, fun
);
1802 JSFunction::sharpSlotBase(JSContext
*cx
)
1804 #if JS_HAS_SHARP_VARS
1805 JSAtom
*name
= js_Atomize(cx
, "#array", 6, 0);
1807 uintN index
= uintN(-1);
1811 js_LookupLocal(cx
, this, name
, &index
);
1812 JS_ASSERT(kind
== JSLOCAL_VAR
);
1820 JSFunction::countInterpretedReservedSlots() const
1822 JS_ASSERT(FUN_INTERPRETED(this));
1824 return (u
.i
.nupvars
== 0) ? 0 : u
.i
.script
->upvars()->length
;
1828 * Reserve two slots in all function objects for XPConnect. Note that this
1829 * does not bloat every instance, only those on which reserved slots are set,
1830 * and those on which ad-hoc properties are defined.
1832 JS_PUBLIC_DATA(Class
) js_FunctionClass
= {
1834 JSCLASS_HAS_PRIVATE
| JSCLASS_NEW_RESOLVE
| JSCLASS_HAS_RESERVED_SLOTS(2) |
1835 JSCLASS_MARK_IS_TRACE
| JSCLASS_HAS_CACHED_PROTO(JSProto_Function
),
1836 PropertyStub
, PropertyStub
,
1837 PropertyStub
, PropertyStub
,
1838 fun_enumerate
, (JSResolveOp
)fun_resolve
,
1839 ConvertStub
, fun_finalize
,
1842 js_XDRFunctionObject
, fun_hasInstance
,
1843 JS_CLASS_TRACE(fun_trace
), NULL
1849 fun_toStringHelper(JSContext
*cx
, JSObject
*obj
, uintN indent
)
1851 if (!obj
->isFunction()) {
1852 if (obj
->isFunctionProxy())
1853 return JSProxy::fun_toString(cx
, obj
, indent
);
1854 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1855 JSMSG_INCOMPATIBLE_PROTO
,
1856 js_Function_str
, js_toString_str
,
1861 JSFunction
*fun
= GET_FUNCTION_PRIVATE(cx
, obj
);
1864 return JS_DecompileFunction(cx
, fun
, indent
);
1867 } /* namespace js */
1870 fun_toString(JSContext
*cx
, uintN argc
, Value
*vp
)
1872 JS_ASSERT(IsFunctionObject(vp
[0]));
1873 uint32_t indent
= 0;
1875 if (argc
!= 0 && !ValueToECMAUint32(cx
, vp
[2], &indent
))
1878 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1882 JSString
*str
= fun_toStringHelper(cx
, obj
, indent
);
1892 fun_toSource(JSContext
*cx
, uintN argc
, Value
*vp
)
1894 JS_ASSERT(IsFunctionObject(vp
[0]));
1896 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1900 JSString
*str
= fun_toStringHelper(cx
, obj
, JS_DONT_PRETTY_PRINT
);
1910 js_fun_call(JSContext
*cx
, uintN argc
, Value
*vp
)
1914 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1919 if (!js_IsCallable(fval
)) {
1920 JSString
*str
= js_ValueToString(cx
, fval
);
1922 const char *bytes
= js_GetStringBytes(cx
, str
);
1925 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1926 JSMSG_INCOMPATIBLE_PROTO
,
1927 js_Function_str
, js_call_str
,
1934 Value
*argv
= vp
+ 2;
1936 /* Call fun with its global object as the 'this' param if no args. */
1939 /* Otherwise convert the first arg to 'this' and skip over it. */
1940 if (argv
[0].isObject())
1941 obj
= &argv
[0].toObject();
1942 else if (!js_ValueToObjectOrNull(cx
, argv
[0], &obj
))
1948 /* Allocate stack space for fval, obj, and the args. */
1949 InvokeArgsGuard args
;
1950 if (!cx
->stack().pushInvokeArgs(cx
, argc
, args
))
1953 /* Push fval, obj, and the args. */
1954 args
.getvp()[0] = fval
;
1955 args
.getvp()[1] = ObjectOrNullValue(obj
);
1956 memcpy(args
.getvp() + 2, argv
, argc
* sizeof *argv
);
1958 bool ok
= Invoke(cx
, args
, 0);
1959 *vp
= *args
.getvp();
1964 js_fun_apply(JSContext
*cx
, uintN argc
, Value
*vp
)
1967 /* Will get globalObject as 'this' and no other arguments. */
1968 return js_fun_call(cx
, argc
, vp
);
1973 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1978 if (!js_IsCallable(fval
)) {
1979 JSString
*str
= js_ValueToString(cx
, fval
);
1981 const char *bytes
= js_GetStringBytes(cx
, str
);
1984 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1985 JSMSG_INCOMPATIBLE_PROTO
,
1986 js_Function_str
, js_apply_str
,
1993 /* Quell GCC overwarnings. */
1994 JSObject
*aobj
= NULL
;
1998 /* If the 2nd arg is null or void, call the function with 0 args. */
1999 if (vp
[3].isNullOrUndefined()) {
2002 /* The second arg must be an array (or arguments object). */
2003 JSBool arraylike
= JS_FALSE
;
2004 if (vp
[3].isObject()) {
2005 aobj
= &vp
[3].toObject();
2006 if (!js_IsArrayLike(cx
, aobj
, &arraylike
, &length
))
2010 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
2011 JSMSG_BAD_APPLY_ARGS
, js_apply_str
);
2017 /* Convert the first arg to 'this' and skip over it. */
2018 if (vp
[2].isObject())
2019 obj
= &vp
[2].toObject();
2020 else if (!js_ValueToObjectOrNull(cx
, vp
[2], &obj
))
2023 /* Allocate stack space for fval, obj, and the args. */
2024 argc
= (uintN
)JS_MIN(length
, JS_ARGS_LENGTH_MAX
);
2026 InvokeArgsGuard args
;
2027 if (!cx
->stack().pushInvokeArgs(cx
, argc
, args
))
2030 /* Push fval, obj, and aobj's elements as args. */
2031 Value
*sp
= args
.getvp();
2033 *sp
++ = ObjectOrNullValue(obj
);
2034 if (aobj
&& aobj
->isArguments() && !aobj
->isArgsLengthOverridden()) {
2036 * Two cases, two loops: note how in the case of an active stack frame
2037 * backing aobj, even though we copy from fp->argv, we still must check
2038 * aobj->getArgsElement(i) for a hole, to handle a delete on the
2039 * corresponding arguments element. See args_delProperty.
2041 JSStackFrame
*fp
= (JSStackFrame
*) aobj
->getPrivate();
2043 memcpy(sp
, fp
->argv
, argc
* sizeof(Value
));
2044 for (uintN i
= 0; i
< argc
; i
++) {
2045 if (aobj
->getArgsElement(i
).isMagic(JS_ARGS_HOLE
)) // suppress deleted element
2046 sp
[i
].setUndefined();
2049 for (uintN i
= 0; i
< argc
; i
++) {
2050 sp
[i
] = aobj
->getArgsElement(i
);
2051 if (sp
[i
].isMagic(JS_ARGS_HOLE
))
2052 sp
[i
].setUndefined();
2056 for (uintN i
= 0; i
< argc
; i
++) {
2057 if (!aobj
->getProperty(cx
, INT_TO_JSID(jsint(i
)), sp
))
2063 bool ok
= Invoke(cx
, args
, 0);
2064 *vp
= *args
.getvp();
2068 static JSFunctionSpec function_methods
[] = {
2070 JS_FN(js_toSource_str
, fun_toSource
, 0,0),
2072 JS_FN(js_toString_str
, fun_toString
, 0,0),
2073 JS_FN(js_apply_str
, js_fun_apply
, 2,0),
2074 JS_FN(js_call_str
, js_fun_call
, 1,0),
2079 Function(JSContext
*cx
, JSObject
*obj
, uintN argc
, Value
*argv
, Value
*rval
)
2083 JSStackFrame
*fp
, *caller
;
2086 const char *filename
;
2088 JSString
*str
, *arg
;
2090 JSPrincipals
*principals
;
2091 jschar
*collected_args
, *cp
;
2093 size_t arg_length
, args_length
, old_args_length
;
2096 if (!JS_IsConstructing(cx
)) {
2097 obj
= NewObject(cx
, &js_FunctionClass
, NULL
, NULL
);
2100 rval
->setObject(*obj
);
2103 * The constructor is called before the private slot is initialized so
2104 * we must use getPrivate, not GET_FUNCTION_PRIVATE here.
2106 if (obj
->getPrivate())
2111 * NB: (new Function) is not lexically closed by its caller, it's just an
2112 * anonymous function in the top-level scope that its constructor inhabits.
2113 * Thus 'var x = 42; f = new Function("return x"); print(f())' prints 42,
2114 * and so would a call to f from another top-level's script or function.
2116 * In older versions, before call objects, a new Function was adopted by
2117 * its running context's globalObject, which might be different from the
2118 * top-level reachable from scopeChain (in HTML frames, e.g.).
2120 parent
= argv
[-2].toObject().getParent();
2122 fun
= js_NewFunction(cx
, obj
, NULL
, 0, JSFUN_LAMBDA
| JSFUN_INTERPRETED
,
2123 parent
, cx
->runtime
->atomState
.anonymousAtom
);
2129 * Function is static and not called directly by other functions in this
2130 * file, therefore it is callable only as a native function by js_Invoke.
2131 * Find the scripted caller, possibly skipping other native frames such as
2132 * are built for Function.prototype.call or .apply activations that invoke
2133 * Function indirectly from a script.
2135 fp
= js_GetTopStackFrame(cx
);
2136 JS_ASSERT(!fp
->script
&& fp
->fun
&& fp
->fun
->u
.n
.native
== Function
);
2137 caller
= js_GetScriptedCaller(cx
, fp
);
2139 principals
= JS_EvalFramePrincipals(cx
, fp
, caller
);
2140 filename
= js_ComputeFilename(cx
, caller
, principals
, &lineno
);
2147 /* Belt-and-braces: check that the caller has access to parent. */
2148 if (!js_CheckPrincipalsAccess(cx
, parent
, principals
,
2149 CLASS_ATOM(cx
, Function
))) {
2154 * CSP check: whether new Function() is allowed at all.
2155 * Report errors via CSP is done in the script security manager.
2156 * js_CheckContentSecurityPolicy is defined in jsobj.cpp
2158 if (!js_CheckContentSecurityPolicy(cx
)) {
2159 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
2160 JSMSG_CSP_BLOCKED_FUNCTION
);
2164 n
= argc
? argc
- 1 : 0;
2166 enum { OK
, BAD
, BAD_FORMAL
} state
;
2169 * Collect the function-argument arguments into one string, separated
2170 * by commas, then make a tokenstream from that string, and scan it to
2171 * get the arguments. We need to throw the full scanner at the
2172 * problem, because the argument string can legitimately contain
2173 * comments and linefeeds. XXX It might be better to concatenate
2174 * everything up into a function definition and pass it to the
2175 * compiler, but doing it this way is less of a delta from the old
2176 * code. See ECMA 15.3.2.1.
2180 for (i
= 0; i
< n
; i
++) {
2181 /* Collect the lengths for all the function-argument arguments. */
2182 arg
= js_ValueToString(cx
, argv
[i
]);
2185 argv
[i
].setString(arg
);
2188 * Check for overflow. The < test works because the maximum
2189 * JSString length fits in 2 fewer bits than size_t has.
2191 old_args_length
= args_length
;
2192 args_length
= old_args_length
+ arg
->length();
2193 if (args_length
< old_args_length
) {
2194 js_ReportAllocationOverflow(cx
);
2199 /* Add 1 for each joining comma and check for overflow (two ways). */
2200 old_args_length
= args_length
;
2201 args_length
= old_args_length
+ n
- 1;
2202 if (args_length
< old_args_length
||
2203 args_length
>= ~(size_t)0 / sizeof(jschar
)) {
2204 js_ReportAllocationOverflow(cx
);
2209 * Allocate a string to hold the concatenated arguments, including room
2210 * for a terminating 0. Mark cx->tempPool for later release, to free
2211 * collected_args and its tokenstream in one swoop.
2213 mark
= JS_ARENA_MARK(&cx
->tempPool
);
2214 JS_ARENA_ALLOCATE_CAST(cp
, jschar
*, &cx
->tempPool
,
2215 (args_length
+1) * sizeof(jschar
));
2217 js_ReportOutOfScriptQuota(cx
);
2220 collected_args
= cp
;
2223 * Concatenate the arguments into the new string, separated by commas.
2225 for (i
= 0; i
< n
; i
++) {
2226 arg
= argv
[i
].toString();
2227 arg_length
= arg
->length();
2228 (void) js_strncpy(cp
, arg
->chars(), arg_length
);
2231 /* Add separating comma or terminating 0. */
2232 *cp
++ = (i
+ 1 < n
) ? ',' : 0;
2235 /* Initialize a tokenstream that reads from the given string. */
2236 if (!ts
.init(collected_args
, args_length
, NULL
, filename
, lineno
)) {
2237 JS_ARENA_RELEASE(&cx
->tempPool
, mark
);
2241 /* The argument string may be empty or contain no tokens. */
2243 if (tt
!= TOK_EOF
) {
2246 * Check that it's a name. This also implicitly guards against
2247 * TOK_ERROR, which was already reported.
2253 * Get the atom corresponding to the name from the token
2254 * stream; we're assured at this point that it's a valid
2257 atom
= ts
.currentToken().t_atom
;
2259 /* Check for a duplicate parameter name. */
2260 if (js_LookupLocal(cx
, fun
, atom
, NULL
) != JSLOCAL_NONE
) {
2263 name
= js_AtomToPrintableString(cx
, atom
);
2264 ok
= name
&& ReportCompileErrorNumber(cx
, &ts
, NULL
,
2265 JSREPORT_WARNING
| JSREPORT_STRICT
,
2266 JSMSG_DUPLICATE_FORMAL
, name
);
2270 if (!js_AddLocal(cx
, fun
, atom
, JSLOCAL_ARG
))
2274 * Get the next token. Stop on end of stream. Otherwise
2275 * insist on a comma, get another name, and iterate.
2280 if (tt
!= TOK_COMMA
)
2288 if (state
== BAD_FORMAL
&& !ts
.isError()) {
2290 * Report "malformed formal parameter" iff no illegal char or
2291 * similar scanner error was already reported.
2293 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
2297 JS_ARENA_RELEASE(&cx
->tempPool
, mark
);
2303 str
= js_ValueToString(cx
, argv
[argc
-1]);
2306 argv
[argc
-1].setString(str
);
2308 str
= cx
->runtime
->emptyString
;
2311 return Compiler::compileFunctionBody(cx
, fun
, principals
,
2312 str
->chars(), str
->length(),
2317 js_InitFunctionClass(JSContext
*cx
, JSObject
*obj
)
2322 proto
= js_InitClass(cx
, obj
, NULL
, &js_FunctionClass
, Function
, 1,
2323 NULL
, function_methods
, NULL
, NULL
);
2326 fun
= js_NewFunction(cx
, proto
, NULL
, 0, JSFUN_INTERPRETED
, obj
, NULL
);
2329 fun
->u
.i
.script
= JSScript::emptyScript();
2334 js_NewFunction(JSContext
*cx
, JSObject
*funobj
, Native native
, uintN nargs
,
2335 uintN flags
, JSObject
*parent
, JSAtom
*atom
)
2340 JS_ASSERT(funobj
->isFunction());
2341 funobj
->setParent(parent
);
2343 funobj
= NewObject(cx
, &js_FunctionClass
, NULL
, parent
);
2347 JS_ASSERT(!funobj
->getPrivate());
2348 fun
= (JSFunction
*) funobj
;
2350 /* Initialize all function members. */
2351 fun
->nargs
= uint16(nargs
);
2352 fun
->flags
= flags
& (JSFUN_FLAGS_MASK
| JSFUN_KINDMASK
| JSFUN_TRCINFO
);
2353 if ((flags
& JSFUN_KINDMASK
) >= JSFUN_INTERPRETED
) {
2355 JS_ASSERT(nargs
== 0);
2357 fun
->u
.i
.nupvars
= 0;
2358 fun
->u
.i
.skipmin
= 0;
2359 fun
->u
.i
.wrapper
= false;
2360 fun
->u
.i
.script
= NULL
;
2362 fun
->u
.i
.names
.taggedAtom
= 0;
2367 fun
->u
.n
.clasp
= NULL
;
2368 if (flags
& JSFUN_TRCINFO
) {
2370 JSNativeTraceInfo
*trcinfo
=
2371 JS_FUNC_TO_DATA_PTR(JSNativeTraceInfo
*, native
);
2372 fun
->u
.n
.native
= (js::Native
) trcinfo
->native
;
2373 fun
->u
.n
.trcinfo
= trcinfo
;
2375 fun
->u
.n
.trcinfo
= NULL
;
2378 fun
->u
.n
.native
= native
;
2379 fun
->u
.n
.trcinfo
= NULL
;
2381 JS_ASSERT(fun
->u
.n
.native
);
2385 /* Set private to self to indicate non-cloned fully initialized function. */
2386 FUN_OBJECT(fun
)->setPrivate(fun
);
2390 JSObject
* JS_FASTCALL
2391 js_CloneFunctionObject(JSContext
*cx
, JSFunction
*fun
, JSObject
*parent
,
2398 * The cloned function object does not need the extra JSFunction members
2399 * beyond JSObject as it points to fun via the private slot.
2401 JSObject
*clone
= NewNativeClassInstance(cx
, &js_FunctionClass
, proto
, parent
);
2404 clone
->setPrivate(fun
);
2409 JS_DEFINE_CALLINFO_4(extern, OBJECT
, js_CloneFunctionObject
, CONTEXT
, FUNCTION
, OBJECT
, OBJECT
, 0,
2410 nanojit::ACC_STORE_ANY
)
2414 * Create a new flat closure, but don't initialize the imported upvar
2415 * values. The tracer calls this function and then initializes the upvar
2418 JSObject
* JS_FASTCALL
2419 js_AllocFlatClosure(JSContext
*cx
, JSFunction
*fun
, JSObject
*scopeChain
)
2421 JS_ASSERT(FUN_FLAT_CLOSURE(fun
));
2422 JS_ASSERT((fun
->u
.i
.script
->upvarsOffset
2423 ? fun
->u
.i
.script
->upvars()->length
2424 : 0) == fun
->u
.i
.nupvars
);
2426 JSObject
*closure
= CloneFunctionObject(cx
, fun
, scopeChain
);
2430 uint32 nslots
= fun
->countInterpretedReservedSlots();
2433 if (!js_EnsureReservedSlots(cx
, closure
, nslots
))
2439 JS_DEFINE_CALLINFO_3(extern, OBJECT
, js_AllocFlatClosure
,
2440 CONTEXT
, FUNCTION
, OBJECT
, 0, nanojit::ACC_STORE_ANY
)
2442 JS_REQUIRES_STACK JSObject
*
2443 js_NewFlatClosure(JSContext
*cx
, JSFunction
*fun
)
2446 * Flat closures can be partial, they may need to search enclosing scope
2447 * objects via JSOP_NAME, etc.
2449 JSObject
*scopeChain
= js_GetScopeChain(cx
, cx
->fp
);
2453 JSObject
*closure
= js_AllocFlatClosure(cx
, fun
, scopeChain
);
2454 if (!closure
|| fun
->u
.i
.nupvars
== 0)
2457 JSUpvarArray
*uva
= fun
->u
.i
.script
->upvars();
2458 JS_ASSERT(uva
->length
<= closure
->dslots
[-1].toPrivateUint32());
2460 uintN level
= fun
->u
.i
.script
->staticLevel
;
2461 for (uint32 i
= 0, n
= uva
->length
; i
< n
; i
++)
2462 closure
->dslots
[i
] = GetUpvar(cx
, level
, uva
->vector
[i
]);
2468 js_NewDebuggableFlatClosure(JSContext
*cx
, JSFunction
*fun
)
2470 JS_ASSERT(cx
->fp
->fun
->flags
& JSFUN_HEAVYWEIGHT
);
2471 JS_ASSERT(!cx
->fp
->fun
->optimizedClosure());
2472 JS_ASSERT(FUN_FLAT_CLOSURE(fun
));
2474 return WrapEscapingClosure(cx
, cx
->fp
, FUN_OBJECT(fun
), fun
);
2478 js_DefineFunction(JSContext
*cx
, JSObject
*obj
, JSAtom
*atom
, Native native
,
2479 uintN nargs
, uintN attrs
)
2484 if (attrs
& JSFUN_STUB_GSOPS
) {
2486 * JSFUN_STUB_GSOPS is a request flag only, not stored in fun->flags or
2487 * the defined property's attributes. This allows us to encode another,
2488 * internal flag using the same bit, JSFUN_EXPR_CLOSURE -- see jsfun.h
2491 attrs
&= ~JSFUN_STUB_GSOPS
;
2492 gsop
= PropertyStub
;
2496 fun
= js_NewFunction(cx
, NULL
, native
, nargs
, attrs
, obj
, atom
);
2499 if (!obj
->defineProperty(cx
, ATOM_TO_JSID(atom
), ObjectValue(*fun
),
2500 gsop
, gsop
, attrs
& ~JSFUN_FLAGS_MASK
)) {
2506 #if (JSV2F_CONSTRUCT & JSV2F_SEARCH_STACK)
2507 # error "JSINVOKE_CONSTRUCT and JSV2F_SEARCH_STACK are not disjoint!"
2511 js_ValueToFunction(JSContext
*cx
, const Value
*vp
, uintN flags
)
2514 if (!IsFunctionObject(*vp
, &funobj
)) {
2515 js_ReportIsNotFunction(cx
, vp
, flags
);
2518 return GET_FUNCTION_PRIVATE(cx
, funobj
);
2522 js_ValueToFunctionObject(JSContext
*cx
, Value
*vp
, uintN flags
)
2525 JSStackFrame
*caller
;
2526 JSPrincipals
*principals
;
2529 if (IsFunctionObject(*vp
, &funobj
))
2532 fun
= js_ValueToFunction(cx
, vp
, flags
);
2535 vp
->setObject(*fun
);
2537 caller
= js_GetScriptedCaller(cx
, NULL
);
2539 principals
= JS_StackFramePrincipals(cx
, caller
);
2541 /* No scripted caller, don't allow access. */
2545 if (!js_CheckPrincipalsAccess(cx
, FUN_OBJECT(fun
), principals
,
2548 : cx
->runtime
->atomState
.anonymousAtom
)) {
2551 return FUN_OBJECT(fun
);
2555 js_ValueToCallableObject(JSContext
*cx
, Value
*vp
, uintN flags
)
2557 if (vp
->isObject()) {
2558 JSObject
*callable
= &vp
->toObject();
2559 if (callable
->isCallable())
2562 return js_ValueToFunctionObject(cx
, vp
, flags
);
2566 js_ReportIsNotFunction(JSContext
*cx
, const Value
*vp
, uintN flags
)
2568 const char *name
= NULL
, *source
= NULL
;
2569 AutoValueRooter
tvr(cx
);
2570 uintN error
= (flags
& JSV2F_CONSTRUCT
) ? JSMSG_NOT_CONSTRUCTOR
: JSMSG_NOT_FUNCTION
;
2572 FrameRegsIter
i(cx
);
2573 while (!i
.done() && !i
.pc())
2577 (!i
.done() && i
.fp()->base() <= vp
&& vp
< i
.sp())
2579 : ((flags
& JSV2F_SEARCH_STACK
) ? JSDVG_SEARCH_STACK
: JSDVG_IGNORE_STACK
);
2581 js_ReportValueError3(cx
, error
, spindex
, *vp
, NULL
, name
, source
);
2585 * When a function has between 2 and MAX_ARRAY_LOCALS arguments and variables,
2586 * their name are stored as the JSLocalNames.array.
2588 #define MAX_ARRAY_LOCALS 8
2590 JS_STATIC_ASSERT(2 <= MAX_ARRAY_LOCALS
);
2591 JS_STATIC_ASSERT(MAX_ARRAY_LOCALS
< JS_BITMASK(16));
2594 * When we use a hash table to store the local names, we use a singly linked
2595 * list to record the indexes of duplicated parameter names to preserve the
2596 * duplicates for the decompiler.
2598 typedef struct JSNameIndexPair JSNameIndexPair
;
2600 struct JSNameIndexPair
{
2603 JSNameIndexPair
*link
;
2606 struct JSLocalNameMap
{
2608 JSNameIndexPair
*lastdup
;
2611 typedef struct JSLocalNameHashEntry
{
2612 JSDHashEntryHdr hdr
;
2616 } JSLocalNameHashEntry
;
2619 FreeLocalNameHash(JSContext
*cx
, JSLocalNameMap
*map
)
2621 JSNameIndexPair
*dup
, *next
;
2623 for (dup
= map
->lastdup
; dup
; dup
= next
) {
2627 JS_DHashTableFinish(&map
->names
);
2632 HashLocalName(JSContext
*cx
, JSLocalNameMap
*map
, JSAtom
*name
,
2633 JSLocalKind localKind
, uintN index
)
2635 JSLocalNameHashEntry
*entry
;
2636 JSNameIndexPair
*dup
;
2638 JS_ASSERT(index
<= JS_BITMASK(16));
2639 #if JS_HAS_DESTRUCTURING
2641 /* A destructuring pattern does not need a hash entry. */
2642 JS_ASSERT(localKind
== JSLOCAL_ARG
);
2646 entry
= (JSLocalNameHashEntry
*)
2647 JS_DHashTableOperate(&map
->names
, name
, JS_DHASH_ADD
);
2649 JS_ReportOutOfMemory(cx
);
2653 JS_ASSERT(entry
->name
== name
);
2654 JS_ASSERT(entry
->localKind
== JSLOCAL_ARG
&& localKind
== JSLOCAL_ARG
);
2655 dup
= (JSNameIndexPair
*) cx
->malloc(sizeof *dup
);
2658 dup
->name
= entry
->name
;
2659 dup
->index
= entry
->index
;
2660 dup
->link
= map
->lastdup
;
2664 entry
->index
= (uint16
) index
;
2665 entry
->localKind
= (uint8
) localKind
;
2670 js_AddLocal(JSContext
*cx
, JSFunction
*fun
, JSAtom
*atom
, JSLocalKind kind
)
2676 JSLocalNameMap
*map
;
2678 JS_ASSERT(FUN_INTERPRETED(fun
));
2679 JS_ASSERT(!fun
->u
.i
.script
);
2680 JS_ASSERT(((jsuword
) atom
& 1) == 0);
2681 taggedAtom
= (jsuword
) atom
;
2682 if (kind
== JSLOCAL_ARG
) {
2683 indexp
= &fun
->nargs
;
2684 } else if (kind
== JSLOCAL_UPVAR
) {
2685 indexp
= &fun
->u
.i
.nupvars
;
2687 indexp
= &fun
->u
.i
.nvars
;
2688 if (kind
== JSLOCAL_CONST
)
2691 JS_ASSERT(kind
== JSLOCAL_VAR
);
2693 n
= fun
->countLocalNames();
2695 JS_ASSERT(fun
->u
.i
.names
.taggedAtom
== 0);
2696 fun
->u
.i
.names
.taggedAtom
= taggedAtom
;
2697 } else if (n
< MAX_ARRAY_LOCALS
) {
2699 array
= fun
->u
.i
.names
.array
;
2701 array
= (jsuword
*) cx
->malloc(MAX_ARRAY_LOCALS
* sizeof *array
);
2704 array
[0] = fun
->u
.i
.names
.taggedAtom
;
2705 fun
->u
.i
.names
.array
= array
;
2707 if (kind
== JSLOCAL_ARG
) {
2709 * A destructuring argument pattern adds variables, not arguments,
2710 * so for the following arguments nvars != 0.
2712 #if JS_HAS_DESTRUCTURING
2713 if (fun
->u
.i
.nvars
!= 0) {
2714 memmove(array
+ fun
->nargs
+ 1, array
+ fun
->nargs
,
2715 fun
->u
.i
.nvars
* sizeof *array
);
2718 JS_ASSERT(fun
->u
.i
.nvars
== 0);
2720 array
[fun
->nargs
] = taggedAtom
;
2722 array
[n
] = taggedAtom
;
2724 } else if (n
== MAX_ARRAY_LOCALS
) {
2725 array
= fun
->u
.i
.names
.array
;
2726 map
= (JSLocalNameMap
*) cx
->malloc(sizeof *map
);
2729 if (!JS_DHashTableInit(&map
->names
, JS_DHashGetStubOps(),
2730 NULL
, sizeof(JSLocalNameHashEntry
),
2731 JS_DHASH_DEFAULT_CAPACITY(MAX_ARRAY_LOCALS
2733 JS_ReportOutOfMemory(cx
);
2738 map
->lastdup
= NULL
;
2739 for (i
= 0; i
!= MAX_ARRAY_LOCALS
; ++i
) {
2740 taggedAtom
= array
[i
];
2742 JSLocalKind k
= JSLOCAL_ARG
;
2743 if (j
>= fun
->nargs
) {
2745 if (j
< fun
->u
.i
.nvars
) {
2746 k
= (taggedAtom
& 1) ? JSLOCAL_CONST
: JSLOCAL_VAR
;
2748 j
-= fun
->u
.i
.nvars
;
2752 if (!HashLocalName(cx
, map
, (JSAtom
*) (taggedAtom
& ~1), k
, j
)) {
2753 FreeLocalNameHash(cx
, map
);
2757 if (!HashLocalName(cx
, map
, atom
, kind
, *indexp
)) {
2758 FreeLocalNameHash(cx
, map
);
2763 * At this point the entry is added and we cannot fail. It is time
2764 * to replace fun->u.i.names with the built map.
2766 fun
->u
.i
.names
.map
= map
;
2769 if (*indexp
== JS_BITMASK(16)) {
2770 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
2771 (kind
== JSLOCAL_ARG
)
2772 ? JSMSG_TOO_MANY_FUN_ARGS
2773 : JSMSG_TOO_MANY_LOCALS
);
2776 if (!HashLocalName(cx
, fun
->u
.i
.names
.map
, atom
, kind
, *indexp
))
2780 /* Update the argument or variable counter. */
2786 js_LookupLocal(JSContext
*cx
, JSFunction
*fun
, JSAtom
*atom
, uintN
*indexp
)
2788 uintN n
, i
, upvar_base
;
2790 JSLocalNameHashEntry
*entry
;
2792 JS_ASSERT(FUN_INTERPRETED(fun
));
2793 n
= fun
->countLocalNames();
2795 return JSLOCAL_NONE
;
2796 if (n
<= MAX_ARRAY_LOCALS
) {
2797 array
= (n
== 1) ? &fun
->u
.i
.names
.taggedAtom
: fun
->u
.i
.names
.array
;
2799 /* Search from the tail to pick up the last duplicated name. */
2801 upvar_base
= fun
->countArgsAndVars();
2804 if (atom
== JS_LOCAL_NAME_TO_ATOM(array
[i
])) {
2805 if (i
< fun
->nargs
) {
2810 if (i
>= upvar_base
) {
2812 *indexp
= i
- upvar_base
;
2813 return JSLOCAL_UPVAR
;
2816 *indexp
= i
- fun
->nargs
;
2817 return JS_LOCAL_NAME_IS_CONST(array
[i
])
2823 entry
= (JSLocalNameHashEntry
*)
2824 JS_DHashTableOperate(&fun
->u
.i
.names
.map
->names
, atom
,
2826 if (JS_DHASH_ENTRY_IS_BUSY(&entry
->hdr
)) {
2827 JS_ASSERT(entry
->localKind
!= JSLOCAL_NONE
);
2829 *indexp
= entry
->index
;
2830 return (JSLocalKind
) entry
->localKind
;
2833 return JSLOCAL_NONE
;
2836 typedef struct JSLocalNameEnumeratorArgs
{
2843 } JSLocalNameEnumeratorArgs
;
2845 static JSDHashOperator
2846 get_local_names_enumerator(JSDHashTable
*table
, JSDHashEntryHdr
*hdr
,
2847 uint32 number
, void *arg
)
2849 JSLocalNameHashEntry
*entry
;
2850 JSLocalNameEnumeratorArgs
*args
;
2854 entry
= (JSLocalNameHashEntry
*) hdr
;
2855 args
= (JSLocalNameEnumeratorArgs
*) arg
;
2856 JS_ASSERT(entry
->name
);
2857 if (entry
->localKind
== JSLOCAL_ARG
) {
2858 JS_ASSERT(entry
->index
< args
->fun
->nargs
);
2859 JS_ASSERT(args
->nCopiedArgs
++ < args
->fun
->nargs
);
2863 JS_ASSERT(entry
->localKind
== JSLOCAL_VAR
||
2864 entry
->localKind
== JSLOCAL_CONST
||
2865 entry
->localKind
== JSLOCAL_UPVAR
);
2866 JS_ASSERT(entry
->index
< args
->fun
->u
.i
.nvars
+ args
->fun
->u
.i
.nupvars
);
2867 JS_ASSERT(args
->nCopiedVars
++ < unsigned(args
->fun
->u
.i
.nvars
+ args
->fun
->u
.i
.nupvars
));
2868 i
= args
->fun
->nargs
;
2869 if (entry
->localKind
== JSLOCAL_UPVAR
)
2870 i
+= args
->fun
->u
.i
.nvars
;
2872 constFlag
= (entry
->localKind
== JSLOCAL_CONST
);
2874 args
->names
[i
] = (jsuword
) entry
->name
| constFlag
;
2875 return JS_DHASH_NEXT
;
2879 js_GetLocalNameArray(JSContext
*cx
, JSFunction
*fun
, JSArenaPool
*pool
)
2883 JSLocalNameMap
*map
;
2884 JSLocalNameEnumeratorArgs args
;
2885 JSNameIndexPair
*dup
;
2887 JS_ASSERT(fun
->hasLocalNames());
2888 n
= fun
->countLocalNames();
2890 if (n
<= MAX_ARRAY_LOCALS
)
2891 return (n
== 1) ? &fun
->u
.i
.names
.taggedAtom
: fun
->u
.i
.names
.array
;
2894 * No need to check for overflow of the allocation size as we are making a
2895 * copy of already allocated data. As such it must fit size_t.
2897 JS_ARENA_ALLOCATE_CAST(names
, jsuword
*, pool
, (size_t) n
* sizeof *names
);
2899 js_ReportOutOfScriptQuota(cx
);
2903 #if JS_HAS_DESTRUCTURING
2904 /* Some parameter names can be NULL due to destructuring patterns. */
2905 PodZero(names
, fun
->nargs
);
2907 map
= fun
->u
.i
.names
.map
;
2911 args
.nCopiedArgs
= 0;
2912 args
.nCopiedVars
= 0;
2914 JS_DHashTableEnumerate(&map
->names
, get_local_names_enumerator
, &args
);
2915 for (dup
= map
->lastdup
; dup
; dup
= dup
->link
) {
2916 JS_ASSERT(dup
->index
< fun
->nargs
);
2917 JS_ASSERT(args
.nCopiedArgs
++ < fun
->nargs
);
2918 names
[dup
->index
] = (jsuword
) dup
->name
;
2920 #if !JS_HAS_DESTRUCTURING
2921 JS_ASSERT(args
.nCopiedArgs
== fun
->nargs
);
2923 JS_ASSERT(args
.nCopiedVars
== fun
->u
.i
.nvars
+ fun
->u
.i
.nupvars
);
2928 static JSDHashOperator
2929 trace_local_names_enumerator(JSDHashTable
*table
, JSDHashEntryHdr
*hdr
,
2930 uint32 number
, void *arg
)
2932 JSLocalNameHashEntry
*entry
;
2935 entry
= (JSLocalNameHashEntry
*) hdr
;
2936 JS_ASSERT(entry
->name
);
2937 trc
= (JSTracer
*) arg
;
2938 JS_SET_TRACING_INDEX(trc
,
2939 entry
->localKind
== JSLOCAL_ARG
? "arg" : "var",
2941 Mark(trc
, ATOM_TO_STRING(entry
->name
), JSTRACE_STRING
);
2942 return JS_DHASH_NEXT
;
2946 TraceLocalNames(JSTracer
*trc
, JSFunction
*fun
)
2952 JS_ASSERT(FUN_INTERPRETED(fun
));
2953 n
= fun
->countLocalNames();
2956 if (n
<= MAX_ARRAY_LOCALS
) {
2957 array
= (n
== 1) ? &fun
->u
.i
.names
.taggedAtom
: fun
->u
.i
.names
.array
;
2961 atom
= (JSAtom
*) (array
[i
] & ~1);
2963 JS_SET_TRACING_INDEX(trc
,
2964 i
< fun
->nargs
? "arg" : "var",
2965 i
< fun
->nargs
? i
: i
- fun
->nargs
);
2966 Mark(trc
, ATOM_TO_STRING(atom
), JSTRACE_STRING
);
2970 JS_DHashTableEnumerate(&fun
->u
.i
.names
.map
->names
,
2971 trace_local_names_enumerator
, trc
);
2974 * No need to trace the list of duplicates in map->lastdup as the
2975 * names there are traced when enumerating the hash table.
2981 DestroyLocalNames(JSContext
*cx
, JSFunction
*fun
)
2985 n
= fun
->countLocalNames();
2988 if (n
<= MAX_ARRAY_LOCALS
)
2989 cx
->free(fun
->u
.i
.names
.array
);
2991 FreeLocalNameHash(cx
, fun
->u
.i
.names
.map
);
2995 js_FreezeLocalNames(JSContext
*cx
, JSFunction
*fun
)
3000 JS_ASSERT(FUN_INTERPRETED(fun
));
3001 JS_ASSERT(!fun
->u
.i
.script
);
3002 n
= fun
->nargs
+ fun
->u
.i
.nvars
+ fun
->u
.i
.nupvars
;
3003 if (2 <= n
&& n
< MAX_ARRAY_LOCALS
) {
3004 /* Shrink over-allocated array ignoring realloc failures. */
3005 array
= (jsuword
*) cx
->realloc(fun
->u
.i
.names
.array
,
3008 fun
->u
.i
.names
.array
= array
;
3011 if (n
> MAX_ARRAY_LOCALS
)
3012 JS_DHashMarkTableImmutable(&fun
->u
.i
.names
.map
->names
);
3017 JSFunction::findDuplicateFormal() const
3022 /* Function with two to MAX_ARRAY_LOCALS parameters use an aray. */
3023 unsigned n
= nargs
+ u
.i
.nvars
+ u
.i
.nupvars
;
3024 if (n
<= MAX_ARRAY_LOCALS
) {
3025 jsuword
*array
= u
.i
.names
.array
;
3027 /* Quadratic, but MAX_ARRAY_LOCALS is 8, so at most 28 comparisons. */
3028 for (unsigned i
= 0; i
< nargs
; i
++) {
3029 for (unsigned j
= i
+ 1; j
< nargs
; j
++) {
3030 if (array
[i
] == array
[j
])
3031 return JS_LOCAL_NAME_TO_ATOM(array
[i
]);
3038 * Functions with more than MAX_ARRAY_LOCALS parameters use a hash
3039 * table. Hashed local name maps have already made a list of any
3040 * duplicate argument names for us.
3042 JSNameIndexPair
*dup
= u
.i
.names
.map
->lastdup
;
3043 return dup
? dup
->name
: NULL
;