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
->containingSegment(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
398 memcpy(wscript
->code
, script
->code
, script
->length
);
399 wscript
->main
= wscript
->code
+ (script
->main
- script
->code
);
401 memcpy(wscript
->notes(), snbase
, nsrcnotes
* sizeof(jssrcnote
));
402 memcpy(wscript
->atomMap
.vector
, script
->atomMap
.vector
,
403 wscript
->atomMap
.length
* sizeof(JSAtom
*));
404 if (script
->objectsOffset
!= 0) {
405 memcpy(wscript
->objects()->vector
, script
->objects()->vector
,
406 wscript
->objects()->length
* sizeof(JSObject
*));
408 if (script
->regexpsOffset
!= 0) {
409 memcpy(wscript
->regexps()->vector
, script
->regexps()->vector
,
410 wscript
->regexps()->length
* sizeof(JSObject
*));
412 if (script
->trynotesOffset
!= 0) {
413 memcpy(wscript
->trynotes()->vector
, script
->trynotes()->vector
,
414 wscript
->trynotes()->length
* sizeof(JSTryNote
));
417 if (wfun
->u
.i
.nupvars
!= 0) {
418 JS_ASSERT(wfun
->u
.i
.nupvars
== wscript
->upvars()->length
);
419 memcpy(wscript
->upvars()->vector
, script
->upvars()->vector
,
420 wfun
->u
.i
.nupvars
* sizeof(uint32
));
423 jsbytecode
*pc
= wscript
->code
;
424 while (*pc
!= JSOP_STOP
) {
425 /* XYZZYbe should copy JSOP_TRAP? */
426 JSOp op
= js_GetOpcode(cx
, wscript
, pc
);
427 const JSCodeSpec
*cs
= &js_CodeSpec
[op
];
428 ptrdiff_t oplen
= cs
->length
;
430 oplen
= js_GetVariableBytecodeLength(pc
);
433 * Rewrite JSOP_{GET,CALL}DSLOT as JSOP_{GET,CALL}UPVAR_DBG for the
434 * case where fun is an escaping flat closure. This works because the
435 * UPVAR and DSLOT ops by design have the same format: an upvar index
439 case JSOP_GETUPVAR
: *pc
= JSOP_GETUPVAR_DBG
; break;
440 case JSOP_CALLUPVAR
: *pc
= JSOP_CALLUPVAR_DBG
; break;
441 case JSOP_GETDSLOT
: *pc
= JSOP_GETUPVAR_DBG
; break;
442 case JSOP_CALLDSLOT
: *pc
= JSOP_CALLUPVAR_DBG
; break;
443 case JSOP_DEFFUN_FC
: *pc
= JSOP_DEFFUN_DBGFC
; break;
444 case JSOP_DEFLOCALFUN_FC
: *pc
= JSOP_DEFLOCALFUN_DBGFC
; break;
445 case JSOP_LAMBDA_FC
: *pc
= JSOP_LAMBDA_DBGFC
; break;
452 * Fill in the rest of wscript. This means if you add members to JSScript
453 * you must update this code. FIXME: factor into JSScript::clone method.
455 wscript
->noScriptRval
= script
->noScriptRval
;
456 wscript
->savedCallerFun
= script
->savedCallerFun
;
457 wscript
->hasSharps
= script
->hasSharps
;
458 wscript
->strictModeCode
= script
->strictModeCode
;
459 wscript
->version
= script
->version
;
460 wscript
->nfixed
= script
->nfixed
;
461 wscript
->filename
= script
->filename
;
462 wscript
->lineno
= script
->lineno
;
463 wscript
->nslots
= script
->nslots
;
464 wscript
->staticLevel
= script
->staticLevel
;
465 wscript
->principals
= script
->principals
;
466 if (wscript
->principals
)
467 JSPRINCIPALS_HOLD(cx
, wscript
->principals
);
468 #ifdef CHECK_SCRIPT_OWNER
469 wscript
->owner
= script
->owner
;
472 /* Deoptimize wfun from FUN_{FLAT,NULL}_CLOSURE to FUN_INTERPRETED. */
473 FUN_SET_KIND(wfun
, JSFUN_INTERPRETED
);
474 wfun
->u
.i
.script
= wscript
;
479 ArgGetter(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
481 if (!InstanceOf(cx
, obj
, &js_ArgumentsClass
, NULL
))
484 if (JSID_IS_INT(id
)) {
486 * arg can exceed the number of arguments if a script changed the
487 * prototype to point to another Arguments object with a bigger argc.
489 uintN arg
= uintN(JSID_TO_INT(id
));
490 if (arg
< obj
->getArgsLength()) {
492 ArgsPrivateNative
*argp
= GetArgsPrivateNative(obj
);
494 ExternNativeToValue(cx
, *vp
, argp
->typemap()[arg
], &argp
->argv
[arg
]);
499 JSStackFrame
*fp
= (JSStackFrame
*) obj
->getPrivate();
503 const Value
&v
= obj
->getArgsElement(arg
);
504 if (!v
.isMagic(JS_ARGS_HOLE
))
508 } else if (JSID_IS_ATOM(id
, cx
->runtime
->atomState
.lengthAtom
)) {
509 if (!obj
->isArgsLengthOverridden())
510 vp
->setInt32(obj
->getArgsLength());
512 JS_ASSERT(JSID_IS_ATOM(id
, cx
->runtime
->atomState
.calleeAtom
));
513 const Value
&v
= obj
->getArgsCallee();
514 if (!v
.isMagic(JS_ARGS_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
, &v
.toObject())->needsWrapper()) {
524 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
525 JSMSG_OPTIMIZED_CLOSURE_LEAK
);
535 ArgSetter(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*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 (!InstanceOf(cx
, obj
, &js_ArgumentsClass
, NULL
))
551 if (JSID_IS_INT(id
)) {
552 uintN arg
= uintN(JSID_TO_INT(id
));
553 if (arg
< obj
->getArgsLength()) {
554 JSStackFrame
*fp
= (JSStackFrame
*) obj
->getPrivate();
561 JS_ASSERT(JSID_IS_ATOM(id
, cx
->runtime
->atomState
.lengthAtom
) ||
562 JSID_IS_ATOM(id
, 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 AutoValueRooter
tvr(cx
);
572 return js_DeleteProperty(cx
, obj
, id
, tvr
.addr()) &&
573 js_SetProperty(cx
, obj
, id
, vp
);
577 args_resolve(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN flags
,
580 JS_ASSERT(obj
->isArguments());
584 if (JSID_IS_INT(id
)) {
585 uint32 arg
= uint32(JSID_TO_INT(id
));
586 if (arg
< obj
->getArgsLength() && !obj
->getArgsElement(arg
).isMagic(JS_ARGS_HOLE
))
588 } else if (JSID_IS_ATOM(id
, cx
->runtime
->atomState
.lengthAtom
)) {
589 if (!obj
->isArgsLengthOverridden())
591 } else if (JSID_IS_ATOM(id
, cx
->runtime
->atomState
.calleeAtom
)) {
592 if (!obj
->getArgsCallee().isMagic(JS_ARGS_HOLE
))
598 * XXX ECMA specs DontEnum even for indexed properties, contrary to
599 * other array-like objects.
601 Value tmp
= UndefinedValue();
602 if (!js_DefineProperty(cx
, obj
, id
, &tmp
, ArgGetter
, ArgSetter
, JSPROP_SHARED
))
610 args_enumerate(JSContext
*cx
, JSObject
*obj
)
612 JS_ASSERT(obj
->isArguments());
615 * Trigger reflection in args_resolve using a series of js_LookupProperty
618 int argc
= int(obj
->getArgsLength());
619 for (int i
= -2; i
!= argc
; i
++) {
621 ? ATOM_TO_JSID(cx
->runtime
->atomState
.lengthAtom
)
623 ? ATOM_TO_JSID(cx
->runtime
->atomState
.calleeAtom
)
628 if (!js_LookupProperty(cx
, obj
, id
, &pobj
, &prop
))
631 /* prop is null when the property was deleted. */
633 pobj
->dropProperty(cx
, prop
);
638 #if JS_HAS_GENERATORS
640 * If a generator's arguments or call object escapes, and the generator frame
641 * is not executing, the generator object needs to be marked because it is not
642 * otherwise reachable. An executing generator is rooted by its invocation. To
643 * distinguish the two cases (which imply different access paths to the
644 * generator object), we use the JSFRAME_FLOATING_GENERATOR flag, which is only
645 * set on the JSStackFrame kept in the generator object's JSGenerator.
648 args_or_call_trace(JSTracer
*trc
, JSObject
*obj
)
650 if (obj
->isArguments()) {
651 if (GetArgsPrivateNative(obj
))
654 JS_ASSERT(obj
->getClass() == &js_CallClass
);
657 JSStackFrame
*fp
= (JSStackFrame
*) obj
->getPrivate();
658 if (fp
&& fp
->isFloatingGenerator()) {
659 JSObject
*obj
= js_FloatingFrameToGenerator(fp
)->obj
;
660 JS_CALL_OBJECT_TRACER(trc
, obj
, "generator object");
664 # define args_or_call_trace NULL
668 * The Arguments class is not initialized via JS_InitClass, because arguments
669 * objects have the initial value of Object.prototype as their [[Prototype]].
670 * However, Object.prototype.toString.call(arguments) === "[object Arguments]"
671 * per ES5 (although not ES3), so its class name is "Arguments" rather than
674 * The JSClass functions below collaborate to lazily reflect and synchronize
675 * actual argument values, argument count, and callee function object stored
676 * in a JSStackFrame with their corresponding property values in the frame's
679 Class js_ArgumentsClass
= {
681 JSCLASS_HAS_PRIVATE
| JSCLASS_NEW_RESOLVE
|
682 JSCLASS_HAS_RESERVED_SLOTS(JSObject::ARGS_FIXED_RESERVED_SLOTS
) |
683 JSCLASS_MARK_IS_TRACE
| JSCLASS_HAS_CACHED_PROTO(JSProto_Object
),
684 PropertyStub
, args_delProperty
,
685 PropertyStub
, PropertyStub
,
686 args_enumerate
, (JSResolveOp
) args_resolve
,
691 JS_CLASS_TRACE(args_or_call_trace
), NULL
694 const uint32 JSSLOT_CALLEE
= JSSLOT_PRIVATE
+ 1;
695 const uint32 JSSLOT_CALL_ARGUMENTS
= JSSLOT_PRIVATE
+ 2;
696 const uint32 CALL_CLASS_FIXED_RESERVED_SLOTS
= 2;
699 * A Declarative Environment object stores its active JSStackFrame pointer in
700 * its private slot, just as Call and Arguments objects do.
702 Class js_DeclEnvClass
= {
704 JSCLASS_HAS_PRIVATE
| JSCLASS_HAS_CACHED_PROTO(JSProto_Object
),
705 PropertyStub
, PropertyStub
, PropertyStub
, PropertyStub
,
706 EnumerateStub
, ResolveStub
, ConvertStub
, NULL
,
707 JSCLASS_NO_OPTIONAL_MEMBERS
711 CheckForEscapingClosure(JSContext
*cx
, JSObject
*obj
, Value
*vp
)
713 JS_ASSERT(obj
->getClass() == &js_CallClass
||
714 obj
->getClass() == &js_DeclEnvClass
);
716 const Value
&v
= *vp
;
719 if (IsFunctionObject(v
, &funobj
)) {
720 JSFunction
*fun
= GET_FUNCTION_PRIVATE(cx
, funobj
);
723 * Any escaping null or flat closure that reaches above itself or
724 * contains nested functions that reach above it must be wrapped.
725 * We can wrap only when this Call or Declarative Environment obj
726 * still has an active stack frame associated with it.
728 if (fun
->needsWrapper()) {
731 JSStackFrame
*fp
= (JSStackFrame
*) obj
->getPrivate();
733 JSObject
*wrapper
= WrapEscapingClosure(cx
, fp
, funobj
, fun
);
736 vp
->setObject(*wrapper
);
740 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
741 JSMSG_OPTIMIZED_CLOSURE_LEAK
);
749 CalleeGetter(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
751 return CheckForEscapingClosure(cx
, obj
, vp
);
755 NewCallObject(JSContext
*cx
, JSFunction
*fun
, JSObject
*scopeChain
)
757 JSObject
*callobj
= js_NewGCObject(cx
);
761 /* Init immediately to avoid GC seeing a half-init'ed object. */
762 callobj
->init(&js_CallClass
, NULL
, scopeChain
, PrivateValue(NULL
));
763 callobj
->map
= cx
->runtime
->emptyCallScope
->hold();
765 /* This must come after callobj->map has been set. */
766 if (!js_EnsureReservedSlots(cx
, callobj
, fun
->countArgsAndVars()))
771 static inline JSObject
*
772 NewDeclEnvObject(JSContext
*cx
, JSStackFrame
*fp
)
774 JSObject
*envobj
= js_NewGCObject(cx
);
778 /* Init immediately to avoid GC seeing a half-init'ed object. */
779 envobj
->init(&js_DeclEnvClass
, NULL
, fp
->scopeChain
, PrivateValue(fp
));
780 envobj
->map
= cx
->runtime
->emptyDeclEnvScope
->hold();
785 js_GetCallObject(JSContext
*cx
, JSStackFrame
*fp
)
789 /* Create a call object for fp only if it lacks one. */
791 callobj
= fp
->callobj
;
796 /* A call object should be a frame's outermost scope chain element. */
797 Class
*classp
= fp
->scopeChain
->getClass();
798 if (classp
== &js_WithClass
|| classp
== &js_BlockClass
)
799 JS_ASSERT(fp
->scopeChain
->getPrivate() != js_FloatingFrameIfGenerator(cx
, fp
));
800 else if (classp
== &js_CallClass
)
801 JS_ASSERT(fp
->scopeChain
->getPrivate() != fp
);
805 * Create the call object, using the frame's enclosing scope as its
806 * parent, and link the call to its stack frame. For a named function
807 * expression Call's parent points to an environment object holding
810 JSAtom
*lambdaName
= (fp
->fun
->flags
& JSFUN_LAMBDA
) ? fp
->fun
->atom
: NULL
;
812 JSObject
*envobj
= NewDeclEnvObject(cx
, fp
);
816 /* Root envobj before js_DefineNativeProperty (-> JSClass.addProperty). */
817 fp
->scopeChain
= envobj
;
819 if (!js_DefineNativeProperty(cx
, fp
->scopeChain
, ATOM_TO_JSID(lambdaName
),
822 JSPROP_PERMANENT
| JSPROP_READONLY
,
828 callobj
= NewCallObject(cx
, fp
->fun
, fp
->scopeChain
);
832 callobj
->setPrivate(fp
);
834 JS_ASSERT(fp
->fun
== GET_FUNCTION_PRIVATE(cx
, fp
->callee()));
835 callobj
->setSlot(JSSLOT_CALLEE
, fp
->calleeValue());
836 fp
->callobj
= callobj
;
839 * Push callobj on the top of the scope chain, and make it the
842 fp
->scopeChain
= callobj
;
846 JSObject
* JS_FASTCALL
847 js_CreateCallObjectOnTrace(JSContext
*cx
, JSFunction
*fun
, JSObject
*callee
, JSObject
*scopeChain
)
849 JS_ASSERT(!js_IsNamedLambda(fun
));
850 JSObject
*callobj
= NewCallObject(cx
, fun
, scopeChain
);
853 callobj
->setSlot(JSSLOT_CALLEE
, ObjectValue(*callee
));
857 JS_DEFINE_CALLINFO_4(extern, OBJECT
, js_CreateCallObjectOnTrace
, CONTEXT
, FUNCTION
, OBJECT
, OBJECT
,
858 0, nanojit::ACC_STORE_ANY
)
861 js_GetCallObjectFunction(JSObject
*obj
)
863 JS_ASSERT(obj
->getClass() == &js_CallClass
);
864 const Value
&v
= obj
->getSlot(JSSLOT_CALLEE
);
865 if (v
.isUndefined()) {
866 /* Newborn or prototype object. */
869 JS_ASSERT(v
.isObject());
870 return GET_FUNCTION_PRIVATE(cx
, &v
.toObject());
874 CopyValuesToCallObject(JSObject
*callobj
, int nargs
, Value
*argv
, int nvars
, Value
*slots
)
876 memcpy(callobj
->dslots
, argv
, nargs
* sizeof(Value
));
877 memcpy(callobj
->dslots
+ nargs
, slots
, nvars
* sizeof(Value
));
881 js_PutCallObject(JSContext
*cx
, JSStackFrame
*fp
)
883 JSObject
*callobj
= fp
->callobj
;
886 /* Get the arguments object to snapshot fp's actual argument values. */
888 if (!(fp
->flags
& JSFRAME_OVERRIDE_ARGS
))
889 callobj
->setSlot(JSSLOT_CALL_ARGUMENTS
, ObjectOrNullValue(fp
->argsobj
));
890 js_PutArgsObject(cx
, fp
);
893 JSFunction
*fun
= fp
->fun
;
894 JS_ASSERT(fun
== js_GetCallObjectFunction(callobj
));
895 uintN n
= fun
->countArgsAndVars();
898 * Since for a call object all fixed slots happen to be taken, we can copy
899 * arguments and variables straight into JSObject.dslots.
901 JS_STATIC_ASSERT(JS_INITIAL_NSLOTS
- JSSLOT_PRIVATE
==
902 1 + CALL_CLASS_FIXED_RESERVED_SLOTS
);
904 JS_ASSERT(callobj
->numSlots() >= JS_INITIAL_NSLOTS
+ n
);
905 n
+= JS_INITIAL_NSLOTS
;
906 CopyValuesToCallObject(callobj
, fun
->nargs
, fp
->argv
, fun
->u
.i
.nvars
, fp
->slots());
909 /* Clear private pointers to fp, which is about to go away (js_Invoke). */
910 if (js_IsNamedLambda(fun
)) {
911 JSObject
*env
= callobj
->getParent();
913 JS_ASSERT(env
->getClass() == &js_DeclEnvClass
);
914 JS_ASSERT(env
->getPrivate() == fp
);
915 env
->setPrivate(NULL
);
918 callobj
->setPrivate(NULL
);
923 js_PutCallObjectOnTrace(JSContext
*cx
, JSObject
*scopeChain
, uint32 nargs
, Value
*argv
,
924 uint32 nvars
, Value
*slots
)
926 JS_ASSERT(scopeChain
->hasClass(&js_CallClass
));
927 JS_ASSERT(!scopeChain
->getPrivate());
929 uintN n
= nargs
+ nvars
;
931 CopyValuesToCallObject(scopeChain
, nargs
, argv
, nvars
, slots
);
936 JS_DEFINE_CALLINFO_6(extern, BOOL
, js_PutCallObjectOnTrace
, CONTEXT
, OBJECT
, UINT32
, VALUEPTR
,
937 UINT32
, VALUEPTR
, 0, nanojit::ACC_STORE_ANY
)
940 call_enumerate(JSContext
*cx
, JSObject
*obj
)
951 fun
= js_GetCallObjectFunction(obj
);
952 n
= fun
? fun
->countArgsAndVars() : 0;
956 mark
= JS_ARENA_MARK(&cx
->tempPool
);
958 MUST_FLOW_THROUGH("out");
959 names
= js_GetLocalNameArray(cx
, fun
, &cx
->tempPool
);
965 for (i
= 0; i
!= n
; ++i
) {
966 name
= JS_LOCAL_NAME_TO_ATOM(names
[i
]);
971 * Trigger reflection by looking up the name of the argument or
974 ok
= js_LookupProperty(cx
, obj
, ATOM_TO_JSID(name
), &pobj
, &prop
);
979 * The call object will always have a property corresponding to the
980 * argument or variable name because call_resolve creates the property
981 * using JSPROP_PERMANENT.
984 JS_ASSERT(pobj
== obj
);
985 pobj
->dropProperty(cx
, prop
);
990 JS_ARENA_RELEASE(&cx
->tempPool
, mark
);
994 enum JSCallPropertyKind
{
1002 CallPropertyOp(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
,
1003 JSCallPropertyKind kind
, JSBool setter
= false)
1005 JS_ASSERT(obj
->getClass() == &js_CallClass
);
1008 if (kind
!= JSCPK_ARGUMENTS
) {
1009 JS_ASSERT((int16
) JSID_TO_INT(id
) == JSID_TO_INT(id
));
1010 i
= (uint16
) JSID_TO_INT(id
);
1014 if (kind
== JSCPK_UPVAR
) {
1015 JSObject
*callee
= &obj
->getSlot(JSSLOT_CALLEE
).toObject();
1018 JSFunction
*callee_fun
= (JSFunction
*) callee
->getPrivate();
1019 JS_ASSERT(FUN_FLAT_CLOSURE(callee_fun
));
1020 JS_ASSERT(i
< callee_fun
->u
.i
.nupvars
);
1023 array
= callee
->dslots
;
1025 JSFunction
*fun
= js_GetCallObjectFunction(obj
);
1026 JS_ASSERT_IF(kind
== JSCPK_ARG
, i
< fun
->nargs
);
1027 JS_ASSERT_IF(kind
== JSCPK_VAR
, i
< fun
->u
.i
.nvars
);
1029 JSStackFrame
*fp
= (JSStackFrame
*) obj
->getPrivate();
1031 if (kind
== JSCPK_ARGUMENTS
) {
1034 fp
->flags
|= JSFRAME_OVERRIDE_ARGS
;
1035 obj
->setSlot(JSSLOT_CALL_ARGUMENTS
, *vp
);
1037 if (fp
&& !(fp
->flags
& JSFRAME_OVERRIDE_ARGS
)) {
1040 argsobj
= js_GetArgsObject(cx
, fp
);
1043 vp
->setObject(*argsobj
);
1045 *vp
= obj
->getSlot(JSSLOT_CALL_ARGUMENTS
);
1052 i
+= CALL_CLASS_FIXED_RESERVED_SLOTS
;
1053 if (kind
== JSCPK_VAR
)
1056 JS_ASSERT(kind
== JSCPK_ARG
);
1058 ? JS_SetReservedSlot(cx
, obj
, i
, Jsvalify(*vp
))
1059 : JS_GetReservedSlot(cx
, obj
, i
, Jsvalify(vp
));
1062 if (kind
== JSCPK_ARG
) {
1065 JS_ASSERT(kind
== JSCPK_VAR
);
1066 array
= fp
->slots();
1071 GC_POKE(cx
, array
[i
]);
1080 GetCallArguments(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
1082 return CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_ARGUMENTS
);
1086 SetCallArguments(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
1088 return CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_ARGUMENTS
, true);
1092 js_GetCallArg(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
1094 return CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_ARG
);
1098 SetCallArg(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
1100 return CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_ARG
, true);
1104 GetFlatUpvar(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
1106 return CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_UPVAR
);
1110 SetFlatUpvar(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
1112 return CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_UPVAR
, true);
1116 js_GetCallVar(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
1118 return CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_VAR
);
1122 js_GetCallVarChecked(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
1124 if (!CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_VAR
))
1127 return CheckForEscapingClosure(cx
, obj
, vp
);
1131 SetCallVar(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
1133 return CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_VAR
, true);
1138 js_SetCallArg(JSContext
*cx
, JSObject
*obj
, jsid slotid
, ValueArgType arg
)
1140 Value argcopy
= ValueArgToConstRef(arg
);
1141 return CallPropertyOp(cx
, obj
, slotid
, &argcopy
, JSCPK_ARG
, true);
1143 JS_DEFINE_CALLINFO_4(extern, BOOL
, js_SetCallArg
, CONTEXT
, OBJECT
, JSID
, VALUE
, 0,
1144 nanojit::ACC_STORE_ANY
)
1147 js_SetCallVar(JSContext
*cx
, JSObject
*obj
, jsid slotid
, ValueArgType arg
)
1149 Value argcopy
= ValueArgToConstRef(arg
);
1150 return CallPropertyOp(cx
, obj
, slotid
, &argcopy
, JSCPK_VAR
, true);
1152 JS_DEFINE_CALLINFO_4(extern, BOOL
, js_SetCallVar
, CONTEXT
, OBJECT
, JSID
, VALUE
, 0,
1153 nanojit::ACC_STORE_ANY
)
1157 call_resolve(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN flags
,
1161 JSLocalKind localKind
;
1162 PropertyOp getter
, setter
;
1165 JS_ASSERT(obj
->getClass() == &js_CallClass
);
1166 JS_ASSERT(!obj
->getProto());
1168 if (!JSID_IS_ATOM(id
))
1171 const Value
&callee
= obj
->getSlot(JSSLOT_CALLEE
);
1172 if (callee
.isUndefined())
1174 fun
= GET_FUNCTION_PRIVATE(cx
, &callee
.toObject());
1177 * Check whether the id refers to a formal parameter, local variable or
1178 * the arguments special name.
1180 * We define all such names using JSDNP_DONT_PURGE to avoid an expensive
1181 * shape invalidation in js_DefineNativeProperty. If such an id happens to
1182 * shadow a global or upvar of the same name, any inner functions can
1183 * never access the outer binding. Thus it cannot invalidate any property
1184 * cache entries or derived trace guards for the outer binding. See also
1185 * comments in js_PurgeScopeChainHelper from jsobj.cpp.
1187 localKind
= js_LookupLocal(cx
, fun
, JSID_TO_ATOM(id
), &slot
);
1188 if (localKind
!= JSLOCAL_NONE
) {
1189 JS_ASSERT((uint16
) slot
== slot
);
1192 * We follow 10.2.3 of ECMA 262 v3 and make argument and variable
1193 * properties of the Call objects enumerable.
1195 attrs
= JSPROP_ENUMERATE
| JSPROP_PERMANENT
| JSPROP_SHARED
;
1196 if (localKind
== JSLOCAL_ARG
) {
1197 JS_ASSERT(slot
< fun
->nargs
);
1198 getter
= js_GetCallArg
;
1199 setter
= SetCallArg
;
1201 JSCallPropertyKind cpkind
;
1202 if (localKind
== JSLOCAL_UPVAR
) {
1203 if (!FUN_FLAT_CLOSURE(fun
))
1205 getter
= GetFlatUpvar
;
1206 setter
= SetFlatUpvar
;
1207 cpkind
= JSCPK_UPVAR
;
1209 JS_ASSERT(localKind
== JSLOCAL_VAR
|| localKind
== JSLOCAL_CONST
);
1210 JS_ASSERT(slot
< fun
->u
.i
.nvars
);
1211 getter
= js_GetCallVar
;
1212 setter
= SetCallVar
;
1214 if (localKind
== JSLOCAL_CONST
)
1215 attrs
|= JSPROP_READONLY
;
1219 * Use js_GetCallVarChecked if the local's value is a null closure.
1220 * This way we penalize performance only slightly on first use of a
1221 * null closure, not on every use.
1224 if (!CallPropertyOp(cx
, obj
, INT_TO_JSID((int16
)slot
), &v
, cpkind
))
1227 if (IsFunctionObject(v
, &funobj
) &&
1228 GET_FUNCTION_PRIVATE(cx
, funobj
)->needsWrapper()) {
1229 getter
= js_GetCallVarChecked
;
1232 if (!js_DefineNativeProperty(cx
, obj
, id
, UndefinedValue(), getter
, setter
,
1233 attrs
, JSScopeProperty::HAS_SHORTID
, (int16
) slot
,
1234 NULL
, JSDNP_DONT_PURGE
)) {
1242 * Resolve arguments so that we never store a particular Call object's
1243 * arguments object reference in a Call prototype's |arguments| slot.
1245 if (JSID_IS_ATOM(id
, cx
->runtime
->atomState
.argumentsAtom
)) {
1246 if (!js_DefineNativeProperty(cx
, obj
, id
, UndefinedValue(),
1247 GetCallArguments
, SetCallArguments
,
1248 JSPROP_PERMANENT
| JSPROP_SHARED
,
1249 0, 0, NULL
, JSDNP_DONT_PURGE
)) {
1256 /* Control flow reaches here only if id was not resolved. */
1260 JS_PUBLIC_DATA(Class
) js_CallClass
= {
1262 JSCLASS_HAS_PRIVATE
|
1263 JSCLASS_HAS_RESERVED_SLOTS(CALL_CLASS_FIXED_RESERVED_SLOTS
) |
1264 JSCLASS_NEW_RESOLVE
| JSCLASS_IS_ANONYMOUS
| JSCLASS_MARK_IS_TRACE
,
1265 PropertyStub
, PropertyStub
,
1266 PropertyStub
, PropertyStub
,
1267 call_enumerate
, (JSResolveOp
)call_resolve
,
1272 JS_CLASS_TRACE(args_or_call_trace
), NULL
1275 /* Generic function tinyids. */
1277 FUN_ARGUMENTS
= -1, /* predefined arguments local variable */
1278 FUN_LENGTH
= -2, /* number of actual args, arity if inactive */
1279 FUN_ARITY
= -3, /* number of formal parameters; desired argc */
1280 FUN_NAME
= -4, /* function name, "" if anonymous */
1281 FUN_CALLER
= -5 /* Function.prototype.caller, backward compat */
1285 fun_getProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
1287 if (!JSID_IS_INT(id
))
1290 jsint slot
= JSID_TO_INT(id
);
1293 * Loop because getter and setter can be delegated from another class,
1294 * but loop only for FUN_LENGTH because we must pretend that f.length
1295 * is in each function instance f, per ECMA-262, instead of only in the
1296 * Function.prototype object (we use JSPROP_PERMANENT with JSPROP_SHARED
1297 * to make it appear so).
1299 * This code couples tightly to the attributes for lazy_function_props[]
1300 * initializers above, and to js_SetProperty and js_HasOwnProperty.
1302 * It's important to allow delegating objects, even though they inherit
1303 * this getter (fun_getProperty), to override arguments, arity, caller,
1304 * and name. If we didn't return early for slot != FUN_LENGTH, we would
1305 * clobber *vp with the native property value, instead of letting script
1306 * override that value in delegating objects.
1308 * Note how that clobbering is what simulates JSPROP_READONLY for all of
1309 * the non-standard properties when the directly addressed object (obj)
1310 * is a function object (i.e., when this loop does not iterate).
1313 while (!(fun
= (JSFunction
*)
1314 GetInstancePrivate(cx
, obj
, &js_FunctionClass
, NULL
))) {
1315 if (slot
!= FUN_LENGTH
)
1317 obj
= obj
->getProto();
1322 /* Find fun's top-most activation record. */
1324 for (fp
= js_GetTopStackFrame(cx
);
1325 fp
&& (fp
->fun
!= fun
|| (fp
->flags
& JSFRAME_SPECIAL
));
1332 /* Warn if strict about f.arguments or equivalent unqualified uses. */
1333 if (!JS_ReportErrorFlagsAndNumber(cx
,
1334 JSREPORT_WARNING
| JSREPORT_STRICT
,
1335 js_GetErrorMessage
, NULL
,
1336 JSMSG_DEPRECATED_USAGE
,
1337 js_arguments_str
)) {
1341 if (!js_GetArgsValue(cx
, fp
, vp
))
1350 vp
->setInt32(fun
->nargs
);
1354 vp
->setString(fun
->atom
? ATOM_TO_STRING(fun
->atom
)
1355 : cx
->runtime
->emptyString
);
1359 if (fp
&& fp
->down
&& fp
->down
->fun
) {
1360 JSFunction
*caller
= fp
->down
->fun
;
1362 * See equivalent condition in args_getProperty for ARGS_CALLEE,
1363 * but here we do not want to throw, since this escape can happen
1364 * via foo.caller alone, without any debugger or indirect eval. And
1365 * it seems foo.caller is still used on the Web.
1367 if (caller
->needsWrapper()) {
1368 JSObject
*wrapper
= WrapEscapingClosure(cx
, fp
->down
, FUN_OBJECT(caller
), caller
);
1371 vp
->setObject(*wrapper
);
1375 JS_ASSERT(fp
->down
->argv
);
1376 *vp
= fp
->down
->calleeValue();
1381 /* Censor the caller if it is from another compartment. */
1382 if (vp
->isObject()) {
1383 if (vp
->toObject().getCompartment(cx
) != cx
->compartment
)
1389 /* XXX fun[0] and fun.arguments[0] are equivalent. */
1390 if (fp
&& fp
->fun
&& (uintN
)slot
< fp
->fun
->nargs
)
1391 *vp
= fp
->argv
[slot
];
1398 struct LazyFunctionProp
{
1404 /* NB: no sentinel at the end -- use JS_ARRAY_LENGTH to bound loops. */
1405 static LazyFunctionProp lazy_function_props
[] = {
1406 {ATOM_OFFSET(arguments
), FUN_ARGUMENTS
, JSPROP_PERMANENT
},
1407 {ATOM_OFFSET(arity
), FUN_ARITY
, JSPROP_PERMANENT
},
1408 {ATOM_OFFSET(caller
), FUN_CALLER
, JSPROP_PERMANENT
},
1409 {ATOM_OFFSET(name
), FUN_NAME
, JSPROP_PERMANENT
},
1413 fun_enumerate(JSContext
*cx
, JSObject
*obj
)
1415 JS_ASSERT(obj
->isFunction());
1418 jsid id
= ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
);
1419 if (!JS_LookupPropertyById(cx
, obj
, id
, &v
))
1421 id
= ATOM_TO_JSID(cx
->runtime
->atomState
.lengthAtom
);
1422 if (!JS_LookupPropertyById(cx
, obj
, id
, &v
))
1425 for (uintN i
= 0; i
< JS_ARRAY_LENGTH(lazy_function_props
); i
++) {
1426 LazyFunctionProp
&lfp
= lazy_function_props
[i
];
1427 id
= ATOM_TO_JSID(OFFSET_TO_ATOM(cx
->runtime
, lfp
.atomOffset
));
1428 if (!JS_LookupPropertyById(cx
, obj
, id
, &v
))
1436 fun_resolve(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN flags
,
1439 if (!JSID_IS_ATOM(id
))
1442 JSFunction
*fun
= GET_FUNCTION_PRIVATE(cx
, obj
);
1445 * No need to reflect fun.prototype in 'fun.prototype = ... '. Assert that
1446 * fun is not a compiler-created function object, which must never leak to
1447 * script or embedding code and then be mutated.
1449 if ((flags
& JSRESOLVE_ASSIGNING
) && !JSID_IS_ATOM(id
, cx
->runtime
->atomState
.lengthAtom
)) {
1450 JS_ASSERT(!IsInternalFunctionObject(obj
));
1455 * Ok, check whether id is 'prototype' and bootstrap the function object's
1456 * prototype property.
1458 JSAtom
*atom
= cx
->runtime
->atomState
.classPrototypeAtom
;
1459 if (id
== ATOM_TO_JSID(atom
)) {
1460 JS_ASSERT(!IsInternalFunctionObject(obj
));
1463 * Beware of the wacky case of a user function named Object -- trying
1464 * to find a prototype for that will recur back here _ad perniciem_.
1466 if (fun
->atom
== CLASS_ATOM(cx
, Object
))
1470 * Make the prototype object an instance of Object with the same parent
1471 * as the function object itself.
1473 JSObject
*parent
= obj
->getParent();
1475 if (!js_GetClassPrototype(cx
, parent
, JSProto_Object
, &proto
))
1477 proto
= NewNativeClassInstance(cx
, &js_ObjectClass
, proto
, parent
);
1482 * ECMA (15.3.5.2) says that constructor.prototype is DontDelete for
1483 * user-defined functions, but DontEnum | ReadOnly | DontDelete for
1484 * native "system" constructors such as Object or Function. So lazily
1485 * set the former here in fun_resolve, but eagerly define the latter
1486 * in JS_InitClass, with the right attributes.
1488 if (!js_SetClassPrototype(cx
, obj
, proto
, JSPROP_PERMANENT
))
1495 atom
= cx
->runtime
->atomState
.lengthAtom
;
1496 if (id
== ATOM_TO_JSID(atom
)) {
1497 JS_ASSERT(!IsInternalFunctionObject(obj
));
1498 if (!js_DefineNativeProperty(cx
, obj
, ATOM_TO_JSID(atom
), Int32Value(fun
->nargs
),
1499 PropertyStub
, PropertyStub
,
1500 JSPROP_PERMANENT
| JSPROP_READONLY
, 0, 0, NULL
)) {
1507 for (uintN i
= 0; i
< JS_ARRAY_LENGTH(lazy_function_props
); i
++) {
1508 LazyFunctionProp
*lfp
= &lazy_function_props
[i
];
1510 atom
= OFFSET_TO_ATOM(cx
->runtime
, lfp
->atomOffset
);
1511 if (id
== ATOM_TO_JSID(atom
)) {
1512 JS_ASSERT(!IsInternalFunctionObject(obj
));
1514 if (!js_DefineNativeProperty(cx
, obj
,
1515 ATOM_TO_JSID(atom
), UndefinedValue(),
1516 fun_getProperty
, PropertyStub
,
1517 lfp
->attrs
, JSScopeProperty::HAS_SHORTID
,
1518 lfp
->tinyid
, NULL
)) {
1531 /* XXX store parent and proto, if defined */
1533 js_XDRFunctionObject(JSXDRState
*xdr
, JSObject
**objp
)
1537 uint32 firstword
; /* flag telling whether fun->atom is non-null,
1538 plus for fun->u.i.skipmin, fun->u.i.wrapper,
1539 and 14 bits reserved for future use */
1540 uintN nargs
, nvars
, nupvars
, n
;
1541 uint32 localsword
; /* word for argument and variable counts */
1542 uint32 flagsword
; /* word for fun->u.i.nupvars and fun->flags */
1545 if (xdr
->mode
== JSXDR_ENCODE
) {
1546 fun
= GET_FUNCTION_PRIVATE(cx
, *objp
);
1547 if (!FUN_INTERPRETED(fun
)) {
1548 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1549 JSMSG_NOT_SCRIPTED_FUNCTION
,
1550 JS_GetFunctionName(fun
));
1553 if (fun
->u
.i
.wrapper
) {
1554 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1555 JSMSG_XDR_CLOSURE_WRAPPER
,
1556 JS_GetFunctionName(fun
));
1559 JS_ASSERT((fun
->u
.i
.wrapper
& ~1U) == 0);
1560 firstword
= (fun
->u
.i
.skipmin
<< 2) | (fun
->u
.i
.wrapper
<< 1) | !!fun
->atom
;
1562 nvars
= fun
->u
.i
.nvars
;
1563 nupvars
= fun
->u
.i
.nupvars
;
1564 localsword
= (nargs
<< 16) | nvars
;
1565 flagsword
= (nupvars
<< 16) | fun
->flags
;
1567 fun
= js_NewFunction(cx
, NULL
, NULL
, 0, JSFUN_INTERPRETED
, NULL
, NULL
);
1570 FUN_OBJECT(fun
)->clearParent();
1571 FUN_OBJECT(fun
)->clearProto();
1573 nvars
= nargs
= nupvars
= 0; /* quell GCC uninitialized warning */
1577 AutoObjectRooter
tvr(cx
, FUN_OBJECT(fun
));
1579 if (!JS_XDRUint32(xdr
, &firstword
))
1581 if ((firstword
& 1U) && !js_XDRAtom(xdr
, &fun
->atom
))
1583 if (!JS_XDRUint32(xdr
, &localsword
) ||
1584 !JS_XDRUint32(xdr
, &flagsword
)) {
1588 if (xdr
->mode
== JSXDR_DECODE
) {
1589 nargs
= localsword
>> 16;
1590 nvars
= uint16(localsword
);
1591 JS_ASSERT((flagsword
& JSFUN_KINDMASK
) >= JSFUN_INTERPRETED
);
1592 nupvars
= flagsword
>> 16;
1593 fun
->flags
= uint16(flagsword
);
1594 fun
->u
.i
.skipmin
= uint16(firstword
>> 2);
1595 fun
->u
.i
.wrapper
= JSPackedBool((firstword
>> 1) & 1);
1598 /* do arguments and local vars */
1599 n
= nargs
+ nvars
+ nupvars
;
1607 JSLocalKind localKind
;
1610 mark
= JS_ARENA_MARK(&xdr
->cx
->tempPool
);
1613 * From this point the control must flow via the label release_mark.
1615 * To xdr the names we prefix the names with a bitmap descriptor and
1616 * then xdr the names as strings. For argument names (indexes below
1617 * nargs) the corresponding bit in the bitmap is unset when the name
1618 * is null. Such null names are not encoded or decoded. For variable
1619 * names (indexes starting from nargs) bitmap's bit is set when the
1620 * name is declared as const, not as ordinary var.
1622 MUST_FLOW_THROUGH("release_mark");
1623 bitmapLength
= JS_HOWMANY(n
, JS_BITS_PER_UINT32
);
1624 JS_ARENA_ALLOCATE_CAST(bitmap
, uint32
*, &xdr
->cx
->tempPool
,
1625 bitmapLength
* sizeof *bitmap
);
1627 js_ReportOutOfScriptQuota(xdr
->cx
);
1631 if (xdr
->mode
== JSXDR_ENCODE
) {
1632 names
= js_GetLocalNameArray(xdr
->cx
, fun
, &xdr
->cx
->tempPool
);
1637 PodZero(bitmap
, bitmapLength
);
1638 for (i
= 0; i
!= n
; ++i
) {
1640 ? JS_LOCAL_NAME_TO_ATOM(names
[i
]) != NULL
1641 : JS_LOCAL_NAME_IS_CONST(names
[i
])) {
1642 bitmap
[i
>> JS_BITS_PER_UINT32_LOG2
] |=
1643 JS_BIT(i
& (JS_BITS_PER_UINT32
- 1));
1649 names
= NULL
; /* quell GCC uninitialized warning */
1652 for (i
= 0; i
!= bitmapLength
; ++i
) {
1653 ok
= !!JS_XDRUint32(xdr
, &bitmap
[i
]);
1657 for (i
= 0; i
!= n
; ++i
) {
1659 !(bitmap
[i
>> JS_BITS_PER_UINT32_LOG2
] &
1660 JS_BIT(i
& (JS_BITS_PER_UINT32
- 1)))) {
1661 if (xdr
->mode
== JSXDR_DECODE
) {
1662 ok
= !!js_AddLocal(xdr
->cx
, fun
, NULL
, JSLOCAL_ARG
);
1666 JS_ASSERT(!JS_LOCAL_NAME_TO_ATOM(names
[i
]));
1670 if (xdr
->mode
== JSXDR_ENCODE
)
1671 name
= JS_LOCAL_NAME_TO_ATOM(names
[i
]);
1672 ok
= !!js_XDRAtom(xdr
, &name
);
1675 if (xdr
->mode
== JSXDR_DECODE
) {
1676 localKind
= (i
< nargs
)
1678 : (i
< nargs
+ nvars
)
1679 ? (bitmap
[i
>> JS_BITS_PER_UINT32_LOG2
] &
1680 JS_BIT(i
& (JS_BITS_PER_UINT32
- 1))
1684 ok
= !!js_AddLocal(xdr
->cx
, fun
, name
, localKind
);
1691 JS_ARENA_RELEASE(&xdr
->cx
->tempPool
, mark
);
1695 if (xdr
->mode
== JSXDR_DECODE
)
1696 js_FreezeLocalNames(cx
, fun
);
1699 if (!js_XDRScript(xdr
, &fun
->u
.i
.script
, false, NULL
))
1702 if (xdr
->mode
== JSXDR_DECODE
) {
1703 *objp
= FUN_OBJECT(fun
);
1704 if (fun
->u
.i
.script
!= JSScript::emptyScript()) {
1705 #ifdef CHECK_SCRIPT_OWNER
1706 fun
->u
.i
.script
->owner
= NULL
;
1708 js_CallNewScriptHook(cx
, fun
->u
.i
.script
, fun
);
1715 #else /* !JS_HAS_XDR */
1717 #define js_XDRFunctionObject NULL
1719 #endif /* !JS_HAS_XDR */
1722 * [[HasInstance]] internal method for Function objects: fetch the .prototype
1723 * property of its 'this' parameter, and walks the prototype chain of v (only
1724 * if v is an object) returning true if .prototype is found.
1727 fun_hasInstance(JSContext
*cx
, JSObject
*obj
, const Value
*v
, JSBool
*bp
)
1729 jsid id
= ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
);
1731 if (!obj
->getProperty(cx
, id
, &pval
))
1734 if (pval
.isPrimitive()) {
1736 * Throw a runtime error if instanceof is called on a function that
1737 * has a non-object as its .prototype value.
1739 js_ReportValueError(cx
, JSMSG_BAD_PROTOTYPE
, -1, ObjectValue(*obj
), NULL
);
1743 *bp
= js_IsDelegate(cx
, &pval
.toObject(), *v
);
1748 TraceLocalNames(JSTracer
*trc
, JSFunction
*fun
);
1751 DestroyLocalNames(JSContext
*cx
, JSFunction
*fun
);
1754 fun_trace(JSTracer
*trc
, JSObject
*obj
)
1756 /* A newborn function object may have a not yet initialized private slot. */
1757 JSFunction
*fun
= (JSFunction
*) obj
->getPrivate();
1761 if (FUN_OBJECT(fun
) != obj
) {
1762 /* obj is cloned function object, trace the original. */
1763 JS_CALL_TRACER(trc
, FUN_OBJECT(fun
), JSTRACE_OBJECT
, "private");
1767 JS_CALL_STRING_TRACER(trc
, ATOM_TO_STRING(fun
->atom
), "atom");
1768 if (FUN_INTERPRETED(fun
)) {
1769 if (fun
->u
.i
.script
)
1770 js_TraceScript(trc
, fun
->u
.i
.script
);
1771 TraceLocalNames(trc
, fun
);
1776 fun_finalize(JSContext
*cx
, JSObject
*obj
)
1778 /* Ignore newborn and cloned function objects. */
1779 JSFunction
*fun
= (JSFunction
*) obj
->getPrivate();
1780 if (!fun
|| FUN_OBJECT(fun
) != obj
)
1784 * Null-check of u.i.script is required since the parser sets interpreted
1787 if (FUN_INTERPRETED(fun
)) {
1788 if (fun
->u
.i
.script
)
1789 js_DestroyScript(cx
, fun
->u
.i
.script
);
1790 DestroyLocalNames(cx
, fun
);
1795 JSFunction::sharpSlotBase(JSContext
*cx
)
1797 #if JS_HAS_SHARP_VARS
1798 JSAtom
*name
= js_Atomize(cx
, "#array", 6, 0);
1800 uintN index
= uintN(-1);
1804 js_LookupLocal(cx
, this, name
, &index
);
1805 JS_ASSERT(kind
== JSLOCAL_VAR
);
1813 JSFunction::countInterpretedReservedSlots() const
1815 JS_ASSERT(FUN_INTERPRETED(this));
1817 return (u
.i
.nupvars
== 0) ? 0 : u
.i
.script
->upvars()->length
;
1821 * Reserve two slots in all function objects for XPConnect. Note that this
1822 * does not bloat every instance, only those on which reserved slots are set,
1823 * and those on which ad-hoc properties are defined.
1825 JS_PUBLIC_DATA(Class
) js_FunctionClass
= {
1827 JSCLASS_HAS_PRIVATE
| JSCLASS_NEW_RESOLVE
| JSCLASS_HAS_RESERVED_SLOTS(2) |
1828 JSCLASS_MARK_IS_TRACE
| JSCLASS_HAS_CACHED_PROTO(JSProto_Function
),
1829 PropertyStub
, PropertyStub
,
1830 PropertyStub
, PropertyStub
,
1831 fun_enumerate
, (JSResolveOp
)fun_resolve
,
1832 ConvertStub
, fun_finalize
,
1835 js_XDRFunctionObject
, fun_hasInstance
,
1836 JS_CLASS_TRACE(fun_trace
), NULL
1842 fun_toStringHelper(JSContext
*cx
, JSObject
*obj
, uintN indent
)
1844 if (!obj
->isFunction()) {
1845 if (obj
->isFunctionProxy())
1846 return JSProxy::fun_toString(cx
, obj
, indent
);
1847 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1848 JSMSG_INCOMPATIBLE_PROTO
,
1849 js_Function_str
, js_toString_str
,
1854 JSFunction
*fun
= GET_FUNCTION_PRIVATE(cx
, obj
);
1857 return JS_DecompileFunction(cx
, fun
, indent
);
1860 } /* namespace js */
1863 fun_toString(JSContext
*cx
, uintN argc
, Value
*vp
)
1865 JS_ASSERT(IsFunctionObject(vp
[0]));
1866 uint32_t indent
= 0;
1868 if (argc
!= 0 && !ValueToECMAUint32(cx
, vp
[2], &indent
))
1871 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1875 JSString
*str
= fun_toStringHelper(cx
, obj
, indent
);
1885 fun_toSource(JSContext
*cx
, uintN argc
, Value
*vp
)
1887 JS_ASSERT(IsFunctionObject(vp
[0]));
1889 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1893 JSString
*str
= fun_toStringHelper(cx
, obj
, JS_DONT_PRETTY_PRINT
);
1903 js_fun_call(JSContext
*cx
, uintN argc
, Value
*vp
)
1907 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1912 if (!js_IsCallable(fval
)) {
1913 JSString
*str
= js_ValueToString(cx
, fval
);
1915 const char *bytes
= js_GetStringBytes(cx
, str
);
1918 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1919 JSMSG_INCOMPATIBLE_PROTO
,
1920 js_Function_str
, js_call_str
,
1927 Value
*argv
= vp
+ 2;
1929 /* Call fun with its global object as the 'this' param if no args. */
1932 /* Otherwise convert the first arg to 'this' and skip over it. */
1933 if (argv
[0].isObject())
1934 obj
= &argv
[0].toObject();
1935 else if (!js_ValueToObjectOrNull(cx
, argv
[0], &obj
))
1941 /* Allocate stack space for fval, obj, and the args. */
1942 InvokeArgsGuard args
;
1943 if (!cx
->stack().pushInvokeArgs(cx
, argc
, args
))
1946 /* Push fval, obj, and the args. */
1947 args
.getvp()[0] = fval
;
1948 args
.getvp()[1] = ObjectOrNullValue(obj
);
1949 memcpy(args
.getvp() + 2, argv
, argc
* sizeof *argv
);
1951 bool ok
= Invoke(cx
, args
, 0);
1952 *vp
= *args
.getvp();
1957 js_fun_apply(JSContext
*cx
, uintN argc
, Value
*vp
)
1960 /* Will get globalObject as 'this' and no other arguments. */
1961 return js_fun_call(cx
, argc
, vp
);
1966 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1971 if (!js_IsCallable(fval
)) {
1972 JSString
*str
= js_ValueToString(cx
, fval
);
1974 const char *bytes
= js_GetStringBytes(cx
, str
);
1977 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1978 JSMSG_INCOMPATIBLE_PROTO
,
1979 js_Function_str
, js_apply_str
,
1986 /* Quell GCC overwarnings. */
1987 JSObject
*aobj
= NULL
;
1991 /* If the 2nd arg is null or void, call the function with 0 args. */
1992 if (vp
[3].isNullOrUndefined()) {
1995 /* The second arg must be an array (or arguments object). */
1996 JSBool arraylike
= JS_FALSE
;
1997 if (vp
[3].isObject()) {
1998 aobj
= &vp
[3].toObject();
1999 if (!js_IsArrayLike(cx
, aobj
, &arraylike
, &length
))
2003 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
2004 JSMSG_BAD_APPLY_ARGS
, js_apply_str
);
2010 /* Convert the first arg to 'this' and skip over it. */
2011 if (vp
[2].isObject())
2012 obj
= &vp
[2].toObject();
2013 else if (!js_ValueToObjectOrNull(cx
, vp
[2], &obj
))
2016 /* Allocate stack space for fval, obj, and the args. */
2017 argc
= (uintN
)JS_MIN(length
, JS_ARGS_LENGTH_MAX
);
2019 InvokeArgsGuard args
;
2020 if (!cx
->stack().pushInvokeArgs(cx
, argc
, args
))
2023 /* Push fval, obj, and aobj's elements as args. */
2024 Value
*sp
= args
.getvp();
2026 *sp
++ = ObjectOrNullValue(obj
);
2027 if (aobj
&& aobj
->isArguments() && !aobj
->isArgsLengthOverridden()) {
2029 * Two cases, two loops: note how in the case of an active stack frame
2030 * backing aobj, even though we copy from fp->argv, we still must check
2031 * aobj->getArgsElement(i) for a hole, to handle a delete on the
2032 * corresponding arguments element. See args_delProperty.
2034 JSStackFrame
*fp
= (JSStackFrame
*) aobj
->getPrivate();
2036 memcpy(sp
, fp
->argv
, argc
* sizeof(Value
));
2037 for (uintN i
= 0; i
< argc
; i
++) {
2038 if (aobj
->getArgsElement(i
).isMagic(JS_ARGS_HOLE
)) // suppress deleted element
2039 sp
[i
].setUndefined();
2042 for (uintN i
= 0; i
< argc
; i
++) {
2043 sp
[i
] = aobj
->getArgsElement(i
);
2044 if (sp
[i
].isMagic(JS_ARGS_HOLE
))
2045 sp
[i
].setUndefined();
2049 for (uintN i
= 0; i
< argc
; i
++) {
2050 if (!aobj
->getProperty(cx
, INT_TO_JSID(jsint(i
)), sp
))
2056 bool ok
= Invoke(cx
, args
, 0);
2057 *vp
= *args
.getvp();
2061 static JSFunctionSpec function_methods
[] = {
2063 JS_FN(js_toSource_str
, fun_toSource
, 0,0),
2065 JS_FN(js_toString_str
, fun_toString
, 0,0),
2066 JS_FN(js_apply_str
, js_fun_apply
, 2,0),
2067 JS_FN(js_call_str
, js_fun_call
, 1,0),
2072 Function(JSContext
*cx
, JSObject
*obj
, uintN argc
, Value
*argv
, Value
*rval
)
2076 JSStackFrame
*fp
, *caller
;
2079 const char *filename
;
2081 JSString
*str
, *arg
;
2083 JSPrincipals
*principals
;
2084 jschar
*collected_args
, *cp
;
2086 size_t arg_length
, args_length
, old_args_length
;
2089 if (!JS_IsConstructing(cx
)) {
2090 obj
= NewObject(cx
, &js_FunctionClass
, NULL
, NULL
);
2093 rval
->setObject(*obj
);
2096 * The constructor is called before the private slot is initialized so
2097 * we must use getPrivate, not GET_FUNCTION_PRIVATE here.
2099 if (obj
->getPrivate())
2104 * NB: (new Function) is not lexically closed by its caller, it's just an
2105 * anonymous function in the top-level scope that its constructor inhabits.
2106 * Thus 'var x = 42; f = new Function("return x"); print(f())' prints 42,
2107 * and so would a call to f from another top-level's script or function.
2109 * In older versions, before call objects, a new Function was adopted by
2110 * its running context's globalObject, which might be different from the
2111 * top-level reachable from scopeChain (in HTML frames, e.g.).
2113 parent
= argv
[-2].toObject().getParent();
2115 fun
= js_NewFunction(cx
, obj
, NULL
, 0, JSFUN_LAMBDA
| JSFUN_INTERPRETED
,
2116 parent
, cx
->runtime
->atomState
.anonymousAtom
);
2122 * Function is static and not called directly by other functions in this
2123 * file, therefore it is callable only as a native function by js_Invoke.
2124 * Find the scripted caller, possibly skipping other native frames such as
2125 * are built for Function.prototype.call or .apply activations that invoke
2126 * Function indirectly from a script.
2128 fp
= js_GetTopStackFrame(cx
);
2129 JS_ASSERT(!fp
->script
&& fp
->fun
&& fp
->fun
->u
.n
.native
== Function
);
2130 caller
= js_GetScriptedCaller(cx
, fp
);
2132 principals
= JS_EvalFramePrincipals(cx
, fp
, caller
);
2133 filename
= js_ComputeFilename(cx
, caller
, principals
, &lineno
);
2140 /* Belt-and-braces: check that the caller has access to parent. */
2141 if (!js_CheckPrincipalsAccess(cx
, parent
, principals
,
2142 CLASS_ATOM(cx
, Function
))) {
2147 * CSP check: whether new Function() is allowed at all.
2148 * Report errors via CSP is done in the script security manager.
2149 * js_CheckContentSecurityPolicy is defined in jsobj.cpp
2151 if (!js_CheckContentSecurityPolicy(cx
)) {
2152 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
2153 JSMSG_CSP_BLOCKED_FUNCTION
);
2157 n
= argc
? argc
- 1 : 0;
2159 enum { OK
, BAD
, BAD_FORMAL
} state
;
2162 * Collect the function-argument arguments into one string, separated
2163 * by commas, then make a tokenstream from that string, and scan it to
2164 * get the arguments. We need to throw the full scanner at the
2165 * problem, because the argument string can legitimately contain
2166 * comments and linefeeds. XXX It might be better to concatenate
2167 * everything up into a function definition and pass it to the
2168 * compiler, but doing it this way is less of a delta from the old
2169 * code. See ECMA 15.3.2.1.
2173 for (i
= 0; i
< n
; i
++) {
2174 /* Collect the lengths for all the function-argument arguments. */
2175 arg
= js_ValueToString(cx
, argv
[i
]);
2178 argv
[i
].setString(arg
);
2181 * Check for overflow. The < test works because the maximum
2182 * JSString length fits in 2 fewer bits than size_t has.
2184 old_args_length
= args_length
;
2185 args_length
= old_args_length
+ arg
->length();
2186 if (args_length
< old_args_length
) {
2187 js_ReportAllocationOverflow(cx
);
2192 /* Add 1 for each joining comma and check for overflow (two ways). */
2193 old_args_length
= args_length
;
2194 args_length
= old_args_length
+ n
- 1;
2195 if (args_length
< old_args_length
||
2196 args_length
>= ~(size_t)0 / sizeof(jschar
)) {
2197 js_ReportAllocationOverflow(cx
);
2202 * Allocate a string to hold the concatenated arguments, including room
2203 * for a terminating 0. Mark cx->tempPool for later release, to free
2204 * collected_args and its tokenstream in one swoop.
2206 mark
= JS_ARENA_MARK(&cx
->tempPool
);
2207 JS_ARENA_ALLOCATE_CAST(cp
, jschar
*, &cx
->tempPool
,
2208 (args_length
+1) * sizeof(jschar
));
2210 js_ReportOutOfScriptQuota(cx
);
2213 collected_args
= cp
;
2216 * Concatenate the arguments into the new string, separated by commas.
2218 for (i
= 0; i
< n
; i
++) {
2219 arg
= argv
[i
].toString();
2220 arg_length
= arg
->length();
2221 (void) js_strncpy(cp
, arg
->chars(), arg_length
);
2224 /* Add separating comma or terminating 0. */
2225 *cp
++ = (i
+ 1 < n
) ? ',' : 0;
2228 /* Initialize a tokenstream that reads from the given string. */
2229 if (!ts
.init(collected_args
, args_length
, NULL
, filename
, lineno
)) {
2230 JS_ARENA_RELEASE(&cx
->tempPool
, mark
);
2234 /* The argument string may be empty or contain no tokens. */
2236 if (tt
!= TOK_EOF
) {
2239 * Check that it's a name. This also implicitly guards against
2240 * TOK_ERROR, which was already reported.
2246 * Get the atom corresponding to the name from the token
2247 * stream; we're assured at this point that it's a valid
2250 atom
= ts
.currentToken().t_atom
;
2252 /* Check for a duplicate parameter name. */
2253 if (js_LookupLocal(cx
, fun
, atom
, NULL
) != JSLOCAL_NONE
) {
2256 name
= js_AtomToPrintableString(cx
, atom
);
2257 ok
= name
&& ReportCompileErrorNumber(cx
, &ts
, NULL
,
2258 JSREPORT_WARNING
| JSREPORT_STRICT
,
2259 JSMSG_DUPLICATE_FORMAL
, name
);
2263 if (!js_AddLocal(cx
, fun
, atom
, JSLOCAL_ARG
))
2267 * Get the next token. Stop on end of stream. Otherwise
2268 * insist on a comma, get another name, and iterate.
2273 if (tt
!= TOK_COMMA
)
2281 if (state
== BAD_FORMAL
&& !ts
.isError()) {
2283 * Report "malformed formal parameter" iff no illegal char or
2284 * similar scanner error was already reported.
2286 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
2290 JS_ARENA_RELEASE(&cx
->tempPool
, mark
);
2296 str
= js_ValueToString(cx
, argv
[argc
-1]);
2299 argv
[argc
-1].setString(str
);
2301 str
= cx
->runtime
->emptyString
;
2304 return Compiler::compileFunctionBody(cx
, fun
, principals
,
2305 str
->chars(), str
->length(),
2310 js_InitFunctionClass(JSContext
*cx
, JSObject
*obj
)
2315 proto
= js_InitClass(cx
, obj
, NULL
, &js_FunctionClass
, Function
, 1,
2316 NULL
, function_methods
, NULL
, NULL
);
2319 fun
= js_NewFunction(cx
, proto
, NULL
, 0, JSFUN_INTERPRETED
, obj
, NULL
);
2322 fun
->u
.i
.script
= JSScript::emptyScript();
2327 js_NewFunction(JSContext
*cx
, JSObject
*funobj
, Native native
, uintN nargs
,
2328 uintN flags
, JSObject
*parent
, JSAtom
*atom
)
2333 JS_ASSERT(funobj
->isFunction());
2334 funobj
->setParent(parent
);
2336 funobj
= NewObject(cx
, &js_FunctionClass
, NULL
, parent
);
2340 JS_ASSERT(!funobj
->getPrivate());
2341 fun
= (JSFunction
*) funobj
;
2343 /* Initialize all function members. */
2344 fun
->nargs
= uint16(nargs
);
2345 fun
->flags
= flags
& (JSFUN_FLAGS_MASK
| JSFUN_KINDMASK
| JSFUN_TRCINFO
);
2346 if ((flags
& JSFUN_KINDMASK
) >= JSFUN_INTERPRETED
) {
2348 JS_ASSERT(nargs
== 0);
2350 fun
->u
.i
.nupvars
= 0;
2351 fun
->u
.i
.skipmin
= 0;
2352 fun
->u
.i
.wrapper
= false;
2353 fun
->u
.i
.script
= NULL
;
2355 fun
->u
.i
.names
.taggedAtom
= 0;
2360 fun
->u
.n
.clasp
= NULL
;
2361 if (flags
& JSFUN_TRCINFO
) {
2363 JSNativeTraceInfo
*trcinfo
=
2364 JS_FUNC_TO_DATA_PTR(JSNativeTraceInfo
*, native
);
2365 fun
->u
.n
.native
= (js::Native
) trcinfo
->native
;
2366 fun
->u
.n
.trcinfo
= trcinfo
;
2368 fun
->u
.n
.trcinfo
= NULL
;
2371 fun
->u
.n
.native
= native
;
2372 fun
->u
.n
.trcinfo
= NULL
;
2374 JS_ASSERT(fun
->u
.n
.native
);
2378 /* Set private to self to indicate non-cloned fully initialized function. */
2379 FUN_OBJECT(fun
)->setPrivate(fun
);
2383 JSObject
* JS_FASTCALL
2384 js_CloneFunctionObject(JSContext
*cx
, JSFunction
*fun
, JSObject
*parent
,
2391 * The cloned function object does not need the extra JSFunction members
2392 * beyond JSObject as it points to fun via the private slot.
2394 JSObject
*clone
= NewNativeClassInstance(cx
, &js_FunctionClass
, proto
, parent
);
2397 clone
->setPrivate(fun
);
2402 JS_DEFINE_CALLINFO_4(extern, OBJECT
, js_CloneFunctionObject
, CONTEXT
, FUNCTION
, OBJECT
, OBJECT
, 0,
2403 nanojit::ACC_STORE_ANY
)
2407 * Create a new flat closure, but don't initialize the imported upvar
2408 * values. The tracer calls this function and then initializes the upvar
2411 JSObject
* JS_FASTCALL
2412 js_AllocFlatClosure(JSContext
*cx
, JSFunction
*fun
, JSObject
*scopeChain
)
2414 JS_ASSERT(FUN_FLAT_CLOSURE(fun
));
2415 JS_ASSERT((fun
->u
.i
.script
->upvarsOffset
2416 ? fun
->u
.i
.script
->upvars()->length
2417 : 0) == fun
->u
.i
.nupvars
);
2419 JSObject
*closure
= CloneFunctionObject(cx
, fun
, scopeChain
);
2423 uint32 nslots
= fun
->countInterpretedReservedSlots();
2426 if (!js_EnsureReservedSlots(cx
, closure
, nslots
))
2432 JS_DEFINE_CALLINFO_3(extern, OBJECT
, js_AllocFlatClosure
,
2433 CONTEXT
, FUNCTION
, OBJECT
, 0, nanojit::ACC_STORE_ANY
)
2435 JS_REQUIRES_STACK JSObject
*
2436 js_NewFlatClosure(JSContext
*cx
, JSFunction
*fun
)
2439 * Flat closures can be partial, they may need to search enclosing scope
2440 * objects via JSOP_NAME, etc.
2442 JSObject
*scopeChain
= js_GetScopeChain(cx
, cx
->fp
);
2446 JSObject
*closure
= js_AllocFlatClosure(cx
, fun
, scopeChain
);
2447 if (!closure
|| fun
->u
.i
.nupvars
== 0)
2450 JSUpvarArray
*uva
= fun
->u
.i
.script
->upvars();
2451 JS_ASSERT(uva
->length
<= closure
->dslots
[-1].toPrivateUint32());
2453 uintN level
= fun
->u
.i
.script
->staticLevel
;
2454 for (uint32 i
= 0, n
= uva
->length
; i
< n
; i
++)
2455 closure
->dslots
[i
] = GetUpvar(cx
, level
, uva
->vector
[i
]);
2461 js_NewDebuggableFlatClosure(JSContext
*cx
, JSFunction
*fun
)
2463 JS_ASSERT(cx
->fp
->fun
->flags
& JSFUN_HEAVYWEIGHT
);
2464 JS_ASSERT(!cx
->fp
->fun
->optimizedClosure());
2465 JS_ASSERT(FUN_FLAT_CLOSURE(fun
));
2467 return WrapEscapingClosure(cx
, cx
->fp
, FUN_OBJECT(fun
), fun
);
2471 js_DefineFunction(JSContext
*cx
, JSObject
*obj
, JSAtom
*atom
, Native native
,
2472 uintN nargs
, uintN attrs
)
2477 if (attrs
& JSFUN_STUB_GSOPS
) {
2479 * JSFUN_STUB_GSOPS is a request flag only, not stored in fun->flags or
2480 * the defined property's attributes. This allows us to encode another,
2481 * internal flag using the same bit, JSFUN_EXPR_CLOSURE -- see jsfun.h
2484 attrs
&= ~JSFUN_STUB_GSOPS
;
2485 gsop
= PropertyStub
;
2489 fun
= js_NewFunction(cx
, NULL
, native
, nargs
, attrs
, obj
, atom
);
2492 if (!obj
->defineProperty(cx
, ATOM_TO_JSID(atom
), ObjectValue(*fun
),
2493 gsop
, gsop
, attrs
& ~JSFUN_FLAGS_MASK
)) {
2499 #if (JSV2F_CONSTRUCT & JSV2F_SEARCH_STACK)
2500 # error "JSINVOKE_CONSTRUCT and JSV2F_SEARCH_STACK are not disjoint!"
2504 js_ValueToFunction(JSContext
*cx
, const Value
*vp
, uintN flags
)
2507 if (!IsFunctionObject(*vp
, &funobj
)) {
2508 js_ReportIsNotFunction(cx
, vp
, flags
);
2511 return GET_FUNCTION_PRIVATE(cx
, funobj
);
2515 js_ValueToFunctionObject(JSContext
*cx
, Value
*vp
, uintN flags
)
2518 JSStackFrame
*caller
;
2519 JSPrincipals
*principals
;
2522 if (IsFunctionObject(*vp
, &funobj
))
2525 fun
= js_ValueToFunction(cx
, vp
, flags
);
2528 vp
->setObject(*fun
);
2530 caller
= js_GetScriptedCaller(cx
, NULL
);
2532 principals
= JS_StackFramePrincipals(cx
, caller
);
2534 /* No scripted caller, don't allow access. */
2538 if (!js_CheckPrincipalsAccess(cx
, FUN_OBJECT(fun
), principals
,
2541 : cx
->runtime
->atomState
.anonymousAtom
)) {
2544 return FUN_OBJECT(fun
);
2548 js_ValueToCallableObject(JSContext
*cx
, Value
*vp
, uintN flags
)
2550 if (vp
->isObject()) {
2551 JSObject
*callable
= &vp
->toObject();
2552 if (callable
->isCallable())
2555 return js_ValueToFunctionObject(cx
, vp
, flags
);
2559 js_ReportIsNotFunction(JSContext
*cx
, const Value
*vp
, uintN flags
)
2561 const char *name
= NULL
, *source
= NULL
;
2562 AutoValueRooter
tvr(cx
);
2563 uintN error
= (flags
& JSV2F_CONSTRUCT
) ? JSMSG_NOT_CONSTRUCTOR
: JSMSG_NOT_FUNCTION
;
2565 FrameRegsIter
i(cx
);
2566 while (!i
.done() && !i
.pc())
2570 (!i
.done() && i
.fp()->base() <= vp
&& vp
< i
.sp())
2572 : ((flags
& JSV2F_SEARCH_STACK
) ? JSDVG_SEARCH_STACK
: JSDVG_IGNORE_STACK
);
2574 js_ReportValueError3(cx
, error
, spindex
, *vp
, NULL
, name
, source
);
2578 * When a function has between 2 and MAX_ARRAY_LOCALS arguments and variables,
2579 * their name are stored as the JSLocalNames.array.
2581 #define MAX_ARRAY_LOCALS 8
2583 JS_STATIC_ASSERT(2 <= MAX_ARRAY_LOCALS
);
2584 JS_STATIC_ASSERT(MAX_ARRAY_LOCALS
< JS_BITMASK(16));
2587 * When we use a hash table to store the local names, we use a singly linked
2588 * list to record the indexes of duplicated parameter names to preserve the
2589 * duplicates for the decompiler.
2591 typedef struct JSNameIndexPair JSNameIndexPair
;
2593 struct JSNameIndexPair
{
2596 JSNameIndexPair
*link
;
2599 struct JSLocalNameMap
{
2601 JSNameIndexPair
*lastdup
;
2604 typedef struct JSLocalNameHashEntry
{
2605 JSDHashEntryHdr hdr
;
2609 } JSLocalNameHashEntry
;
2612 FreeLocalNameHash(JSContext
*cx
, JSLocalNameMap
*map
)
2614 JSNameIndexPair
*dup
, *next
;
2616 for (dup
= map
->lastdup
; dup
; dup
= next
) {
2620 JS_DHashTableFinish(&map
->names
);
2625 HashLocalName(JSContext
*cx
, JSLocalNameMap
*map
, JSAtom
*name
,
2626 JSLocalKind localKind
, uintN index
)
2628 JSLocalNameHashEntry
*entry
;
2629 JSNameIndexPair
*dup
;
2631 JS_ASSERT(index
<= JS_BITMASK(16));
2632 #if JS_HAS_DESTRUCTURING
2634 /* A destructuring pattern does not need a hash entry. */
2635 JS_ASSERT(localKind
== JSLOCAL_ARG
);
2639 entry
= (JSLocalNameHashEntry
*)
2640 JS_DHashTableOperate(&map
->names
, name
, JS_DHASH_ADD
);
2642 JS_ReportOutOfMemory(cx
);
2646 JS_ASSERT(entry
->name
== name
);
2647 JS_ASSERT(entry
->localKind
== JSLOCAL_ARG
&& localKind
== JSLOCAL_ARG
);
2648 dup
= (JSNameIndexPair
*) cx
->malloc(sizeof *dup
);
2651 dup
->name
= entry
->name
;
2652 dup
->index
= entry
->index
;
2653 dup
->link
= map
->lastdup
;
2657 entry
->index
= (uint16
) index
;
2658 entry
->localKind
= (uint8
) localKind
;
2663 js_AddLocal(JSContext
*cx
, JSFunction
*fun
, JSAtom
*atom
, JSLocalKind kind
)
2669 JSLocalNameMap
*map
;
2671 JS_ASSERT(FUN_INTERPRETED(fun
));
2672 JS_ASSERT(!fun
->u
.i
.script
);
2673 JS_ASSERT(((jsuword
) atom
& 1) == 0);
2674 taggedAtom
= (jsuword
) atom
;
2675 if (kind
== JSLOCAL_ARG
) {
2676 indexp
= &fun
->nargs
;
2677 } else if (kind
== JSLOCAL_UPVAR
) {
2678 indexp
= &fun
->u
.i
.nupvars
;
2680 indexp
= &fun
->u
.i
.nvars
;
2681 if (kind
== JSLOCAL_CONST
)
2684 JS_ASSERT(kind
== JSLOCAL_VAR
);
2686 n
= fun
->countLocalNames();
2688 JS_ASSERT(fun
->u
.i
.names
.taggedAtom
== 0);
2689 fun
->u
.i
.names
.taggedAtom
= taggedAtom
;
2690 } else if (n
< MAX_ARRAY_LOCALS
) {
2692 array
= fun
->u
.i
.names
.array
;
2694 array
= (jsuword
*) cx
->malloc(MAX_ARRAY_LOCALS
* sizeof *array
);
2697 array
[0] = fun
->u
.i
.names
.taggedAtom
;
2698 fun
->u
.i
.names
.array
= array
;
2700 if (kind
== JSLOCAL_ARG
) {
2702 * A destructuring argument pattern adds variables, not arguments,
2703 * so for the following arguments nvars != 0.
2705 #if JS_HAS_DESTRUCTURING
2706 if (fun
->u
.i
.nvars
!= 0) {
2707 memmove(array
+ fun
->nargs
+ 1, array
+ fun
->nargs
,
2708 fun
->u
.i
.nvars
* sizeof *array
);
2711 JS_ASSERT(fun
->u
.i
.nvars
== 0);
2713 array
[fun
->nargs
] = taggedAtom
;
2715 array
[n
] = taggedAtom
;
2717 } else if (n
== MAX_ARRAY_LOCALS
) {
2718 array
= fun
->u
.i
.names
.array
;
2719 map
= (JSLocalNameMap
*) cx
->malloc(sizeof *map
);
2722 if (!JS_DHashTableInit(&map
->names
, JS_DHashGetStubOps(),
2723 NULL
, sizeof(JSLocalNameHashEntry
),
2724 JS_DHASH_DEFAULT_CAPACITY(MAX_ARRAY_LOCALS
2726 JS_ReportOutOfMemory(cx
);
2731 map
->lastdup
= NULL
;
2732 for (i
= 0; i
!= MAX_ARRAY_LOCALS
; ++i
) {
2733 taggedAtom
= array
[i
];
2735 JSLocalKind k
= JSLOCAL_ARG
;
2736 if (j
>= fun
->nargs
) {
2738 if (j
< fun
->u
.i
.nvars
) {
2739 k
= (taggedAtom
& 1) ? JSLOCAL_CONST
: JSLOCAL_VAR
;
2741 j
-= fun
->u
.i
.nvars
;
2745 if (!HashLocalName(cx
, map
, (JSAtom
*) (taggedAtom
& ~1), k
, j
)) {
2746 FreeLocalNameHash(cx
, map
);
2750 if (!HashLocalName(cx
, map
, atom
, kind
, *indexp
)) {
2751 FreeLocalNameHash(cx
, map
);
2756 * At this point the entry is added and we cannot fail. It is time
2757 * to replace fun->u.i.names with the built map.
2759 fun
->u
.i
.names
.map
= map
;
2762 if (*indexp
== JS_BITMASK(16)) {
2763 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
2764 (kind
== JSLOCAL_ARG
)
2765 ? JSMSG_TOO_MANY_FUN_ARGS
2766 : JSMSG_TOO_MANY_LOCALS
);
2769 if (!HashLocalName(cx
, fun
->u
.i
.names
.map
, atom
, kind
, *indexp
))
2773 /* Update the argument or variable counter. */
2779 js_LookupLocal(JSContext
*cx
, JSFunction
*fun
, JSAtom
*atom
, uintN
*indexp
)
2781 uintN n
, i
, upvar_base
;
2783 JSLocalNameHashEntry
*entry
;
2785 JS_ASSERT(FUN_INTERPRETED(fun
));
2786 n
= fun
->countLocalNames();
2788 return JSLOCAL_NONE
;
2789 if (n
<= MAX_ARRAY_LOCALS
) {
2790 array
= (n
== 1) ? &fun
->u
.i
.names
.taggedAtom
: fun
->u
.i
.names
.array
;
2792 /* Search from the tail to pick up the last duplicated name. */
2794 upvar_base
= fun
->countArgsAndVars();
2797 if (atom
== JS_LOCAL_NAME_TO_ATOM(array
[i
])) {
2798 if (i
< fun
->nargs
) {
2803 if (i
>= upvar_base
) {
2805 *indexp
= i
- upvar_base
;
2806 return JSLOCAL_UPVAR
;
2809 *indexp
= i
- fun
->nargs
;
2810 return JS_LOCAL_NAME_IS_CONST(array
[i
])
2816 entry
= (JSLocalNameHashEntry
*)
2817 JS_DHashTableOperate(&fun
->u
.i
.names
.map
->names
, atom
,
2819 if (JS_DHASH_ENTRY_IS_BUSY(&entry
->hdr
)) {
2820 JS_ASSERT(entry
->localKind
!= JSLOCAL_NONE
);
2822 *indexp
= entry
->index
;
2823 return (JSLocalKind
) entry
->localKind
;
2826 return JSLOCAL_NONE
;
2829 typedef struct JSLocalNameEnumeratorArgs
{
2836 } JSLocalNameEnumeratorArgs
;
2838 static JSDHashOperator
2839 get_local_names_enumerator(JSDHashTable
*table
, JSDHashEntryHdr
*hdr
,
2840 uint32 number
, void *arg
)
2842 JSLocalNameHashEntry
*entry
;
2843 JSLocalNameEnumeratorArgs
*args
;
2847 entry
= (JSLocalNameHashEntry
*) hdr
;
2848 args
= (JSLocalNameEnumeratorArgs
*) arg
;
2849 JS_ASSERT(entry
->name
);
2850 if (entry
->localKind
== JSLOCAL_ARG
) {
2851 JS_ASSERT(entry
->index
< args
->fun
->nargs
);
2852 JS_ASSERT(args
->nCopiedArgs
++ < args
->fun
->nargs
);
2856 JS_ASSERT(entry
->localKind
== JSLOCAL_VAR
||
2857 entry
->localKind
== JSLOCAL_CONST
||
2858 entry
->localKind
== JSLOCAL_UPVAR
);
2859 JS_ASSERT(entry
->index
< args
->fun
->u
.i
.nvars
+ args
->fun
->u
.i
.nupvars
);
2860 JS_ASSERT(args
->nCopiedVars
++ < unsigned(args
->fun
->u
.i
.nvars
+ args
->fun
->u
.i
.nupvars
));
2861 i
= args
->fun
->nargs
;
2862 if (entry
->localKind
== JSLOCAL_UPVAR
)
2863 i
+= args
->fun
->u
.i
.nvars
;
2865 constFlag
= (entry
->localKind
== JSLOCAL_CONST
);
2867 args
->names
[i
] = (jsuword
) entry
->name
| constFlag
;
2868 return JS_DHASH_NEXT
;
2872 js_GetLocalNameArray(JSContext
*cx
, JSFunction
*fun
, JSArenaPool
*pool
)
2876 JSLocalNameMap
*map
;
2877 JSLocalNameEnumeratorArgs args
;
2878 JSNameIndexPair
*dup
;
2880 JS_ASSERT(fun
->hasLocalNames());
2881 n
= fun
->countLocalNames();
2883 if (n
<= MAX_ARRAY_LOCALS
)
2884 return (n
== 1) ? &fun
->u
.i
.names
.taggedAtom
: fun
->u
.i
.names
.array
;
2887 * No need to check for overflow of the allocation size as we are making a
2888 * copy of already allocated data. As such it must fit size_t.
2890 JS_ARENA_ALLOCATE_CAST(names
, jsuword
*, pool
, (size_t) n
* sizeof *names
);
2892 js_ReportOutOfScriptQuota(cx
);
2896 #if JS_HAS_DESTRUCTURING
2897 /* Some parameter names can be NULL due to destructuring patterns. */
2898 PodZero(names
, fun
->nargs
);
2900 map
= fun
->u
.i
.names
.map
;
2904 args
.nCopiedArgs
= 0;
2905 args
.nCopiedVars
= 0;
2907 JS_DHashTableEnumerate(&map
->names
, get_local_names_enumerator
, &args
);
2908 for (dup
= map
->lastdup
; dup
; dup
= dup
->link
) {
2909 JS_ASSERT(dup
->index
< fun
->nargs
);
2910 JS_ASSERT(args
.nCopiedArgs
++ < fun
->nargs
);
2911 names
[dup
->index
] = (jsuword
) dup
->name
;
2913 #if !JS_HAS_DESTRUCTURING
2914 JS_ASSERT(args
.nCopiedArgs
== fun
->nargs
);
2916 JS_ASSERT(args
.nCopiedVars
== fun
->u
.i
.nvars
+ fun
->u
.i
.nupvars
);
2921 static JSDHashOperator
2922 trace_local_names_enumerator(JSDHashTable
*table
, JSDHashEntryHdr
*hdr
,
2923 uint32 number
, void *arg
)
2925 JSLocalNameHashEntry
*entry
;
2928 entry
= (JSLocalNameHashEntry
*) hdr
;
2929 JS_ASSERT(entry
->name
);
2930 trc
= (JSTracer
*) arg
;
2931 JS_SET_TRACING_INDEX(trc
,
2932 entry
->localKind
== JSLOCAL_ARG
? "arg" : "var",
2934 Mark(trc
, ATOM_TO_STRING(entry
->name
), JSTRACE_STRING
);
2935 return JS_DHASH_NEXT
;
2939 TraceLocalNames(JSTracer
*trc
, JSFunction
*fun
)
2945 JS_ASSERT(FUN_INTERPRETED(fun
));
2946 n
= fun
->countLocalNames();
2949 if (n
<= MAX_ARRAY_LOCALS
) {
2950 array
= (n
== 1) ? &fun
->u
.i
.names
.taggedAtom
: fun
->u
.i
.names
.array
;
2954 atom
= (JSAtom
*) (array
[i
] & ~1);
2956 JS_SET_TRACING_INDEX(trc
,
2957 i
< fun
->nargs
? "arg" : "var",
2958 i
< fun
->nargs
? i
: i
- fun
->nargs
);
2959 Mark(trc
, ATOM_TO_STRING(atom
), JSTRACE_STRING
);
2963 JS_DHashTableEnumerate(&fun
->u
.i
.names
.map
->names
,
2964 trace_local_names_enumerator
, trc
);
2967 * No need to trace the list of duplicates in map->lastdup as the
2968 * names there are traced when enumerating the hash table.
2974 DestroyLocalNames(JSContext
*cx
, JSFunction
*fun
)
2978 n
= fun
->countLocalNames();
2981 if (n
<= MAX_ARRAY_LOCALS
)
2982 cx
->free(fun
->u
.i
.names
.array
);
2984 FreeLocalNameHash(cx
, fun
->u
.i
.names
.map
);
2988 js_FreezeLocalNames(JSContext
*cx
, JSFunction
*fun
)
2993 JS_ASSERT(FUN_INTERPRETED(fun
));
2994 JS_ASSERT(!fun
->u
.i
.script
);
2995 n
= fun
->nargs
+ fun
->u
.i
.nvars
+ fun
->u
.i
.nupvars
;
2996 if (2 <= n
&& n
< MAX_ARRAY_LOCALS
) {
2997 /* Shrink over-allocated array ignoring realloc failures. */
2998 array
= (jsuword
*) cx
->realloc(fun
->u
.i
.names
.array
,
3001 fun
->u
.i
.names
.array
= array
;
3004 if (n
> MAX_ARRAY_LOCALS
)
3005 JS_DHashMarkTableImmutable(&fun
->u
.i
.names
.map
->names
);
3010 JSFunction::findDuplicateFormal() const
3015 /* Function with two to MAX_ARRAY_LOCALS parameters use an aray. */
3016 unsigned n
= nargs
+ u
.i
.nvars
+ u
.i
.nupvars
;
3017 if (n
<= MAX_ARRAY_LOCALS
) {
3018 jsuword
*array
= u
.i
.names
.array
;
3020 /* Quadratic, but MAX_ARRAY_LOCALS is 8, so at most 28 comparisons. */
3021 for (unsigned i
= 0; i
< nargs
; i
++) {
3022 for (unsigned j
= i
+ 1; j
< nargs
; j
++) {
3023 if (array
[i
] == array
[j
])
3024 return JS_LOCAL_NAME_TO_ATOM(array
[i
]);
3031 * Functions with more than MAX_ARRAY_LOCALS parameters use a hash
3032 * table. Hashed local name maps have already made a list of any
3033 * duplicate argument names for us.
3035 JSNameIndexPair
*dup
= u
.i
.names
.map
->lastdup
;
3036 return dup
? dup
->name
: NULL
;