Backed out changeset b88172246b66 due to Win32 debug failures.
[mozilla-central.git] / js / src / jsfun.cpp
blobd2d5550364d217bb1f2d72c28b1d67a77a4a7a9d
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"
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 "jspropertytree.h"
67 #include "jsproxy.h"
68 #include "jsscan.h"
69 #include "jsscope.h"
70 #include "jsscript.h"
71 #include "jsstr.h"
72 #include "jsexn.h"
73 #include "jsstaticcheck.h"
74 #include "jstracer.h"
76 #if JS_HAS_GENERATORS
77 # include "jsiter.h"
78 #endif
80 #if JS_HAS_XDR
81 # include "jsxdrapi.h"
82 #endif
84 #ifdef JS_METHODJIT
85 #include "methodjit/MethodJIT.h"
86 #endif
88 #include "jsatominlines.h"
89 #include "jscntxtinlines.h"
90 #include "jsfuninlines.h"
91 #include "jsinterpinlines.h"
92 #include "jsobjinlines.h"
93 #include "jsscriptinlines.h"
95 using namespace js;
96 using namespace js::gc;
98 inline JSObject *
99 JSObject::getThrowTypeError() const
101 return &getGlobal()->getReservedSlot(JSRESERVED_GLOBAL_THROWTYPEERROR).toObject();
104 JSBool
105 js_GetArgsValue(JSContext *cx, JSStackFrame *fp, Value *vp)
107 JSObject *argsobj;
109 if (fp->hasOverriddenArgs()) {
110 JS_ASSERT(fp->hasCallObj());
111 jsid id = ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom);
112 return fp->callObj().getProperty(cx, id, vp);
114 argsobj = js_GetArgsObject(cx, fp);
115 if (!argsobj)
116 return JS_FALSE;
117 vp->setObject(*argsobj);
118 return JS_TRUE;
121 JSBool
122 js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id, Value *vp)
124 JS_ASSERT(fp->isFunctionFrame());
126 if (fp->hasOverriddenArgs()) {
127 JS_ASSERT(fp->hasCallObj());
129 jsid argumentsid = ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom);
130 Value v;
131 if (!fp->callObj().getProperty(cx, argumentsid, &v))
132 return false;
134 JSObject *obj;
135 if (v.isPrimitive()) {
136 obj = js_ValueToNonNullObject(cx, v);
137 if (!obj)
138 return false;
139 } else {
140 obj = &v.toObject();
142 return obj->getProperty(cx, id, vp);
145 vp->setUndefined();
146 if (JSID_IS_INT(id)) {
147 uint32 arg = uint32(JSID_TO_INT(id));
148 JSObject *argsobj = fp->maybeArgsObj();
149 if (arg < fp->numActualArgs()) {
150 if (argsobj) {
151 if (argsobj->getArgsElement(arg).isMagic(JS_ARGS_HOLE))
152 return argsobj->getProperty(cx, id, vp);
154 *vp = fp->canonicalActualArg(arg);
155 } else {
157 * Per ECMA-262 Ed. 3, 10.1.8, last bulleted item, do not share
158 * storage between the formal parameter and arguments[k] for all
159 * fp->argc <= k && k < fp->fun->nargs. For example, in
161 * function f(x) { x = 42; return arguments[0]; }
162 * f();
164 * the call to f should return undefined, not 42. If fp->argsobj
165 * is null at this point, as it would be in the example, return
166 * undefined in *vp.
168 if (argsobj)
169 return argsobj->getProperty(cx, id, vp);
171 } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
172 JSObject *argsobj = fp->maybeArgsObj();
173 if (argsobj && argsobj->isArgsLengthOverridden())
174 return argsobj->getProperty(cx, id, vp);
175 vp->setInt32(fp->numActualArgs());
177 return true;
180 static JSObject *
181 NewArguments(JSContext *cx, JSObject *parent, uint32 argc, JSObject &callee)
183 JSObject *proto;
184 if (!js_GetClassPrototype(cx, parent, JSProto_Object, &proto))
185 return NULL;
187 JS_STATIC_ASSERT(JSObject::ARGS_CLASS_RESERVED_SLOTS == 2);
188 JSObject *argsobj = js_NewGCObject(cx, FINALIZE_OBJECT2);
189 if (!argsobj)
190 return NULL;
192 ArgumentsData *data = (ArgumentsData *)
193 cx->malloc(offsetof(ArgumentsData, slots) + argc * sizeof(Value));
194 if (!data)
195 return NULL;
196 SetValueRangeToUndefined(data->slots, argc);
198 /* Can't fail from here on, so initialize everything in argsobj. */
199 argsobj->init(cx, callee.getFunctionPrivate()->inStrictMode()
200 ? &StrictArgumentsClass
201 : &js_ArgumentsClass,
202 proto, parent, NULL, false);
204 argsobj->setMap(cx->runtime->emptyArgumentsShape);
206 argsobj->setArgsLength(argc);
207 argsobj->setArgsData(data);
208 data->callee.setObject(callee);
210 return argsobj;
213 struct STATIC_SKIP_INFERENCE PutArg
215 PutArg(Value *dst) : dst(dst) {}
216 Value *dst;
217 void operator()(uintN, Value *src) {
218 if (!dst->isMagic(JS_ARGS_HOLE))
219 *dst = *src;
220 ++dst;
224 JSObject *
225 js_GetArgsObject(JSContext *cx, JSStackFrame *fp)
228 * We must be in a function activation; the function must be lightweight
229 * or else fp must have a variable object.
231 JS_ASSERT_IF(fp->fun()->isHeavyweight(), fp->hasCallObj());
233 while (fp->isEvalOrDebuggerFrame())
234 fp = fp->prev();
236 /* Create an arguments object for fp only if it lacks one. */
237 if (fp->hasArgsObj())
238 return &fp->argsObj();
240 /* Compute the arguments object's parent slot from fp's scope chain. */
241 JSObject *global = fp->scopeChain().getGlobal();
242 JSObject *argsobj = NewArguments(cx, global, fp->numActualArgs(), fp->callee());
243 if (!argsobj)
244 return argsobj;
247 * Strict mode functions have arguments objects that copy the initial
248 * actual parameter values. It is the caller's responsibility to get the
249 * arguments object before any parameters are modified! (The emitter
250 * ensures this by synthesizing an arguments access at the start of any
251 * strict mode function that contains an assignment to a parameter, or
252 * that calls eval.) Non-strict mode arguments use the frame pointer to
253 * retrieve up-to-date parameter values.
255 if (argsobj->isStrictArguments())
256 fp->forEachCanonicalActualArg(PutArg(argsobj->getArgsData()->slots));
257 else
258 argsobj->setPrivate(fp);
260 fp->setArgsObj(*argsobj);
261 return argsobj;
264 void
265 js_PutArgsObject(JSContext *cx, JSStackFrame *fp)
267 JSObject &argsobj = fp->argsObj();
268 if (argsobj.isNormalArguments()) {
269 JS_ASSERT(argsobj.getPrivate() == fp);
270 fp->forEachCanonicalActualArg(PutArg(argsobj.getArgsData()->slots));
271 argsobj.setPrivate(NULL);
272 } else {
273 JS_ASSERT(!argsobj.getPrivate());
275 fp->clearArgsObj();
278 #ifdef JS_TRACER
281 * Traced versions of js_GetArgsObject and js_PutArgsObject.
283 JSObject * JS_FASTCALL
284 js_NewArgumentsOnTrace(JSContext *cx, JSObject *parent, uint32 argc, JSObject *callee)
286 JSObject *argsobj = NewArguments(cx, parent, argc, *callee);
287 if (!argsobj)
288 return NULL;
290 if (argsobj->isStrictArguments()) {
292 * Strict mode callers must copy arguments into the created arguments
293 * object. The trace-JITting code is in TraceRecorder::newArguments.
295 JS_ASSERT(!argsobj->getPrivate());
296 } else {
297 argsobj->setPrivate(JS_ARGUMENTS_OBJECT_ON_TRACE);
300 return argsobj;
302 JS_DEFINE_CALLINFO_4(extern, OBJECT, js_NewArgumentsOnTrace, CONTEXT, OBJECT, UINT32, OBJECT,
303 0, nanojit::ACCSET_STORE_ANY)
305 /* FIXME change the return type to void. */
306 JSBool JS_FASTCALL
307 js_PutArgumentsOnTrace(JSContext *cx, JSObject *argsobj, Value *args)
309 JS_ASSERT(argsobj->isNormalArguments());
310 JS_ASSERT(argsobj->getPrivate() == JS_ARGUMENTS_OBJECT_ON_TRACE);
313 * TraceRecorder::putActivationObjects builds a single, contiguous array of
314 * the arguments, regardless of whether #actuals > #formals so there is no
315 * need to worry about actual vs. formal arguments.
317 Value *srcend = args + argsobj->getArgsInitialLength();
318 Value *dst = argsobj->getArgsData()->slots;
319 for (Value *src = args; src != srcend; ++src, ++dst) {
320 if (!dst->isMagic(JS_ARGS_HOLE))
321 *dst = *src;
324 argsobj->setPrivate(NULL);
325 return true;
327 JS_DEFINE_CALLINFO_3(extern, BOOL, js_PutArgumentsOnTrace, CONTEXT, OBJECT, VALUEPTR, 0,
328 nanojit::ACCSET_STORE_ANY)
330 #endif /* JS_TRACER */
332 static JSBool
333 args_delProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
335 JS_ASSERT(obj->isArguments());
337 if (JSID_IS_INT(id)) {
338 uintN arg = uintN(JSID_TO_INT(id));
339 if (arg < obj->getArgsInitialLength())
340 obj->setArgsElement(arg, MagicValue(JS_ARGS_HOLE));
341 } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
342 obj->setArgsLengthOverridden();
343 } else if (JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom)) {
344 obj->setArgsCallee(MagicValue(JS_ARGS_HOLE));
346 return true;
349 static JS_REQUIRES_STACK JSObject *
350 WrapEscapingClosure(JSContext *cx, JSStackFrame *fp, JSFunction *fun)
352 JS_ASSERT(fun->optimizedClosure());
353 JS_ASSERT(!fun->u.i.wrapper);
356 * We do not attempt to reify Call and Block objects on demand for outer
357 * scopes. This could be done (see the "v8" patch in bug 494235) but it is
358 * fragile in the face of ongoing compile-time optimization. Instead, the
359 * _DBG* opcodes used by wrappers created here must cope with unresolved
360 * upvars and throw them as reference errors. Caveat debuggers!
362 JSObject *scopeChain = GetScopeChain(cx, fp);
363 if (!scopeChain)
364 return NULL;
366 JSObject *wfunobj = NewFunction(cx, scopeChain);
367 if (!wfunobj)
368 return NULL;
369 AutoObjectRooter tvr(cx, wfunobj);
371 JSFunction *wfun = (JSFunction *) wfunobj;
372 wfunobj->setPrivate(wfun);
373 wfun->nargs = fun->nargs;
374 wfun->flags = fun->flags | JSFUN_HEAVYWEIGHT;
375 wfun->u.i.skipmin = fun->u.i.skipmin;
376 wfun->u.i.wrapper = true;
377 wfun->u.i.script = NULL;
378 wfun->atom = fun->atom;
380 JSScript *script = fun->script();
381 jssrcnote *snbase = script->notes();
382 jssrcnote *sn = snbase;
383 while (!SN_IS_TERMINATOR(sn))
384 sn = SN_NEXT(sn);
385 uintN nsrcnotes = (sn - snbase) + 1;
387 /* NB: GC must not occur before wscript is homed in wfun->u.i.script. */
388 JSScript *wscript = JSScript::NewScript(cx, script->length, nsrcnotes,
389 script->atomMap.length,
390 JSScript::isValidOffset(script->objectsOffset)
391 ? script->objects()->length
392 : 0,
393 script->bindings.countUpvars(),
394 JSScript::isValidOffset(script->regexpsOffset)
395 ? script->regexps()->length
396 : 0,
397 JSScript::isValidOffset(script->trynotesOffset)
398 ? script->trynotes()->length
399 : 0,
400 JSScript::isValidOffset(script->constOffset)
401 ? script->consts()->length
402 : 0,
403 JSScript::isValidOffset(script->globalsOffset)
404 ? script->globals()->length
405 : 0,
406 script->nClosedArgs,
407 script->nClosedVars);
408 if (!wscript)
409 return NULL;
411 memcpy(wscript->code, script->code, script->length);
412 wscript->main = wscript->code + (script->main - script->code);
414 memcpy(wscript->notes(), snbase, nsrcnotes * sizeof(jssrcnote));
415 memcpy(wscript->atomMap.vector, script->atomMap.vector,
416 wscript->atomMap.length * sizeof(JSAtom *));
417 if (JSScript::isValidOffset(script->objectsOffset)) {
418 memcpy(wscript->objects()->vector, script->objects()->vector,
419 wscript->objects()->length * sizeof(JSObject *));
421 if (JSScript::isValidOffset(script->regexpsOffset)) {
422 memcpy(wscript->regexps()->vector, script->regexps()->vector,
423 wscript->regexps()->length * sizeof(JSObject *));
425 if (JSScript::isValidOffset(script->trynotesOffset)) {
426 memcpy(wscript->trynotes()->vector, script->trynotes()->vector,
427 wscript->trynotes()->length * sizeof(JSTryNote));
429 if (JSScript::isValidOffset(script->globalsOffset)) {
430 memcpy(wscript->globals()->vector, script->globals()->vector,
431 wscript->globals()->length * sizeof(GlobalSlotArray::Entry));
433 if (script->nClosedArgs + script->nClosedVars != 0)
434 script->copyClosedSlotsTo(wscript);
436 if (script->bindings.hasUpvars()) {
437 JS_ASSERT(script->bindings.countUpvars() == wscript->upvars()->length);
438 memcpy(wscript->upvars()->vector, script->upvars()->vector,
439 script->bindings.countUpvars() * sizeof(uint32));
442 jsbytecode *pc = wscript->code;
443 while (*pc != JSOP_STOP) {
444 /* FIXME should copy JSOP_TRAP? */
445 JSOp op = js_GetOpcode(cx, wscript, pc);
446 const JSCodeSpec *cs = &js_CodeSpec[op];
447 ptrdiff_t oplen = cs->length;
448 if (oplen < 0)
449 oplen = js_GetVariableBytecodeLength(pc);
452 * Rewrite JSOP_{GET,CALL}FCSLOT as JSOP_{GET,CALL}UPVAR_DBG for the
453 * case where fun is an escaping flat closure. This works because the
454 * UPVAR and FCSLOT ops by design have the same format: an upvar index
455 * immediate operand.
457 switch (op) {
458 case JSOP_GETFCSLOT: *pc = JSOP_GETUPVAR_DBG; break;
459 case JSOP_CALLFCSLOT: *pc = JSOP_CALLUPVAR_DBG; break;
460 case JSOP_DEFFUN_FC: *pc = JSOP_DEFFUN_DBGFC; break;
461 case JSOP_DEFLOCALFUN_FC: *pc = JSOP_DEFLOCALFUN_DBGFC; break;
462 case JSOP_LAMBDA_FC: *pc = JSOP_LAMBDA_DBGFC; break;
463 default:;
465 pc += oplen;
469 * Fill in the rest of wscript. This means if you add members to JSScript
470 * you must update this code. FIXME: factor into JSScript::clone method.
472 wscript->setVersion(script->getVersion());
473 wscript->nfixed = script->nfixed;
474 wscript->filename = script->filename;
475 wscript->lineno = script->lineno;
476 wscript->nslots = script->nslots;
477 wscript->staticLevel = script->staticLevel;
478 wscript->principals = script->principals;
479 wscript->noScriptRval = script->noScriptRval;
480 wscript->savedCallerFun = script->savedCallerFun;
481 wscript->hasSharps = script->hasSharps;
482 wscript->strictModeCode = script->strictModeCode;
483 wscript->compileAndGo = script->compileAndGo;
484 wscript->usesEval = script->usesEval;
485 wscript->usesArguments = script->usesArguments;
486 wscript->warnedAboutTwoArgumentEval = script->warnedAboutTwoArgumentEval;
487 if (wscript->principals)
488 JSPRINCIPALS_HOLD(cx, wscript->principals);
489 #ifdef CHECK_SCRIPT_OWNER
490 wscript->owner = script->owner;
491 #endif
493 wscript->bindings.clone(cx, &script->bindings);
495 /* Deoptimize wfun from FUN_{FLAT,NULL}_CLOSURE to FUN_INTERPRETED. */
496 FUN_SET_KIND(wfun, JSFUN_INTERPRETED);
497 wfun->u.i.script = wscript;
498 return wfunobj;
501 static JSBool
502 ArgGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
504 LeaveTrace(cx);
506 if (!InstanceOf(cx, obj, &js_ArgumentsClass, NULL))
507 return true;
509 if (JSID_IS_INT(id)) {
511 * arg can exceed the number of arguments if a script changed the
512 * prototype to point to another Arguments object with a bigger argc.
514 uintN arg = uintN(JSID_TO_INT(id));
515 if (arg < obj->getArgsInitialLength()) {
516 JS_ASSERT(!obj->getArgsElement(arg).isMagic(JS_ARGS_HOLE));
517 if (JSStackFrame *fp = (JSStackFrame *) obj->getPrivate())
518 *vp = fp->canonicalActualArg(arg);
519 else
520 *vp = obj->getArgsElement(arg);
522 } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
523 if (!obj->isArgsLengthOverridden())
524 vp->setInt32(obj->getArgsInitialLength());
525 } else {
526 JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom));
527 const Value &v = obj->getArgsCallee();
528 if (!v.isMagic(JS_ARGS_HOLE)) {
530 * If this function or one in it needs upvars that reach above it
531 * in the scope chain, it must not be a null closure (it could be a
532 * flat closure, or an unoptimized closure -- the latter itself not
533 * necessarily heavyweight). Rather than wrap here, we simply throw
534 * to reduce code size and tell debugger users the truth instead of
535 * passing off a fibbing wrapper.
537 if (GET_FUNCTION_PRIVATE(cx, &v.toObject())->needsWrapper()) {
538 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
539 JSMSG_OPTIMIZED_CLOSURE_LEAK);
540 return false;
542 *vp = v;
545 return true;
548 static JSBool
549 ArgSetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
551 #ifdef JS_TRACER
552 // To be able to set a property here on trace, we would have to make
553 // sure any updates also get written back to the trace native stack.
554 // For simplicity, we just leave trace, since this is presumably not
555 // a common operation.
556 if (JS_ON_TRACE(cx)) {
557 DeepBail(cx);
558 return false;
560 #endif
562 if (!InstanceOf(cx, obj, &js_ArgumentsClass, NULL))
563 return true;
565 if (JSID_IS_INT(id)) {
566 uintN arg = uintN(JSID_TO_INT(id));
567 if (arg < obj->getArgsInitialLength()) {
568 JSStackFrame *fp = (JSStackFrame *) obj->getPrivate();
569 if (fp) {
570 JSScript *script = fp->functionScript();
571 if (script->usesArguments)
572 fp->canonicalActualArg(arg) = *vp;
573 return true;
576 } else {
577 JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom) ||
578 JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom));
582 * For simplicity we use delete/set to replace the property with one
583 * backed by the default Object getter and setter. Note that we rely on
584 * args_delProperty to clear the corresponding reserved slot so the GC can
585 * collect its value.
587 AutoValueRooter tvr(cx);
588 return js_DeleteProperty(cx, obj, id, tvr.addr(), false) &&
589 js_SetProperty(cx, obj, id, vp, false);
592 static JSBool
593 args_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
594 JSObject **objp)
596 JS_ASSERT(obj->isNormalArguments());
598 *objp = NULL;
600 uintN attrs = JSPROP_SHARED | JSPROP_SHADOWABLE;
601 if (JSID_IS_INT(id)) {
602 uint32 arg = uint32(JSID_TO_INT(id));
603 if (arg >= obj->getArgsInitialLength() || obj->getArgsElement(arg).isMagic(JS_ARGS_HOLE))
604 return true;
606 attrs |= JSPROP_ENUMERATE;
607 } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
608 if (obj->isArgsLengthOverridden())
609 return true;
610 } else {
611 if (!JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom))
612 return true;
614 if (obj->getArgsCallee().isMagic(JS_ARGS_HOLE))
615 return true;
618 Value undef = UndefinedValue();
619 if (!js_DefineProperty(cx, obj, id, &undef, ArgGetter, ArgSetter, attrs))
620 return JS_FALSE;
622 *objp = obj;
623 return true;
626 static JSBool
627 args_enumerate(JSContext *cx, JSObject *obj)
629 JS_ASSERT(obj->isNormalArguments());
632 * Trigger reflection in args_resolve using a series of js_LookupProperty
633 * calls.
635 int argc = int(obj->getArgsInitialLength());
636 for (int i = -2; i != argc; i++) {
637 jsid id = (i == -2)
638 ? ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)
639 : (i == -1)
640 ? ATOM_TO_JSID(cx->runtime->atomState.calleeAtom)
641 : INT_TO_JSID(i);
643 JSObject *pobj;
644 JSProperty *prop;
645 if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
646 return false;
648 return true;
651 static JSBool
652 StrictArgGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
654 LeaveTrace(cx);
656 if (!InstanceOf(cx, obj, &StrictArgumentsClass, NULL))
657 return true;
659 if (JSID_IS_INT(id)) {
661 * arg can exceed the number of arguments if a script changed the
662 * prototype to point to another Arguments object with a bigger argc.
664 uintN arg = uintN(JSID_TO_INT(id));
665 if (arg < obj->getArgsInitialLength()) {
666 const Value &v = obj->getArgsElement(arg);
667 if (!v.isMagic(JS_ARGS_HOLE))
668 *vp = v;
670 } else {
671 JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom));
672 if (!obj->isArgsLengthOverridden())
673 vp->setInt32(obj->getArgsInitialLength());
675 return true;
678 static JSBool
679 StrictArgSetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
681 if (!InstanceOf(cx, obj, &StrictArgumentsClass, NULL))
682 return true;
684 if (JSID_IS_INT(id)) {
685 uintN arg = uintN(JSID_TO_INT(id));
686 if (arg < obj->getArgsInitialLength()) {
687 obj->setArgsElement(arg, *vp);
688 return true;
690 } else {
691 JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom));
695 * For simplicity we use delete/set to replace the property with one
696 * backed by the default Object getter and setter. Note that we rely on
697 * args_delProperty to clear the corresponding reserved slot so the GC can
698 * collect its value.
700 AutoValueRooter tvr(cx);
701 return js_DeleteProperty(cx, obj, id, tvr.addr(), true) &&
702 js_SetProperty(cx, obj, id, vp, true);
705 static JSBool
706 strictargs_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp)
708 JS_ASSERT(obj->isStrictArguments());
710 *objp = NULL;
712 uintN attrs = JSPROP_SHARED | JSPROP_SHADOWABLE;
713 PropertyOp getter = StrictArgGetter;
714 PropertyOp setter = StrictArgSetter;
716 if (JSID_IS_INT(id)) {
717 uint32 arg = uint32(JSID_TO_INT(id));
718 if (arg >= obj->getArgsInitialLength() || obj->getArgsElement(arg).isMagic(JS_ARGS_HOLE))
719 return true;
721 attrs |= JSPROP_ENUMERATE;
722 } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
723 if (obj->isArgsLengthOverridden())
724 return true;
725 } else {
726 if (!JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom) &&
727 !JSID_IS_ATOM(id, cx->runtime->atomState.callerAtom)) {
728 return true;
731 attrs = JSPROP_PERMANENT | JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED;
732 getter = setter = CastAsPropertyOp(obj->getThrowTypeError());
735 Value undef = UndefinedValue();
736 if (!js_DefineProperty(cx, obj, id, &undef, getter, setter, attrs))
737 return false;
739 *objp = obj;
740 return true;
743 static JSBool
744 strictargs_enumerate(JSContext *cx, JSObject *obj)
746 JS_ASSERT(obj->isStrictArguments());
749 * Trigger reflection in strictargs_resolve using a series of
750 * js_LookupProperty calls.
752 JSObject *pobj;
753 JSProperty *prop;
755 // length
756 if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), &pobj, &prop))
757 return false;
759 // callee
760 if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.calleeAtom), &pobj, &prop))
761 return false;
763 // caller
764 if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.callerAtom), &pobj, &prop))
765 return false;
767 for (uint32 i = 0, argc = obj->getArgsInitialLength(); i < argc; i++) {
768 if (!js_LookupProperty(cx, obj, INT_TO_JSID(i), &pobj, &prop))
769 return false;
772 return true;
775 static void
776 args_finalize(JSContext *cx, JSObject *obj)
778 cx->free((void *) obj->getArgsData());
782 * If a generator's arguments or call object escapes, and the generator frame
783 * is not executing, the generator object needs to be marked because it is not
784 * otherwise reachable. An executing generator is rooted by its invocation. To
785 * distinguish the two cases (which imply different access paths to the
786 * generator object), we use the JSFRAME_FLOATING_GENERATOR flag, which is only
787 * set on the JSStackFrame kept in the generator object's JSGenerator.
789 static inline void
790 MaybeMarkGenerator(JSTracer *trc, JSObject *obj)
792 #if JS_HAS_GENERATORS
793 JSStackFrame *fp = (JSStackFrame *) obj->getPrivate();
794 if (fp && fp->isFloatingGenerator()) {
795 JSObject *genobj = js_FloatingFrameToGenerator(fp)->obj;
796 MarkObject(trc, *genobj, "generator object");
798 #endif
801 static void
802 args_trace(JSTracer *trc, JSObject *obj)
804 JS_ASSERT(obj->isArguments());
805 if (obj->getPrivate() == JS_ARGUMENTS_OBJECT_ON_TRACE) {
806 JS_ASSERT(!obj->isStrictArguments());
807 return;
810 ArgumentsData *data = obj->getArgsData();
811 if (data->callee.isObject())
812 MarkObject(trc, data->callee.toObject(), js_callee_str);
813 MarkValueRange(trc, obj->getArgsInitialLength(), data->slots, js_arguments_str);
815 MaybeMarkGenerator(trc, obj);
819 * The Arguments classes aren't initialized via js_InitClass, because arguments
820 * objects have the initial value of Object.prototype as their [[Prototype]].
821 * However, Object.prototype.toString.call(arguments) === "[object Arguments]"
822 * per ES5 (although not ES3), so the class name is "Arguments" rather than
823 * "Object".
825 * The JSClass functions below collaborate to lazily reflect and synchronize
826 * actual argument values, argument count, and callee function object stored
827 * in a JSStackFrame with their corresponding property values in the frame's
828 * arguments object.
830 Class js_ArgumentsClass = {
831 "Arguments",
832 JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE |
833 JSCLASS_HAS_RESERVED_SLOTS(JSObject::ARGS_CLASS_RESERVED_SLOTS) |
834 JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
835 PropertyStub, /* addProperty */
836 args_delProperty,
837 PropertyStub, /* getProperty */
838 PropertyStub, /* setProperty */
839 args_enumerate,
840 (JSResolveOp) args_resolve,
841 ConvertStub,
842 args_finalize, /* finalize */
843 NULL, /* reserved0 */
844 NULL, /* checkAccess */
845 NULL, /* call */
846 NULL, /* construct */
847 NULL, /* xdrObject */
848 NULL, /* hasInstance */
849 JS_CLASS_TRACE(args_trace)
852 namespace js {
855 * Strict mode arguments is significantly less magical than non-strict mode
856 * arguments, so it is represented by a different class while sharing some
857 * functionality.
859 Class StrictArgumentsClass = {
860 "Arguments",
861 JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE |
862 JSCLASS_HAS_RESERVED_SLOTS(JSObject::ARGS_CLASS_RESERVED_SLOTS) |
863 JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
864 PropertyStub, /* addProperty */
865 args_delProperty,
866 PropertyStub, /* getProperty */
867 PropertyStub, /* setProperty */
868 strictargs_enumerate,
869 reinterpret_cast<JSResolveOp>(strictargs_resolve),
870 ConvertStub,
871 args_finalize, /* finalize */
872 NULL, /* reserved0 */
873 NULL, /* checkAccess */
874 NULL, /* call */
875 NULL, /* construct */
876 NULL, /* xdrObject */
877 NULL, /* hasInstance */
878 JS_CLASS_TRACE(args_trace)
884 * A Declarative Environment object stores its active JSStackFrame pointer in
885 * its private slot, just as Call and Arguments objects do.
887 Class js_DeclEnvClass = {
888 js_Object_str,
889 JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
890 PropertyStub, /* addProperty */
891 PropertyStub, /* delProperty */
892 PropertyStub, /* getProperty */
893 PropertyStub, /* setProperty */
894 EnumerateStub,
895 ResolveStub,
896 ConvertStub
899 static JSBool
900 CheckForEscapingClosure(JSContext *cx, JSObject *obj, Value *vp)
902 JS_ASSERT(obj->isCall() || obj->getClass() == &js_DeclEnvClass);
904 const Value &v = *vp;
906 JSObject *funobj;
907 if (IsFunctionObject(v, &funobj)) {
908 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
911 * Any escaping null or flat closure that reaches above itself or
912 * contains nested functions that reach above it must be wrapped.
913 * We can wrap only when this Call or Declarative Environment obj
914 * still has an active stack frame associated with it.
916 if (fun->needsWrapper()) {
917 LeaveTrace(cx);
919 JSStackFrame *fp = (JSStackFrame *) obj->getPrivate();
920 if (fp) {
921 JSObject *wrapper = WrapEscapingClosure(cx, fp, fun);
922 if (!wrapper)
923 return false;
924 vp->setObject(*wrapper);
925 return true;
928 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
929 JSMSG_OPTIMIZED_CLOSURE_LEAK);
930 return false;
933 return true;
936 static JSBool
937 CalleeGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
939 return CheckForEscapingClosure(cx, obj, vp);
942 namespace js {
945 * Construct a call object for the given bindings. The callee is the function
946 * on behalf of which the call object is being created.
948 JSObject *
949 NewCallObject(JSContext *cx, Bindings *bindings, JSObject &scopeChain, JSObject *callee)
951 size_t argsVars = bindings->countArgsAndVars();
952 size_t slots = JSObject::CALL_RESERVED_SLOTS + argsVars;
953 gc::FinalizeKind kind = gc::GetGCObjectKind(slots);
955 JSObject *callobj = js_NewGCObject(cx, kind);
956 if (!callobj)
957 return NULL;
959 /* Init immediately to avoid GC seeing a half-init'ed object. */
960 callobj->init(cx, &js_CallClass, NULL, &scopeChain, NULL, false);
961 callobj->setMap(bindings->lastShape());
963 /* This must come after callobj->lastProp has been set. */
964 if (!callobj->ensureInstanceReservedSlots(cx, argsVars))
965 return NULL;
967 #ifdef DEBUG
968 for (Shape::Range r = callobj->lastProp; !r.empty(); r.popFront()) {
969 const Shape &s = r.front();
970 if (s.slot != SHAPE_INVALID_SLOT) {
971 JS_ASSERT(s.slot + 1 == callobj->slotSpan());
972 break;
975 #endif
977 callobj->setCallObjCallee(callee);
978 return callobj;
981 } // namespace js
983 static inline JSObject *
984 NewDeclEnvObject(JSContext *cx, JSStackFrame *fp)
986 JSObject *envobj = js_NewGCObject(cx, FINALIZE_OBJECT2);
987 if (!envobj)
988 return NULL;
990 envobj->init(cx, &js_DeclEnvClass, NULL, &fp->scopeChain(), fp, false);
991 envobj->setMap(cx->runtime->emptyDeclEnvShape);
992 return envobj;
995 JSObject *
996 js_GetCallObject(JSContext *cx, JSStackFrame *fp)
998 /* Create a call object for fp only if it lacks one. */
999 JS_ASSERT(fp->isFunctionFrame());
1000 if (fp->hasCallObj())
1001 return &fp->callObj();
1003 #ifdef DEBUG
1004 /* A call object should be a frame's outermost scope chain element. */
1005 Class *clasp = fp->scopeChain().getClass();
1006 if (clasp == &js_WithClass || clasp == &js_BlockClass)
1007 JS_ASSERT(fp->scopeChain().getPrivate() != js_FloatingFrameIfGenerator(cx, fp));
1008 else if (clasp == &js_CallClass)
1009 JS_ASSERT(fp->scopeChain().getPrivate() != fp);
1010 #endif
1013 * Create the call object, using the frame's enclosing scope as its
1014 * parent, and link the call to its stack frame. For a named function
1015 * expression Call's parent points to an environment object holding
1016 * function's name.
1018 JSAtom *lambdaName =
1019 (fp->fun()->flags & JSFUN_LAMBDA) ? fp->fun()->atom : NULL;
1020 if (lambdaName) {
1021 JSObject *envobj = NewDeclEnvObject(cx, fp);
1022 if (!envobj)
1023 return NULL;
1025 /* Root envobj before js_DefineNativeProperty (-> JSClass.addProperty). */
1026 fp->setScopeChainNoCallObj(*envobj);
1027 if (!js_DefineNativeProperty(cx, &fp->scopeChain(), ATOM_TO_JSID(lambdaName),
1028 ObjectValue(fp->callee()),
1029 CalleeGetter, NULL,
1030 JSPROP_PERMANENT | JSPROP_READONLY,
1031 0, 0, NULL)) {
1032 return NULL;
1036 JSObject *callobj =
1037 NewCallObject(cx, &fp->fun()->script()->bindings, fp->scopeChain(), &fp->callee());
1038 if (!callobj)
1039 return NULL;
1041 callobj->setPrivate(fp);
1042 JS_ASSERT(fp->fun() == fp->callee().getFunctionPrivate());
1045 * Push callobj on the top of the scope chain, and make it the
1046 * variables object.
1048 fp->setScopeChainAndCallObj(*callobj);
1049 return callobj;
1052 JSObject * JS_FASTCALL
1053 js_CreateCallObjectOnTrace(JSContext *cx, JSFunction *fun, JSObject *callee, JSObject *scopeChain)
1055 JS_ASSERT(!js_IsNamedLambda(fun));
1056 JS_ASSERT(scopeChain);
1057 JS_ASSERT(callee);
1058 return NewCallObject(cx, &fun->script()->bindings, *scopeChain, callee);
1061 JS_DEFINE_CALLINFO_4(extern, OBJECT, js_CreateCallObjectOnTrace, CONTEXT, FUNCTION, OBJECT, OBJECT,
1062 0, nanojit::ACCSET_STORE_ANY)
1064 inline static void
1065 CopyValuesToCallObject(JSObject &callobj, uintN nargs, Value *argv, uintN nvars, Value *slots)
1067 JS_ASSERT(callobj.numSlots() >= JSObject::CALL_RESERVED_SLOTS + nargs + nvars);
1068 Value *base = callobj.getSlots() + JSObject::CALL_RESERVED_SLOTS;
1069 memcpy(base, argv, nargs * sizeof(Value));
1070 memcpy(base + nargs, slots, nvars * sizeof(Value));
1073 void
1074 js_PutCallObject(JSContext *cx, JSStackFrame *fp)
1076 JSObject &callobj = fp->callObj();
1079 * Strict mode eval frames have Call objects to put. Normal eval frames
1080 * never put a Call object.
1082 JS_ASSERT(fp->isEvalFrame() == callobj.callIsForEval());
1084 /* Get the arguments object to snapshot fp's actual argument values. */
1085 if (fp->hasArgsObj()) {
1086 if (!fp->hasOverriddenArgs())
1087 callobj.setCallObjArguments(ObjectValue(fp->argsObj()));
1088 js_PutArgsObject(cx, fp);
1091 JSScript *script = fp->script();
1092 Bindings &bindings = script->bindings;
1094 if (callobj.callIsForEval()) {
1095 JS_ASSERT(script->strictModeCode);
1096 JS_ASSERT(bindings.countArgs() == 0);
1098 /* This could be optimized as below, but keep it simple for now. */
1099 CopyValuesToCallObject(callobj, 0, NULL, bindings.countVars(), fp->slots());
1100 } else {
1101 JSFunction *fun = fp->fun();
1102 JS_ASSERT(fun == callobj.getCallObjCalleeFunction());
1103 JS_ASSERT(script == fun->script());
1105 uintN n = bindings.countArgsAndVars();
1106 if (n > 0) {
1107 JS_ASSERT(JSObject::CALL_RESERVED_SLOTS + n <= callobj.numSlots());
1109 uint32 nvars = bindings.countVars();
1110 uint32 nargs = bindings.countArgs();
1111 JS_ASSERT(fun->nargs == nargs);
1112 JS_ASSERT(nvars + nargs == n);
1114 JSScript *script = fun->script();
1115 if (script->usesEval
1116 #ifdef JS_METHODJIT
1117 || script->debugMode
1118 #endif
1120 CopyValuesToCallObject(callobj, nargs, fp->formalArgs(), nvars, fp->slots());
1121 } else {
1123 * For each arg & var that is closed over, copy it from the stack
1124 * into the call object.
1126 uint32 nclosed = script->nClosedArgs;
1127 for (uint32 i = 0; i < nclosed; i++) {
1128 uint32 e = script->getClosedArg(i);
1129 callobj.setSlot(JSObject::CALL_RESERVED_SLOTS + e, fp->formalArg(e));
1132 nclosed = script->nClosedVars;
1133 for (uint32 i = 0; i < nclosed; i++) {
1134 uint32 e = script->getClosedVar(i);
1135 callobj.setSlot(JSObject::CALL_RESERVED_SLOTS + nargs + e, fp->slots()[e]);
1140 /* Clear private pointers to fp, which is about to go away (js_Invoke). */
1141 if (js_IsNamedLambda(fun)) {
1142 JSObject *env = callobj.getParent();
1144 JS_ASSERT(env->getClass() == &js_DeclEnvClass);
1145 JS_ASSERT(env->getPrivate() == fp);
1146 env->setPrivate(NULL);
1150 callobj.setPrivate(NULL);
1151 fp->clearCallObj();
1154 JSBool JS_FASTCALL
1155 js_PutCallObjectOnTrace(JSContext *cx, JSObject *callobj, uint32 nargs, Value *argv,
1156 uint32 nvars, Value *slots)
1158 JS_ASSERT(callobj->isCall());
1159 JS_ASSERT(!callobj->getPrivate());
1161 uintN n = nargs + nvars;
1162 if (n != 0)
1163 CopyValuesToCallObject(*callobj, nargs, argv, nvars, slots);
1165 return true;
1168 JS_DEFINE_CALLINFO_6(extern, BOOL, js_PutCallObjectOnTrace, CONTEXT, OBJECT, UINT32, VALUEPTR,
1169 UINT32, VALUEPTR, 0, nanojit::ACCSET_STORE_ANY)
1171 namespace js {
1173 static JSBool
1174 GetCallArguments(JSContext *cx, JSObject *obj, jsid id, Value *vp)
1176 JSStackFrame *fp = obj->maybeCallObjStackFrame();
1177 if (fp && !fp->hasOverriddenArgs()) {
1178 JSObject *argsobj = js_GetArgsObject(cx, fp);
1179 if (!argsobj)
1180 return false;
1181 vp->setObject(*argsobj);
1182 } else {
1183 *vp = obj->getCallObjArguments();
1185 return true;
1188 static JSBool
1189 SetCallArguments(JSContext *cx, JSObject *obj, jsid id, Value *vp)
1191 if (JSStackFrame *fp = obj->maybeCallObjStackFrame())
1192 fp->setOverriddenArgs();
1193 obj->setCallObjArguments(*vp);
1194 return true;
1197 JSBool
1198 GetCallArg(JSContext *cx, JSObject *obj, jsid id, Value *vp)
1200 JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
1201 uintN i = (uint16) JSID_TO_INT(id);
1203 if (JSStackFrame *fp = obj->maybeCallObjStackFrame())
1204 *vp = fp->formalArg(i);
1205 else
1206 *vp = obj->callObjArg(i);
1207 return true;
1210 JSBool
1211 SetCallArg(JSContext *cx, JSObject *obj, jsid id, Value *vp)
1213 JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
1214 uintN i = (uint16) JSID_TO_INT(id);
1216 Value *argp;
1217 if (JSStackFrame *fp = obj->maybeCallObjStackFrame())
1218 argp = &fp->formalArg(i);
1219 else
1220 argp = &obj->callObjArg(i);
1222 GC_POKE(cx, *argp);
1223 *argp = *vp;
1224 return true;
1227 JSBool
1228 GetCallUpvar(JSContext *cx, JSObject *obj, jsid id, Value *vp)
1230 JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
1231 uintN i = (uint16) JSID_TO_INT(id);
1233 *vp = obj->getCallObjCallee()->getFlatClosureUpvar(i);
1234 return true;
1237 JSBool
1238 SetCallUpvar(JSContext *cx, JSObject *obj, jsid id, Value *vp)
1240 JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
1241 uintN i = (uint16) JSID_TO_INT(id);
1243 Value *up = &obj->getCallObjCallee()->getFlatClosureUpvar(i);
1245 GC_POKE(cx, *up);
1246 *up = *vp;
1247 return true;
1250 JSBool
1251 GetCallVar(JSContext *cx, JSObject *obj, jsid id, Value *vp)
1253 JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
1254 uintN i = (uint16) JSID_TO_INT(id);
1256 if (JSStackFrame *fp = obj->maybeCallObjStackFrame())
1257 *vp = fp->varSlot(i);
1258 else
1259 *vp = obj->callObjVar(i);
1261 return true;
1264 JSBool
1265 GetCallVarChecked(JSContext *cx, JSObject *obj, jsid id, Value *vp)
1267 if (!GetCallVar(cx, obj, id, vp))
1268 return false;
1270 return CheckForEscapingClosure(cx, obj, vp);
1273 JSBool
1274 SetCallVar(JSContext *cx, JSObject *obj, jsid id, Value *vp)
1276 JS_ASSERT(obj->isCall());
1278 JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
1279 uintN i = (uint16) JSID_TO_INT(id);
1282 * As documented in TraceRecorder::attemptTreeCall(), when recording an
1283 * inner tree call, the recorder assumes the inner tree does not mutate
1284 * any tracked upvars. The abort here is a pessimistic precaution against
1285 * bug 620662, where an inner tree setting a closed stack variable in an
1286 * outer tree is illegal, and runtime would fall off trace.
1288 #ifdef JS_TRACER
1289 TraceMonitor *tm = &JS_TRACE_MONITOR(cx);
1290 if (tm->recorder && tm->tracecx)
1291 AbortRecording(cx, "upvar write in nested tree");
1292 #endif
1294 Value *varp;
1295 if (JSStackFrame *fp = obj->maybeCallObjStackFrame())
1296 varp = &fp->varSlot(i);
1297 else
1298 varp = &obj->callObjVar(i);
1300 GC_POKE(cx, *varp);
1301 *varp = *vp;
1302 return true;
1305 } // namespace js
1307 #if JS_TRACER
1308 JSBool JS_FASTCALL
1309 js_SetCallArg(JSContext *cx, JSObject *obj, jsid slotid, ValueArgType arg)
1311 Value argcopy = ValueArgToConstRef(arg);
1312 return SetCallArg(cx, obj, slotid, &argcopy);
1314 JS_DEFINE_CALLINFO_4(extern, BOOL, js_SetCallArg, CONTEXT, OBJECT, JSID, VALUE, 0,
1315 nanojit::ACCSET_STORE_ANY)
1317 JSBool JS_FASTCALL
1318 js_SetCallVar(JSContext *cx, JSObject *obj, jsid slotid, ValueArgType arg)
1320 Value argcopy = ValueArgToConstRef(arg);
1321 return SetCallVar(cx, obj, slotid, &argcopy);
1323 JS_DEFINE_CALLINFO_4(extern, BOOL, js_SetCallVar, CONTEXT, OBJECT, JSID, VALUE, 0,
1324 nanojit::ACCSET_STORE_ANY)
1325 #endif
1327 static JSBool
1328 call_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
1329 JSObject **objp)
1331 JS_ASSERT(obj->isCall());
1332 JS_ASSERT(!obj->getProto());
1334 if (!JSID_IS_ATOM(id))
1335 return true;
1337 JSObject *callee = obj->getCallObjCallee();
1338 #ifdef DEBUG
1339 if (callee) {
1340 JSScript *script = callee->getFunctionPrivate()->script();
1341 JS_ASSERT(!script->bindings.hasBinding(cx, JSID_TO_ATOM(id)));
1343 #endif
1346 * Resolve arguments so that we never store a particular Call object's
1347 * arguments object reference in a Call prototype's |arguments| slot.
1349 * Include JSPROP_ENUMERATE for consistency with all other Call object
1350 * properties; see js::Bindings::add and js::Interpret's JSOP_DEFFUN
1351 * rebinding-Call-property logic.
1353 if (callee && id == ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom)) {
1354 if (!js_DefineNativeProperty(cx, obj, id, UndefinedValue(),
1355 GetCallArguments, SetCallArguments,
1356 JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_ENUMERATE,
1357 0, 0, NULL, JSDNP_DONT_PURGE)) {
1358 return false;
1360 *objp = obj;
1361 return true;
1364 /* Control flow reaches here only if id was not resolved. */
1365 return true;
1368 static void
1369 call_trace(JSTracer *trc, JSObject *obj)
1371 JS_ASSERT(obj->isCall());
1372 if (JSStackFrame *fp = obj->maybeCallObjStackFrame()) {
1374 * FIXME: Hide copies of stack values rooted by fp from the Cycle
1375 * Collector, which currently lacks a non-stub Unlink implementation
1376 * for JS objects (including Call objects), so is unable to collect
1377 * cycles involving Call objects whose frames are active without this
1378 * hiding hack.
1380 uintN first = JSObject::CALL_RESERVED_SLOTS;
1381 uintN count = fp->script()->bindings.countArgsAndVars();
1383 JS_ASSERT(obj->numSlots() >= first + count);
1384 SetValueRangeToUndefined(obj->getSlots() + first, count);
1387 MaybeMarkGenerator(trc, obj);
1390 JS_PUBLIC_DATA(Class) js_CallClass = {
1391 "Call",
1392 JSCLASS_HAS_PRIVATE |
1393 JSCLASS_HAS_RESERVED_SLOTS(JSObject::CALL_RESERVED_SLOTS) |
1394 JSCLASS_NEW_RESOLVE | JSCLASS_IS_ANONYMOUS | JSCLASS_MARK_IS_TRACE,
1395 PropertyStub, /* addProperty */
1396 PropertyStub, /* delProperty */
1397 PropertyStub, /* getProperty */
1398 PropertyStub, /* setProperty */
1399 JS_EnumerateStub,
1400 (JSResolveOp)call_resolve,
1401 NULL, /* convert: Leave it NULL so we notice if calls ever escape */
1402 NULL, /* finalize */
1403 NULL, /* reserved0 */
1404 NULL, /* checkAccess */
1405 NULL, /* call */
1406 NULL, /* construct */
1407 NULL, /* xdrObject */
1408 NULL, /* hasInstance */
1409 JS_CLASS_TRACE(call_trace)
1412 bool
1413 JSStackFrame::getValidCalleeObject(JSContext *cx, Value *vp)
1415 if (!isFunctionFrame()) {
1416 vp->setUndefined();
1417 return true;
1420 JSFunction *fun = this->fun();
1423 * See the equivalent condition in ArgGetter for the 'callee' id case, but
1424 * note that here we do not want to throw, since this escape can happen via
1425 * a foo.caller reference alone, without any debugger or indirect eval. And
1426 * alas, it seems foo.caller is still used on the Web.
1428 if (fun->needsWrapper()) {
1429 JSObject *wrapper = WrapEscapingClosure(cx, this, fun);
1430 if (!wrapper)
1431 return false;
1432 vp->setObject(*wrapper);
1433 return true;
1436 JSObject &funobj = callee();
1437 vp->setObject(funobj);
1440 * Check for an escape attempt by a joined function object, which must go
1441 * through the frame's |this| object's method read barrier for the method
1442 * atom by which it was uniquely associated with a property.
1444 const Value &thisv = functionThis();
1445 if (thisv.isObject()) {
1446 JS_ASSERT(funobj.getFunctionPrivate() == fun);
1448 if (&fun->compiledFunObj() == &funobj && fun->methodAtom()) {
1449 JSObject *thisp = &thisv.toObject();
1450 JSObject *first_barriered_thisp = NULL;
1452 do {
1454 * While a non-native object is responsible for handling its
1455 * entire prototype chain, notable non-natives including dense
1456 * and typed arrays have native prototypes, so keep going.
1458 if (!thisp->isNative())
1459 continue;
1461 if (thisp->hasMethodBarrier()) {
1462 const Shape *shape = thisp->nativeLookup(ATOM_TO_JSID(fun->methodAtom()));
1463 if (shape) {
1465 * Two cases follow: the method barrier was not crossed
1466 * yet, so we cross it here; the method barrier *was*
1467 * crossed but after the call, in which case we fetch
1468 * and validate the cloned (unjoined) funobj from the
1469 * method property's slot.
1471 * In either case we must allow for the method property
1472 * to have been replaced, or its value overwritten.
1474 if (shape->isMethod() && &shape->methodObject() == &funobj) {
1475 if (!thisp->methodReadBarrier(cx, *shape, vp))
1476 return false;
1477 calleeValue().setObject(vp->toObject());
1478 return true;
1481 if (shape->hasSlot()) {
1482 Value v = thisp->getSlot(shape->slot);
1483 JSObject *clone;
1485 if (IsFunctionObject(v, &clone) &&
1486 GET_FUNCTION_PRIVATE(cx, clone) == fun &&
1487 clone->hasMethodObj(*thisp)) {
1488 JS_ASSERT(clone != &funobj);
1489 *vp = v;
1490 calleeValue().setObject(*clone);
1491 return true;
1496 if (!first_barriered_thisp)
1497 first_barriered_thisp = thisp;
1499 } while ((thisp = thisp->getProto()) != NULL);
1501 if (!first_barriered_thisp)
1502 return true;
1505 * At this point, we couldn't find an already-existing clone (or
1506 * force to exist a fresh clone) created via thisp's method read
1507 * barrier, so we must clone fun and store it in fp's callee to
1508 * avoid re-cloning upon repeated foo.caller access.
1510 * This must mean the code in js_DeleteProperty could not find this
1511 * stack frame on the stack when the method was deleted. We've lost
1512 * track of the method, so we associate it with the first barriered
1513 * object found starting from thisp on the prototype chain.
1515 JSObject *newfunobj = CloneFunctionObject(cx, fun, fun->getParent());
1516 if (!newfunobj)
1517 return false;
1518 newfunobj->setMethodObj(*first_barriered_thisp);
1519 calleeValue().setObject(*newfunobj);
1520 vp->setObject(*newfunobj);
1521 return true;
1525 return true;
1528 /* Generic function tinyids. */
1529 enum {
1530 FUN_ARGUMENTS = -1, /* predefined arguments local variable */
1531 FUN_LENGTH = -2, /* number of actual args, arity if inactive */
1532 FUN_ARITY = -3, /* number of formal parameters; desired argc */
1533 FUN_NAME = -4, /* function name, "" if anonymous */
1534 FUN_CALLER = -5 /* Function.prototype.caller, backward compat */
1537 static JSBool
1538 fun_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
1540 if (!JSID_IS_INT(id))
1541 return true;
1543 jsint slot = JSID_TO_INT(id);
1546 * Loop because getter and setter can be delegated from another class,
1547 * but loop only for FUN_LENGTH because we must pretend that f.length
1548 * is in each function instance f, per ECMA-262, instead of only in the
1549 * Function.prototype object (we use JSPROP_PERMANENT with JSPROP_SHARED
1550 * to make it appear so).
1552 * This code couples tightly to the attributes for lazyFunctionDataProps[]
1553 * and poisonPillProps[] initializers below, and to js_SetProperty and
1554 * js_HasOwnProperty.
1556 * It's important to allow delegating objects, even though they inherit
1557 * this getter (fun_getProperty), to override arguments, arity, caller,
1558 * and name. If we didn't return early for slot != FUN_LENGTH, we would
1559 * clobber *vp with the native property value, instead of letting script
1560 * override that value in delegating objects.
1562 * Note how that clobbering is what simulates JSPROP_READONLY for all of
1563 * the non-standard properties when the directly addressed object (obj)
1564 * is a function object (i.e., when this loop does not iterate).
1566 JSFunction *fun;
1567 while (!(fun = (JSFunction *)
1568 GetInstancePrivate(cx, obj, &js_FunctionClass, NULL))) {
1569 if (slot != FUN_LENGTH)
1570 return true;
1571 obj = obj->getProto();
1572 if (!obj)
1573 return true;
1576 /* Find fun's top-most activation record. */
1577 JSStackFrame *fp;
1578 for (fp = js_GetTopStackFrame(cx);
1579 fp && (fp->maybeFun() != fun || fp->isEvalOrDebuggerFrame());
1580 fp = fp->prev()) {
1581 continue;
1584 switch (slot) {
1585 case FUN_ARGUMENTS:
1586 /* Warn if strict about f.arguments or equivalent unqualified uses. */
1587 if (!JS_ReportErrorFlagsAndNumber(cx,
1588 JSREPORT_WARNING | JSREPORT_STRICT,
1589 js_GetErrorMessage, NULL,
1590 JSMSG_DEPRECATED_USAGE,
1591 js_arguments_str)) {
1592 return false;
1594 if (fp) {
1595 if (!js_GetArgsValue(cx, fp, vp))
1596 return false;
1597 } else {
1598 vp->setNull();
1600 break;
1602 case FUN_LENGTH:
1603 case FUN_ARITY:
1604 vp->setInt32(fun->nargs);
1605 break;
1607 case FUN_NAME:
1608 vp->setString(fun->atom ? ATOM_TO_STRING(fun->atom)
1609 : cx->runtime->emptyString);
1610 break;
1612 case FUN_CALLER:
1613 vp->setNull();
1614 if (fp && fp->prev() && !fp->prev()->getValidCalleeObject(cx, vp))
1615 return false;
1617 if (vp->isObject()) {
1618 JSObject &caller = vp->toObject();
1620 /* Censor the caller if it is from another compartment. */
1621 if (caller.getCompartment() != cx->compartment) {
1622 vp->setNull();
1623 } else if (caller.isFunction()) {
1624 JSFunction *callerFun = caller.getFunctionPrivate();
1625 if (callerFun->isInterpreted() && callerFun->inStrictMode()) {
1626 JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL,
1627 JSMSG_CALLER_IS_STRICT);
1628 return false;
1632 break;
1634 default:
1635 /* XXX fun[0] and fun.arguments[0] are equivalent. */
1636 if (fp && fp->isFunctionFrame() && uint16(slot) < fp->numFormalArgs())
1637 *vp = fp->formalArg(slot);
1638 break;
1641 return true;
1644 struct LazyFunctionDataProp {
1645 uint16 atomOffset;
1646 int8 tinyid;
1647 uint8 attrs;
1650 struct PoisonPillProp {
1651 uint16 atomOffset;
1652 int8 tinyid;
1655 /* NB: no sentinels at ends -- use JS_ARRAY_LENGTH to bound loops. */
1657 static const LazyFunctionDataProp lazyFunctionDataProps[] = {
1658 {ATOM_OFFSET(arity), FUN_ARITY, JSPROP_PERMANENT|JSPROP_READONLY},
1659 {ATOM_OFFSET(name), FUN_NAME, JSPROP_PERMANENT|JSPROP_READONLY},
1662 /* Properties censored into [[ThrowTypeError]] in strict mode. */
1663 static const PoisonPillProp poisonPillProps[] = {
1664 {ATOM_OFFSET(arguments), FUN_ARGUMENTS },
1665 {ATOM_OFFSET(caller), FUN_CALLER },
1668 static JSBool
1669 fun_enumerate(JSContext *cx, JSObject *obj)
1671 JS_ASSERT(obj->isFunction());
1673 jsid id;
1674 bool found;
1676 if (!obj->isBoundFunction()) {
1677 id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
1678 if (!obj->hasProperty(cx, id, &found, JSRESOLVE_QUALIFIED))
1679 return false;
1682 id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
1683 if (!obj->hasProperty(cx, id, &found, JSRESOLVE_QUALIFIED))
1684 return false;
1686 for (uintN i = 0; i < JS_ARRAY_LENGTH(lazyFunctionDataProps); i++) {
1687 const LazyFunctionDataProp &lfp = lazyFunctionDataProps[i];
1688 id = ATOM_TO_JSID(OFFSET_TO_ATOM(cx->runtime, lfp.atomOffset));
1689 if (!obj->hasProperty(cx, id, &found, JSRESOLVE_QUALIFIED))
1690 return false;
1693 for (uintN i = 0; i < JS_ARRAY_LENGTH(poisonPillProps); i++) {
1694 const PoisonPillProp &p = poisonPillProps[i];
1695 id = ATOM_TO_JSID(OFFSET_TO_ATOM(cx->runtime, p.atomOffset));
1696 if (!obj->hasProperty(cx, id, &found, JSRESOLVE_QUALIFIED))
1697 return false;
1700 return true;
1703 static JSBool
1704 fun_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
1705 JSObject **objp)
1707 if (!JSID_IS_ATOM(id))
1708 return true;
1710 JSFunction *fun = obj->getFunctionPrivate();
1712 if (JSID_IS_ATOM(id, cx->runtime->atomState.classPrototypeAtom)) {
1714 * Native or "built-in" functions do not have a .prototype property per
1715 * ECMA-262 (all editions). Built-in constructor functions, e.g. Object
1716 * and Function to name two conspicuous examples, do have a .prototype
1717 * property, but it is created eagerly by js_InitClass (jsobj.cpp).
1719 * ES5 15.3.4: the non-native function object named Function.prototype
1720 * must not have a .prototype property.
1722 * ES5 15.3.4.5: bound functions don't have a prototype property. The
1723 * isNative() test covers this case because bound functions are native
1724 * functions by definition/construction.
1726 if (fun->isNative() || fun->isFunctionPrototype())
1727 return true;
1730 * Assert that fun is not a compiler-created function object, which
1731 * must never leak to script or embedding code and then be mutated.
1732 * Also assert that obj is not bound, per the ES5 15.3.4.5 ref above.
1734 JS_ASSERT(!IsInternalFunctionObject(obj));
1735 JS_ASSERT(!obj->isBoundFunction());
1738 * Make the prototype object an instance of Object with the same parent
1739 * as the function object itself.
1741 JSObject *parent = obj->getParent();
1742 JSObject *proto;
1743 if (!js_GetClassPrototype(cx, parent, JSProto_Object, &proto))
1744 return false;
1745 proto = NewNativeClassInstance(cx, &js_ObjectClass, proto, parent);
1746 if (!proto)
1747 return false;
1750 * ECMA (15.3.5.2) says that constructor.prototype is DontDelete for
1751 * user-defined functions, but DontEnum | ReadOnly | DontDelete for
1752 * native "system" constructors such as Object or Function. So lazily
1753 * set the former here in fun_resolve, but eagerly define the latter
1754 * in js_InitClass, with the right attributes.
1756 if (!js_SetClassPrototype(cx, obj, proto, JSPROP_PERMANENT))
1757 return false;
1759 *objp = obj;
1760 return true;
1763 if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
1764 JS_ASSERT(!IsInternalFunctionObject(obj));
1765 if (!js_DefineNativeProperty(cx, obj, id, Int32Value(fun->nargs),
1766 PropertyStub, PropertyStub,
1767 JSPROP_PERMANENT | JSPROP_READONLY, 0, 0, NULL)) {
1768 return false;
1770 *objp = obj;
1771 return true;
1774 for (uintN i = 0; i < JS_ARRAY_LENGTH(lazyFunctionDataProps); i++) {
1775 const LazyFunctionDataProp *lfp = &lazyFunctionDataProps[i];
1777 if (JSID_IS_ATOM(id, OFFSET_TO_ATOM(cx->runtime, lfp->atomOffset))) {
1778 JS_ASSERT(!IsInternalFunctionObject(obj));
1780 if (!js_DefineNativeProperty(cx, obj, id, UndefinedValue(),
1781 fun_getProperty, PropertyStub,
1782 lfp->attrs, Shape::HAS_SHORTID,
1783 lfp->tinyid, NULL)) {
1784 return false;
1786 *objp = obj;
1787 return true;
1791 for (uintN i = 0; i < JS_ARRAY_LENGTH(poisonPillProps); i++) {
1792 const PoisonPillProp &p = poisonPillProps[i];
1794 if (JSID_IS_ATOM(id, OFFSET_TO_ATOM(cx->runtime, p.atomOffset))) {
1795 JS_ASSERT(!IsInternalFunctionObject(obj));
1797 PropertyOp getter, setter;
1798 uintN attrs = JSPROP_PERMANENT;
1799 if (fun->isInterpreted() ? fun->inStrictMode() : obj->isBoundFunction()) {
1800 JSObject *throwTypeError = obj->getThrowTypeError();
1802 getter = CastAsPropertyOp(throwTypeError);
1803 setter = CastAsPropertyOp(throwTypeError);
1804 attrs |= JSPROP_GETTER | JSPROP_SETTER;
1805 } else {
1806 getter = fun_getProperty;
1807 setter = PropertyStub;
1810 if (!js_DefineNativeProperty(cx, obj, id, UndefinedValue(),
1811 getter, setter,
1812 attrs, Shape::HAS_SHORTID,
1813 p.tinyid, NULL)) {
1814 return false;
1816 *objp = obj;
1817 return true;
1821 return true;
1824 #if JS_HAS_XDR
1826 /* XXX store parent and proto, if defined */
1827 JSBool
1828 js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp)
1830 JSContext *cx;
1831 JSFunction *fun;
1832 uint32 firstword; /* flag telling whether fun->atom is non-null,
1833 plus for fun->u.i.skipmin, fun->u.i.wrapper,
1834 and 14 bits reserved for future use */
1835 uint32 flagsword; /* word for argument count and fun->flags */
1837 cx = xdr->cx;
1838 if (xdr->mode == JSXDR_ENCODE) {
1839 fun = GET_FUNCTION_PRIVATE(cx, *objp);
1840 if (!FUN_INTERPRETED(fun)) {
1841 JSAutoByteString funNameBytes;
1842 if (const char *name = GetFunctionNameBytes(cx, fun, &funNameBytes)) {
1843 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_SCRIPTED_FUNCTION,
1844 name);
1846 return false;
1848 if (fun->u.i.wrapper) {
1849 JSAutoByteString funNameBytes;
1850 if (const char *name = GetFunctionNameBytes(cx, fun, &funNameBytes))
1851 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_XDR_CLOSURE_WRAPPER, name);
1852 return false;
1854 JS_ASSERT((fun->u.i.wrapper & ~1U) == 0);
1855 firstword = (fun->u.i.skipmin << 2) | (fun->u.i.wrapper << 1) | !!fun->atom;
1856 flagsword = (fun->nargs << 16) | fun->flags;
1857 } else {
1858 fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, NULL, NULL);
1859 if (!fun)
1860 return false;
1861 FUN_OBJECT(fun)->clearParent();
1862 FUN_OBJECT(fun)->clearProto();
1865 AutoObjectRooter tvr(cx, FUN_OBJECT(fun));
1867 if (!JS_XDRUint32(xdr, &firstword))
1868 return false;
1869 if ((firstword & 1U) && !js_XDRAtom(xdr, &fun->atom))
1870 return false;
1871 if (!JS_XDRUint32(xdr, &flagsword))
1872 return false;
1874 if (xdr->mode == JSXDR_DECODE) {
1875 fun->nargs = flagsword >> 16;
1876 JS_ASSERT((flagsword & JSFUN_KINDMASK) >= JSFUN_INTERPRETED);
1877 fun->flags = uint16(flagsword);
1878 fun->u.i.skipmin = uint16(firstword >> 2);
1879 fun->u.i.wrapper = JSPackedBool((firstword >> 1) & 1);
1882 if (!js_XDRScript(xdr, &fun->u.i.script, NULL))
1883 return false;
1885 if (xdr->mode == JSXDR_DECODE) {
1886 *objp = FUN_OBJECT(fun);
1887 #ifdef CHECK_SCRIPT_OWNER
1888 fun->script()->owner = NULL;
1889 #endif
1890 JS_ASSERT(fun->nargs == fun->script()->bindings.countArgs());
1891 js_CallNewScriptHook(cx, fun->script(), fun);
1894 return true;
1897 #else /* !JS_HAS_XDR */
1899 #define js_XDRFunctionObject NULL
1901 #endif /* !JS_HAS_XDR */
1904 * [[HasInstance]] internal method for Function objects: fetch the .prototype
1905 * property of its 'this' parameter, and walks the prototype chain of v (only
1906 * if v is an object) returning true if .prototype is found.
1908 static JSBool
1909 fun_hasInstance(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp)
1911 while (obj->isFunction()) {
1912 if (!obj->isBoundFunction())
1913 break;
1914 obj = obj->getBoundFunctionTarget();
1917 jsid id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
1918 Value pval;
1919 if (!obj->getProperty(cx, id, &pval))
1920 return JS_FALSE;
1922 if (pval.isPrimitive()) {
1924 * Throw a runtime error if instanceof is called on a function that
1925 * has a non-object as its .prototype value.
1927 js_ReportValueError(cx, JSMSG_BAD_PROTOTYPE, -1, ObjectValue(*obj), NULL);
1928 return JS_FALSE;
1931 *bp = js_IsDelegate(cx, &pval.toObject(), *v);
1932 return JS_TRUE;
1935 static void
1936 fun_trace(JSTracer *trc, JSObject *obj)
1938 /* A newborn function object may have a not yet initialized private slot. */
1939 JSFunction *fun = (JSFunction *) obj->getPrivate();
1940 if (!fun)
1941 return;
1943 if (fun != obj) {
1944 /* obj is a cloned function object, trace the clone-parent, fun. */
1945 MarkObject(trc, *fun, "private");
1947 /* The function could be a flat closure with upvar copies in the clone. */
1948 if (fun->isFlatClosure() && fun->script()->bindings.hasUpvars()) {
1949 MarkValueRange(trc, fun->script()->bindings.countUpvars(),
1950 obj->getFlatClosureUpvars(), "upvars");
1952 return;
1955 if (fun->atom)
1956 MarkString(trc, ATOM_TO_STRING(fun->atom), "atom");
1958 if (fun->isInterpreted() && fun->script())
1959 js_TraceScript(trc, fun->script());
1962 static void
1963 fun_finalize(JSContext *cx, JSObject *obj)
1965 /* Ignore newborn function objects. */
1966 JSFunction *fun = obj->getFunctionPrivate();
1967 if (!fun)
1968 return;
1970 /* Cloned function objects may be flat closures with upvars to free. */
1971 if (fun != obj) {
1972 if (fun->isFlatClosure() && fun->script()->bindings.hasUpvars())
1973 cx->free((void *) obj->getFlatClosureUpvars());
1974 return;
1978 * Null-check fun->script() because the parser sets interpreted very early.
1980 if (fun->isInterpreted() && fun->script())
1981 js_DestroyScriptFromGC(cx, fun->script());
1985 * Reserve two slots in all function objects for XPConnect. Note that this
1986 * does not bloat every instance, only those on which reserved slots are set,
1987 * and those on which ad-hoc properties are defined.
1989 JS_PUBLIC_DATA(Class) js_FunctionClass = {
1990 js_Function_str,
1991 JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE |
1992 JSCLASS_HAS_RESERVED_SLOTS(JSFunction::CLASS_RESERVED_SLOTS) |
1993 JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Function),
1994 PropertyStub, /* addProperty */
1995 PropertyStub, /* delProperty */
1996 PropertyStub, /* getProperty */
1997 PropertyStub, /* setProperty */
1998 fun_enumerate,
1999 (JSResolveOp)fun_resolve,
2000 ConvertStub,
2001 fun_finalize,
2002 NULL, /* reserved0 */
2003 NULL, /* checkAccess */
2004 NULL, /* call */
2005 NULL, /* construct */
2006 js_XDRFunctionObject,
2007 fun_hasInstance,
2008 JS_CLASS_TRACE(fun_trace)
2011 JSString *
2012 fun_toStringHelper(JSContext *cx, JSObject *obj, uintN indent)
2014 if (!obj->isFunction()) {
2015 if (obj->isFunctionProxy())
2016 return JSProxy::fun_toString(cx, obj, indent);
2017 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2018 JSMSG_INCOMPATIBLE_PROTO,
2019 js_Function_str, js_toString_str,
2020 "object");
2021 return NULL;
2024 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, obj);
2025 if (!fun)
2026 return NULL;
2027 return JS_DecompileFunction(cx, fun, indent);
2030 static JSBool
2031 fun_toString(JSContext *cx, uintN argc, Value *vp)
2033 JS_ASSERT(IsFunctionObject(vp[0]));
2034 uint32_t indent = 0;
2036 if (argc != 0 && !ValueToECMAUint32(cx, vp[2], &indent))
2037 return false;
2039 JSObject *obj = ComputeThisFromVp(cx, vp);
2040 if (!obj)
2041 return false;
2043 JSString *str = fun_toStringHelper(cx, obj, indent);
2044 if (!str)
2045 return false;
2047 vp->setString(str);
2048 return true;
2051 #if JS_HAS_TOSOURCE
2052 static JSBool
2053 fun_toSource(JSContext *cx, uintN argc, Value *vp)
2055 JS_ASSERT(IsFunctionObject(vp[0]));
2057 JSObject *obj = ComputeThisFromVp(cx, vp);
2058 if (!obj)
2059 return false;
2061 JSString *str = fun_toStringHelper(cx, obj, JS_DONT_PRETTY_PRINT);
2062 if (!str)
2063 return false;
2065 vp->setString(str);
2066 return true;
2068 #endif
2070 JSBool
2071 js_fun_call(JSContext *cx, uintN argc, Value *vp)
2073 LeaveTrace(cx);
2075 JSObject *obj = ComputeThisFromVp(cx, vp);
2076 if (!obj)
2077 return JS_FALSE;
2078 Value fval = vp[1];
2080 if (!js_IsCallable(fval)) {
2081 JSString *str = js_ValueToString(cx, fval);
2082 if (str) {
2083 JSAutoByteString bytes(cx, str);
2084 if (!!bytes) {
2085 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2086 JSMSG_INCOMPATIBLE_PROTO,
2087 js_Function_str, js_call_str,
2088 bytes.ptr());
2091 return JS_FALSE;
2094 Value *argv = vp + 2;
2095 Value thisv;
2096 if (argc == 0) {
2097 thisv.setUndefined();
2098 } else {
2099 thisv = argv[0];
2101 argc--;
2102 argv++;
2105 /* Allocate stack space for fval, obj, and the args. */
2106 InvokeArgsGuard args;
2107 if (!cx->stack().pushInvokeArgs(cx, argc, &args))
2108 return JS_FALSE;
2110 /* Push fval, thisv, and the args. */
2111 args.callee() = fval;
2112 args.thisv() = thisv;
2113 memcpy(args.argv(), argv, argc * sizeof *argv);
2115 bool ok = Invoke(cx, args, 0);
2116 *vp = args.rval();
2117 return ok;
2120 /* ES5 15.3.4.3 */
2121 JSBool
2122 js_fun_apply(JSContext *cx, uintN argc, Value *vp)
2124 JSObject *obj = ComputeThisFromVp(cx, vp);
2125 if (!obj)
2126 return false;
2128 /* Step 1. */
2129 Value fval = vp[1];
2130 if (!js_IsCallable(fval)) {
2131 if (JSString *str = js_ValueToString(cx, fval)) {
2132 JSAutoByteString bytes(cx, str);
2133 if (!!bytes) {
2134 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2135 JSMSG_INCOMPATIBLE_PROTO,
2136 js_Function_str, js_apply_str,
2137 bytes.ptr());
2140 return false;
2143 /* Step 2. */
2144 if (argc < 2 || vp[3].isNullOrUndefined())
2145 return js_fun_call(cx, (argc > 0) ? 1 : 0, vp);
2147 /* N.B. Changes need to be propagated to stubs::SplatApplyArgs. */
2149 /* Step 3. */
2150 if (!vp[3].isObject()) {
2151 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_APPLY_ARGS, js_apply_str);
2152 return false;
2156 * Steps 4-5 (note erratum removing steps originally numbered 5 and 7 in
2157 * original version of ES5).
2159 JSObject *aobj = &vp[3].toObject();
2160 jsuint length;
2161 if (!js_GetLengthProperty(cx, aobj, &length))
2162 return false;
2164 LeaveTrace(cx);
2166 /* Step 6. */
2167 uintN n = uintN(JS_MIN(length, JS_ARGS_LENGTH_MAX));
2169 InvokeArgsGuard args;
2170 if (!cx->stack().pushInvokeArgs(cx, n, &args))
2171 return false;
2173 /* Push fval, obj, and aobj's elements as args. */
2174 args.callee() = fval;
2175 args.thisv() = vp[2];
2177 /* Steps 7-8. */
2178 if (!GetElements(cx, aobj, n, args.argv()))
2179 return false;
2181 /* Step 9. */
2182 if (!Invoke(cx, args, 0))
2183 return false;
2184 *vp = args.rval();
2185 return true;
2188 static JSBool
2189 CallOrConstructBoundFunction(JSContext *cx, uintN argc, Value *vp);
2191 inline bool
2192 JSObject::initBoundFunction(JSContext *cx, const Value &thisArg,
2193 const Value *args, uintN argslen)
2195 JS_ASSERT(isFunction());
2197 flags |= JSObject::BOUND_FUNCTION;
2198 getSlotRef(JSSLOT_BOUND_FUNCTION_THIS) = thisArg;
2199 getSlotRef(JSSLOT_BOUND_FUNCTION_ARGS_COUNT).setPrivateUint32(argslen);
2200 if (argslen != 0) {
2201 /* FIXME? Burn memory on an empty scope whose shape covers the args slots. */
2202 EmptyShape *empty = EmptyShape::create(cx, clasp);
2203 if (!empty)
2204 return false;
2206 empty->slotSpan += argslen;
2207 map = empty;
2209 if (!ensureInstanceReservedSlots(cx, argslen))
2210 return false;
2212 JS_ASSERT(numSlots() >= argslen + FUN_CLASS_RESERVED_SLOTS);
2213 memcpy(getSlots() + FUN_CLASS_RESERVED_SLOTS, args, argslen * sizeof(Value));
2215 return true;
2218 inline JSObject *
2219 JSObject::getBoundFunctionTarget() const
2221 JS_ASSERT(isFunction());
2222 JS_ASSERT(isBoundFunction());
2224 /* Bound functions abuse |parent| to store their target function. */
2225 return getParent();
2228 inline const js::Value &
2229 JSObject::getBoundFunctionThis() const
2231 JS_ASSERT(isFunction());
2232 JS_ASSERT(isBoundFunction());
2234 return getSlot(JSSLOT_BOUND_FUNCTION_THIS);
2237 inline const js::Value *
2238 JSObject::getBoundFunctionArguments(uintN &argslen) const
2240 JS_ASSERT(isFunction());
2241 JS_ASSERT(isBoundFunction());
2243 argslen = getSlot(JSSLOT_BOUND_FUNCTION_ARGS_COUNT).toPrivateUint32();
2244 JS_ASSERT_IF(argslen > 0, numSlots() >= argslen);
2246 return getSlots() + FUN_CLASS_RESERVED_SLOTS;
2249 /* ES5 15.3.4.5.1 and 15.3.4.5.2. */
2250 static JSBool
2251 CallOrConstructBoundFunction(JSContext *cx, uintN argc, Value *vp)
2253 JSObject *obj = &vp[0].toObject();
2254 JS_ASSERT(obj->isFunction());
2255 JS_ASSERT(obj->isBoundFunction());
2257 LeaveTrace(cx);
2259 bool constructing = IsConstructing(vp);
2261 /* 15.3.4.5.1 step 1, 15.3.4.5.2 step 3. */
2262 uintN argslen;
2263 const Value *boundArgs = obj->getBoundFunctionArguments(argslen);
2265 if (argc + argslen > JS_ARGS_LENGTH_MAX) {
2266 js_ReportAllocationOverflow(cx);
2267 return false;
2270 /* 15.3.4.5.1 step 3, 15.3.4.5.2 step 1. */
2271 JSObject *target = obj->getBoundFunctionTarget();
2273 /* 15.3.4.5.1 step 2. */
2274 const Value &boundThis = obj->getBoundFunctionThis();
2276 InvokeArgsGuard args;
2277 if (!cx->stack().pushInvokeArgs(cx, argc + argslen, &args))
2278 return false;
2280 /* 15.3.4.5.1, 15.3.4.5.2 step 4. */
2281 memcpy(args.argv(), boundArgs, argslen * sizeof(Value));
2282 memcpy(args.argv() + argslen, vp + 2, argc * sizeof(Value));
2284 /* 15.3.4.5.1, 15.3.4.5.2 step 5. */
2285 args.callee().setObject(*target);
2287 if (!constructing)
2288 args.thisv() = boundThis;
2290 if (constructing ? !InvokeConstructor(cx, args) : !Invoke(cx, args, 0))
2291 return false;
2293 *vp = args.rval();
2294 return true;
2297 /* ES5 15.3.4.5. */
2298 static JSBool
2299 fun_bind(JSContext *cx, uintN argc, Value *vp)
2301 /* Step 1. */
2302 JSObject *target = ComputeThisFromVp(cx, vp);
2303 if (!target)
2304 return false;
2306 /* Step 2. */
2307 if (!target->isCallable()) {
2308 if (JSString *str = js_ValueToString(cx, vp[1])) {
2309 JSAutoByteString bytes(cx, str);
2310 if (!!bytes) {
2311 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2312 JSMSG_INCOMPATIBLE_PROTO,
2313 js_Function_str, "bind", bytes.ptr());
2316 return false;
2319 /* Step 3. */
2320 Value *args = NULL;
2321 uintN argslen = 0;
2322 if (argc > 1) {
2323 args = vp + 3;
2324 argslen = argc - 1;
2327 /* Steps 15-16. */
2328 uintN length = 0;
2329 if (target->isFunction()) {
2330 uintN nargs = target->getFunctionPrivate()->nargs;
2331 if (nargs > argslen)
2332 length = nargs - argslen;
2335 /* Step 4-6, 10-11. */
2336 JSAtom *name = target->isFunction() ? target->getFunctionPrivate()->atom : NULL;
2338 /* NB: Bound functions abuse |parent| to store their target. */
2339 JSObject *funobj =
2340 js_NewFunction(cx, NULL, CallOrConstructBoundFunction, length,
2341 JSFUN_CONSTRUCTOR, target, name);
2342 if (!funobj)
2343 return false;
2345 /* Steps 7-9. */
2346 Value thisArg = argc >= 1 ? vp[2] : UndefinedValue();
2347 if (!funobj->initBoundFunction(cx, thisArg, args, argslen))
2348 return false;
2350 /* Steps 17, 19-21 are handled by fun_resolve. */
2351 /* Step 18 is the default for new functions. */
2353 /* Step 22. */
2354 vp->setObject(*funobj);
2355 return true;
2358 static JSFunctionSpec function_methods[] = {
2359 #if JS_HAS_TOSOURCE
2360 JS_FN(js_toSource_str, fun_toSource, 0,0),
2361 #endif
2362 JS_FN(js_toString_str, fun_toString, 0,0),
2363 JS_FN(js_apply_str, js_fun_apply, 2,0),
2364 JS_FN(js_call_str, js_fun_call, 1,0),
2365 JS_FN("bind", fun_bind, 1,0),
2366 JS_FS_END
2369 static JSBool
2370 Function(JSContext *cx, uintN argc, Value *vp)
2372 JSObject *obj = NewFunction(cx, NULL);
2373 if (!obj)
2374 return JS_FALSE;
2376 /* N.B. overwriting callee with return value */
2377 JSObject *parent = vp[0].toObject().getParent();
2378 vp[0].setObject(*obj);
2381 * NB: (new Function) is not lexically closed by its caller, it's just an
2382 * anonymous function in the top-level scope that its constructor inhabits.
2383 * Thus 'var x = 42; f = new Function("return x"); print(f())' prints 42,
2384 * and so would a call to f from another top-level's script or function.
2386 * In older versions, before call objects, a new Function was adopted by
2387 * its running context's globalObject, which might be different from the
2388 * top-level reachable from scopeChain (in HTML frames, e.g.).
2390 JSFunction *fun = js_NewFunction(cx, obj, NULL, 0, JSFUN_LAMBDA | JSFUN_INTERPRETED,
2391 parent, cx->runtime->atomState.anonymousAtom);
2392 if (!fun)
2393 return JS_FALSE;
2396 * Function is static and not called directly by other functions in this
2397 * file, therefore it is callable only as a native function by js_Invoke.
2398 * Find the scripted caller, possibly skipping other native frames such as
2399 * are built for Function.prototype.call or .apply activations that invoke
2400 * Function indirectly from a script.
2402 JSStackFrame *caller = js_GetScriptedCaller(cx, NULL);
2403 uintN lineno;
2404 const char *filename;
2405 JSPrincipals *principals;
2406 if (caller) {
2407 JSObject *callee = &JS_CALLEE(cx, vp).toObject();
2408 principals = js_EvalFramePrincipals(cx, callee, caller);
2409 filename = js_ComputeFilename(cx, caller, principals, &lineno);
2410 } else {
2411 filename = NULL;
2412 lineno = 0;
2413 principals = NULL;
2416 /* Belt-and-braces: check that the caller has access to parent. */
2417 if (!js_CheckPrincipalsAccess(cx, parent, principals,
2418 CLASS_ATOM(cx, Function))) {
2419 return JS_FALSE;
2423 * CSP check: whether new Function() is allowed at all.
2424 * Report errors via CSP is done in the script security manager.
2425 * js_CheckContentSecurityPolicy is defined in jsobj.cpp
2427 if (!js_CheckContentSecurityPolicy(cx, parent)) {
2428 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CSP_BLOCKED_FUNCTION);
2429 return JS_FALSE;
2432 Bindings bindings(cx);
2433 AutoBindingsRooter root(cx, bindings);
2435 Value *argv = vp + 2;
2436 uintN n = argc ? argc - 1 : 0;
2437 if (n > 0) {
2438 enum { OK, BAD, BAD_FORMAL } state;
2441 * Collect the function-argument arguments into one string, separated
2442 * by commas, then make a tokenstream from that string, and scan it to
2443 * get the arguments. We need to throw the full scanner at the
2444 * problem, because the argument string can legitimately contain
2445 * comments and linefeeds. XXX It might be better to concatenate
2446 * everything up into a function definition and pass it to the
2447 * compiler, but doing it this way is less of a delta from the old
2448 * code. See ECMA 15.3.2.1.
2450 state = BAD_FORMAL;
2451 size_t args_length = 0;
2452 for (uintN i = 0; i < n; i++) {
2453 /* Collect the lengths for all the function-argument arguments. */
2454 JSString *arg = js_ValueToString(cx, argv[i]);
2455 if (!arg)
2456 return JS_FALSE;
2457 argv[i].setString(arg);
2460 * Check for overflow. The < test works because the maximum
2461 * JSString length fits in 2 fewer bits than size_t has.
2463 size_t old_args_length = args_length;
2464 args_length = old_args_length + arg->length();
2465 if (args_length < old_args_length) {
2466 js_ReportAllocationOverflow(cx);
2467 return JS_FALSE;
2471 /* Add 1 for each joining comma and check for overflow (two ways). */
2472 size_t old_args_length = args_length;
2473 args_length = old_args_length + n - 1;
2474 if (args_length < old_args_length ||
2475 args_length >= ~(size_t)0 / sizeof(jschar)) {
2476 js_ReportAllocationOverflow(cx);
2477 return JS_FALSE;
2481 * Allocate a string to hold the concatenated arguments, including room
2482 * for a terminating 0. Mark cx->tempPool for later release, to free
2483 * collected_args and its tokenstream in one swoop.
2485 void *mark = JS_ARENA_MARK(&cx->tempPool);
2486 jschar *cp;
2487 JS_ARENA_ALLOCATE_CAST(cp, jschar *, &cx->tempPool,
2488 (args_length+1) * sizeof(jschar));
2489 if (!cp) {
2490 js_ReportOutOfScriptQuota(cx);
2491 return JS_FALSE;
2493 jschar *collected_args = cp;
2496 * Concatenate the arguments into the new string, separated by commas.
2498 for (uintN i = 0; i < n; i++) {
2499 JSString *arg = argv[i].toString();
2500 size_t arg_length = arg->length();
2501 const jschar *arg_chars = arg->getChars(cx);
2502 if (!arg_chars) {
2503 JS_ARENA_RELEASE(&cx->tempPool, mark);
2504 return JS_FALSE;
2506 (void) js_strncpy(cp, arg_chars, arg_length);
2507 cp += arg_length;
2509 /* Add separating comma or terminating 0. */
2510 *cp++ = (i + 1 < n) ? ',' : 0;
2513 /* Initialize a tokenstream that reads from the given string. */
2514 TokenStream ts(cx);
2515 if (!ts.init(cx->findVersion(), collected_args, args_length, filename, lineno)) {
2516 JS_ARENA_RELEASE(&cx->tempPool, mark);
2517 return JS_FALSE;
2520 /* The argument string may be empty or contain no tokens. */
2521 TokenKind tt = ts.getToken();
2522 if (tt != TOK_EOF) {
2523 for (;;) {
2525 * Check that it's a name. This also implicitly guards against
2526 * TOK_ERROR, which was already reported.
2528 if (tt != TOK_NAME)
2529 goto after_args;
2532 * Get the atom corresponding to the name from the token
2533 * stream; we're assured at this point that it's a valid
2534 * identifier.
2536 JSAtom *atom = ts.currentToken().t_atom;
2538 /* Check for a duplicate parameter name. */
2539 if (bindings.hasBinding(cx, atom)) {
2540 JSAutoByteString name;
2541 if (!js_AtomToPrintableString(cx, atom, &name)) {
2542 state = BAD;
2543 goto after_args;
2545 if (!ReportCompileErrorNumber(cx, &ts, NULL,
2546 JSREPORT_WARNING | JSREPORT_STRICT,
2547 JSMSG_DUPLICATE_FORMAL, name.ptr())) {
2548 state = BAD;
2549 goto after_args;
2553 uint16 dummy;
2554 if (!bindings.addArgument(cx, atom, &dummy)) {
2555 state = BAD;
2556 goto after_args;
2560 * Get the next token. Stop on end of stream. Otherwise
2561 * insist on a comma, get another name, and iterate.
2563 tt = ts.getToken();
2564 if (tt == TOK_EOF)
2565 break;
2566 if (tt != TOK_COMMA)
2567 goto after_args;
2568 tt = ts.getToken();
2572 state = OK;
2573 after_args:
2574 if (state == BAD_FORMAL && !ts.isError()) {
2576 * Report "malformed formal parameter" iff no illegal char or
2577 * similar scanner error was already reported.
2579 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2580 JSMSG_BAD_FORMAL);
2582 ts.close();
2583 JS_ARENA_RELEASE(&cx->tempPool, mark);
2584 if (state != OK)
2585 return JS_FALSE;
2588 JSString *str;
2589 if (argc) {
2590 str = js_ValueToString(cx, argv[argc - 1]);
2591 if (!str)
2592 return JS_FALSE;
2593 argv[argc - 1].setString(str);
2594 } else {
2595 str = cx->runtime->emptyString;
2598 size_t length = str->length();
2599 const jschar *chars = str->getChars(cx);
2600 if (!chars)
2601 return JS_FALSE;
2603 return Compiler::compileFunctionBody(cx, fun, principals, &bindings,
2604 chars, length, filename, lineno);
2607 static JSBool
2608 ThrowTypeError(JSContext *cx, uintN argc, Value *vp)
2610 JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL,
2611 JSMSG_THROW_TYPE_ERROR);
2612 return false;
2615 JSObject *
2616 js_InitFunctionClass(JSContext *cx, JSObject *obj)
2618 JSObject *proto = js_InitClass(cx, obj, NULL, &js_FunctionClass, Function, 1,
2619 NULL, function_methods, NULL, NULL);
2620 if (!proto)
2621 return NULL;
2623 JSFunction *fun = js_NewFunction(cx, proto, NULL, 0, JSFUN_INTERPRETED, obj, NULL);
2624 if (!fun)
2625 return NULL;
2626 fun->flags |= JSFUN_PROTOTYPE;
2628 JSScript *script = JSScript::NewScript(cx, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2629 if (!script)
2630 return NULL;
2631 script->setVersion(JSVERSION_DEFAULT);
2632 script->noScriptRval = true;
2633 script->code[0] = JSOP_STOP;
2634 script->code[1] = SRC_NULL;
2635 #ifdef CHECK_SCRIPT_OWNER
2636 script->owner = NULL;
2637 #endif
2638 fun->u.i.script = script;
2640 if (obj->isGlobal()) {
2641 /* ES5 13.2.3: Construct the unique [[ThrowTypeError]] function object. */
2642 JSObject *throwTypeError =
2643 js_NewFunction(cx, NULL, reinterpret_cast<Native>(ThrowTypeError), 0,
2644 0, obj, NULL);
2645 if (!throwTypeError)
2646 return NULL;
2648 JS_ALWAYS_TRUE(js_SetReservedSlot(cx, obj, JSRESERVED_GLOBAL_THROWTYPEERROR,
2649 ObjectValue(*throwTypeError)));
2652 return proto;
2655 JSFunction *
2656 js_NewFunction(JSContext *cx, JSObject *funobj, Native native, uintN nargs,
2657 uintN flags, JSObject *parent, JSAtom *atom)
2659 JSFunction *fun;
2661 if (funobj) {
2662 JS_ASSERT(funobj->isFunction());
2663 funobj->setParent(parent);
2664 } else {
2665 funobj = NewFunction(cx, parent);
2666 if (!funobj)
2667 return NULL;
2669 JS_ASSERT(!funobj->getPrivate());
2670 fun = (JSFunction *) funobj;
2672 /* Initialize all function members. */
2673 fun->nargs = uint16(nargs);
2674 fun->flags = flags & (JSFUN_FLAGS_MASK | JSFUN_KINDMASK | JSFUN_TRCINFO);
2675 if ((flags & JSFUN_KINDMASK) >= JSFUN_INTERPRETED) {
2676 JS_ASSERT(!native);
2677 JS_ASSERT(nargs == 0);
2678 fun->u.i.skipmin = 0;
2679 fun->u.i.wrapper = false;
2680 fun->u.i.script = NULL;
2681 } else {
2682 fun->u.n.clasp = NULL;
2683 if (flags & JSFUN_TRCINFO) {
2684 #ifdef JS_TRACER
2685 JSNativeTraceInfo *trcinfo =
2686 JS_FUNC_TO_DATA_PTR(JSNativeTraceInfo *, native);
2687 fun->u.n.native = (js::Native) trcinfo->native;
2688 fun->u.n.trcinfo = trcinfo;
2689 #else
2690 fun->u.n.trcinfo = NULL;
2691 #endif
2692 } else {
2693 fun->u.n.native = native;
2694 fun->u.n.trcinfo = NULL;
2696 JS_ASSERT(fun->u.n.native);
2698 fun->atom = atom;
2700 /* Set private to self to indicate non-cloned fully initialized function. */
2701 FUN_OBJECT(fun)->setPrivate(fun);
2702 return fun;
2705 JSObject * JS_FASTCALL
2706 js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent,
2707 JSObject *proto)
2709 JS_ASSERT(parent);
2710 JS_ASSERT(proto);
2712 JSObject *clone;
2713 if (cx->compartment == fun->compartment()) {
2715 * The cloned function object does not need the extra JSFunction members
2716 * beyond JSObject as it points to fun via the private slot.
2718 clone = NewNativeClassInstance(cx, &js_FunctionClass, proto, parent);
2719 if (!clone)
2720 return NULL;
2721 clone->setPrivate(fun);
2722 } else {
2724 * Across compartments we have to deep copy JSFunction and clone the
2725 * script (for interpreted functions).
2727 clone = NewFunction(cx, parent);
2728 if (!clone)
2729 return NULL;
2730 JSFunction *cfun = (JSFunction *) clone;
2731 cfun->nargs = fun->nargs;
2732 cfun->flags = fun->flags;
2733 cfun->u = fun->getFunctionPrivate()->u;
2734 cfun->atom = fun->atom;
2735 clone->setPrivate(cfun);
2736 if (cfun->isInterpreted()) {
2737 JSScript *script = cfun->u.i.script;
2738 JS_ASSERT(script);
2739 JS_ASSERT(script->compartment == fun->compartment());
2740 JS_ASSERT(script->compartment != cx->compartment);
2742 cfun->u.i.script = js_CloneScript(cx, script);
2743 if (!cfun->u.i.script)
2744 return NULL;
2745 #ifdef CHECK_SCRIPT_OWNER
2746 cfun->script()->owner = NULL;
2747 #endif
2748 js_CallNewScriptHook(cx, cfun->script(), cfun);
2751 return clone;
2754 #ifdef JS_TRACER
2755 JS_DEFINE_CALLINFO_4(extern, OBJECT, js_CloneFunctionObject, CONTEXT, FUNCTION, OBJECT, OBJECT, 0,
2756 nanojit::ACCSET_STORE_ANY)
2757 #endif
2760 * Create a new flat closure, but don't initialize the imported upvar
2761 * values. The tracer calls this function and then initializes the upvar
2762 * slots on trace.
2764 JSObject * JS_FASTCALL
2765 js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain)
2767 JS_ASSERT(fun->isFlatClosure());
2768 JS_ASSERT(JSScript::isValidOffset(fun->script()->upvarsOffset) ==
2769 fun->script()->bindings.hasUpvars());
2770 JS_ASSERT_IF(JSScript::isValidOffset(fun->script()->upvarsOffset),
2771 fun->script()->upvars()->length == fun->script()->bindings.countUpvars());
2773 JSObject *closure = CloneFunctionObject(cx, fun, scopeChain);
2774 if (!closure)
2775 return closure;
2777 uint32 nslots = fun->script()->bindings.countUpvars();
2778 if (nslots == 0)
2779 return closure;
2781 Value *upvars = (Value *) cx->malloc(nslots * sizeof(Value));
2782 if (!upvars)
2783 return NULL;
2785 closure->setFlatClosureUpvars(upvars);
2786 return closure;
2789 JS_DEFINE_CALLINFO_3(extern, OBJECT, js_AllocFlatClosure,
2790 CONTEXT, FUNCTION, OBJECT, 0, nanojit::ACCSET_STORE_ANY)
2792 JSObject *
2793 js_NewFlatClosure(JSContext *cx, JSFunction *fun, JSOp op, size_t oplen)
2796 * Flat closures cannot yet be partial, that is, all upvars must be copied,
2797 * or the closure won't be flattened. Therefore they do not need to search
2798 * enclosing scope objects via JSOP_NAME, etc.
2800 * FIXME: bug 545759 proposes to enable partial flat closures. Fixing this
2801 * bug requires a GetScopeChainFast call here, along with JS_REQUIRES_STACK
2802 * annotations on this function's prototype and definition.
2804 VOUCH_DOES_NOT_REQUIRE_STACK();
2805 JSObject *scopeChain = &cx->fp()->scopeChain();
2807 JSObject *closure = js_AllocFlatClosure(cx, fun, scopeChain);
2808 if (!closure || !fun->script()->bindings.hasUpvars())
2809 return closure;
2811 Value *upvars = closure->getFlatClosureUpvars();
2812 uintN level = fun->u.i.script->staticLevel;
2813 JSUpvarArray *uva = fun->script()->upvars();
2815 for (uint32 i = 0, n = uva->length; i < n; i++)
2816 upvars[i] = GetUpvar(cx, level, uva->vector[i]);
2818 return closure;
2821 JSObject *
2822 js_NewDebuggableFlatClosure(JSContext *cx, JSFunction *fun)
2824 JS_ASSERT(cx->fp()->fun()->flags & JSFUN_HEAVYWEIGHT);
2825 JS_ASSERT(!cx->fp()->fun()->optimizedClosure());
2826 JS_ASSERT(FUN_FLAT_CLOSURE(fun));
2828 return WrapEscapingClosure(cx, cx->fp(), fun);
2831 JSFunction *
2832 js_DefineFunction(JSContext *cx, JSObject *obj, jsid id, Native native,
2833 uintN nargs, uintN attrs)
2835 PropertyOp gsop;
2836 JSFunction *fun;
2838 if (attrs & JSFUN_STUB_GSOPS) {
2840 * JSFUN_STUB_GSOPS is a request flag only, not stored in fun->flags or
2841 * the defined property's attributes. This allows us to encode another,
2842 * internal flag using the same bit, JSFUN_EXPR_CLOSURE -- see jsfun.h
2843 * for more on this.
2845 attrs &= ~JSFUN_STUB_GSOPS;
2846 gsop = PropertyStub;
2847 } else {
2848 gsop = NULL;
2852 * Historically, all objects have had a parent member as intrinsic scope
2853 * chain link. We want to move away from this universal parent, but JS
2854 * requires that function objects have something like parent (ES3 and ES5
2855 * call it the [[Scope]] internal property), to bake a particular static
2856 * scope environment into each function object.
2858 * All function objects thus have parent, including all native functions.
2859 * All native functions defined by the JS_DefineFunction* APIs are created
2860 * via the call below to js_NewFunction, which passes obj as the parent
2861 * parameter, and so binds fun's parent to obj using JSObject::setParent,
2862 * under js_NewFunction (in JSObject::init, called from NewObject -- see
2863 * jsobjinlines.h).
2865 * But JSObject::setParent sets the DELEGATE object flag on its receiver,
2866 * to mark the object as a proto or parent of another object. Such objects
2867 * may intervene in property lookups and scope chain searches, so require
2868 * special handling when caching lookup and search results (since such
2869 * intervening objects can in general grow shadowing properties later).
2871 * Thus using setParent prematurely flags certain objects, notably class
2872 * prototypes, so that defining native methods on them, where the method's
2873 * name (e.g., toString) is already bound on Object.prototype, triggers
2874 * shadowingShapeChange events and gratuitous shape regeneration.
2876 * To fix this longstanding bug, we set check whether obj is already a
2877 * delegate, and if not, then if js_NewFunction flagged obj as a delegate,
2878 * we clear the flag.
2880 * We thus rely on the fact that native functions (including indirect eval)
2881 * do not use the property cache or equivalent JIT techniques that require
2882 * this bit to be set on their parent-linked scope chain objects.
2884 * Note: we keep API compatibility by setting parent to obj for all native
2885 * function objects, even if obj->getGlobal() would suffice. This should be
2886 * revisited when parent is narrowed to exist only for function objects and
2887 * possibly a few prehistoric scope objects (e.g. event targets).
2889 * FIXME: bug 611190.
2891 bool wasDelegate = obj->isDelegate();
2893 fun = js_NewFunction(cx, NULL, native, nargs,
2894 attrs & (JSFUN_FLAGS_MASK | JSFUN_TRCINFO),
2895 obj,
2896 JSID_IS_ATOM(id) ? JSID_TO_ATOM(id) : NULL);
2897 if (!fun)
2898 return NULL;
2900 if (!wasDelegate && obj->isDelegate())
2901 obj->clearDelegate();
2903 if (!obj->defineProperty(cx, id, ObjectValue(*fun), gsop, gsop, attrs & ~JSFUN_FLAGS_MASK))
2904 return NULL;
2905 return fun;
2908 #if (JSV2F_CONSTRUCT & JSV2F_SEARCH_STACK)
2909 # error "JSINVOKE_CONSTRUCT and JSV2F_SEARCH_STACK are not disjoint!"
2910 #endif
2912 JSFunction *
2913 js_ValueToFunction(JSContext *cx, const Value *vp, uintN flags)
2915 JSObject *funobj;
2916 if (!IsFunctionObject(*vp, &funobj)) {
2917 js_ReportIsNotFunction(cx, vp, flags);
2918 return NULL;
2920 return GET_FUNCTION_PRIVATE(cx, funobj);
2923 JSObject *
2924 js_ValueToFunctionObject(JSContext *cx, Value *vp, uintN flags)
2926 JSObject *funobj;
2927 if (!IsFunctionObject(*vp, &funobj)) {
2928 js_ReportIsNotFunction(cx, vp, flags);
2929 return NULL;
2932 return funobj;
2935 JSObject *
2936 js_ValueToCallableObject(JSContext *cx, Value *vp, uintN flags)
2938 if (vp->isObject()) {
2939 JSObject *callable = &vp->toObject();
2940 if (callable->isCallable())
2941 return callable;
2944 js_ReportIsNotFunction(cx, vp, flags);
2945 return NULL;
2948 void
2949 js_ReportIsNotFunction(JSContext *cx, const Value *vp, uintN flags)
2951 const char *name = NULL, *source = NULL;
2952 AutoValueRooter tvr(cx);
2953 uintN error = (flags & JSV2F_CONSTRUCT) ? JSMSG_NOT_CONSTRUCTOR : JSMSG_NOT_FUNCTION;
2954 LeaveTrace(cx);
2957 * We try to the print the code that produced vp if vp is a value in the
2958 * most recent interpreted stack frame. Note that additional values, not
2959 * directly produced by the script, may have been pushed onto the frame's
2960 * expression stack (e.g. by pushInvokeArgs) thereby incrementing sp past
2961 * the depth simulated by ReconstructPCStack.
2963 * Conversely, values may have been popped from the stack in preparation
2964 * for a call (e.g., by SplatApplyArgs). Since we must pass an offset from
2965 * the top of the simulated stack to js_ReportValueError3, we do bounds
2966 * checking using the minimum of both the simulated and actual stack depth.
2968 ptrdiff_t spindex = 0;
2970 FrameRegsIter i(cx);
2971 while (!i.done() && !i.pc())
2972 ++i;
2974 if (!i.done()) {
2975 uintN depth = js_ReconstructStackDepth(cx, i.fp()->script(), i.pc());
2976 Value *simsp = i.fp()->base() + depth;
2977 if (i.fp()->base() <= vp && vp < Min(simsp, i.sp()))
2978 spindex = vp - simsp;
2981 if (!spindex)
2982 spindex = ((flags & JSV2F_SEARCH_STACK) ? JSDVG_SEARCH_STACK : JSDVG_IGNORE_STACK);
2984 js_ReportValueError3(cx, error, spindex, *vp, NULL, name, source);