[JAEGER] Merge from tracemonkey.
[mozilla-central.git] / js / src / jsfun.cpp
blobeebba984dc8edbf941df1ec76649c836ca883cd5
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
15 * License.
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
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.
25 * Contributor(s):
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.
44 #include <string.h>
45 #include "jstypes.h"
46 #include "jsstdint.h"
47 #include "jsbit.h"
48 #include "jsutil.h" /* Added by JSIFY */
49 #include "jsapi.h"
50 #include "jsarray.h"
51 #include "jsatom.h"
52 #include "jsbool.h"
53 #include "jsbuiltins.h"
54 #include "jscntxt.h"
55 #include "jsversion.h"
56 #include "jsdbgapi.h"
57 #include "jsemit.h"
58 #include "jsfun.h"
59 #include "jsgc.h"
60 #include "jsinterp.h"
61 #include "jslock.h"
62 #include "jsnum.h"
63 #include "jsobj.h"
64 #include "jsopcode.h"
65 #include "jsparse.h"
66 #include "jsproxy.h"
67 #include "jsscan.h"
68 #include "jsscope.h"
69 #include "jsscript.h"
70 #include "jsstr.h"
71 #include "jsexn.h"
72 #include "jsstaticcheck.h"
73 #include "jstracer.h"
75 #if JS_HAS_GENERATORS
76 # include "jsiter.h"
77 #endif
79 #if JS_HAS_XDR
80 # include "jsxdrapi.h"
81 #endif
83 #include "jsatominlines.h"
84 #include "jscntxtinlines.h"
85 #include "jsobjinlines.h"
86 #include "jscntxtinlines.h"
88 using namespace js;
90 static inline void
91 SetArgsPrivateNative(JSObject *argsobj, ArgsPrivateNative *apn)
93 JS_ASSERT(argsobj->isArguments());
94 uintptr_t p = (uintptr_t) apn;
95 argsobj->setPrivate((void*) (p | 2));
98 JSBool
99 js_GetArgsValue(JSContext *cx, JSStackFrame *fp, Value *vp)
101 JSObject *argsobj;
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);
109 if (!argsobj)
110 return JS_FALSE;
111 vp->setObject(*argsobj);
112 return JS_TRUE;
115 JSBool
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);
122 Value v;
123 if (!fp->callobj->getProperty(cx, argumentsid, &v))
124 return false;
126 JSObject *obj;
127 if (v.isPrimitive()) {
128 obj = js_ValueToNonNullObject(cx, v);
129 if (!obj)
130 return false;
131 } else {
132 obj = &v.toObject();
134 return obj->getProperty(cx, id, vp);
137 vp->setUndefined();
138 if (JSID_IS_INT(id)) {
139 uint32 arg = uint32(JSID_TO_INT(id));
140 JSObject *argsobj = fp->argsobj;
141 if (arg < fp->argc) {
142 if (argsobj) {
143 if (argsobj->getArgsElement(arg).isMagic(JS_ARGS_HOLE))
144 return argsobj->getProperty(cx, id, vp);
146 *vp = fp->argv[arg];
147 } else {
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]; }
154 * f();
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
158 * undefined in *vp.
160 if (argsobj)
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);
169 return true;
172 static JSObject *
173 NewArguments(JSContext *cx, JSObject *parent, uint32 argc, JSObject *callee)
175 JSObject *proto;
176 if (!js_GetClassPrototype(cx, parent, JSProto_Object, &proto))
177 return NULL;
179 JSObject *argsobj = js_NewGCObject(cx);
180 if (!argsobj)
181 return NULL;
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))
192 return NULL;
193 return argsobj;
196 static void
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]);
206 JSObject *
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.
213 JS_ASSERT(fp->fun);
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)
219 fp = fp->down;
221 /* Create an arguments object for fp only if it lacks one. */
222 JSObject *argsobj = fp->argsobj;
223 if (argsobj)
224 return 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());
229 if (!argsobj)
230 return argsobj;
232 /* Link the new object to fp so it can get actual argument values. */
233 argsobj->setPrivate(fp);
234 fp->argsobj = argsobj;
235 return argsobj;
238 void
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);
245 fp->argsobj = NULL;
249 * Traced versions of js_GetArgsObject and js_PutArgsObject.
252 #ifdef JS_TRACER
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);
258 if (!argsobj)
259 return NULL;
260 apn->argv = argv;
261 SetArgsPrivateNative(argsobj, apn);
262 return argsobj;
264 #endif
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. */
270 JSBool JS_FASTCALL
271 js_PutArguments(JSContext *cx, JSObject *argsobj, Value *args)
273 JS_ASSERT(GetArgsPrivateNative(argsobj));
274 PutArguments(cx, argsobj, args);
275 argsobj->setPrivate(NULL);
276 return true;
279 JS_DEFINE_CALLINFO_3(extern, BOOL, js_PutArguments, CONTEXT, OBJECT, VALUEPTR, 0,
280 nanojit::ACC_STORE_ANY)
282 static JSBool
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));
296 return true;
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);
314 if (!scopeChain)
315 return NULL;
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,
323 funobj, scopeChain);
324 if (!wfunobj)
325 return NULL;
326 AutoObjectRooter tvr(cx, wfunobj);
328 JSFunction *wfun = (JSFunction *) wfunobj;
329 wfunobj->setPrivate(wfun);
330 wfun->nargs = 0;
331 wfun->flags = fun->flags | JSFUN_HEAVYWEIGHT;
332 wfun->u.i.nvars = 0;
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);
343 if (!names)
344 return NULL;
346 JSBool ok = true;
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)
351 ? JSLOCAL_ARG
352 : (i < fun->countArgsAndVars())
353 ? (JS_LOCAL_NAME_IS_CONST(name)
354 ? JSLOCAL_CONST
355 : JSLOCAL_VAR)
356 : JSLOCAL_UPVAR;
358 ok = js_AddLocal(cx, wfun, atom, localKind);
359 if (!ok)
360 break;
363 JS_ARENA_RELEASE(&cx->tempPool, mark);
364 if (!ok)
365 return NULL;
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))
376 sn = SN_NEXT(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
384 : 0,
385 fun->u.i.nupvars,
386 (script->regexpsOffset != 0)
387 ? script->regexps()->length
388 : 0,
389 (script->trynotesOffset != 0)
390 ? script->trynotes()->length
391 : 0,
392 (script->constOffset != 0)
393 ? script->consts()->length
394 : 0,
395 (script->globalsOffset != 0)
396 ? script->globals()->length
397 : 0);
398 if (!wscript)
399 return NULL;
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;
436 if (oplen < 0)
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
443 * immediate operand.
445 switch (op) {
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;
453 default:;
455 pc += oplen;
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;
477 #endif
479 /* Deoptimize wfun from FUN_{FLAT,NULL}_CLOSURE to FUN_INTERPRETED. */
480 FUN_SET_KIND(wfun, JSFUN_INTERPRETED);
481 wfun->u.i.script = wscript;
482 return wfunobj;
485 static JSBool
486 ArgGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
488 if (!InstanceOf(cx, obj, &js_ArgumentsClass, NULL))
489 return true;
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()) {
498 #ifdef JS_TRACER
499 ArgsPrivateNative *argp = GetArgsPrivateNative(obj);
500 if (argp) {
501 ExternNativeToValue(cx, *vp, argp->typemap()[arg], &argp->argv[arg]);
502 return true;
504 #endif
506 JSStackFrame *fp = (JSStackFrame *) obj->getPrivate();
507 if (fp) {
508 *vp = fp->argv[arg];
509 } else {
510 const Value &v = obj->getArgsElement(arg);
511 if (!v.isMagic(JS_ARGS_HOLE))
512 *vp = v;
515 } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
516 if (!obj->isArgsLengthOverridden())
517 vp->setInt32(obj->getArgsLength());
518 } else {
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);
533 return false;
535 *vp = v;
538 return true;
541 static JSBool
542 ArgSetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
544 #ifdef JS_TRACER
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)) {
550 DeepBail(cx);
551 return false;
553 #endif
555 if (!InstanceOf(cx, obj, &js_ArgumentsClass, NULL))
556 return true;
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();
562 if (fp) {
563 fp->argv[arg] = *vp;
564 return true;
567 } else {
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
576 * collect its value.
578 AutoValueRooter tvr(cx);
579 return js_DeleteProperty(cx, obj, id, tvr.addr()) &&
580 js_SetProperty(cx, obj, id, vp);
583 static JSBool
584 args_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
585 JSObject **objp)
587 JS_ASSERT(obj->isArguments());
589 *objp = NULL;
590 bool valid = false;
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))
594 valid = true;
595 } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
596 if (!obj->isArgsLengthOverridden())
597 valid = true;
598 } else if (JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom)) {
599 if (!obj->getArgsCallee().isMagic(JS_ARGS_HOLE))
600 valid = true;
603 if (valid) {
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))
610 return JS_FALSE;
611 *objp = obj;
613 return true;
616 static JSBool
617 args_enumerate(JSContext *cx, JSObject *obj)
619 JS_ASSERT(obj->isArguments());
622 * Trigger reflection in args_resolve using a series of js_LookupProperty
623 * calls.
625 int argc = int(obj->getArgsLength());
626 for (int i = -2; i != argc; i++) {
627 jsid id = (i == -2)
628 ? ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)
629 : (i == -1)
630 ? ATOM_TO_JSID(cx->runtime->atomState.calleeAtom)
631 : INT_TO_JSID(i);
633 JSObject *pobj;
634 JSProperty *prop;
635 if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
636 return false;
638 /* prop is null when the property was deleted. */
639 if (prop)
640 pobj->dropProperty(cx, prop);
642 return true;
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.
654 static void
655 args_or_call_trace(JSTracer *trc, JSObject *obj)
657 if (obj->isArguments()) {
658 if (GetArgsPrivateNative(obj))
659 return;
660 } else {
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");
670 #else
671 # define args_or_call_trace NULL
672 #endif
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
679 * "Object".
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
684 * arguments object.
686 Class js_ArgumentsClass = {
687 "Arguments",
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,
694 ConvertStub, NULL,
695 NULL, NULL,
696 NULL, NULL,
697 NULL, NULL,
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 = {
710 js_Object_str,
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
717 static JSBool
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;
725 JSObject *funobj;
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()) {
736 LeaveTrace(cx);
738 JSStackFrame *fp = (JSStackFrame *) obj->getPrivate();
739 if (fp) {
740 JSObject *wrapper = WrapEscapingClosure(cx, fp, funobj, fun);
741 if (!wrapper)
742 return false;
743 vp->setObject(*wrapper);
744 return true;
747 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
748 JSMSG_OPTIMIZED_CLOSURE_LEAK);
749 return false;
752 return true;
755 static JSBool
756 CalleeGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
758 return CheckForEscapingClosure(cx, obj, vp);
761 static JSObject *
762 NewCallObject(JSContext *cx, JSFunction *fun, JSObject *scopeChain)
764 JSObject *callobj = js_NewGCObject(cx);
765 if (!callobj)
766 return NULL;
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()))
774 return NULL;
775 return callobj;
778 static inline JSObject *
779 NewDeclEnvObject(JSContext *cx, JSStackFrame *fp)
781 JSObject *envobj = js_NewGCObject(cx);
782 if (!envobj)
783 return NULL;
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();
788 return envobj;
791 JSObject *
792 js_GetCallObject(JSContext *cx, JSStackFrame *fp)
794 JSObject *callobj;
796 /* Create a call object for fp only if it lacks one. */
797 JS_ASSERT(fp->fun);
798 callobj = fp->callobj;
799 if (callobj)
800 return callobj;
802 #ifdef DEBUG
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);
809 #endif
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
815 * function's name.
817 JSAtom *lambdaName = (fp->fun->flags & JSFUN_LAMBDA) ? fp->fun->atom : NULL;
818 if (lambdaName) {
819 JSObject *envobj = NewDeclEnvObject(cx, fp);
820 if (!envobj)
821 return NULL;
823 /* Root envobj before js_DefineNativeProperty (-> JSClass.addProperty). */
824 fp->scopeChain = envobj;
825 JS_ASSERT(fp->argv);
826 if (!js_DefineNativeProperty(cx, fp->scopeChain, ATOM_TO_JSID(lambdaName),
827 fp->calleeValue(),
828 CalleeGetter, NULL,
829 JSPROP_PERMANENT | JSPROP_READONLY,
830 0, 0, NULL)) {
831 return NULL;
835 callobj = NewCallObject(cx, fp->fun, fp->scopeChain);
836 if (!callobj)
837 return NULL;
839 callobj->setPrivate(fp);
840 JS_ASSERT(fp->argv);
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
847 * variables object.
849 fp->scopeChain = callobj;
850 return 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);
858 if (!callobj)
859 return NULL;
860 callobj->setSlot(JSSLOT_CALLEE, ObjectValue(*callee));
861 return callobj;
864 JS_DEFINE_CALLINFO_4(extern, OBJECT, js_CreateCallObjectOnTrace, CONTEXT, FUNCTION, OBJECT, OBJECT,
865 0, nanojit::ACC_STORE_ANY)
867 JSFunction *
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. */
874 return NULL;
876 JS_ASSERT(v.isObject());
877 return GET_FUNCTION_PRIVATE(cx, &v.toObject());
880 inline static void
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));
887 void
888 js_PutCallObject(JSContext *cx, JSStackFrame *fp)
890 JSObject *callobj = fp->callobj;
891 JS_ASSERT(callobj);
893 /* Get the arguments object to snapshot fp's actual argument values. */
894 if (fp->argsobj) {
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);
910 if (n != 0) {
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);
926 fp->callobj = NULL;
929 JSBool JS_FASTCALL
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;
937 if (n != 0)
938 CopyValuesToCallObject(scopeChain, nargs, argv, nvars, slots);
940 return true;
943 JS_DEFINE_CALLINFO_6(extern, BOOL, js_PutCallObjectOnTrace, CONTEXT, OBJECT, UINT32, VALUEPTR,
944 UINT32, VALUEPTR, 0, nanojit::ACC_STORE_ANY)
946 static JSBool
947 call_enumerate(JSContext *cx, JSObject *obj)
949 JSFunction *fun;
950 uintN n, i;
951 void *mark;
952 jsuword *names;
953 JSBool ok;
954 JSAtom *name;
955 JSObject *pobj;
956 JSProperty *prop;
958 fun = js_GetCallObjectFunction(obj);
959 n = fun ? fun->countArgsAndVars() : 0;
960 if (n == 0)
961 return JS_TRUE;
963 mark = JS_ARENA_MARK(&cx->tempPool);
965 MUST_FLOW_THROUGH("out");
966 names = js_GetLocalNameArray(cx, fun, &cx->tempPool);
967 if (!names) {
968 ok = JS_FALSE;
969 goto out;
972 for (i = 0; i != n; ++i) {
973 name = JS_LOCAL_NAME_TO_ATOM(names[i]);
974 if (!name)
975 continue;
978 * Trigger reflection by looking up the name of the argument or
979 * variable.
981 ok = js_LookupProperty(cx, obj, ATOM_TO_JSID(name), &pobj, &prop);
982 if (!ok)
983 goto out;
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.
990 JS_ASSERT(prop);
991 JS_ASSERT(pobj == obj);
992 pobj->dropProperty(cx, prop);
994 ok = JS_TRUE;
996 out:
997 JS_ARENA_RELEASE(&cx->tempPool, mark);
998 return ok;
1001 enum JSCallPropertyKind {
1002 JSCPK_ARGUMENTS,
1003 JSCPK_ARG,
1004 JSCPK_VAR,
1005 JSCPK_UPVAR
1008 static JSBool
1009 CallPropertyOp(JSContext *cx, JSObject *obj, jsid id, Value *vp,
1010 JSCallPropertyKind kind, JSBool setter = false)
1012 JS_ASSERT(obj->getClass() == &js_CallClass);
1014 uintN i = 0;
1015 if (kind != JSCPK_ARGUMENTS) {
1016 JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
1017 i = (uint16) JSID_TO_INT(id);
1020 Value *array;
1021 if (kind == JSCPK_UPVAR) {
1022 JSObject *callee = &obj->getSlot(JSSLOT_CALLEE).toObject();
1024 #ifdef DEBUG
1025 JSFunction *callee_fun = (JSFunction *) callee->getPrivate();
1026 JS_ASSERT(FUN_FLAT_CLOSURE(callee_fun));
1027 JS_ASSERT(i < callee_fun->u.i.nupvars);
1028 #endif
1030 array = callee->dslots;
1031 } else {
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) {
1039 if (setter) {
1040 if (fp)
1041 fp->flags |= JSFRAME_OVERRIDE_ARGS;
1042 obj->setSlot(JSSLOT_CALL_ARGUMENTS, *vp);
1043 } else {
1044 if (fp && !(fp->flags & JSFRAME_OVERRIDE_ARGS)) {
1045 JSObject *argsobj;
1047 argsobj = js_GetArgsObject(cx, fp);
1048 if (!argsobj)
1049 return false;
1050 vp->setObject(*argsobj);
1051 } else {
1052 *vp = obj->getSlot(JSSLOT_CALL_ARGUMENTS);
1055 return true;
1058 if (!fp) {
1059 i += CALL_CLASS_FIXED_RESERVED_SLOTS;
1060 if (kind == JSCPK_VAR)
1061 i += fun->nargs;
1062 else
1063 JS_ASSERT(kind == JSCPK_ARG);
1064 return setter
1065 ? JS_SetReservedSlot(cx, obj, i, Jsvalify(*vp))
1066 : JS_GetReservedSlot(cx, obj, i, Jsvalify(vp));
1069 if (kind == JSCPK_ARG) {
1070 array = fp->argv;
1071 } else {
1072 JS_ASSERT(kind == JSCPK_VAR);
1073 array = fp->slots();
1077 if (setter) {
1078 GC_POKE(cx, array[i]);
1079 array[i] = *vp;
1080 } else {
1081 *vp = array[i];
1083 return true;
1086 static JSBool
1087 GetCallArguments(JSContext *cx, JSObject *obj, jsid id, Value *vp)
1089 return CallPropertyOp(cx, obj, id, vp, JSCPK_ARGUMENTS);
1092 static JSBool
1093 SetCallArguments(JSContext *cx, JSObject *obj, jsid id, Value *vp)
1095 return CallPropertyOp(cx, obj, id, vp, JSCPK_ARGUMENTS, true);
1098 JSBool
1099 js_GetCallArg(JSContext *cx, JSObject *obj, jsid id, Value *vp)
1101 return CallPropertyOp(cx, obj, id, vp, JSCPK_ARG);
1104 JSBool
1105 SetCallArg(JSContext *cx, JSObject *obj, jsid id, Value *vp)
1107 return CallPropertyOp(cx, obj, id, vp, JSCPK_ARG, true);
1110 JSBool
1111 GetFlatUpvar(JSContext *cx, JSObject *obj, jsid id, Value *vp)
1113 return CallPropertyOp(cx, obj, id, vp, JSCPK_UPVAR);
1116 JSBool
1117 SetFlatUpvar(JSContext *cx, JSObject *obj, jsid id, Value *vp)
1119 return CallPropertyOp(cx, obj, id, vp, JSCPK_UPVAR, true);
1122 JSBool
1123 js_GetCallVar(JSContext *cx, JSObject *obj, jsid id, Value *vp)
1125 return CallPropertyOp(cx, obj, id, vp, JSCPK_VAR);
1128 JSBool
1129 js_GetCallVarChecked(JSContext *cx, JSObject *obj, jsid id, Value *vp)
1131 if (!CallPropertyOp(cx, obj, id, vp, JSCPK_VAR))
1132 return false;
1134 return CheckForEscapingClosure(cx, obj, vp);
1137 JSBool
1138 SetCallVar(JSContext *cx, JSObject *obj, jsid id, Value *vp)
1140 return CallPropertyOp(cx, obj, id, vp, JSCPK_VAR, true);
1143 #if JS_TRACER
1144 JSBool JS_FASTCALL
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)
1153 JSBool JS_FASTCALL
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)
1161 #endif
1163 static JSBool
1164 call_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
1165 JSObject **objp)
1167 JSFunction *fun;
1168 JSLocalKind localKind;
1169 PropertyOp getter, setter;
1170 uintN slot, attrs;
1172 JS_ASSERT(obj->getClass() == &js_CallClass);
1173 JS_ASSERT(!obj->getProto());
1175 if (!JSID_IS_ATOM(id))
1176 return JS_TRUE;
1178 const Value &callee = obj->getSlot(JSSLOT_CALLEE);
1179 if (callee.isUndefined())
1180 return JS_TRUE;
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;
1207 } else {
1208 JSCallPropertyKind cpkind;
1209 if (localKind == JSLOCAL_UPVAR) {
1210 if (!FUN_FLAT_CLOSURE(fun))
1211 return JS_TRUE;
1212 getter = GetFlatUpvar;
1213 setter = SetFlatUpvar;
1214 cpkind = JSCPK_UPVAR;
1215 } else {
1216 JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
1217 JS_ASSERT(slot < fun->u.i.nvars);
1218 getter = js_GetCallVar;
1219 setter = SetCallVar;
1220 cpkind = JSCPK_VAR;
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.
1230 Value v;
1231 if (!CallPropertyOp(cx, obj, INT_TO_JSID((int16)slot), &v, cpkind))
1232 return JS_FALSE;
1233 JSObject *funobj;
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)) {
1242 return JS_FALSE;
1244 *objp = obj;
1245 return JS_TRUE;
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)) {
1257 return JS_FALSE;
1259 *objp = obj;
1260 return JS_TRUE;
1263 /* Control flow reaches here only if id was not resolved. */
1264 return JS_TRUE;
1267 JS_PUBLIC_DATA(Class) js_CallClass = {
1268 "Call",
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,
1275 NULL, NULL,
1276 NULL, NULL,
1277 NULL, NULL,
1278 NULL, NULL,
1279 JS_CLASS_TRACE(args_or_call_trace), NULL
1282 /* Generic function tinyids. */
1283 enum {
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 */
1291 static JSBool
1292 fun_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
1294 if (!JSID_IS_INT(id))
1295 return JS_TRUE;
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).
1319 JSFunction *fun;
1320 while (!(fun = (JSFunction *)
1321 GetInstancePrivate(cx, obj, &js_FunctionClass, NULL))) {
1322 if (slot != FUN_LENGTH)
1323 return JS_TRUE;
1324 obj = obj->getProto();
1325 if (!obj)
1326 return JS_TRUE;
1329 /* Find fun's top-most activation record. */
1330 JSStackFrame *fp;
1331 for (fp = js_GetTopStackFrame(cx);
1332 fp && (fp->fun != fun || (fp->flags & JSFRAME_SPECIAL));
1333 fp = fp->down) {
1334 continue;
1337 switch (slot) {
1338 case FUN_ARGUMENTS:
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)) {
1345 return JS_FALSE;
1347 if (fp) {
1348 if (!js_GetArgsValue(cx, fp, vp))
1349 return JS_FALSE;
1350 } else {
1351 vp->setNull();
1353 break;
1355 case FUN_LENGTH:
1356 case FUN_ARITY:
1357 vp->setInt32(fun->nargs);
1358 break;
1360 case FUN_NAME:
1361 vp->setString(fun->atom ? ATOM_TO_STRING(fun->atom)
1362 : cx->runtime->emptyString);
1363 break;
1365 case FUN_CALLER:
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);
1376 if (!wrapper)
1377 return JS_FALSE;
1378 vp->setObject(*wrapper);
1379 return JS_TRUE;
1382 JS_ASSERT(fp->down->argv);
1383 *vp = fp->down->calleeValue();
1384 } else {
1385 vp->setNull();
1388 /* Censor the caller if it is from another compartment. */
1389 if (vp->isObject()) {
1390 if (vp->toObject().getCompartment(cx) != cx->compartment)
1391 vp->setNull();
1393 break;
1395 default:
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];
1399 break;
1402 return JS_TRUE;
1405 struct LazyFunctionProp {
1406 uint16 atomOffset;
1407 int8 tinyid;
1408 uint8 attrs;
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},
1419 static JSBool
1420 fun_enumerate(JSContext *cx, JSObject *obj)
1422 JS_ASSERT(obj->isFunction());
1424 jsval v;
1425 jsid id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
1426 if (!JS_LookupPropertyById(cx, obj, id, &v))
1427 return false;
1428 id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
1429 if (!JS_LookupPropertyById(cx, obj, id, &v))
1430 return false;
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))
1436 return false;
1439 return true;
1442 static JSBool
1443 fun_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
1444 JSObject **objp)
1446 if (!JSID_IS_ATOM(id))
1447 return JS_TRUE;
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));
1458 return JS_TRUE;
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))
1474 return JS_TRUE;
1477 * Make the prototype object an instance of Object with the same parent
1478 * as the function object itself.
1480 JSObject *parent = obj->getParent();
1481 JSObject *proto;
1482 if (!js_GetClassPrototype(cx, parent, JSProto_Object, &proto))
1483 return JS_FALSE;
1484 proto = NewNativeClassInstance(cx, &js_ObjectClass, proto, parent);
1485 if (!proto)
1486 return JS_FALSE;
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))
1496 return JS_FALSE;
1498 *objp = obj;
1499 return JS_TRUE;
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)) {
1508 return JS_FALSE;
1510 *objp = obj;
1511 return JS_TRUE;
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)) {
1526 return JS_FALSE;
1528 *objp = obj;
1529 return JS_TRUE;
1533 return JS_TRUE;
1536 #if JS_HAS_XDR
1538 /* XXX store parent and proto, if defined */
1539 JSBool
1540 js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp)
1542 JSContext *cx;
1543 JSFunction *fun;
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 */
1551 cx = xdr->cx;
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));
1558 return false;
1560 if (fun->u.i.wrapper) {
1561 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1562 JSMSG_XDR_CLOSURE_WRAPPER,
1563 JS_GetFunctionName(fun));
1564 return false;
1566 JS_ASSERT((fun->u.i.wrapper & ~1U) == 0);
1567 firstword = (fun->u.i.skipmin << 2) | (fun->u.i.wrapper << 1) | !!fun->atom;
1568 nargs = fun->nargs;
1569 nvars = fun->u.i.nvars;
1570 nupvars = fun->u.i.nupvars;
1571 localsword = (nargs << 16) | nvars;
1572 flagsword = (nupvars << 16) | fun->flags;
1573 } else {
1574 fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, NULL, NULL);
1575 if (!fun)
1576 return false;
1577 FUN_OBJECT(fun)->clearParent();
1578 FUN_OBJECT(fun)->clearProto();
1579 #ifdef __GNUC__
1580 nvars = nargs = nupvars = 0; /* quell GCC uninitialized warning */
1581 #endif
1584 AutoObjectRooter tvr(cx, FUN_OBJECT(fun));
1586 if (!JS_XDRUint32(xdr, &firstword))
1587 return false;
1588 if ((firstword & 1U) && !js_XDRAtom(xdr, &fun->atom))
1589 return false;
1590 if (!JS_XDRUint32(xdr, &localsword) ||
1591 !JS_XDRUint32(xdr, &flagsword)) {
1592 return false;
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;
1607 if (n != 0) {
1608 void *mark;
1609 uintN i;
1610 uintN bitmapLength;
1611 uint32 *bitmap;
1612 jsuword *names;
1613 JSAtom *name;
1614 JSLocalKind localKind;
1616 bool ok = true;
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.
1628 * */
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);
1633 if (!bitmap) {
1634 js_ReportOutOfScriptQuota(xdr->cx);
1635 ok = false;
1636 goto release_mark;
1638 if (xdr->mode == JSXDR_ENCODE) {
1639 names = js_GetLocalNameArray(xdr->cx, fun, &xdr->cx->tempPool);
1640 if (!names) {
1641 ok = false;
1642 goto release_mark;
1644 PodZero(bitmap, bitmapLength);
1645 for (i = 0; i != n; ++i) {
1646 if (i < fun->nargs
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));
1654 #ifdef __GNUC__
1655 else {
1656 names = NULL; /* quell GCC uninitialized warning */
1658 #endif
1659 for (i = 0; i != bitmapLength; ++i) {
1660 ok = !!JS_XDRUint32(xdr, &bitmap[i]);
1661 if (!ok)
1662 goto release_mark;
1664 for (i = 0; i != n; ++i) {
1665 if (i < nargs &&
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);
1670 if (!ok)
1671 goto release_mark;
1672 } else {
1673 JS_ASSERT(!JS_LOCAL_NAME_TO_ATOM(names[i]));
1675 continue;
1677 if (xdr->mode == JSXDR_ENCODE)
1678 name = JS_LOCAL_NAME_TO_ATOM(names[i]);
1679 ok = !!js_XDRAtom(xdr, &name);
1680 if (!ok)
1681 goto release_mark;
1682 if (xdr->mode == JSXDR_DECODE) {
1683 localKind = (i < nargs)
1684 ? JSLOCAL_ARG
1685 : (i < nargs + nvars)
1686 ? (bitmap[i >> JS_BITS_PER_UINT32_LOG2] &
1687 JS_BIT(i & (JS_BITS_PER_UINT32 - 1))
1688 ? JSLOCAL_CONST
1689 : JSLOCAL_VAR)
1690 : JSLOCAL_UPVAR;
1691 ok = !!js_AddLocal(xdr->cx, fun, name, localKind);
1692 if (!ok)
1693 goto release_mark;
1697 release_mark:
1698 JS_ARENA_RELEASE(&xdr->cx->tempPool, mark);
1699 if (!ok)
1700 return false;
1702 if (xdr->mode == JSXDR_DECODE)
1703 js_FreezeLocalNames(cx, fun);
1706 if (!js_XDRScript(xdr, &fun->u.i.script, false, NULL))
1707 return false;
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;
1714 #endif
1715 js_CallNewScriptHook(cx, fun->u.i.script, fun);
1719 return true;
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.
1733 static JSBool
1734 fun_hasInstance(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp)
1736 jsid id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
1737 Value pval;
1738 if (!obj->getProperty(cx, id, &pval))
1739 return JS_FALSE;
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);
1747 return JS_FALSE;
1750 *bp = js_IsDelegate(cx, &pval.toObject(), *v);
1751 return JS_TRUE;
1754 static void
1755 TraceLocalNames(JSTracer *trc, JSFunction *fun);
1757 static void
1758 DestroyLocalNames(JSContext *cx, JSFunction *fun);
1760 static void
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();
1765 if (!fun)
1766 return;
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");
1771 return;
1773 if (fun->atom)
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);
1782 static void
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)
1788 return;
1791 * Null-check of u.i.script is required since the parser sets interpreted
1792 * very early.
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);
1806 if (name) {
1807 uintN index = uintN(-1);
1808 #ifdef DEBUG
1809 JSLocalKind kind =
1810 #endif
1811 js_LookupLocal(cx, this, name, &index);
1812 JS_ASSERT(kind == JSLOCAL_VAR);
1813 return int(index);
1815 #endif
1816 return -1;
1819 uint32
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 = {
1833 js_Function_str,
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,
1840 NULL, NULL,
1841 NULL, NULL,
1842 js_XDRFunctionObject, fun_hasInstance,
1843 JS_CLASS_TRACE(fun_trace), NULL
1846 namespace js {
1848 JSString *
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,
1857 "object");
1858 return NULL;
1861 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, obj);
1862 if (!fun)
1863 return NULL;
1864 return JS_DecompileFunction(cx, fun, indent);
1867 } /* namespace js */
1869 static JSBool
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))
1876 return false;
1878 JSObject *obj = ComputeThisFromVp(cx, vp);
1879 if (!obj)
1880 return false;
1882 JSString *str = fun_toStringHelper(cx, obj, indent);
1883 if (!str)
1884 return false;
1886 vp->setString(str);
1887 return true;
1890 #if JS_HAS_TOSOURCE
1891 static JSBool
1892 fun_toSource(JSContext *cx, uintN argc, Value *vp)
1894 JS_ASSERT(IsFunctionObject(vp[0]));
1896 JSObject *obj = ComputeThisFromVp(cx, vp);
1897 if (!obj)
1898 return false;
1900 JSString *str = fun_toStringHelper(cx, obj, JS_DONT_PRETTY_PRINT);
1901 if (!str)
1902 return false;
1904 vp->setString(str);
1905 return true;
1907 #endif
1909 JSBool
1910 js_fun_call(JSContext *cx, uintN argc, Value *vp)
1912 LeaveTrace(cx);
1914 JSObject *obj = ComputeThisFromVp(cx, vp);
1915 if (!obj)
1916 return JS_FALSE;
1917 Value fval = vp[1];
1919 if (!js_IsCallable(fval)) {
1920 JSString *str = js_ValueToString(cx, fval);
1921 if (str) {
1922 const char *bytes = js_GetStringBytes(cx, str);
1924 if (bytes) {
1925 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1926 JSMSG_INCOMPATIBLE_PROTO,
1927 js_Function_str, js_call_str,
1928 bytes);
1931 return JS_FALSE;
1934 Value *argv = vp + 2;
1935 if (argc == 0) {
1936 /* Call fun with its global object as the 'this' param if no args. */
1937 obj = NULL;
1938 } else {
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))
1943 return JS_FALSE;
1944 argc--;
1945 argv++;
1948 /* Allocate stack space for fval, obj, and the args. */
1949 InvokeArgsGuard args;
1950 if (!cx->stack().pushInvokeArgs(cx, argc, args))
1951 return JS_FALSE;
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();
1960 return ok;
1963 JSBool
1964 js_fun_apply(JSContext *cx, uintN argc, Value *vp)
1966 if (argc == 0) {
1967 /* Will get globalObject as 'this' and no other arguments. */
1968 return js_fun_call(cx, argc, vp);
1971 LeaveTrace(cx);
1973 JSObject *obj = ComputeThisFromVp(cx, vp);
1974 if (!obj)
1975 return JS_FALSE;
1977 Value fval = vp[1];
1978 if (!js_IsCallable(fval)) {
1979 JSString *str = js_ValueToString(cx, fval);
1980 if (str) {
1981 const char *bytes = js_GetStringBytes(cx, str);
1983 if (bytes) {
1984 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1985 JSMSG_INCOMPATIBLE_PROTO,
1986 js_Function_str, js_apply_str,
1987 bytes);
1990 return JS_FALSE;
1993 /* Quell GCC overwarnings. */
1994 JSObject *aobj = NULL;
1995 jsuint length = 0;
1997 if (argc >= 2) {
1998 /* If the 2nd arg is null or void, call the function with 0 args. */
1999 if (vp[3].isNullOrUndefined()) {
2000 argc = 0;
2001 } else {
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))
2007 return JS_FALSE;
2009 if (!arraylike) {
2010 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2011 JSMSG_BAD_APPLY_ARGS, js_apply_str);
2012 return JS_FALSE;
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))
2021 return JS_FALSE;
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))
2028 return JS_FALSE;
2030 /* Push fval, obj, and aobj's elements as args. */
2031 Value *sp = args.getvp();
2032 *sp++ = fval;
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();
2042 if (fp) {
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();
2048 } else {
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();
2055 } else {
2056 for (uintN i = 0; i < argc; i++) {
2057 if (!aobj->getProperty(cx, INT_TO_JSID(jsint(i)), sp))
2058 return JS_FALSE;
2059 sp++;
2063 bool ok = Invoke(cx, args, 0);
2064 *vp = *args.getvp();
2065 return ok;
2068 static JSFunctionSpec function_methods[] = {
2069 #if JS_HAS_TOSOURCE
2070 JS_FN(js_toSource_str, fun_toSource, 0,0),
2071 #endif
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),
2075 JS_FS_END
2078 static JSBool
2079 Function(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval)
2081 JSFunction *fun;
2082 JSObject *parent;
2083 JSStackFrame *fp, *caller;
2084 uintN i, n, lineno;
2085 JSAtom *atom;
2086 const char *filename;
2087 JSBool ok;
2088 JSString *str, *arg;
2089 TokenStream ts(cx);
2090 JSPrincipals *principals;
2091 jschar *collected_args, *cp;
2092 void *mark;
2093 size_t arg_length, args_length, old_args_length;
2094 TokenKind tt;
2096 if (!JS_IsConstructing(cx)) {
2097 obj = NewObject(cx, &js_FunctionClass, NULL, NULL);
2098 if (!obj)
2099 return JS_FALSE;
2100 rval->setObject(*obj);
2101 } else {
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())
2107 return JS_TRUE;
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);
2125 if (!fun)
2126 return JS_FALSE;
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);
2138 if (caller) {
2139 principals = JS_EvalFramePrincipals(cx, fp, caller);
2140 filename = js_ComputeFilename(cx, caller, principals, &lineno);
2141 } else {
2142 filename = NULL;
2143 lineno = 0;
2144 principals = NULL;
2147 /* Belt-and-braces: check that the caller has access to parent. */
2148 if (!js_CheckPrincipalsAccess(cx, parent, principals,
2149 CLASS_ATOM(cx, Function))) {
2150 return JS_FALSE;
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);
2161 return JS_FALSE;
2164 n = argc ? argc - 1 : 0;
2165 if (n > 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.
2178 state = BAD_FORMAL;
2179 args_length = 0;
2180 for (i = 0; i < n; i++) {
2181 /* Collect the lengths for all the function-argument arguments. */
2182 arg = js_ValueToString(cx, argv[i]);
2183 if (!arg)
2184 return JS_FALSE;
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);
2195 return JS_FALSE;
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);
2205 return JS_FALSE;
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));
2216 if (!cp) {
2217 js_ReportOutOfScriptQuota(cx);
2218 return JS_FALSE;
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);
2229 cp += 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);
2238 return JS_FALSE;
2241 /* The argument string may be empty or contain no tokens. */
2242 tt = ts.getToken();
2243 if (tt != TOK_EOF) {
2244 for (;;) {
2246 * Check that it's a name. This also implicitly guards against
2247 * TOK_ERROR, which was already reported.
2249 if (tt != TOK_NAME)
2250 goto after_args;
2253 * Get the atom corresponding to the name from the token
2254 * stream; we're assured at this point that it's a valid
2255 * identifier.
2257 atom = ts.currentToken().t_atom;
2259 /* Check for a duplicate parameter name. */
2260 if (js_LookupLocal(cx, fun, atom, NULL) != JSLOCAL_NONE) {
2261 const char *name;
2263 name = js_AtomToPrintableString(cx, atom);
2264 ok = name && ReportCompileErrorNumber(cx, &ts, NULL,
2265 JSREPORT_WARNING | JSREPORT_STRICT,
2266 JSMSG_DUPLICATE_FORMAL, name);
2267 if (!ok)
2268 goto after_args;
2270 if (!js_AddLocal(cx, fun, atom, JSLOCAL_ARG))
2271 goto after_args;
2274 * Get the next token. Stop on end of stream. Otherwise
2275 * insist on a comma, get another name, and iterate.
2277 tt = ts.getToken();
2278 if (tt == TOK_EOF)
2279 break;
2280 if (tt != TOK_COMMA)
2281 goto after_args;
2282 tt = ts.getToken();
2286 state = OK;
2287 after_args:
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,
2294 JSMSG_BAD_FORMAL);
2296 ts.close();
2297 JS_ARENA_RELEASE(&cx->tempPool, mark);
2298 if (state != OK)
2299 return JS_FALSE;
2302 if (argc) {
2303 str = js_ValueToString(cx, argv[argc-1]);
2304 if (!str)
2305 return JS_FALSE;
2306 argv[argc-1].setString(str);
2307 } else {
2308 str = cx->runtime->emptyString;
2311 return Compiler::compileFunctionBody(cx, fun, principals,
2312 str->chars(), str->length(),
2313 filename, lineno);
2316 JSObject *
2317 js_InitFunctionClass(JSContext *cx, JSObject *obj)
2319 JSObject *proto;
2320 JSFunction *fun;
2322 proto = js_InitClass(cx, obj, NULL, &js_FunctionClass, Function, 1,
2323 NULL, function_methods, NULL, NULL);
2324 if (!proto)
2325 return NULL;
2326 fun = js_NewFunction(cx, proto, NULL, 0, JSFUN_INTERPRETED, obj, NULL);
2327 if (!fun)
2328 return NULL;
2329 fun->u.i.script = JSScript::emptyScript();
2330 return proto;
2333 JSFunction *
2334 js_NewFunction(JSContext *cx, JSObject *funobj, Native native, uintN nargs,
2335 uintN flags, JSObject *parent, JSAtom *atom)
2337 JSFunction *fun;
2339 if (funobj) {
2340 JS_ASSERT(funobj->isFunction());
2341 funobj->setParent(parent);
2342 } else {
2343 funobj = NewObject(cx, &js_FunctionClass, NULL, parent);
2344 if (!funobj)
2345 return NULL;
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) {
2354 JS_ASSERT(!native);
2355 JS_ASSERT(nargs == 0);
2356 fun->u.i.nvars = 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;
2361 #ifdef DEBUG
2362 fun->u.i.names.taggedAtom = 0;
2363 #endif
2364 } else {
2365 fun->u.n.extra = 0;
2366 fun->u.n.spare = 0;
2367 fun->u.n.clasp = NULL;
2368 if (flags & JSFUN_TRCINFO) {
2369 #ifdef JS_TRACER
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;
2374 #else
2375 fun->u.n.trcinfo = NULL;
2376 #endif
2377 } else {
2378 fun->u.n.native = native;
2379 fun->u.n.trcinfo = NULL;
2381 JS_ASSERT(fun->u.n.native);
2383 fun->atom = atom;
2385 /* Set private to self to indicate non-cloned fully initialized function. */
2386 FUN_OBJECT(fun)->setPrivate(fun);
2387 return fun;
2390 JSObject * JS_FASTCALL
2391 js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent,
2392 JSObject *proto)
2394 JS_ASSERT(parent);
2395 JS_ASSERT(proto);
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);
2402 if (!clone)
2403 return NULL;
2404 clone->setPrivate(fun);
2405 return clone;
2408 #ifdef JS_TRACER
2409 JS_DEFINE_CALLINFO_4(extern, OBJECT, js_CloneFunctionObject, CONTEXT, FUNCTION, OBJECT, OBJECT, 0,
2410 nanojit::ACC_STORE_ANY)
2411 #endif
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
2416 * slots on trace.
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);
2427 if (!closure)
2428 return closure;
2430 uint32 nslots = fun->countInterpretedReservedSlots();
2431 if (nslots == 0)
2432 return closure;
2433 if (!js_EnsureReservedSlots(cx, closure, nslots))
2434 return NULL;
2436 return closure;
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);
2450 if (!scopeChain)
2451 return NULL;
2453 JSObject *closure = js_AllocFlatClosure(cx, fun, scopeChain);
2454 if (!closure || fun->u.i.nupvars == 0)
2455 return closure;
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]);
2464 return closure;
2467 JSObject *
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);
2477 JSFunction *
2478 js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, Native native,
2479 uintN nargs, uintN attrs)
2481 PropertyOp gsop;
2482 JSFunction *fun;
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
2489 * for more on this.
2491 attrs &= ~JSFUN_STUB_GSOPS;
2492 gsop = PropertyStub;
2493 } else {
2494 gsop = NULL;
2496 fun = js_NewFunction(cx, NULL, native, nargs, attrs, obj, atom);
2497 if (!fun)
2498 return NULL;
2499 if (!obj->defineProperty(cx, ATOM_TO_JSID(atom), ObjectValue(*fun),
2500 gsop, gsop, attrs & ~JSFUN_FLAGS_MASK)) {
2501 return NULL;
2503 return fun;
2506 #if (JSV2F_CONSTRUCT & JSV2F_SEARCH_STACK)
2507 # error "JSINVOKE_CONSTRUCT and JSV2F_SEARCH_STACK are not disjoint!"
2508 #endif
2510 JSFunction *
2511 js_ValueToFunction(JSContext *cx, const Value *vp, uintN flags)
2513 JSObject *funobj;
2514 if (!IsFunctionObject(*vp, &funobj)) {
2515 js_ReportIsNotFunction(cx, vp, flags);
2516 return NULL;
2518 return GET_FUNCTION_PRIVATE(cx, funobj);
2521 JSObject *
2522 js_ValueToFunctionObject(JSContext *cx, Value *vp, uintN flags)
2524 JSFunction *fun;
2525 JSStackFrame *caller;
2526 JSPrincipals *principals;
2528 JSObject *funobj;
2529 if (IsFunctionObject(*vp, &funobj))
2530 return funobj;
2532 fun = js_ValueToFunction(cx, vp, flags);
2533 if (!fun)
2534 return NULL;
2535 vp->setObject(*fun);
2537 caller = js_GetScriptedCaller(cx, NULL);
2538 if (caller) {
2539 principals = JS_StackFramePrincipals(cx, caller);
2540 } else {
2541 /* No scripted caller, don't allow access. */
2542 principals = NULL;
2545 if (!js_CheckPrincipalsAccess(cx, FUN_OBJECT(fun), principals,
2546 fun->atom
2547 ? fun->atom
2548 : cx->runtime->atomState.anonymousAtom)) {
2549 return NULL;
2551 return FUN_OBJECT(fun);
2554 JSObject *
2555 js_ValueToCallableObject(JSContext *cx, Value *vp, uintN flags)
2557 if (vp->isObject()) {
2558 JSObject *callable = &vp->toObject();
2559 if (callable->isCallable())
2560 return callable;
2562 return js_ValueToFunctionObject(cx, vp, flags);
2565 void
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;
2571 LeaveTrace(cx);
2572 FrameRegsIter i(cx);
2573 while (!i.done() && !i.pc())
2574 ++i;
2576 ptrdiff_t spindex =
2577 (!i.done() && i.fp()->base() <= vp && vp < i.sp())
2578 ? 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 {
2601 JSAtom *name;
2602 uint16 index;
2603 JSNameIndexPair *link;
2606 struct JSLocalNameMap {
2607 JSDHashTable names;
2608 JSNameIndexPair *lastdup;
2611 typedef struct JSLocalNameHashEntry {
2612 JSDHashEntryHdr hdr;
2613 JSAtom *name;
2614 uint16 index;
2615 uint8 localKind;
2616 } JSLocalNameHashEntry;
2618 static void
2619 FreeLocalNameHash(JSContext *cx, JSLocalNameMap *map)
2621 JSNameIndexPair *dup, *next;
2623 for (dup = map->lastdup; dup; dup = next) {
2624 next = dup->link;
2625 cx->free(dup);
2627 JS_DHashTableFinish(&map->names);
2628 cx->free(map);
2631 static JSBool
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
2640 if (!name) {
2641 /* A destructuring pattern does not need a hash entry. */
2642 JS_ASSERT(localKind == JSLOCAL_ARG);
2643 return JS_TRUE;
2645 #endif
2646 entry = (JSLocalNameHashEntry *)
2647 JS_DHashTableOperate(&map->names, name, JS_DHASH_ADD);
2648 if (!entry) {
2649 JS_ReportOutOfMemory(cx);
2650 return JS_FALSE;
2652 if (entry->name) {
2653 JS_ASSERT(entry->name == name);
2654 JS_ASSERT(entry->localKind == JSLOCAL_ARG && localKind == JSLOCAL_ARG);
2655 dup = (JSNameIndexPair *) cx->malloc(sizeof *dup);
2656 if (!dup)
2657 return JS_FALSE;
2658 dup->name = entry->name;
2659 dup->index = entry->index;
2660 dup->link = map->lastdup;
2661 map->lastdup = dup;
2663 entry->name = name;
2664 entry->index = (uint16) index;
2665 entry->localKind = (uint8) localKind;
2666 return JS_TRUE;
2669 JSBool
2670 js_AddLocal(JSContext *cx, JSFunction *fun, JSAtom *atom, JSLocalKind kind)
2672 jsuword taggedAtom;
2673 uint16 *indexp;
2674 uintN n, i;
2675 jsuword *array;
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;
2686 } else {
2687 indexp = &fun->u.i.nvars;
2688 if (kind == JSLOCAL_CONST)
2689 taggedAtom |= 1;
2690 else
2691 JS_ASSERT(kind == JSLOCAL_VAR);
2693 n = fun->countLocalNames();
2694 if (n == 0) {
2695 JS_ASSERT(fun->u.i.names.taggedAtom == 0);
2696 fun->u.i.names.taggedAtom = taggedAtom;
2697 } else if (n < MAX_ARRAY_LOCALS) {
2698 if (n > 1) {
2699 array = fun->u.i.names.array;
2700 } else {
2701 array = (jsuword *) cx->malloc(MAX_ARRAY_LOCALS * sizeof *array);
2702 if (!array)
2703 return JS_FALSE;
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);
2717 #else
2718 JS_ASSERT(fun->u.i.nvars == 0);
2719 #endif
2720 array[fun->nargs] = taggedAtom;
2721 } else {
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);
2727 if (!map)
2728 return JS_FALSE;
2729 if (!JS_DHashTableInit(&map->names, JS_DHashGetStubOps(),
2730 NULL, sizeof(JSLocalNameHashEntry),
2731 JS_DHASH_DEFAULT_CAPACITY(MAX_ARRAY_LOCALS
2732 * 2))) {
2733 JS_ReportOutOfMemory(cx);
2734 cx->free(map);
2735 return JS_FALSE;
2738 map->lastdup = NULL;
2739 for (i = 0; i != MAX_ARRAY_LOCALS; ++i) {
2740 taggedAtom = array[i];
2741 uintN j = i;
2742 JSLocalKind k = JSLOCAL_ARG;
2743 if (j >= fun->nargs) {
2744 j -= fun->nargs;
2745 if (j < fun->u.i.nvars) {
2746 k = (taggedAtom & 1) ? JSLOCAL_CONST : JSLOCAL_VAR;
2747 } else {
2748 j -= fun->u.i.nvars;
2749 k = JSLOCAL_UPVAR;
2752 if (!HashLocalName(cx, map, (JSAtom *) (taggedAtom & ~1), k, j)) {
2753 FreeLocalNameHash(cx, map);
2754 return JS_FALSE;
2757 if (!HashLocalName(cx, map, atom, kind, *indexp)) {
2758 FreeLocalNameHash(cx, map);
2759 return JS_FALSE;
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;
2767 cx->free(array);
2768 } else {
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);
2774 return JS_FALSE;
2776 if (!HashLocalName(cx, fun->u.i.names.map, atom, kind, *indexp))
2777 return JS_FALSE;
2780 /* Update the argument or variable counter. */
2781 ++*indexp;
2782 return JS_TRUE;
2785 JSLocalKind
2786 js_LookupLocal(JSContext *cx, JSFunction *fun, JSAtom *atom, uintN *indexp)
2788 uintN n, i, upvar_base;
2789 jsuword *array;
2790 JSLocalNameHashEntry *entry;
2792 JS_ASSERT(FUN_INTERPRETED(fun));
2793 n = fun->countLocalNames();
2794 if (n == 0)
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. */
2800 i = n;
2801 upvar_base = fun->countArgsAndVars();
2802 do {
2803 --i;
2804 if (atom == JS_LOCAL_NAME_TO_ATOM(array[i])) {
2805 if (i < fun->nargs) {
2806 if (indexp)
2807 *indexp = i;
2808 return JSLOCAL_ARG;
2810 if (i >= upvar_base) {
2811 if (indexp)
2812 *indexp = i - upvar_base;
2813 return JSLOCAL_UPVAR;
2815 if (indexp)
2816 *indexp = i - fun->nargs;
2817 return JS_LOCAL_NAME_IS_CONST(array[i])
2818 ? JSLOCAL_CONST
2819 : JSLOCAL_VAR;
2821 } while (i != 0);
2822 } else {
2823 entry = (JSLocalNameHashEntry *)
2824 JS_DHashTableOperate(&fun->u.i.names.map->names, atom,
2825 JS_DHASH_LOOKUP);
2826 if (JS_DHASH_ENTRY_IS_BUSY(&entry->hdr)) {
2827 JS_ASSERT(entry->localKind != JSLOCAL_NONE);
2828 if (indexp)
2829 *indexp = entry->index;
2830 return (JSLocalKind) entry->localKind;
2833 return JSLOCAL_NONE;
2836 typedef struct JSLocalNameEnumeratorArgs {
2837 JSFunction *fun;
2838 jsuword *names;
2839 #ifdef DEBUG
2840 uintN nCopiedArgs;
2841 uintN nCopiedVars;
2842 #endif
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;
2851 uint i;
2852 jsuword constFlag;
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);
2860 i = entry->index;
2861 constFlag = 0;
2862 } else {
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;
2871 i += entry->index;
2872 constFlag = (entry->localKind == JSLOCAL_CONST);
2874 args->names[i] = (jsuword) entry->name | constFlag;
2875 return JS_DHASH_NEXT;
2878 jsuword *
2879 js_GetLocalNameArray(JSContext *cx, JSFunction *fun, JSArenaPool *pool)
2881 uintN n;
2882 jsuword *names;
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);
2898 if (!names) {
2899 js_ReportOutOfScriptQuota(cx);
2900 return NULL;
2903 #if JS_HAS_DESTRUCTURING
2904 /* Some parameter names can be NULL due to destructuring patterns. */
2905 PodZero(names, fun->nargs);
2906 #endif
2907 map = fun->u.i.names.map;
2908 args.fun = fun;
2909 args.names = names;
2910 #ifdef DEBUG
2911 args.nCopiedArgs = 0;
2912 args.nCopiedVars = 0;
2913 #endif
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);
2922 #endif
2923 JS_ASSERT(args.nCopiedVars == fun->u.i.nvars + fun->u.i.nupvars);
2925 return names;
2928 static JSDHashOperator
2929 trace_local_names_enumerator(JSDHashTable *table, JSDHashEntryHdr *hdr,
2930 uint32 number, void *arg)
2932 JSLocalNameHashEntry *entry;
2933 JSTracer *trc;
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",
2940 entry->index);
2941 Mark(trc, ATOM_TO_STRING(entry->name), JSTRACE_STRING);
2942 return JS_DHASH_NEXT;
2945 static void
2946 TraceLocalNames(JSTracer *trc, JSFunction *fun)
2948 uintN n, i;
2949 JSAtom *atom;
2950 jsuword *array;
2952 JS_ASSERT(FUN_INTERPRETED(fun));
2953 n = fun->countLocalNames();
2954 if (n == 0)
2955 return;
2956 if (n <= MAX_ARRAY_LOCALS) {
2957 array = (n == 1) ? &fun->u.i.names.taggedAtom : fun->u.i.names.array;
2958 i = n;
2959 do {
2960 --i;
2961 atom = (JSAtom *) (array[i] & ~1);
2962 if (atom) {
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);
2968 } while (i != 0);
2969 } else {
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.
2980 void
2981 DestroyLocalNames(JSContext *cx, JSFunction *fun)
2983 uintN n;
2985 n = fun->countLocalNames();
2986 if (n <= 1)
2987 return;
2988 if (n <= MAX_ARRAY_LOCALS)
2989 cx->free(fun->u.i.names.array);
2990 else
2991 FreeLocalNameHash(cx, fun->u.i.names.map);
2994 void
2995 js_FreezeLocalNames(JSContext *cx, JSFunction *fun)
2997 uintN n;
2998 jsuword *array;
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,
3006 n * sizeof *array);
3007 if (array)
3008 fun->u.i.names.array = array;
3010 #ifdef DEBUG
3011 if (n > MAX_ARRAY_LOCALS)
3012 JS_DHashMarkTableImmutable(&fun->u.i.names.map->names);
3013 #endif
3016 JSAtom *
3017 JSFunction::findDuplicateFormal() const
3019 if (nargs <= 1)
3020 return NULL;
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]);
3034 return NULL;
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;