Merge mozilla-central and tracemonkey. (a=blockers)
[mozilla-central.git] / js / src / jsfun.cpp
blob7941cbd54da6f10e077d579f9d40aeac9c6a2b11
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->compartment->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 script->getVersion());
409 if (!wscript)
410 return NULL;
412 memcpy(wscript->code, script->code, script->length);
413 wscript->main = wscript->code + (script->main - script->code);
415 memcpy(wscript->notes(), snbase, nsrcnotes * sizeof(jssrcnote));
416 memcpy(wscript->atomMap.vector, script->atomMap.vector,
417 wscript->atomMap.length * sizeof(JSAtom *));
418 if (JSScript::isValidOffset(script->objectsOffset)) {
419 memcpy(wscript->objects()->vector, script->objects()->vector,
420 wscript->objects()->length * sizeof(JSObject *));
422 if (JSScript::isValidOffset(script->regexpsOffset)) {
423 memcpy(wscript->regexps()->vector, script->regexps()->vector,
424 wscript->regexps()->length * sizeof(JSObject *));
426 if (JSScript::isValidOffset(script->trynotesOffset)) {
427 memcpy(wscript->trynotes()->vector, script->trynotes()->vector,
428 wscript->trynotes()->length * sizeof(JSTryNote));
430 if (JSScript::isValidOffset(script->globalsOffset)) {
431 memcpy(wscript->globals()->vector, script->globals()->vector,
432 wscript->globals()->length * sizeof(GlobalSlotArray::Entry));
434 if (script->nClosedArgs + script->nClosedVars != 0)
435 script->copyClosedSlotsTo(wscript);
437 if (script->bindings.hasUpvars()) {
438 JS_ASSERT(script->bindings.countUpvars() == wscript->upvars()->length);
439 memcpy(wscript->upvars()->vector, script->upvars()->vector,
440 script->bindings.countUpvars() * sizeof(uint32));
443 jsbytecode *pc = wscript->code;
444 while (*pc != JSOP_STOP) {
445 /* FIXME should copy JSOP_TRAP? */
446 JSOp op = js_GetOpcode(cx, wscript, pc);
447 const JSCodeSpec *cs = &js_CodeSpec[op];
448 ptrdiff_t oplen = cs->length;
449 if (oplen < 0)
450 oplen = js_GetVariableBytecodeLength(pc);
453 * Rewrite JSOP_{GET,CALL}FCSLOT as JSOP_{GET,CALL}UPVAR_DBG for the
454 * case where fun is an escaping flat closure. This works because the
455 * UPVAR and FCSLOT ops by design have the same format: an upvar index
456 * immediate operand.
458 switch (op) {
459 case JSOP_GETFCSLOT: *pc = JSOP_GETUPVAR_DBG; break;
460 case JSOP_CALLFCSLOT: *pc = JSOP_CALLUPVAR_DBG; break;
461 case JSOP_DEFFUN_FC: *pc = JSOP_DEFFUN_DBGFC; break;
462 case JSOP_DEFLOCALFUN_FC: *pc = JSOP_DEFLOCALFUN_DBGFC; break;
463 case JSOP_LAMBDA_FC: *pc = JSOP_LAMBDA_DBGFC; break;
464 default:;
466 pc += oplen;
470 * Fill in the rest of wscript. This means if you add members to JSScript
471 * you must update this code. FIXME: factor into JSScript::clone method.
473 JS_ASSERT(wscript->getVersion() == script->getVersion());
474 wscript->nfixed = script->nfixed;
475 wscript->filename = script->filename;
476 wscript->lineno = script->lineno;
477 wscript->nslots = script->nslots;
478 wscript->staticLevel = script->staticLevel;
479 wscript->principals = script->principals;
480 wscript->noScriptRval = script->noScriptRval;
481 wscript->savedCallerFun = script->savedCallerFun;
482 wscript->hasSharps = script->hasSharps;
483 wscript->strictModeCode = script->strictModeCode;
484 wscript->compileAndGo = script->compileAndGo;
485 wscript->usesEval = script->usesEval;
486 wscript->usesArguments = script->usesArguments;
487 wscript->warnedAboutTwoArgumentEval = script->warnedAboutTwoArgumentEval;
488 if (wscript->principals)
489 JSPRINCIPALS_HOLD(cx, wscript->principals);
490 #ifdef CHECK_SCRIPT_OWNER
491 wscript->owner = script->owner;
492 #endif
494 wscript->bindings.clone(cx, &script->bindings);
496 /* Deoptimize wfun from FUN_{FLAT,NULL}_CLOSURE to FUN_INTERPRETED. */
497 FUN_SET_KIND(wfun, JSFUN_INTERPRETED);
498 wfun->u.i.script = wscript;
499 return wfunobj;
502 static JSBool
503 ArgGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
505 LeaveTrace(cx);
507 if (!InstanceOf(cx, obj, &js_ArgumentsClass, NULL))
508 return true;
510 if (JSID_IS_INT(id)) {
512 * arg can exceed the number of arguments if a script changed the
513 * prototype to point to another Arguments object with a bigger argc.
515 uintN arg = uintN(JSID_TO_INT(id));
516 if (arg < obj->getArgsInitialLength()) {
517 JS_ASSERT(!obj->getArgsElement(arg).isMagic(JS_ARGS_HOLE));
518 if (JSStackFrame *fp = (JSStackFrame *) obj->getPrivate())
519 *vp = fp->canonicalActualArg(arg);
520 else
521 *vp = obj->getArgsElement(arg);
523 } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
524 if (!obj->isArgsLengthOverridden())
525 vp->setInt32(obj->getArgsInitialLength());
526 } else {
527 JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom));
528 const Value &v = obj->getArgsCallee();
529 if (!v.isMagic(JS_ARGS_HOLE)) {
531 * If this function or one in it needs upvars that reach above it
532 * in the scope chain, it must not be a null closure (it could be a
533 * flat closure, or an unoptimized closure -- the latter itself not
534 * necessarily heavyweight). Rather than wrap here, we simply throw
535 * to reduce code size and tell debugger users the truth instead of
536 * passing off a fibbing wrapper.
538 if (GET_FUNCTION_PRIVATE(cx, &v.toObject())->needsWrapper()) {
539 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
540 JSMSG_OPTIMIZED_CLOSURE_LEAK);
541 return false;
543 *vp = v;
546 return true;
549 static JSBool
550 ArgSetter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
552 #ifdef JS_TRACER
553 // To be able to set a property here on trace, we would have to make
554 // sure any updates also get written back to the trace native stack.
555 // For simplicity, we just leave trace, since this is presumably not
556 // a common operation.
557 LeaveTrace(cx);
558 #endif
560 if (!InstanceOf(cx, obj, &js_ArgumentsClass, NULL))
561 return true;
563 if (JSID_IS_INT(id)) {
564 uintN arg = uintN(JSID_TO_INT(id));
565 if (arg < obj->getArgsInitialLength()) {
566 JSStackFrame *fp = (JSStackFrame *) obj->getPrivate();
567 if (fp) {
568 JSScript *script = fp->functionScript();
569 if (script->usesArguments)
570 fp->canonicalActualArg(arg) = *vp;
571 return true;
574 } else {
575 JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom) ||
576 JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom));
580 * For simplicity we use delete/define to replace the property with one
581 * backed by the default Object getter and setter. Note that we rely on
582 * args_delProperty to clear the corresponding reserved slot so the GC can
583 * collect its value. Note also that we must define the property instead
584 * of setting it in case the user has changed the prototype to an object
585 * that has a setter for this id.
587 AutoValueRooter tvr(cx);
588 return js_DeleteProperty(cx, obj, id, tvr.addr(), false) &&
589 js_DefineProperty(cx, obj, id, vp, NULL, NULL, JSPROP_ENUMERATE);
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
680 StrictArgSetter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
682 if (!InstanceOf(cx, obj, &StrictArgumentsClass, NULL))
683 return true;
685 if (JSID_IS_INT(id)) {
686 uintN arg = uintN(JSID_TO_INT(id));
687 if (arg < obj->getArgsInitialLength()) {
688 obj->setArgsElement(arg, *vp);
689 return true;
691 } else {
692 JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom));
696 * For simplicity we use delete/set to replace the property with one
697 * backed by the default Object getter and setter. Note that we rely on
698 * args_delProperty to clear the corresponding reserved slot so the GC can
699 * collect its value.
701 AutoValueRooter tvr(cx);
702 return js_DeleteProperty(cx, obj, id, tvr.addr(), strict) &&
703 js_SetProperty(cx, obj, id, vp, strict);
706 static JSBool
707 strictargs_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp)
709 JS_ASSERT(obj->isStrictArguments());
711 *objp = NULL;
713 uintN attrs = JSPROP_SHARED | JSPROP_SHADOWABLE;
714 PropertyOp getter = StrictArgGetter;
715 StrictPropertyOp setter = StrictArgSetter;
717 if (JSID_IS_INT(id)) {
718 uint32 arg = uint32(JSID_TO_INT(id));
719 if (arg >= obj->getArgsInitialLength() || obj->getArgsElement(arg).isMagic(JS_ARGS_HOLE))
720 return true;
722 attrs |= JSPROP_ENUMERATE;
723 } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
724 if (obj->isArgsLengthOverridden())
725 return true;
726 } else {
727 if (!JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom) &&
728 !JSID_IS_ATOM(id, cx->runtime->atomState.callerAtom)) {
729 return true;
732 attrs = JSPROP_PERMANENT | JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED;
733 getter = CastAsPropertyOp(obj->getThrowTypeError());
734 setter = CastAsStrictPropertyOp(obj->getThrowTypeError());
737 Value undef = UndefinedValue();
738 if (!js_DefineProperty(cx, obj, id, &undef, getter, setter, attrs))
739 return false;
741 *objp = obj;
742 return true;
745 static JSBool
746 strictargs_enumerate(JSContext *cx, JSObject *obj)
748 JS_ASSERT(obj->isStrictArguments());
751 * Trigger reflection in strictargs_resolve using a series of
752 * js_LookupProperty calls.
754 JSObject *pobj;
755 JSProperty *prop;
757 // length
758 if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), &pobj, &prop))
759 return false;
761 // callee
762 if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.calleeAtom), &pobj, &prop))
763 return false;
765 // caller
766 if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.callerAtom), &pobj, &prop))
767 return false;
769 for (uint32 i = 0, argc = obj->getArgsInitialLength(); i < argc; i++) {
770 if (!js_LookupProperty(cx, obj, INT_TO_JSID(i), &pobj, &prop))
771 return false;
774 return true;
777 static void
778 args_finalize(JSContext *cx, JSObject *obj)
780 cx->free((void *) obj->getArgsData());
784 * If a generator's arguments or call object escapes, and the generator frame
785 * is not executing, the generator object needs to be marked because it is not
786 * otherwise reachable. An executing generator is rooted by its invocation. To
787 * distinguish the two cases (which imply different access paths to the
788 * generator object), we use the JSFRAME_FLOATING_GENERATOR flag, which is only
789 * set on the JSStackFrame kept in the generator object's JSGenerator.
791 static inline void
792 MaybeMarkGenerator(JSTracer *trc, JSObject *obj)
794 #if JS_HAS_GENERATORS
795 JSStackFrame *fp = (JSStackFrame *) obj->getPrivate();
796 if (fp && fp->isFloatingGenerator()) {
797 JSObject *genobj = js_FloatingFrameToGenerator(fp)->obj;
798 MarkObject(trc, *genobj, "generator object");
800 #endif
803 static void
804 args_trace(JSTracer *trc, JSObject *obj)
806 JS_ASSERT(obj->isArguments());
807 if (obj->getPrivate() == JS_ARGUMENTS_OBJECT_ON_TRACE) {
808 JS_ASSERT(!obj->isStrictArguments());
809 return;
812 ArgumentsData *data = obj->getArgsData();
813 if (data->callee.isObject())
814 MarkObject(trc, data->callee.toObject(), js_callee_str);
815 MarkValueRange(trc, obj->getArgsInitialLength(), data->slots, js_arguments_str);
817 MaybeMarkGenerator(trc, obj);
821 * The Arguments classes aren't initialized via js_InitClass, because arguments
822 * objects have the initial value of Object.prototype as their [[Prototype]].
823 * However, Object.prototype.toString.call(arguments) === "[object Arguments]"
824 * per ES5 (although not ES3), so the class name is "Arguments" rather than
825 * "Object".
827 * The JSClass functions below collaborate to lazily reflect and synchronize
828 * actual argument values, argument count, and callee function object stored
829 * in a JSStackFrame with their corresponding property values in the frame's
830 * arguments object.
832 Class js_ArgumentsClass = {
833 "Arguments",
834 JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE |
835 JSCLASS_HAS_RESERVED_SLOTS(JSObject::ARGS_CLASS_RESERVED_SLOTS) |
836 JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
837 PropertyStub, /* addProperty */
838 args_delProperty,
839 PropertyStub, /* getProperty */
840 StrictPropertyStub, /* setProperty */
841 args_enumerate,
842 (JSResolveOp) args_resolve,
843 ConvertStub,
844 args_finalize, /* finalize */
845 NULL, /* reserved0 */
846 NULL, /* checkAccess */
847 NULL, /* call */
848 NULL, /* construct */
849 NULL, /* xdrObject */
850 NULL, /* hasInstance */
851 JS_CLASS_TRACE(args_trace)
854 namespace js {
857 * Strict mode arguments is significantly less magical than non-strict mode
858 * arguments, so it is represented by a different class while sharing some
859 * functionality.
861 Class StrictArgumentsClass = {
862 "Arguments",
863 JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE |
864 JSCLASS_HAS_RESERVED_SLOTS(JSObject::ARGS_CLASS_RESERVED_SLOTS) |
865 JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
866 PropertyStub, /* addProperty */
867 args_delProperty,
868 PropertyStub, /* getProperty */
869 StrictPropertyStub, /* setProperty */
870 strictargs_enumerate,
871 reinterpret_cast<JSResolveOp>(strictargs_resolve),
872 ConvertStub,
873 args_finalize, /* finalize */
874 NULL, /* reserved0 */
875 NULL, /* checkAccess */
876 NULL, /* call */
877 NULL, /* construct */
878 NULL, /* xdrObject */
879 NULL, /* hasInstance */
880 JS_CLASS_TRACE(args_trace)
886 * A Declarative Environment object stores its active JSStackFrame pointer in
887 * its private slot, just as Call and Arguments objects do.
889 Class js_DeclEnvClass = {
890 js_Object_str,
891 JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
892 PropertyStub, /* addProperty */
893 PropertyStub, /* delProperty */
894 PropertyStub, /* getProperty */
895 StrictPropertyStub, /* setProperty */
896 EnumerateStub,
897 ResolveStub,
898 ConvertStub
901 static JSBool
902 CheckForEscapingClosure(JSContext *cx, JSObject *obj, Value *vp)
904 JS_ASSERT(obj->isCall() || obj->getClass() == &js_DeclEnvClass);
906 const Value &v = *vp;
908 JSObject *funobj;
909 if (IsFunctionObject(v, &funobj)) {
910 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
913 * Any escaping null or flat closure that reaches above itself or
914 * contains nested functions that reach above it must be wrapped.
915 * We can wrap only when this Call or Declarative Environment obj
916 * still has an active stack frame associated with it.
918 if (fun->needsWrapper()) {
919 LeaveTrace(cx);
921 JSStackFrame *fp = (JSStackFrame *) obj->getPrivate();
922 if (fp) {
923 JSObject *wrapper = WrapEscapingClosure(cx, fp, fun);
924 if (!wrapper)
925 return false;
926 vp->setObject(*wrapper);
927 return true;
930 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
931 JSMSG_OPTIMIZED_CLOSURE_LEAK);
932 return false;
935 return true;
938 static JSBool
939 CalleeGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
941 return CheckForEscapingClosure(cx, obj, vp);
944 namespace js {
947 * Construct a call object for the given bindings. The callee is the function
948 * on behalf of which the call object is being created.
950 JSObject *
951 NewCallObject(JSContext *cx, Bindings *bindings, JSObject &scopeChain, JSObject *callee)
953 size_t argsVars = bindings->countArgsAndVars();
954 size_t slots = JSObject::CALL_RESERVED_SLOTS + argsVars;
955 gc::FinalizeKind kind = gc::GetGCObjectKind(slots);
957 JSObject *callobj = js_NewGCObject(cx, kind);
958 if (!callobj)
959 return NULL;
961 /* Init immediately to avoid GC seeing a half-init'ed object. */
962 callobj->init(cx, &js_CallClass, NULL, &scopeChain, NULL, false);
963 callobj->setMap(bindings->lastShape());
965 /* This must come after callobj->lastProp has been set. */
966 if (!callobj->ensureInstanceReservedSlots(cx, argsVars))
967 return NULL;
969 #ifdef DEBUG
970 for (Shape::Range r = callobj->lastProp; !r.empty(); r.popFront()) {
971 const Shape &s = r.front();
972 if (s.slot != SHAPE_INVALID_SLOT) {
973 JS_ASSERT(s.slot + 1 == callobj->slotSpan());
974 break;
977 #endif
979 callobj->setCallObjCallee(callee);
980 return callobj;
983 } // namespace js
985 static inline JSObject *
986 NewDeclEnvObject(JSContext *cx, JSStackFrame *fp)
988 JSObject *envobj = js_NewGCObject(cx, FINALIZE_OBJECT2);
989 if (!envobj)
990 return NULL;
992 envobj->init(cx, &js_DeclEnvClass, NULL, &fp->scopeChain(), fp, false);
993 envobj->setMap(cx->compartment->emptyDeclEnvShape);
994 return envobj;
997 JSObject *
998 js_GetCallObject(JSContext *cx, JSStackFrame *fp)
1000 /* Create a call object for fp only if it lacks one. */
1001 JS_ASSERT(fp->isFunctionFrame());
1002 if (fp->hasCallObj())
1003 return &fp->callObj();
1005 #ifdef DEBUG
1006 /* A call object should be a frame's outermost scope chain element. */
1007 Class *clasp = fp->scopeChain().getClass();
1008 if (clasp == &js_WithClass || clasp == &js_BlockClass)
1009 JS_ASSERT(fp->scopeChain().getPrivate() != js_FloatingFrameIfGenerator(cx, fp));
1010 else if (clasp == &js_CallClass)
1011 JS_ASSERT(fp->scopeChain().getPrivate() != fp);
1012 #endif
1015 * Create the call object, using the frame's enclosing scope as its
1016 * parent, and link the call to its stack frame. For a named function
1017 * expression Call's parent points to an environment object holding
1018 * function's name.
1020 JSAtom *lambdaName =
1021 (fp->fun()->flags & JSFUN_LAMBDA) ? fp->fun()->atom : NULL;
1022 if (lambdaName) {
1023 JSObject *envobj = NewDeclEnvObject(cx, fp);
1024 if (!envobj)
1025 return NULL;
1027 /* Root envobj before js_DefineNativeProperty (-> JSClass.addProperty). */
1028 fp->setScopeChainNoCallObj(*envobj);
1029 if (!js_DefineNativeProperty(cx, &fp->scopeChain(), ATOM_TO_JSID(lambdaName),
1030 ObjectValue(fp->callee()),
1031 CalleeGetter, NULL,
1032 JSPROP_PERMANENT | JSPROP_READONLY,
1033 0, 0, NULL)) {
1034 return NULL;
1038 JSObject *callobj =
1039 NewCallObject(cx, &fp->fun()->script()->bindings, fp->scopeChain(), &fp->callee());
1040 if (!callobj)
1041 return NULL;
1043 callobj->setPrivate(fp);
1044 JS_ASSERT(fp->fun() == fp->callee().getFunctionPrivate());
1047 * Push callobj on the top of the scope chain, and make it the
1048 * variables object.
1050 fp->setScopeChainAndCallObj(*callobj);
1051 return callobj;
1054 JSObject * JS_FASTCALL
1055 js_CreateCallObjectOnTrace(JSContext *cx, JSFunction *fun, JSObject *callee, JSObject *scopeChain)
1057 JS_ASSERT(!js_IsNamedLambda(fun));
1058 JS_ASSERT(scopeChain);
1059 JS_ASSERT(callee);
1060 return NewCallObject(cx, &fun->script()->bindings, *scopeChain, callee);
1063 JS_DEFINE_CALLINFO_4(extern, OBJECT, js_CreateCallObjectOnTrace, CONTEXT, FUNCTION, OBJECT, OBJECT,
1064 0, nanojit::ACCSET_STORE_ANY)
1066 inline static void
1067 CopyValuesToCallObject(JSObject &callobj, uintN nargs, Value *argv, uintN nvars, Value *slots)
1069 JS_ASSERT(callobj.numSlots() >= JSObject::CALL_RESERVED_SLOTS + nargs + nvars);
1070 Value *base = callobj.getSlots() + JSObject::CALL_RESERVED_SLOTS;
1071 memcpy(base, argv, nargs * sizeof(Value));
1072 memcpy(base + nargs, slots, nvars * sizeof(Value));
1075 void
1076 js_PutCallObject(JSContext *cx, JSStackFrame *fp)
1078 JSObject &callobj = fp->callObj();
1081 * Strict mode eval frames have Call objects to put. Normal eval frames
1082 * never put a Call object.
1084 JS_ASSERT(fp->isEvalFrame() == callobj.callIsForEval());
1086 /* Get the arguments object to snapshot fp's actual argument values. */
1087 if (fp->hasArgsObj()) {
1088 if (!fp->hasOverriddenArgs())
1089 callobj.setCallObjArguments(ObjectValue(fp->argsObj()));
1090 js_PutArgsObject(cx, fp);
1093 JSScript *script = fp->script();
1094 Bindings &bindings = script->bindings;
1096 if (callobj.callIsForEval()) {
1097 JS_ASSERT(script->strictModeCode);
1098 JS_ASSERT(bindings.countArgs() == 0);
1100 /* This could be optimized as below, but keep it simple for now. */
1101 CopyValuesToCallObject(callobj, 0, NULL, bindings.countVars(), fp->slots());
1102 } else {
1103 JSFunction *fun = fp->fun();
1104 JS_ASSERT(fun == callobj.getCallObjCalleeFunction());
1105 JS_ASSERT(script == fun->script());
1107 uintN n = bindings.countArgsAndVars();
1108 if (n > 0) {
1109 JS_ASSERT(JSObject::CALL_RESERVED_SLOTS + n <= callobj.numSlots());
1111 uint32 nvars = bindings.countVars();
1112 uint32 nargs = bindings.countArgs();
1113 JS_ASSERT(fun->nargs == nargs);
1114 JS_ASSERT(nvars + nargs == n);
1116 JSScript *script = fun->script();
1117 if (script->usesEval
1118 #ifdef JS_METHODJIT
1119 || script->debugMode
1120 #endif
1122 CopyValuesToCallObject(callobj, nargs, fp->formalArgs(), nvars, fp->slots());
1123 } else {
1125 * For each arg & var that is closed over, copy it from the stack
1126 * into the call object.
1128 uint32 nclosed = script->nClosedArgs;
1129 for (uint32 i = 0; i < nclosed; i++) {
1130 uint32 e = script->getClosedArg(i);
1131 callobj.setSlot(JSObject::CALL_RESERVED_SLOTS + e, fp->formalArg(e));
1134 nclosed = script->nClosedVars;
1135 for (uint32 i = 0; i < nclosed; i++) {
1136 uint32 e = script->getClosedVar(i);
1137 callobj.setSlot(JSObject::CALL_RESERVED_SLOTS + nargs + e, fp->slots()[e]);
1142 /* Clear private pointers to fp, which is about to go away (js_Invoke). */
1143 if (js_IsNamedLambda(fun)) {
1144 JSObject *env = callobj.getParent();
1146 JS_ASSERT(env->getClass() == &js_DeclEnvClass);
1147 JS_ASSERT(env->getPrivate() == fp);
1148 env->setPrivate(NULL);
1152 callobj.setPrivate(NULL);
1153 fp->clearCallObj();
1156 JSBool JS_FASTCALL
1157 js_PutCallObjectOnTrace(JSContext *cx, JSObject *callobj, uint32 nargs, Value *argv,
1158 uint32 nvars, Value *slots)
1160 JS_ASSERT(callobj->isCall());
1161 JS_ASSERT(!callobj->getPrivate());
1163 uintN n = nargs + nvars;
1164 if (n != 0)
1165 CopyValuesToCallObject(*callobj, nargs, argv, nvars, slots);
1167 return true;
1170 JS_DEFINE_CALLINFO_6(extern, BOOL, js_PutCallObjectOnTrace, CONTEXT, OBJECT, UINT32, VALUEPTR,
1171 UINT32, VALUEPTR, 0, nanojit::ACCSET_STORE_ANY)
1173 namespace js {
1175 static JSBool
1176 GetCallArguments(JSContext *cx, JSObject *obj, jsid id, Value *vp)
1178 JSStackFrame *fp = obj->maybeCallObjStackFrame();
1179 if (fp && !fp->hasOverriddenArgs()) {
1180 JSObject *argsobj = js_GetArgsObject(cx, fp);
1181 if (!argsobj)
1182 return false;
1183 vp->setObject(*argsobj);
1184 } else {
1185 *vp = obj->getCallObjArguments();
1187 return true;
1190 static JSBool
1191 SetCallArguments(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
1193 if (JSStackFrame *fp = obj->maybeCallObjStackFrame())
1194 fp->setOverriddenArgs();
1195 obj->setCallObjArguments(*vp);
1196 return true;
1199 JSBool
1200 GetCallArg(JSContext *cx, JSObject *obj, jsid id, Value *vp)
1202 JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
1203 uintN i = (uint16) JSID_TO_INT(id);
1205 if (JSStackFrame *fp = obj->maybeCallObjStackFrame())
1206 *vp = fp->formalArg(i);
1207 else
1208 *vp = obj->callObjArg(i);
1209 return true;
1212 JSBool
1213 SetCallArg(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
1215 JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
1216 uintN i = (uint16) JSID_TO_INT(id);
1218 Value *argp;
1219 if (JSStackFrame *fp = obj->maybeCallObjStackFrame())
1220 argp = &fp->formalArg(i);
1221 else
1222 argp = &obj->callObjArg(i);
1224 GC_POKE(cx, *argp);
1225 *argp = *vp;
1226 return true;
1229 JSBool
1230 GetCallUpvar(JSContext *cx, JSObject *obj, jsid id, Value *vp)
1232 JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
1233 uintN i = (uint16) JSID_TO_INT(id);
1235 *vp = obj->getCallObjCallee()->getFlatClosureUpvar(i);
1236 return true;
1239 JSBool
1240 SetCallUpvar(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
1242 JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
1243 uintN i = (uint16) JSID_TO_INT(id);
1245 Value *up = &obj->getCallObjCallee()->getFlatClosureUpvar(i);
1247 GC_POKE(cx, *up);
1248 *up = *vp;
1249 return true;
1252 JSBool
1253 GetCallVar(JSContext *cx, JSObject *obj, jsid id, Value *vp)
1255 JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
1256 uintN i = (uint16) JSID_TO_INT(id);
1258 if (JSStackFrame *fp = obj->maybeCallObjStackFrame())
1259 *vp = fp->varSlot(i);
1260 else
1261 *vp = obj->callObjVar(i);
1263 return true;
1266 JSBool
1267 GetCallVarChecked(JSContext *cx, JSObject *obj, jsid id, Value *vp)
1269 if (!GetCallVar(cx, obj, id, vp))
1270 return false;
1272 return CheckForEscapingClosure(cx, obj, vp);
1275 JSBool
1276 SetCallVar(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
1278 JS_ASSERT(obj->isCall());
1280 JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
1281 uintN i = (uint16) JSID_TO_INT(id);
1284 * As documented in TraceRecorder::attemptTreeCall(), when recording an
1285 * inner tree call, the recorder assumes the inner tree does not mutate
1286 * any tracked upvars. The abort here is a pessimistic precaution against
1287 * bug 620662, where an inner tree setting a closed stack variable in an
1288 * outer tree is illegal, and runtime would fall off trace.
1290 #ifdef JS_TRACER
1291 if (JS_ON_TRACE(cx)) {
1292 TraceMonitor *tm = JS_TRACE_MONITOR_ON_TRACE(cx);
1293 if (tm->recorder && tm->tracecx)
1294 AbortRecording(cx, "upvar write in nested tree");
1296 #endif
1298 Value *varp;
1299 if (JSStackFrame *fp = obj->maybeCallObjStackFrame())
1300 varp = &fp->varSlot(i);
1301 else
1302 varp = &obj->callObjVar(i);
1304 GC_POKE(cx, *varp);
1305 *varp = *vp;
1306 return true;
1309 } // namespace js
1311 #if JS_TRACER
1312 JSBool JS_FASTCALL
1313 js_SetCallArg(JSContext *cx, JSObject *obj, jsid slotid, ValueArgType arg)
1315 Value argcopy = ValueArgToConstRef(arg);
1316 return SetCallArg(cx, obj, slotid, false /* STRICT DUMMY */, &argcopy);
1318 JS_DEFINE_CALLINFO_4(extern, BOOL, js_SetCallArg, CONTEXT, OBJECT, JSID, VALUE, 0,
1319 nanojit::ACCSET_STORE_ANY)
1321 JSBool JS_FASTCALL
1322 js_SetCallVar(JSContext *cx, JSObject *obj, jsid slotid, ValueArgType arg)
1324 Value argcopy = ValueArgToConstRef(arg);
1325 return SetCallVar(cx, obj, slotid, false /* STRICT DUMMY */, &argcopy);
1327 JS_DEFINE_CALLINFO_4(extern, BOOL, js_SetCallVar, CONTEXT, OBJECT, JSID, VALUE, 0,
1328 nanojit::ACCSET_STORE_ANY)
1329 #endif
1331 static JSBool
1332 call_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
1333 JSObject **objp)
1335 JS_ASSERT(obj->isCall());
1336 JS_ASSERT(!obj->getProto());
1338 if (!JSID_IS_ATOM(id))
1339 return true;
1341 JSObject *callee = obj->getCallObjCallee();
1342 #ifdef DEBUG
1343 if (callee) {
1344 JSScript *script = callee->getFunctionPrivate()->script();
1345 JS_ASSERT(!script->bindings.hasBinding(cx, JSID_TO_ATOM(id)));
1347 #endif
1350 * Resolve arguments so that we never store a particular Call object's
1351 * arguments object reference in a Call prototype's |arguments| slot.
1353 * Include JSPROP_ENUMERATE for consistency with all other Call object
1354 * properties; see js::Bindings::add and js::Interpret's JSOP_DEFFUN
1355 * rebinding-Call-property logic.
1357 if (callee && id == ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom)) {
1358 if (!js_DefineNativeProperty(cx, obj, id, UndefinedValue(),
1359 GetCallArguments, SetCallArguments,
1360 JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_ENUMERATE,
1361 0, 0, NULL, JSDNP_DONT_PURGE)) {
1362 return false;
1364 *objp = obj;
1365 return true;
1368 /* Control flow reaches here only if id was not resolved. */
1369 return true;
1372 static void
1373 call_trace(JSTracer *trc, JSObject *obj)
1375 JS_ASSERT(obj->isCall());
1376 if (JSStackFrame *fp = obj->maybeCallObjStackFrame()) {
1378 * FIXME: Hide copies of stack values rooted by fp from the Cycle
1379 * Collector, which currently lacks a non-stub Unlink implementation
1380 * for JS objects (including Call objects), so is unable to collect
1381 * cycles involving Call objects whose frames are active without this
1382 * hiding hack.
1384 uintN first = JSObject::CALL_RESERVED_SLOTS;
1385 uintN count = fp->script()->bindings.countArgsAndVars();
1387 JS_ASSERT(obj->numSlots() >= first + count);
1388 SetValueRangeToUndefined(obj->getSlots() + first, count);
1391 MaybeMarkGenerator(trc, obj);
1394 JS_PUBLIC_DATA(Class) js_CallClass = {
1395 "Call",
1396 JSCLASS_HAS_PRIVATE |
1397 JSCLASS_HAS_RESERVED_SLOTS(JSObject::CALL_RESERVED_SLOTS) |
1398 JSCLASS_NEW_RESOLVE | JSCLASS_IS_ANONYMOUS | JSCLASS_MARK_IS_TRACE,
1399 PropertyStub, /* addProperty */
1400 PropertyStub, /* delProperty */
1401 PropertyStub, /* getProperty */
1402 StrictPropertyStub, /* setProperty */
1403 JS_EnumerateStub,
1404 (JSResolveOp)call_resolve,
1405 NULL, /* convert: Leave it NULL so we notice if calls ever escape */
1406 NULL, /* finalize */
1407 NULL, /* reserved0 */
1408 NULL, /* checkAccess */
1409 NULL, /* call */
1410 NULL, /* construct */
1411 NULL, /* xdrObject */
1412 NULL, /* hasInstance */
1413 JS_CLASS_TRACE(call_trace)
1416 bool
1417 JSStackFrame::getValidCalleeObject(JSContext *cx, Value *vp)
1419 if (!isFunctionFrame()) {
1420 vp->setUndefined();
1421 return true;
1424 JSFunction *fun = this->fun();
1427 * See the equivalent condition in ArgGetter for the 'callee' id case, but
1428 * note that here we do not want to throw, since this escape can happen via
1429 * a foo.caller reference alone, without any debugger or indirect eval. And
1430 * alas, it seems foo.caller is still used on the Web.
1432 if (fun->needsWrapper()) {
1433 JSObject *wrapper = WrapEscapingClosure(cx, this, fun);
1434 if (!wrapper)
1435 return false;
1436 vp->setObject(*wrapper);
1437 return true;
1440 JSObject &funobj = callee();
1441 vp->setObject(funobj);
1444 * Check for an escape attempt by a joined function object, which must go
1445 * through the frame's |this| object's method read barrier for the method
1446 * atom by which it was uniquely associated with a property.
1448 const Value &thisv = functionThis();
1449 if (thisv.isObject()) {
1450 JS_ASSERT(funobj.getFunctionPrivate() == fun);
1452 if (&fun->compiledFunObj() == &funobj && fun->methodAtom()) {
1453 JSObject *thisp = &thisv.toObject();
1454 JSObject *first_barriered_thisp = NULL;
1456 do {
1458 * While a non-native object is responsible for handling its
1459 * entire prototype chain, notable non-natives including dense
1460 * and typed arrays have native prototypes, so keep going.
1462 if (!thisp->isNative())
1463 continue;
1465 if (thisp->hasMethodBarrier()) {
1466 const Shape *shape = thisp->nativeLookup(ATOM_TO_JSID(fun->methodAtom()));
1467 if (shape) {
1469 * Two cases follow: the method barrier was not crossed
1470 * yet, so we cross it here; the method barrier *was*
1471 * crossed but after the call, in which case we fetch
1472 * and validate the cloned (unjoined) funobj from the
1473 * method property's slot.
1475 * In either case we must allow for the method property
1476 * to have been replaced, or its value overwritten.
1478 if (shape->isMethod() && &shape->methodObject() == &funobj) {
1479 if (!thisp->methodReadBarrier(cx, *shape, vp))
1480 return false;
1481 calleeValue().setObject(vp->toObject());
1482 return true;
1485 if (shape->hasSlot()) {
1486 Value v = thisp->getSlot(shape->slot);
1487 JSObject *clone;
1489 if (IsFunctionObject(v, &clone) &&
1490 GET_FUNCTION_PRIVATE(cx, clone) == fun &&
1491 clone->hasMethodObj(*thisp)) {
1492 JS_ASSERT(clone != &funobj);
1493 *vp = v;
1494 calleeValue().setObject(*clone);
1495 return true;
1500 if (!first_barriered_thisp)
1501 first_barriered_thisp = thisp;
1503 } while ((thisp = thisp->getProto()) != NULL);
1505 if (!first_barriered_thisp)
1506 return true;
1509 * At this point, we couldn't find an already-existing clone (or
1510 * force to exist a fresh clone) created via thisp's method read
1511 * barrier, so we must clone fun and store it in fp's callee to
1512 * avoid re-cloning upon repeated foo.caller access.
1514 * This must mean the code in js_DeleteProperty could not find this
1515 * stack frame on the stack when the method was deleted. We've lost
1516 * track of the method, so we associate it with the first barriered
1517 * object found starting from thisp on the prototype chain.
1519 JSObject *newfunobj = CloneFunctionObject(cx, fun, fun->getParent());
1520 if (!newfunobj)
1521 return false;
1522 newfunobj->setMethodObj(*first_barriered_thisp);
1523 calleeValue().setObject(*newfunobj);
1524 vp->setObject(*newfunobj);
1525 return true;
1529 return true;
1532 /* Generic function tinyids. */
1533 enum {
1534 FUN_ARGUMENTS = -1, /* predefined arguments local variable */
1535 FUN_LENGTH = -2, /* number of actual args, arity if inactive */
1536 FUN_ARITY = -3, /* number of formal parameters; desired argc */
1537 FUN_NAME = -4, /* function name, "" if anonymous */
1538 FUN_CALLER = -5 /* Function.prototype.caller, backward compat */
1541 static JSBool
1542 fun_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
1544 if (!JSID_IS_INT(id))
1545 return true;
1547 jsint slot = JSID_TO_INT(id);
1550 * Loop because getter and setter can be delegated from another class,
1551 * but loop only for FUN_LENGTH because we must pretend that f.length
1552 * is in each function instance f, per ECMA-262, instead of only in the
1553 * Function.prototype object (we use JSPROP_PERMANENT with JSPROP_SHARED
1554 * to make it appear so).
1556 * This code couples tightly to the attributes for lazyFunctionDataProps[]
1557 * and poisonPillProps[] initializers below, and to js_SetProperty and
1558 * js_HasOwnProperty.
1560 * It's important to allow delegating objects, even though they inherit
1561 * this getter (fun_getProperty), to override arguments, arity, caller,
1562 * and name. If we didn't return early for slot != FUN_LENGTH, we would
1563 * clobber *vp with the native property value, instead of letting script
1564 * override that value in delegating objects.
1566 * Note how that clobbering is what simulates JSPROP_READONLY for all of
1567 * the non-standard properties when the directly addressed object (obj)
1568 * is a function object (i.e., when this loop does not iterate).
1570 JSFunction *fun;
1571 while (!(fun = (JSFunction *)
1572 GetInstancePrivate(cx, obj, &js_FunctionClass, NULL))) {
1573 if (slot != FUN_LENGTH)
1574 return true;
1575 obj = obj->getProto();
1576 if (!obj)
1577 return true;
1580 /* Find fun's top-most activation record. */
1581 JSStackFrame *fp;
1582 for (fp = js_GetTopStackFrame(cx);
1583 fp && (fp->maybeFun() != fun || fp->isEvalOrDebuggerFrame());
1584 fp = fp->prev()) {
1585 continue;
1588 switch (slot) {
1589 case FUN_ARGUMENTS:
1590 /* Warn if strict about f.arguments or equivalent unqualified uses. */
1591 if (!JS_ReportErrorFlagsAndNumber(cx,
1592 JSREPORT_WARNING | JSREPORT_STRICT,
1593 js_GetErrorMessage, NULL,
1594 JSMSG_DEPRECATED_USAGE,
1595 js_arguments_str)) {
1596 return false;
1598 if (fp) {
1599 if (!js_GetArgsValue(cx, fp, vp))
1600 return false;
1601 } else {
1602 vp->setNull();
1604 break;
1606 case FUN_LENGTH:
1607 case FUN_ARITY:
1608 vp->setInt32(fun->nargs);
1609 break;
1611 case FUN_NAME:
1612 vp->setString(fun->atom ? ATOM_TO_STRING(fun->atom)
1613 : cx->runtime->emptyString);
1614 break;
1616 case FUN_CALLER:
1617 vp->setNull();
1618 if (fp && fp->prev() && !fp->prev()->getValidCalleeObject(cx, vp))
1619 return false;
1621 if (vp->isObject()) {
1622 JSObject &caller = vp->toObject();
1624 /* Censor the caller if it is from another compartment. */
1625 if (caller.getCompartment() != cx->compartment) {
1626 vp->setNull();
1627 } else if (caller.isFunction()) {
1628 JSFunction *callerFun = caller.getFunctionPrivate();
1629 if (callerFun->isInterpreted() && callerFun->inStrictMode()) {
1630 JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL,
1631 JSMSG_CALLER_IS_STRICT);
1632 return false;
1636 break;
1638 default:
1639 /* XXX fun[0] and fun.arguments[0] are equivalent. */
1640 if (fp && fp->isFunctionFrame() && uint16(slot) < fp->numFormalArgs())
1641 *vp = fp->formalArg(slot);
1642 break;
1645 return true;
1648 struct LazyFunctionDataProp {
1649 uint16 atomOffset;
1650 int8 tinyid;
1651 uint8 attrs;
1654 struct PoisonPillProp {
1655 uint16 atomOffset;
1656 int8 tinyid;
1659 /* NB: no sentinels at ends -- use JS_ARRAY_LENGTH to bound loops. */
1661 static const LazyFunctionDataProp lazyFunctionDataProps[] = {
1662 {ATOM_OFFSET(arity), FUN_ARITY, JSPROP_PERMANENT|JSPROP_READONLY},
1663 {ATOM_OFFSET(name), FUN_NAME, JSPROP_PERMANENT|JSPROP_READONLY},
1666 /* Properties censored into [[ThrowTypeError]] in strict mode. */
1667 static const PoisonPillProp poisonPillProps[] = {
1668 {ATOM_OFFSET(arguments), FUN_ARGUMENTS },
1669 {ATOM_OFFSET(caller), FUN_CALLER },
1672 static JSBool
1673 fun_enumerate(JSContext *cx, JSObject *obj)
1675 JS_ASSERT(obj->isFunction());
1677 jsid id;
1678 bool found;
1680 if (!obj->isBoundFunction()) {
1681 id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
1682 if (!obj->hasProperty(cx, id, &found, JSRESOLVE_QUALIFIED))
1683 return false;
1686 id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
1687 if (!obj->hasProperty(cx, id, &found, JSRESOLVE_QUALIFIED))
1688 return false;
1690 for (uintN i = 0; i < JS_ARRAY_LENGTH(lazyFunctionDataProps); i++) {
1691 const LazyFunctionDataProp &lfp = lazyFunctionDataProps[i];
1692 id = ATOM_TO_JSID(OFFSET_TO_ATOM(cx->runtime, lfp.atomOffset));
1693 if (!obj->hasProperty(cx, id, &found, JSRESOLVE_QUALIFIED))
1694 return false;
1697 for (uintN i = 0; i < JS_ARRAY_LENGTH(poisonPillProps); i++) {
1698 const PoisonPillProp &p = poisonPillProps[i];
1699 id = ATOM_TO_JSID(OFFSET_TO_ATOM(cx->runtime, p.atomOffset));
1700 if (!obj->hasProperty(cx, id, &found, JSRESOLVE_QUALIFIED))
1701 return false;
1704 return true;
1707 static JSObject *
1708 ResolveInterpretedFunctionPrototype(JSContext *cx, JSObject *obj)
1710 #ifdef DEBUG
1711 JSFunction *fun = obj->getFunctionPrivate();
1712 JS_ASSERT(fun->isInterpreted());
1713 JS_ASSERT(!fun->isFunctionPrototype());
1714 #endif
1717 * Assert that fun is not a compiler-created function object, which
1718 * must never leak to script or embedding code and then be mutated.
1719 * Also assert that obj is not bound, per the ES5 15.3.4.5 ref above.
1721 JS_ASSERT(!IsInternalFunctionObject(obj));
1722 JS_ASSERT(!obj->isBoundFunction());
1725 * Make the prototype object an instance of Object with the same parent
1726 * as the function object itself.
1728 JSObject *parent = obj->getParent();
1729 JSObject *proto;
1730 if (!js_GetClassPrototype(cx, parent, JSProto_Object, &proto))
1731 return NULL;
1732 proto = NewNativeClassInstance(cx, &js_ObjectClass, proto, parent);
1733 if (!proto)
1734 return NULL;
1737 * ECMA (15.3.5.2) says that a user-defined function's .prototype property
1738 * is non-configurable, non-enumerable, and (initially) writable. Hence
1739 * JSPROP_PERMANENT below. By contrast, the built-in constructors, such as
1740 * Object (15.2.3.1) and Function (15.3.3.1), have non-writable
1741 * .prototype properties. Those are eagerly defined, with attributes
1742 * JSPROP_PERMANENT | JSPROP_READONLY, in js_InitClass.
1744 if (!js_SetClassPrototype(cx, obj, proto, JSPROP_PERMANENT))
1745 return NULL;
1746 return proto;
1749 static JSBool
1750 fun_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
1751 JSObject **objp)
1753 if (!JSID_IS_ATOM(id))
1754 return true;
1756 JSFunction *fun = obj->getFunctionPrivate();
1758 if (JSID_IS_ATOM(id, cx->runtime->atomState.classPrototypeAtom)) {
1760 * Native or "built-in" functions do not have a .prototype property per
1761 * ECMA-262 (all editions). Built-in constructor functions, e.g. Object
1762 * and Function to name two conspicuous examples, do have a .prototype
1763 * property, but it is created eagerly by js_InitClass (jsobj.cpp).
1765 * ES5 15.3.4: the non-native function object named Function.prototype
1766 * must not have a .prototype property.
1768 * ES5 15.3.4.5: bound functions don't have a prototype property. The
1769 * isNative() test covers this case because bound functions are native
1770 * functions by definition/construction.
1772 if (fun->isNative() || fun->isFunctionPrototype())
1773 return true;
1775 if (!ResolveInterpretedFunctionPrototype(cx, obj))
1776 return false;
1777 *objp = obj;
1778 return true;
1781 if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
1782 JS_ASSERT(!IsInternalFunctionObject(obj));
1783 if (!js_DefineNativeProperty(cx, obj, id, Int32Value(fun->nargs),
1784 PropertyStub, StrictPropertyStub,
1785 JSPROP_PERMANENT | JSPROP_READONLY, 0, 0, NULL)) {
1786 return false;
1788 *objp = obj;
1789 return true;
1792 for (uintN i = 0; i < JS_ARRAY_LENGTH(lazyFunctionDataProps); i++) {
1793 const LazyFunctionDataProp *lfp = &lazyFunctionDataProps[i];
1795 if (JSID_IS_ATOM(id, OFFSET_TO_ATOM(cx->runtime, lfp->atomOffset))) {
1796 JS_ASSERT(!IsInternalFunctionObject(obj));
1798 if (!js_DefineNativeProperty(cx, obj, id, UndefinedValue(),
1799 fun_getProperty, StrictPropertyStub,
1800 lfp->attrs, Shape::HAS_SHORTID,
1801 lfp->tinyid, NULL)) {
1802 return false;
1804 *objp = obj;
1805 return true;
1809 for (uintN i = 0; i < JS_ARRAY_LENGTH(poisonPillProps); i++) {
1810 const PoisonPillProp &p = poisonPillProps[i];
1812 if (JSID_IS_ATOM(id, OFFSET_TO_ATOM(cx->runtime, p.atomOffset))) {
1813 JS_ASSERT(!IsInternalFunctionObject(obj));
1815 PropertyOp getter;
1816 StrictPropertyOp setter;
1817 uintN attrs = JSPROP_PERMANENT;
1818 if (fun->isInterpreted() ? fun->inStrictMode() : obj->isBoundFunction()) {
1819 JSObject *throwTypeError = obj->getThrowTypeError();
1821 getter = CastAsPropertyOp(throwTypeError);
1822 setter = CastAsStrictPropertyOp(throwTypeError);
1823 attrs |= JSPROP_GETTER | JSPROP_SETTER;
1824 } else {
1825 getter = fun_getProperty;
1826 setter = StrictPropertyStub;
1829 if (!js_DefineNativeProperty(cx, obj, id, UndefinedValue(),
1830 getter, setter,
1831 attrs, Shape::HAS_SHORTID,
1832 p.tinyid, NULL)) {
1833 return false;
1835 *objp = obj;
1836 return true;
1840 return true;
1843 #if JS_HAS_XDR
1845 /* XXX store parent and proto, if defined */
1846 JSBool
1847 js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp)
1849 JSContext *cx;
1850 JSFunction *fun;
1851 uint32 firstword; /* flag telling whether fun->atom is non-null,
1852 plus for fun->u.i.skipmin, fun->u.i.wrapper,
1853 and 14 bits reserved for future use */
1854 uint32 flagsword; /* word for argument count and fun->flags */
1856 cx = xdr->cx;
1857 if (xdr->mode == JSXDR_ENCODE) {
1858 fun = GET_FUNCTION_PRIVATE(cx, *objp);
1859 if (!FUN_INTERPRETED(fun)) {
1860 JSAutoByteString funNameBytes;
1861 if (const char *name = GetFunctionNameBytes(cx, fun, &funNameBytes)) {
1862 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_SCRIPTED_FUNCTION,
1863 name);
1865 return false;
1867 if (fun->u.i.wrapper) {
1868 JSAutoByteString funNameBytes;
1869 if (const char *name = GetFunctionNameBytes(cx, fun, &funNameBytes))
1870 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_XDR_CLOSURE_WRAPPER, name);
1871 return false;
1873 JS_ASSERT((fun->u.i.wrapper & ~1U) == 0);
1874 firstword = (fun->u.i.skipmin << 2) | (fun->u.i.wrapper << 1) | !!fun->atom;
1875 flagsword = (fun->nargs << 16) | fun->flags;
1876 } else {
1877 fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, NULL, NULL);
1878 if (!fun)
1879 return false;
1880 FUN_OBJECT(fun)->clearParent();
1881 FUN_OBJECT(fun)->clearProto();
1884 AutoObjectRooter tvr(cx, FUN_OBJECT(fun));
1886 if (!JS_XDRUint32(xdr, &firstword))
1887 return false;
1888 if ((firstword & 1U) && !js_XDRAtom(xdr, &fun->atom))
1889 return false;
1890 if (!JS_XDRUint32(xdr, &flagsword))
1891 return false;
1893 if (xdr->mode == JSXDR_DECODE) {
1894 fun->nargs = flagsword >> 16;
1895 JS_ASSERT((flagsword & JSFUN_KINDMASK) >= JSFUN_INTERPRETED);
1896 fun->flags = uint16(flagsword);
1897 fun->u.i.skipmin = uint16(firstword >> 2);
1898 fun->u.i.wrapper = JSPackedBool((firstword >> 1) & 1);
1901 if (!js_XDRScript(xdr, &fun->u.i.script, NULL))
1902 return false;
1904 if (xdr->mode == JSXDR_DECODE) {
1905 *objp = FUN_OBJECT(fun);
1906 #ifdef CHECK_SCRIPT_OWNER
1907 fun->script()->owner = NULL;
1908 #endif
1909 JS_ASSERT(fun->nargs == fun->script()->bindings.countArgs());
1910 js_CallNewScriptHook(cx, fun->script(), fun);
1913 return true;
1916 #else /* !JS_HAS_XDR */
1918 #define js_XDRFunctionObject NULL
1920 #endif /* !JS_HAS_XDR */
1923 * [[HasInstance]] internal method for Function objects: fetch the .prototype
1924 * property of its 'this' parameter, and walks the prototype chain of v (only
1925 * if v is an object) returning true if .prototype is found.
1927 static JSBool
1928 fun_hasInstance(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp)
1930 while (obj->isFunction()) {
1931 if (!obj->isBoundFunction())
1932 break;
1933 obj = obj->getBoundFunctionTarget();
1936 jsid id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
1937 Value pval;
1938 if (!obj->getProperty(cx, id, &pval))
1939 return JS_FALSE;
1941 if (pval.isPrimitive()) {
1943 * Throw a runtime error if instanceof is called on a function that
1944 * has a non-object as its .prototype value.
1946 js_ReportValueError(cx, JSMSG_BAD_PROTOTYPE, -1, ObjectValue(*obj), NULL);
1947 return JS_FALSE;
1950 *bp = js_IsDelegate(cx, &pval.toObject(), *v);
1951 return JS_TRUE;
1954 static void
1955 fun_trace(JSTracer *trc, JSObject *obj)
1957 /* A newborn function object may have a not yet initialized private slot. */
1958 JSFunction *fun = (JSFunction *) obj->getPrivate();
1959 if (!fun)
1960 return;
1962 if (fun != obj) {
1963 /* obj is a cloned function object, trace the clone-parent, fun. */
1964 MarkObject(trc, *fun, "private");
1966 /* The function could be a flat closure with upvar copies in the clone. */
1967 if (fun->isFlatClosure() && fun->script()->bindings.hasUpvars()) {
1968 MarkValueRange(trc, fun->script()->bindings.countUpvars(),
1969 obj->getFlatClosureUpvars(), "upvars");
1971 return;
1974 if (fun->atom)
1975 MarkString(trc, ATOM_TO_STRING(fun->atom), "atom");
1977 if (fun->isInterpreted() && fun->script())
1978 js_TraceScript(trc, fun->script());
1981 static void
1982 fun_finalize(JSContext *cx, JSObject *obj)
1984 /* Ignore newborn function objects. */
1985 JSFunction *fun = obj->getFunctionPrivate();
1986 if (!fun)
1987 return;
1989 /* Cloned function objects may be flat closures with upvars to free. */
1990 if (fun != obj) {
1991 if (fun->isFlatClosure() && fun->script()->bindings.hasUpvars())
1992 cx->free((void *) obj->getFlatClosureUpvars());
1993 return;
1997 * Null-check fun->script() because the parser sets interpreted very early.
1999 if (fun->isInterpreted() && fun->script())
2000 js_DestroyScriptFromGC(cx, fun->script());
2004 * Reserve two slots in all function objects for XPConnect. Note that this
2005 * does not bloat every instance, only those on which reserved slots are set,
2006 * and those on which ad-hoc properties are defined.
2008 JS_PUBLIC_DATA(Class) js_FunctionClass = {
2009 js_Function_str,
2010 JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE |
2011 JSCLASS_HAS_RESERVED_SLOTS(JSFunction::CLASS_RESERVED_SLOTS) |
2012 JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Function),
2013 PropertyStub, /* addProperty */
2014 PropertyStub, /* delProperty */
2015 PropertyStub, /* getProperty */
2016 StrictPropertyStub, /* setProperty */
2017 fun_enumerate,
2018 (JSResolveOp)fun_resolve,
2019 ConvertStub,
2020 fun_finalize,
2021 NULL, /* reserved0 */
2022 NULL, /* checkAccess */
2023 NULL, /* call */
2024 NULL, /* construct */
2025 js_XDRFunctionObject,
2026 fun_hasInstance,
2027 JS_CLASS_TRACE(fun_trace)
2030 JSString *
2031 fun_toStringHelper(JSContext *cx, JSObject *obj, uintN indent)
2033 if (!obj->isFunction()) {
2034 if (obj->isFunctionProxy())
2035 return JSProxy::fun_toString(cx, obj, indent);
2036 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2037 JSMSG_INCOMPATIBLE_PROTO,
2038 js_Function_str, js_toString_str,
2039 "object");
2040 return NULL;
2043 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, obj);
2044 if (!fun)
2045 return NULL;
2047 if (!indent) {
2048 ToSourceCache::Ptr p = cx->compartment->toSourceCache.lookup(fun);
2049 if (p)
2050 return p->value;
2053 JSString *str = JS_DecompileFunction(cx, fun, indent);
2054 if (!str)
2055 return false;
2057 if (!indent)
2058 cx->compartment->toSourceCache.put(fun, str);
2060 return str;
2063 static JSBool
2064 fun_toString(JSContext *cx, uintN argc, Value *vp)
2066 JS_ASSERT(IsFunctionObject(vp[0]));
2067 uint32_t indent = 0;
2069 if (argc != 0 && !ValueToECMAUint32(cx, vp[2], &indent))
2070 return false;
2072 JSObject *obj = ToObject(cx, &vp[1]);
2073 if (!obj)
2074 return false;
2076 JSString *str = fun_toStringHelper(cx, obj, indent);
2077 if (!str)
2078 return false;
2080 vp->setString(str);
2081 return true;
2084 #if JS_HAS_TOSOURCE
2085 static JSBool
2086 fun_toSource(JSContext *cx, uintN argc, Value *vp)
2088 JS_ASSERT(IsFunctionObject(vp[0]));
2090 JSObject *obj = ToObject(cx, &vp[1]);
2091 if (!obj)
2092 return false;
2094 JSString *str = fun_toStringHelper(cx, obj, JS_DONT_PRETTY_PRINT);
2095 if (!str)
2096 return false;
2098 vp->setString(str);
2099 return true;
2101 #endif
2103 JSBool
2104 js_fun_call(JSContext *cx, uintN argc, Value *vp)
2106 LeaveTrace(cx);
2107 Value fval = vp[1];
2109 if (!js_IsCallable(fval)) {
2110 if (JSString *str = js_ValueToString(cx, fval)) {
2111 JSAutoByteString bytes(cx, str);
2112 if (!!bytes) {
2113 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2114 JSMSG_INCOMPATIBLE_PROTO,
2115 js_Function_str, js_call_str,
2116 bytes.ptr());
2119 return false;
2122 Value *argv = vp + 2;
2123 Value thisv;
2124 if (argc == 0) {
2125 thisv.setUndefined();
2126 } else {
2127 thisv = argv[0];
2129 argc--;
2130 argv++;
2133 /* Allocate stack space for fval, obj, and the args. */
2134 InvokeArgsGuard args;
2135 if (!cx->stack().pushInvokeArgs(cx, argc, &args))
2136 return JS_FALSE;
2138 /* Push fval, thisv, and the args. */
2139 args.callee() = fval;
2140 args.thisv() = thisv;
2141 memcpy(args.argv(), argv, argc * sizeof *argv);
2143 bool ok = Invoke(cx, args, 0);
2144 *vp = args.rval();
2145 return ok;
2148 /* ES5 15.3.4.3 */
2149 JSBool
2150 js_fun_apply(JSContext *cx, uintN argc, Value *vp)
2152 /* Step 1. */
2153 Value fval = vp[1];
2154 if (!js_IsCallable(fval)) {
2155 if (JSString *str = js_ValueToString(cx, fval)) {
2156 JSAutoByteString bytes(cx, str);
2157 if (!!bytes) {
2158 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2159 JSMSG_INCOMPATIBLE_PROTO,
2160 js_Function_str, js_apply_str,
2161 bytes.ptr());
2164 return false;
2167 /* Step 2. */
2168 if (argc < 2 || vp[3].isNullOrUndefined())
2169 return js_fun_call(cx, (argc > 0) ? 1 : 0, vp);
2171 /* N.B. Changes need to be propagated to stubs::SplatApplyArgs. */
2173 /* Step 3. */
2174 if (!vp[3].isObject()) {
2175 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_APPLY_ARGS, js_apply_str);
2176 return false;
2180 * Steps 4-5 (note erratum removing steps originally numbered 5 and 7 in
2181 * original version of ES5).
2183 JSObject *aobj = &vp[3].toObject();
2184 jsuint length;
2185 if (!js_GetLengthProperty(cx, aobj, &length))
2186 return false;
2188 LeaveTrace(cx);
2190 /* Step 6. */
2191 uintN n = uintN(JS_MIN(length, JS_ARGS_LENGTH_MAX));
2193 InvokeArgsGuard args;
2194 if (!cx->stack().pushInvokeArgs(cx, n, &args))
2195 return false;
2197 /* Push fval, obj, and aobj's elements as args. */
2198 args.callee() = fval;
2199 args.thisv() = vp[2];
2201 /* Steps 7-8. */
2202 if (!GetElements(cx, aobj, n, args.argv()))
2203 return false;
2205 /* Step 9. */
2206 if (!Invoke(cx, args, 0))
2207 return false;
2208 *vp = args.rval();
2209 return true;
2212 namespace js {
2214 JSBool
2215 CallOrConstructBoundFunction(JSContext *cx, uintN argc, Value *vp);
2219 inline bool
2220 JSObject::initBoundFunction(JSContext *cx, const Value &thisArg,
2221 const Value *args, uintN argslen)
2223 JS_ASSERT(isFunction());
2225 flags |= JSObject::BOUND_FUNCTION;
2226 getSlotRef(JSSLOT_BOUND_FUNCTION_THIS) = thisArg;
2227 getSlotRef(JSSLOT_BOUND_FUNCTION_ARGS_COUNT).setPrivateUint32(argslen);
2228 if (argslen != 0) {
2229 /* FIXME? Burn memory on an empty scope whose shape covers the args slots. */
2230 EmptyShape *empty = EmptyShape::create(cx, clasp);
2231 if (!empty)
2232 return false;
2234 empty->slotSpan += argslen;
2235 map = empty;
2237 if (!ensureInstanceReservedSlots(cx, argslen))
2238 return false;
2240 JS_ASSERT(numSlots() >= argslen + FUN_CLASS_RESERVED_SLOTS);
2241 memcpy(getSlots() + FUN_CLASS_RESERVED_SLOTS, args, argslen * sizeof(Value));
2243 return true;
2246 inline JSObject *
2247 JSObject::getBoundFunctionTarget() const
2249 JS_ASSERT(isFunction());
2250 JS_ASSERT(isBoundFunction());
2252 /* Bound functions abuse |parent| to store their target function. */
2253 return getParent();
2256 inline const js::Value &
2257 JSObject::getBoundFunctionThis() const
2259 JS_ASSERT(isFunction());
2260 JS_ASSERT(isBoundFunction());
2262 return getSlot(JSSLOT_BOUND_FUNCTION_THIS);
2265 inline const js::Value *
2266 JSObject::getBoundFunctionArguments(uintN &argslen) const
2268 JS_ASSERT(isFunction());
2269 JS_ASSERT(isBoundFunction());
2271 argslen = getSlot(JSSLOT_BOUND_FUNCTION_ARGS_COUNT).toPrivateUint32();
2272 JS_ASSERT_IF(argslen > 0, numSlots() >= argslen);
2274 return getSlots() + FUN_CLASS_RESERVED_SLOTS;
2277 namespace js {
2279 /* ES5 15.3.4.5.1 and 15.3.4.5.2. */
2280 JSBool
2281 CallOrConstructBoundFunction(JSContext *cx, uintN argc, Value *vp)
2283 JSObject *obj = &vp[0].toObject();
2284 JS_ASSERT(obj->isFunction());
2285 JS_ASSERT(obj->isBoundFunction());
2287 LeaveTrace(cx);
2289 bool constructing = IsConstructing(vp);
2291 /* 15.3.4.5.1 step 1, 15.3.4.5.2 step 3. */
2292 uintN argslen;
2293 const Value *boundArgs = obj->getBoundFunctionArguments(argslen);
2295 if (argc + argslen > JS_ARGS_LENGTH_MAX) {
2296 js_ReportAllocationOverflow(cx);
2297 return false;
2300 /* 15.3.4.5.1 step 3, 15.3.4.5.2 step 1. */
2301 JSObject *target = obj->getBoundFunctionTarget();
2303 /* 15.3.4.5.1 step 2. */
2304 const Value &boundThis = obj->getBoundFunctionThis();
2306 InvokeArgsGuard args;
2307 if (!cx->stack().pushInvokeArgs(cx, argc + argslen, &args))
2308 return false;
2310 /* 15.3.4.5.1, 15.3.4.5.2 step 4. */
2311 memcpy(args.argv(), boundArgs, argslen * sizeof(Value));
2312 memcpy(args.argv() + argslen, vp + 2, argc * sizeof(Value));
2314 /* 15.3.4.5.1, 15.3.4.5.2 step 5. */
2315 args.callee().setObject(*target);
2317 if (!constructing)
2318 args.thisv() = boundThis;
2320 if (constructing ? !InvokeConstructor(cx, args) : !Invoke(cx, args, 0))
2321 return false;
2323 *vp = args.rval();
2324 return true;
2329 /* ES5 15.3.4.5. */
2330 static JSBool
2331 fun_bind(JSContext *cx, uintN argc, Value *vp)
2333 /* Step 1. */
2334 Value &thisv = vp[1];
2336 /* Step 2. */
2337 if (!js_IsCallable(thisv)) {
2338 if (JSString *str = js_ValueToString(cx, thisv)) {
2339 JSAutoByteString bytes(cx, str);
2340 if (!!bytes) {
2341 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2342 JSMSG_INCOMPATIBLE_PROTO,
2343 js_Function_str, "bind", bytes.ptr());
2346 return false;
2349 JSObject *target = &thisv.toObject();
2351 /* Step 3. */
2352 Value *args = NULL;
2353 uintN argslen = 0;
2354 if (argc > 1) {
2355 args = vp + 3;
2356 argslen = argc - 1;
2359 /* Steps 15-16. */
2360 uintN length = 0;
2361 if (target->isFunction()) {
2362 uintN nargs = target->getFunctionPrivate()->nargs;
2363 if (nargs > argslen)
2364 length = nargs - argslen;
2367 /* Step 4-6, 10-11. */
2368 JSAtom *name = target->isFunction() ? target->getFunctionPrivate()->atom : NULL;
2370 /* NB: Bound functions abuse |parent| to store their target. */
2371 JSObject *funobj =
2372 js_NewFunction(cx, NULL, CallOrConstructBoundFunction, length,
2373 JSFUN_CONSTRUCTOR, target, name);
2374 if (!funobj)
2375 return false;
2377 /* Steps 7-9. */
2378 Value thisArg = argc >= 1 ? vp[2] : UndefinedValue();
2379 if (!funobj->initBoundFunction(cx, thisArg, args, argslen))
2380 return false;
2382 /* Steps 17, 19-21 are handled by fun_resolve. */
2383 /* Step 18 is the default for new functions. */
2385 /* Step 22. */
2386 vp->setObject(*funobj);
2387 return true;
2390 static JSFunctionSpec function_methods[] = {
2391 #if JS_HAS_TOSOURCE
2392 JS_FN(js_toSource_str, fun_toSource, 0,0),
2393 #endif
2394 JS_FN(js_toString_str, fun_toString, 0,0),
2395 JS_FN(js_apply_str, js_fun_apply, 2,0),
2396 JS_FN(js_call_str, js_fun_call, 1,0),
2397 JS_FN("bind", fun_bind, 1,0),
2398 JS_FS_END
2401 static JSBool
2402 Function(JSContext *cx, uintN argc, Value *vp)
2404 JSObject *obj = NewFunction(cx, NULL);
2405 if (!obj)
2406 return JS_FALSE;
2408 /* N.B. overwriting callee with return value */
2409 JSObject *parent = vp[0].toObject().getParent();
2410 vp[0].setObject(*obj);
2413 * NB: (new Function) is not lexically closed by its caller, it's just an
2414 * anonymous function in the top-level scope that its constructor inhabits.
2415 * Thus 'var x = 42; f = new Function("return x"); print(f())' prints 42,
2416 * and so would a call to f from another top-level's script or function.
2418 * In older versions, before call objects, a new Function was adopted by
2419 * its running context's globalObject, which might be different from the
2420 * top-level reachable from scopeChain (in HTML frames, e.g.).
2422 JSFunction *fun = js_NewFunction(cx, obj, NULL, 0, JSFUN_LAMBDA | JSFUN_INTERPRETED,
2423 parent, cx->runtime->atomState.anonymousAtom);
2424 if (!fun)
2425 return JS_FALSE;
2428 * Function is static and not called directly by other functions in this
2429 * file, therefore it is callable only as a native function by js_Invoke.
2430 * Find the scripted caller, possibly skipping other native frames such as
2431 * are built for Function.prototype.call or .apply activations that invoke
2432 * Function indirectly from a script.
2434 JSStackFrame *caller = js_GetScriptedCaller(cx, NULL);
2435 uintN lineno;
2436 const char *filename;
2437 JSPrincipals *principals;
2438 if (caller) {
2439 JSObject *callee = &JS_CALLEE(cx, vp).toObject();
2440 principals = js_EvalFramePrincipals(cx, callee, caller);
2441 filename = js_ComputeFilename(cx, caller, principals, &lineno);
2442 } else {
2443 filename = NULL;
2444 lineno = 0;
2445 principals = NULL;
2448 /* Belt-and-braces: check that the caller has access to parent. */
2449 if (!js_CheckPrincipalsAccess(cx, parent, principals,
2450 CLASS_ATOM(cx, Function))) {
2451 return JS_FALSE;
2455 * CSP check: whether new Function() is allowed at all.
2456 * Report errors via CSP is done in the script security manager.
2457 * js_CheckContentSecurityPolicy is defined in jsobj.cpp
2459 if (!js_CheckContentSecurityPolicy(cx, parent)) {
2460 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CSP_BLOCKED_FUNCTION);
2461 return JS_FALSE;
2464 Bindings bindings(cx);
2465 AutoBindingsRooter root(cx, bindings);
2467 Value *argv = vp + 2;
2468 uintN n = argc ? argc - 1 : 0;
2469 if (n > 0) {
2470 enum { OK, BAD, BAD_FORMAL } state;
2473 * Collect the function-argument arguments into one string, separated
2474 * by commas, then make a tokenstream from that string, and scan it to
2475 * get the arguments. We need to throw the full scanner at the
2476 * problem, because the argument string can legitimately contain
2477 * comments and linefeeds. XXX It might be better to concatenate
2478 * everything up into a function definition and pass it to the
2479 * compiler, but doing it this way is less of a delta from the old
2480 * code. See ECMA 15.3.2.1.
2482 state = BAD_FORMAL;
2483 size_t args_length = 0;
2484 for (uintN i = 0; i < n; i++) {
2485 /* Collect the lengths for all the function-argument arguments. */
2486 JSString *arg = js_ValueToString(cx, argv[i]);
2487 if (!arg)
2488 return JS_FALSE;
2489 argv[i].setString(arg);
2492 * Check for overflow. The < test works because the maximum
2493 * JSString length fits in 2 fewer bits than size_t has.
2495 size_t old_args_length = args_length;
2496 args_length = old_args_length + arg->length();
2497 if (args_length < old_args_length) {
2498 js_ReportAllocationOverflow(cx);
2499 return JS_FALSE;
2503 /* Add 1 for each joining comma and check for overflow (two ways). */
2504 size_t old_args_length = args_length;
2505 args_length = old_args_length + n - 1;
2506 if (args_length < old_args_length ||
2507 args_length >= ~(size_t)0 / sizeof(jschar)) {
2508 js_ReportAllocationOverflow(cx);
2509 return JS_FALSE;
2513 * Allocate a string to hold the concatenated arguments, including room
2514 * for a terminating 0. Mark cx->tempPool for later release, to free
2515 * collected_args and its tokenstream in one swoop.
2517 void *mark = JS_ARENA_MARK(&cx->tempPool);
2518 jschar *cp;
2519 JS_ARENA_ALLOCATE_CAST(cp, jschar *, &cx->tempPool,
2520 (args_length+1) * sizeof(jschar));
2521 if (!cp) {
2522 js_ReportOutOfScriptQuota(cx);
2523 return JS_FALSE;
2525 jschar *collected_args = cp;
2528 * Concatenate the arguments into the new string, separated by commas.
2530 for (uintN i = 0; i < n; i++) {
2531 JSString *arg = argv[i].toString();
2532 size_t arg_length = arg->length();
2533 const jschar *arg_chars = arg->getChars(cx);
2534 if (!arg_chars) {
2535 JS_ARENA_RELEASE(&cx->tempPool, mark);
2536 return JS_FALSE;
2538 (void) js_strncpy(cp, arg_chars, arg_length);
2539 cp += arg_length;
2541 /* Add separating comma or terminating 0. */
2542 *cp++ = (i + 1 < n) ? ',' : 0;
2545 /* Initialize a tokenstream that reads from the given string. */
2546 TokenStream ts(cx);
2547 if (!ts.init(collected_args, args_length, filename, lineno, cx->findVersion())) {
2548 JS_ARENA_RELEASE(&cx->tempPool, mark);
2549 return JS_FALSE;
2552 /* The argument string may be empty or contain no tokens. */
2553 TokenKind tt = ts.getToken();
2554 if (tt != TOK_EOF) {
2555 for (;;) {
2557 * Check that it's a name. This also implicitly guards against
2558 * TOK_ERROR, which was already reported.
2560 if (tt != TOK_NAME)
2561 goto after_args;
2564 * Get the atom corresponding to the name from the token
2565 * stream; we're assured at this point that it's a valid
2566 * identifier.
2568 JSAtom *atom = ts.currentToken().t_atom;
2570 /* Check for a duplicate parameter name. */
2571 if (bindings.hasBinding(cx, atom)) {
2572 JSAutoByteString name;
2573 if (!js_AtomToPrintableString(cx, atom, &name)) {
2574 state = BAD;
2575 goto after_args;
2577 if (!ReportCompileErrorNumber(cx, &ts, NULL,
2578 JSREPORT_WARNING | JSREPORT_STRICT,
2579 JSMSG_DUPLICATE_FORMAL, name.ptr())) {
2580 state = BAD;
2581 goto after_args;
2585 uint16 dummy;
2586 if (!bindings.addArgument(cx, atom, &dummy)) {
2587 state = BAD;
2588 goto after_args;
2592 * Get the next token. Stop on end of stream. Otherwise
2593 * insist on a comma, get another name, and iterate.
2595 tt = ts.getToken();
2596 if (tt == TOK_EOF)
2597 break;
2598 if (tt != TOK_COMMA)
2599 goto after_args;
2600 tt = ts.getToken();
2604 state = OK;
2605 after_args:
2606 if (state == BAD_FORMAL && !ts.isError()) {
2608 * Report "malformed formal parameter" iff no illegal char or
2609 * similar scanner error was already reported.
2611 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2612 JSMSG_BAD_FORMAL);
2614 ts.close();
2615 JS_ARENA_RELEASE(&cx->tempPool, mark);
2616 if (state != OK)
2617 return JS_FALSE;
2620 JSString *str;
2621 if (argc) {
2622 str = js_ValueToString(cx, argv[argc - 1]);
2623 if (!str)
2624 return JS_FALSE;
2625 argv[argc - 1].setString(str);
2626 } else {
2627 str = cx->runtime->emptyString;
2630 size_t length = str->length();
2631 const jschar *chars = str->getChars(cx);
2632 if (!chars)
2633 return JS_FALSE;
2635 return Compiler::compileFunctionBody(cx, fun, principals, &bindings,
2636 chars, length, filename, lineno, cx->findVersion());
2639 namespace js {
2641 JS_FRIEND_API(bool)
2642 IsBuiltinFunctionConstructor(JSFunction *fun)
2644 return fun->maybeNative() == Function;
2647 const Shape *
2648 LookupInterpretedFunctionPrototype(JSContext *cx, JSObject *funobj)
2650 #ifdef DEBUG
2651 JSFunction *fun = funobj->getFunctionPrivate();
2652 JS_ASSERT(fun->isInterpreted());
2653 JS_ASSERT(!fun->isFunctionPrototype());
2654 JS_ASSERT(!funobj->isBoundFunction());
2655 #endif
2657 jsid id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
2658 const Shape *shape = funobj->nativeLookup(id);
2659 if (!shape) {
2660 if (!ResolveInterpretedFunctionPrototype(cx, funobj))
2661 return false;
2662 shape = funobj->nativeLookup(id);
2664 JS_ASSERT(!shape->configurable());
2665 JS_ASSERT(shape->isDataDescriptor());
2666 JS_ASSERT(shape->hasSlot());
2667 JS_ASSERT(!shape->isMethod());
2668 return shape;
2673 static JSBool
2674 ThrowTypeError(JSContext *cx, uintN argc, Value *vp)
2676 JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL,
2677 JSMSG_THROW_TYPE_ERROR);
2678 return false;
2681 JSObject *
2682 js_InitFunctionClass(JSContext *cx, JSObject *obj)
2684 JSObject *proto = js_InitClass(cx, obj, NULL, &js_FunctionClass, Function, 1,
2685 NULL, function_methods, NULL, NULL);
2686 if (!proto)
2687 return NULL;
2689 JSFunction *fun = js_NewFunction(cx, proto, NULL, 0, JSFUN_INTERPRETED, obj, NULL);
2690 if (!fun)
2691 return NULL;
2692 fun->flags |= JSFUN_PROTOTYPE;
2694 JSScript *script = JSScript::NewScript(cx, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, JSVERSION_DEFAULT);
2695 if (!script)
2696 return NULL;
2697 script->noScriptRval = true;
2698 script->code[0] = JSOP_STOP;
2699 script->code[1] = SRC_NULL;
2700 #ifdef CHECK_SCRIPT_OWNER
2701 script->owner = NULL;
2702 #endif
2703 fun->u.i.script = script;
2705 if (obj->isGlobal()) {
2706 /* ES5 13.2.3: Construct the unique [[ThrowTypeError]] function object. */
2707 JSObject *throwTypeError =
2708 js_NewFunction(cx, NULL, reinterpret_cast<Native>(ThrowTypeError), 0,
2709 0, obj, NULL);
2710 if (!throwTypeError)
2711 return NULL;
2713 JS_ALWAYS_TRUE(js_SetReservedSlot(cx, obj, JSRESERVED_GLOBAL_THROWTYPEERROR,
2714 ObjectValue(*throwTypeError)));
2717 return proto;
2720 JSFunction *
2721 js_NewFunction(JSContext *cx, JSObject *funobj, Native native, uintN nargs,
2722 uintN flags, JSObject *parent, JSAtom *atom)
2724 JSFunction *fun;
2726 if (funobj) {
2727 JS_ASSERT(funobj->isFunction());
2728 funobj->setParent(parent);
2729 } else {
2730 funobj = NewFunction(cx, parent);
2731 if (!funobj)
2732 return NULL;
2734 JS_ASSERT(!funobj->getPrivate());
2735 fun = (JSFunction *) funobj;
2737 /* Initialize all function members. */
2738 fun->nargs = uint16(nargs);
2739 fun->flags = flags & (JSFUN_FLAGS_MASK | JSFUN_KINDMASK | JSFUN_TRCINFO);
2740 if ((flags & JSFUN_KINDMASK) >= JSFUN_INTERPRETED) {
2741 JS_ASSERT(!native);
2742 JS_ASSERT(nargs == 0);
2743 fun->u.i.skipmin = 0;
2744 fun->u.i.wrapper = false;
2745 fun->u.i.script = NULL;
2746 } else {
2747 fun->u.n.clasp = NULL;
2748 if (flags & JSFUN_TRCINFO) {
2749 #ifdef JS_TRACER
2750 JSNativeTraceInfo *trcinfo =
2751 JS_FUNC_TO_DATA_PTR(JSNativeTraceInfo *, native);
2752 fun->u.n.native = (js::Native) trcinfo->native;
2753 fun->u.n.trcinfo = trcinfo;
2754 #else
2755 fun->u.n.trcinfo = NULL;
2756 #endif
2757 } else {
2758 fun->u.n.native = native;
2759 fun->u.n.trcinfo = NULL;
2761 JS_ASSERT(fun->u.n.native);
2763 fun->atom = atom;
2765 /* Set private to self to indicate non-cloned fully initialized function. */
2766 FUN_OBJECT(fun)->setPrivate(fun);
2767 return fun;
2770 JSObject * JS_FASTCALL
2771 js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent,
2772 JSObject *proto)
2774 JS_ASSERT(parent);
2775 JS_ASSERT(proto);
2777 JSObject *clone;
2778 if (cx->compartment == fun->compartment()) {
2780 * The cloned function object does not need the extra JSFunction members
2781 * beyond JSObject as it points to fun via the private slot.
2783 clone = NewNativeClassInstance(cx, &js_FunctionClass, proto, parent);
2784 if (!clone)
2785 return NULL;
2786 clone->setPrivate(fun);
2787 } else {
2789 * Across compartments we have to deep copy JSFunction and clone the
2790 * script (for interpreted functions).
2792 clone = NewFunction(cx, parent);
2793 if (!clone)
2794 return NULL;
2795 JSFunction *cfun = (JSFunction *) clone;
2796 cfun->nargs = fun->nargs;
2797 cfun->flags = fun->flags;
2798 cfun->u = fun->getFunctionPrivate()->u;
2799 cfun->atom = fun->atom;
2800 clone->setPrivate(cfun);
2801 if (cfun->isInterpreted()) {
2802 JSScript *script = cfun->u.i.script;
2803 JS_ASSERT(script);
2804 JS_ASSERT(script->compartment == fun->compartment());
2805 JS_ASSERT(script->compartment != cx->compartment);
2807 cfun->u.i.script = js_CloneScript(cx, script);
2808 if (!cfun->u.i.script)
2809 return NULL;
2810 #ifdef CHECK_SCRIPT_OWNER
2811 cfun->script()->owner = NULL;
2812 #endif
2813 js_CallNewScriptHook(cx, cfun->script(), cfun);
2816 return clone;
2819 #ifdef JS_TRACER
2820 JS_DEFINE_CALLINFO_4(extern, OBJECT, js_CloneFunctionObject, CONTEXT, FUNCTION, OBJECT, OBJECT, 0,
2821 nanojit::ACCSET_STORE_ANY)
2822 #endif
2825 * Create a new flat closure, but don't initialize the imported upvar
2826 * values. The tracer calls this function and then initializes the upvar
2827 * slots on trace.
2829 JSObject * JS_FASTCALL
2830 js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain)
2832 JS_ASSERT(fun->isFlatClosure());
2833 JS_ASSERT(JSScript::isValidOffset(fun->script()->upvarsOffset) ==
2834 fun->script()->bindings.hasUpvars());
2835 JS_ASSERT_IF(JSScript::isValidOffset(fun->script()->upvarsOffset),
2836 fun->script()->upvars()->length == fun->script()->bindings.countUpvars());
2838 JSObject *closure = CloneFunctionObject(cx, fun, scopeChain);
2839 if (!closure)
2840 return closure;
2842 uint32 nslots = fun->script()->bindings.countUpvars();
2843 if (nslots == 0)
2844 return closure;
2846 Value *upvars = (Value *) cx->malloc(nslots * sizeof(Value));
2847 if (!upvars)
2848 return NULL;
2850 closure->setFlatClosureUpvars(upvars);
2851 return closure;
2854 JS_DEFINE_CALLINFO_3(extern, OBJECT, js_AllocFlatClosure,
2855 CONTEXT, FUNCTION, OBJECT, 0, nanojit::ACCSET_STORE_ANY)
2857 JSObject *
2858 js_NewFlatClosure(JSContext *cx, JSFunction *fun, JSOp op, size_t oplen)
2861 * Flat closures cannot yet be partial, that is, all upvars must be copied,
2862 * or the closure won't be flattened. Therefore they do not need to search
2863 * enclosing scope objects via JSOP_NAME, etc.
2865 * FIXME: bug 545759 proposes to enable partial flat closures. Fixing this
2866 * bug requires a GetScopeChainFast call here, along with JS_REQUIRES_STACK
2867 * annotations on this function's prototype and definition.
2869 VOUCH_DOES_NOT_REQUIRE_STACK();
2870 JSObject *scopeChain = &cx->fp()->scopeChain();
2872 JSObject *closure = js_AllocFlatClosure(cx, fun, scopeChain);
2873 if (!closure || !fun->script()->bindings.hasUpvars())
2874 return closure;
2876 Value *upvars = closure->getFlatClosureUpvars();
2877 uintN level = fun->u.i.script->staticLevel;
2878 JSUpvarArray *uva = fun->script()->upvars();
2880 for (uint32 i = 0, n = uva->length; i < n; i++)
2881 upvars[i] = GetUpvar(cx, level, uva->vector[i]);
2883 return closure;
2886 JSObject *
2887 js_NewDebuggableFlatClosure(JSContext *cx, JSFunction *fun)
2889 JS_ASSERT(cx->fp()->fun()->flags & JSFUN_HEAVYWEIGHT);
2890 JS_ASSERT(!cx->fp()->fun()->optimizedClosure());
2891 JS_ASSERT(FUN_FLAT_CLOSURE(fun));
2893 return WrapEscapingClosure(cx, cx->fp(), fun);
2896 JSFunction *
2897 js_DefineFunction(JSContext *cx, JSObject *obj, jsid id, Native native,
2898 uintN nargs, uintN attrs)
2900 PropertyOp gop;
2901 StrictPropertyOp sop;
2902 JSFunction *fun;
2904 if (attrs & JSFUN_STUB_GSOPS) {
2906 * JSFUN_STUB_GSOPS is a request flag only, not stored in fun->flags or
2907 * the defined property's attributes. This allows us to encode another,
2908 * internal flag using the same bit, JSFUN_EXPR_CLOSURE -- see jsfun.h
2909 * for more on this.
2911 attrs &= ~JSFUN_STUB_GSOPS;
2912 gop = PropertyStub;
2913 sop = StrictPropertyStub;
2914 } else {
2915 gop = NULL;
2916 sop = NULL;
2920 * Historically, all objects have had a parent member as intrinsic scope
2921 * chain link. We want to move away from this universal parent, but JS
2922 * requires that function objects have something like parent (ES3 and ES5
2923 * call it the [[Scope]] internal property), to bake a particular static
2924 * scope environment into each function object.
2926 * All function objects thus have parent, including all native functions.
2927 * All native functions defined by the JS_DefineFunction* APIs are created
2928 * via the call below to js_NewFunction, which passes obj as the parent
2929 * parameter, and so binds fun's parent to obj using JSObject::setParent,
2930 * under js_NewFunction (in JSObject::init, called from NewObject -- see
2931 * jsobjinlines.h).
2933 * But JSObject::setParent sets the DELEGATE object flag on its receiver,
2934 * to mark the object as a proto or parent of another object. Such objects
2935 * may intervene in property lookups and scope chain searches, so require
2936 * special handling when caching lookup and search results (since such
2937 * intervening objects can in general grow shadowing properties later).
2939 * Thus using setParent prematurely flags certain objects, notably class
2940 * prototypes, so that defining native methods on them, where the method's
2941 * name (e.g., toString) is already bound on Object.prototype, triggers
2942 * shadowingShapeChange events and gratuitous shape regeneration.
2944 * To fix this longstanding bug, we set check whether obj is already a
2945 * delegate, and if not, then if js_NewFunction flagged obj as a delegate,
2946 * we clear the flag.
2948 * We thus rely on the fact that native functions (including indirect eval)
2949 * do not use the property cache or equivalent JIT techniques that require
2950 * this bit to be set on their parent-linked scope chain objects.
2952 * Note: we keep API compatibility by setting parent to obj for all native
2953 * function objects, even if obj->getGlobal() would suffice. This should be
2954 * revisited when parent is narrowed to exist only for function objects and
2955 * possibly a few prehistoric scope objects (e.g. event targets).
2957 * FIXME: bug 611190.
2959 bool wasDelegate = obj->isDelegate();
2961 fun = js_NewFunction(cx, NULL, native, nargs,
2962 attrs & (JSFUN_FLAGS_MASK | JSFUN_TRCINFO),
2963 obj,
2964 JSID_IS_ATOM(id) ? JSID_TO_ATOM(id) : NULL);
2965 if (!fun)
2966 return NULL;
2968 if (!wasDelegate && obj->isDelegate())
2969 obj->clearDelegate();
2971 if (!obj->defineProperty(cx, id, ObjectValue(*fun), gop, sop, attrs & ~JSFUN_FLAGS_MASK))
2972 return NULL;
2973 return fun;
2976 #if (JSV2F_CONSTRUCT & JSV2F_SEARCH_STACK)
2977 # error "JSINVOKE_CONSTRUCT and JSV2F_SEARCH_STACK are not disjoint!"
2978 #endif
2980 JSFunction *
2981 js_ValueToFunction(JSContext *cx, const Value *vp, uintN flags)
2983 JSObject *funobj;
2984 if (!IsFunctionObject(*vp, &funobj)) {
2985 js_ReportIsNotFunction(cx, vp, flags);
2986 return NULL;
2988 return GET_FUNCTION_PRIVATE(cx, funobj);
2991 JSObject *
2992 js_ValueToFunctionObject(JSContext *cx, Value *vp, uintN flags)
2994 JSObject *funobj;
2995 if (!IsFunctionObject(*vp, &funobj)) {
2996 js_ReportIsNotFunction(cx, vp, flags);
2997 return NULL;
3000 return funobj;
3003 JSObject *
3004 js_ValueToCallableObject(JSContext *cx, Value *vp, uintN flags)
3006 if (vp->isObject()) {
3007 JSObject *callable = &vp->toObject();
3008 if (callable->isCallable())
3009 return callable;
3012 js_ReportIsNotFunction(cx, vp, flags);
3013 return NULL;
3016 void
3017 js_ReportIsNotFunction(JSContext *cx, const Value *vp, uintN flags)
3019 const char *name = NULL, *source = NULL;
3020 AutoValueRooter tvr(cx);
3021 uintN error = (flags & JSV2F_CONSTRUCT) ? JSMSG_NOT_CONSTRUCTOR : JSMSG_NOT_FUNCTION;
3022 LeaveTrace(cx);
3025 * We try to the print the code that produced vp if vp is a value in the
3026 * most recent interpreted stack frame. Note that additional values, not
3027 * directly produced by the script, may have been pushed onto the frame's
3028 * expression stack (e.g. by pushInvokeArgs) thereby incrementing sp past
3029 * the depth simulated by ReconstructPCStack.
3031 * Conversely, values may have been popped from the stack in preparation
3032 * for a call (e.g., by SplatApplyArgs). Since we must pass an offset from
3033 * the top of the simulated stack to js_ReportValueError3, we do bounds
3034 * checking using the minimum of both the simulated and actual stack depth.
3036 ptrdiff_t spindex = 0;
3038 FrameRegsIter i(cx);
3039 while (!i.done() && !i.pc())
3040 ++i;
3042 if (!i.done()) {
3043 uintN depth = js_ReconstructStackDepth(cx, i.fp()->script(), i.pc());
3044 Value *simsp = i.fp()->base() + depth;
3045 if (i.fp()->base() <= vp && vp < Min(simsp, i.sp()))
3046 spindex = vp - simsp;
3049 if (!spindex)
3050 spindex = ((flags & JSV2F_SEARCH_STACK) ? JSDVG_SEARCH_STACK : JSDVG_IGNORE_STACK);
3052 js_ReportValueError3(cx, error, spindex, *vp, NULL, name, source);