1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sw=4 et tw=99:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
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/
11 : *
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.
16 : *
17 : * The Original Code is Mozilla Communicator client code, released
18 : * March 31, 1998.
19 : *
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.
24 : *
25 : * Contributor(s):
26 : *
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.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : /*
42 : * JS function support.
43 : */
44 : #include <string.h>
45 :
46 : #include "mozilla/Util.h"
47 :
48 : #include "jstypes.h"
49 : #include "jsutil.h"
50 : #include "jsapi.h"
51 : #include "jsarray.h"
52 : #include "jsatom.h"
53 : #include "jsbool.h"
54 : #include "jscntxt.h"
55 : #include "jsexn.h"
56 : #include "jsfun.h"
57 : #include "jsgc.h"
58 : #include "jsgcmark.h"
59 : #include "jsinterp.h"
60 : #include "jslock.h"
61 : #include "jsnum.h"
62 : #include "jsobj.h"
63 : #include "jsopcode.h"
64 : #include "jspropertytree.h"
65 : #include "jsproxy.h"
66 : #include "jsscope.h"
67 : #include "jsscript.h"
68 : #include "jsstr.h"
69 :
70 : #include "frontend/BytecodeCompiler.h"
71 : #include "frontend/BytecodeEmitter.h"
72 : #include "frontend/TokenStream.h"
73 : #include "vm/Debugger.h"
74 : #include "vm/MethodGuard.h"
75 : #include "vm/ScopeObject.h"
76 : #include "vm/Xdr.h"
77 :
78 : #if JS_HAS_GENERATORS
79 : # include "jsiter.h"
80 : #endif
81 :
82 : #ifdef JS_METHODJIT
83 : #include "methodjit/MethodJIT.h"
84 : #endif
85 :
86 : #include "jsatominlines.h"
87 : #include "jsfuninlines.h"
88 : #include "jsinferinlines.h"
89 : #include "jsobjinlines.h"
90 : #include "jsscriptinlines.h"
91 : #include "vm/ArgumentsObject-inl.h"
92 : #include "vm/ScopeObject-inl.h"
93 : #include "vm/Stack-inl.h"
94 :
95 : using namespace mozilla;
96 : using namespace js;
97 : using namespace js::gc;
98 : using namespace js::types;
99 :
100 : static JSBool
101 1910 : fun_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
102 : {
103 3820 : while (!obj->isFunction()) {
104 0 : obj = obj->getProto();
105 0 : if (!obj)
106 0 : return true;
107 : }
108 1910 : JSFunction *fun = obj->toFunction();
109 :
110 : /*
111 : * Mark the function's script as uninlineable, to expand any of its
112 : * frames on the stack before we go looking for them. This allows the
113 : * below walk to only check each explicit frame rather than needing to
114 : * check any calls that were inlined.
115 : */
116 1910 : if (fun->isInterpreted()) {
117 1910 : fun->script()->uninlineable = true;
118 1910 : MarkTypeObjectFlags(cx, fun, OBJECT_FLAG_UNINLINEABLE);
119 : }
120 :
121 : /* Set to early to null in case of error */
122 1910 : vp->setNull();
123 :
124 : /* Find fun's top-most activation record. */
125 1910 : StackFrame *fp = js_GetTopStackFrame(cx, FRAME_EXPAND_NONE);
126 2153 : for (; fp; fp = fp->prev()) {
127 2079 : if (!fp->isFunctionFrame() || fp->isEvalFrame())
128 81 : continue;
129 1998 : if (fp->callee().toFunction() == fun)
130 1836 : break;
131 : }
132 1910 : if (!fp)
133 74 : return true;
134 :
135 : #ifdef JS_METHODJIT
136 1836 : if (JSID_IS_ATOM(id, cx->runtime->atomState.callerAtom) && fp && fp->prev()) {
137 : /*
138 : * If the frame was called from within an inlined frame, mark the
139 : * innermost function as uninlineable to expand its frame and allow us
140 : * to recover its callee object.
141 : */
142 : JSInlinedSite *inlined;
143 1305 : jsbytecode *prevpc = fp->prev()->pcQuadratic(cx->stack, fp, &inlined);
144 1305 : if (inlined) {
145 0 : mjit::JITChunk *chunk = fp->prev()->jit()->chunk(prevpc);
146 0 : JSFunction *fun = chunk->inlineFrames()[inlined->inlineIndex].fun;
147 0 : fun->script()->uninlineable = true;
148 0 : MarkTypeObjectFlags(cx, fun, OBJECT_FLAG_UNINLINEABLE);
149 : }
150 : }
151 : #endif
152 :
153 1836 : if (JSID_IS_ATOM(id, cx->runtime->atomState.argumentsAtom)) {
154 : /* Warn if strict about f.arguments or equivalent unqualified uses. */
155 531 : if (!JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING | JSREPORT_STRICT, js_GetErrorMessage,
156 531 : NULL, JSMSG_DEPRECATED_USAGE, js_arguments_str)) {
157 0 : return false;
158 : }
159 :
160 531 : ArgumentsObject *argsobj = ArgumentsObject::createUnexpected(cx, fp);
161 531 : if (!argsobj)
162 0 : return false;
163 :
164 531 : *vp = ObjectValue(*argsobj);
165 531 : return true;
166 : }
167 :
168 1305 : if (JSID_IS_ATOM(id, cx->runtime->atomState.callerAtom)) {
169 1305 : if (!fp->prev())
170 0 : return true;
171 :
172 1305 : StackFrame *frame = fp->prev();
173 2610 : while (frame && frame->isDummyFrame())
174 0 : frame = frame->prev();
175 :
176 1305 : if (!frame || !frame->isFunctionFrame()) {
177 297 : JS_ASSERT(vp->isNull());
178 297 : return true;
179 : }
180 :
181 1008 : vp->setObject(frame->callee());
182 :
183 : /* Censor the caller if it is from another compartment. */
184 1008 : JSObject &caller = vp->toObject();
185 1008 : if (caller.compartment() != cx->compartment) {
186 0 : vp->setNull();
187 1008 : } else if (caller.isFunction()) {
188 1008 : JSFunction *callerFun = caller.toFunction();
189 1008 : if (callerFun->isInterpreted() && callerFun->inStrictMode()) {
190 : JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL,
191 0 : JSMSG_CALLER_IS_STRICT);
192 0 : return false;
193 : }
194 : }
195 :
196 1008 : return true;
197 : }
198 :
199 0 : JS_NOT_REACHED("fun_getProperty");
200 : return false;
201 : }
202 :
203 :
204 :
205 : /* NB: no sentinels at ends -- use ArrayLength to bound loops.
206 : * Properties censored into [[ThrowTypeError]] in strict mode. */
207 : static const uint16_t poisonPillProps[] = {
208 : ATOM_OFFSET(arguments),
209 : ATOM_OFFSET(caller),
210 : };
211 :
212 : static JSBool
213 258061 : fun_enumerate(JSContext *cx, JSObject *obj)
214 : {
215 258061 : JS_ASSERT(obj->isFunction());
216 :
217 516122 : RootObject root(cx, &obj);
218 :
219 : jsid id;
220 : bool found;
221 :
222 258061 : if (!obj->isBoundFunction()) {
223 258061 : id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
224 258061 : if (!obj->hasProperty(cx, id, &found, JSRESOLVE_QUALIFIED))
225 0 : return false;
226 : }
227 :
228 258061 : id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
229 258061 : if (!obj->hasProperty(cx, id, &found, JSRESOLVE_QUALIFIED))
230 0 : return false;
231 :
232 258061 : id = ATOM_TO_JSID(cx->runtime->atomState.nameAtom);
233 258061 : if (!obj->hasProperty(cx, id, &found, JSRESOLVE_QUALIFIED))
234 0 : return false;
235 :
236 774183 : for (unsigned i = 0; i < ArrayLength(poisonPillProps); i++) {
237 516122 : const uint16_t offset = poisonPillProps[i];
238 516122 : id = ATOM_TO_JSID(OFFSET_TO_ATOM(cx->runtime, offset));
239 516122 : if (!obj->hasProperty(cx, id, &found, JSRESOLVE_QUALIFIED))
240 0 : return false;
241 : }
242 :
243 258061 : return true;
244 : }
245 :
246 : static JSObject *
247 2862 : ResolveInterpretedFunctionPrototype(JSContext *cx, JSObject *obj)
248 : {
249 : #ifdef DEBUG
250 2862 : JSFunction *fun = obj->toFunction();
251 2862 : JS_ASSERT(fun->isInterpreted());
252 2862 : JS_ASSERT(!fun->isFunctionPrototype());
253 : #endif
254 :
255 : /*
256 : * Assert that fun is not a compiler-created function object, which
257 : * must never leak to script or embedding code and then be mutated.
258 : * Also assert that obj is not bound, per the ES5 15.3.4.5 ref above.
259 : */
260 2862 : JS_ASSERT(!IsInternalFunctionObject(obj));
261 2862 : JS_ASSERT(!obj->isBoundFunction());
262 :
263 : /*
264 : * Make the prototype object an instance of Object with the same parent
265 : * as the function object itself.
266 : */
267 2862 : JSObject *objProto = obj->global().getOrCreateObjectPrototype(cx);
268 2862 : if (!objProto)
269 0 : return NULL;
270 2862 : JSObject *proto = NewObjectWithGivenProto(cx, &ObjectClass, objProto, NULL);
271 2862 : if (!proto || !proto->setSingletonType(cx))
272 0 : return NULL;
273 :
274 : /*
275 : * Per ES5 15.3.5.2 a user-defined function's .prototype property is
276 : * initially non-configurable, non-enumerable, and writable. Per ES5 13.2
277 : * the prototype's .constructor property is configurable, non-enumerable,
278 : * and writable.
279 : */
280 5724 : if (!obj->defineProperty(cx, cx->runtime->atomState.classPrototypeAtom,
281 : ObjectValue(*proto), JS_PropertyStub, JS_StrictPropertyStub,
282 2862 : JSPROP_PERMANENT) ||
283 : !proto->defineProperty(cx, cx->runtime->atomState.constructorAtom,
284 2862 : ObjectValue(*obj), JS_PropertyStub, JS_StrictPropertyStub, 0))
285 : {
286 0 : return NULL;
287 : }
288 :
289 2862 : return proto;
290 : }
291 :
292 : static JSBool
293 1836282 : fun_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
294 : JSObject **objp)
295 : {
296 1836282 : if (!JSID_IS_ATOM(id))
297 1484 : return true;
298 :
299 3669596 : RootedVarFunction fun(cx);
300 1834798 : fun = obj->toFunction();
301 :
302 1834798 : if (JSID_IS_ATOM(id, cx->runtime->atomState.classPrototypeAtom)) {
303 : /*
304 : * Native or "built-in" functions do not have a .prototype property per
305 : * ECMA-262, or (Object.prototype, Function.prototype, etc.) have that
306 : * property created eagerly.
307 : *
308 : * ES5 15.3.4: the non-native function object named Function.prototype
309 : * does not have a .prototype property.
310 : *
311 : * ES5 15.3.4.5: bound functions don't have a prototype property. The
312 : * isNative() test covers this case because bound functions are native
313 : * functions by definition/construction.
314 : */
315 96101 : if (fun->isNative() || fun->isFunctionPrototype())
316 93239 : return true;
317 :
318 2862 : if (!ResolveInterpretedFunctionPrototype(cx, fun))
319 0 : return false;
320 2862 : *objp = fun;
321 2862 : return true;
322 : }
323 :
324 3239556 : if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom) ||
325 1500859 : JSID_IS_ATOM(id, cx->runtime->atomState.nameAtom)) {
326 474982 : JS_ASSERT(!IsInternalFunctionObject(obj));
327 :
328 : Value v;
329 474982 : if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom))
330 237838 : v.setInt32(fun->nargs);
331 : else
332 237144 : v.setString(fun->atom ? fun->atom : cx->runtime->emptyString);
333 :
334 474982 : if (!DefineNativeProperty(cx, fun, id, v, JS_PropertyStub, JS_StrictPropertyStub,
335 474982 : JSPROP_PERMANENT | JSPROP_READONLY, 0, 0)) {
336 0 : return false;
337 : }
338 474982 : *objp = fun;
339 474982 : return true;
340 : }
341 :
342 3080508 : for (unsigned i = 0; i < ArrayLength(poisonPillProps); i++) {
343 2290545 : const uint16_t offset = poisonPillProps[i];
344 :
345 2290545 : if (JSID_IS_ATOM(id, OFFSET_TO_ATOM(cx->runtime, offset))) {
346 473752 : JS_ASSERT(!IsInternalFunctionObject(fun));
347 :
348 : PropertyOp getter;
349 : StrictPropertyOp setter;
350 473752 : unsigned attrs = JSPROP_PERMANENT;
351 473752 : if (fun->isInterpreted() ? fun->inStrictMode() : fun->isBoundFunction()) {
352 0 : JSObject *throwTypeError = fun->global().getThrowTypeError();
353 :
354 0 : getter = CastAsPropertyOp(throwTypeError);
355 0 : setter = CastAsStrictPropertyOp(throwTypeError);
356 0 : attrs |= JSPROP_GETTER | JSPROP_SETTER;
357 : } else {
358 473752 : getter = fun_getProperty;
359 473752 : setter = JS_StrictPropertyStub;
360 : }
361 :
362 473752 : if (!DefineNativeProperty(cx, fun, id, UndefinedValue(), getter, setter,
363 473752 : attrs, 0, 0)) {
364 0 : return false;
365 : }
366 473752 : *objp = fun;
367 473752 : return true;
368 : }
369 : }
370 :
371 789963 : return true;
372 : }
373 :
374 : template<XDRMode mode>
375 : bool
376 224 : js::XDRInterpretedFunction(XDRState<mode> *xdr, JSObject **objp, JSScript *parentScript)
377 : {
378 : JSFunction *fun;
379 : JSAtom *atom;
380 : uint32_t firstword; /* flag telling whether fun->atom is non-null,
381 : plus for fun->u.i.skipmin, fun->u.i.wrapper,
382 : and 14 bits reserved for future use */
383 : uint32_t flagsword; /* word for argument count and fun->flags */
384 :
385 224 : JSContext *cx = xdr->cx();
386 : JSScript *script;
387 : if (mode == XDR_ENCODE) {
388 112 : fun = (*objp)->toFunction();
389 112 : if (!fun->isInterpreted()) {
390 0 : JSAutoByteString funNameBytes;
391 0 : if (const char *name = GetFunctionNameBytes(cx, fun, &funNameBytes)) {
392 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_SCRIPTED_FUNCTION,
393 : name);
394 : }
395 0 : return false;
396 : }
397 112 : firstword = !!fun->atom;
398 112 : flagsword = (fun->nargs << 16) | fun->flags;
399 112 : atom = fun->atom;
400 112 : script = fun->script();
401 : } else {
402 224 : RootedVarObject parent(cx, NULL);
403 112 : fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, parent, NULL);
404 112 : if (!fun)
405 0 : return false;
406 112 : if (!fun->clearParent(cx))
407 0 : return false;
408 112 : if (!fun->clearType(cx))
409 0 : return false;
410 112 : atom = NULL;
411 224 : script = NULL;
412 : }
413 :
414 224 : if (!xdr->codeUint32(&firstword))
415 0 : return false;
416 224 : if ((firstword & 1U) && !XDRAtom(xdr, &atom))
417 0 : return false;
418 224 : if (!xdr->codeUint32(&flagsword))
419 0 : return false;
420 :
421 224 : if (!XDRScript(xdr, &script, parentScript))
422 0 : return false;
423 :
424 : if (mode == XDR_DECODE) {
425 112 : fun->nargs = flagsword >> 16;
426 112 : JS_ASSERT((flagsword & JSFUN_KINDMASK) >= JSFUN_INTERPRETED);
427 112 : fun->flags = uint16_t(flagsword);
428 112 : fun->atom.init(atom);
429 112 : fun->initScript(script);
430 112 : if (!script->typeSetFunction(cx, fun))
431 0 : return false;
432 112 : JS_ASSERT(fun->nargs == fun->script()->bindings.countArgs());
433 112 : js_CallNewScriptHook(cx, fun->script(), fun);
434 112 : *objp = fun;
435 : }
436 :
437 224 : return true;
438 : }
439 :
440 : template bool
441 : js::XDRInterpretedFunction(XDRState<XDR_ENCODE> *xdr, JSObject **objp, JSScript *parentScript);
442 :
443 : template bool
444 : js::XDRInterpretedFunction(XDRState<XDR_DECODE> *xdr, JSObject **objp, JSScript *parentScript);
445 :
446 : /*
447 : * [[HasInstance]] internal method for Function objects: fetch the .prototype
448 : * property of its 'this' parameter, and walks the prototype chain of v (only
449 : * if v is an object) returning true if .prototype is found.
450 : */
451 : static JSBool
452 1050258 : fun_hasInstance(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp)
453 : {
454 2100516 : while (obj->isFunction()) {
455 1050258 : if (!obj->isBoundFunction())
456 1050258 : break;
457 0 : obj = obj->toFunction()->getBoundFunctionTarget();
458 : }
459 :
460 : Value pval;
461 1050258 : if (!obj->getProperty(cx, cx->runtime->atomState.classPrototypeAtom, &pval))
462 0 : return JS_FALSE;
463 :
464 1050258 : if (pval.isPrimitive()) {
465 : /*
466 : * Throw a runtime error if instanceof is called on a function that
467 : * has a non-object as its .prototype value.
468 : */
469 7 : js_ReportValueError(cx, JSMSG_BAD_PROTOTYPE, -1, ObjectValue(*obj), NULL);
470 7 : return JS_FALSE;
471 : }
472 :
473 1050251 : *bp = js_IsDelegate(cx, &pval.toObject(), *v);
474 1050251 : return JS_TRUE;
475 : }
476 :
477 : inline void
478 13223025 : JSFunction::trace(JSTracer *trc)
479 : {
480 13223025 : if (isExtended()) {
481 822111 : MarkValueRange(trc, ArrayLength(toExtended()->extendedSlots),
482 1644222 : toExtended()->extendedSlots, "nativeReserved");
483 : }
484 :
485 13223025 : if (atom)
486 11237169 : MarkString(trc, &atom, "atom");
487 :
488 13223025 : if (isInterpreted()) {
489 950866 : if (u.i.script_)
490 950866 : MarkScriptUnbarriered(trc, &u.i.script_, "script");
491 950866 : if (u.i.env_)
492 949955 : MarkObjectUnbarriered(trc, &u.i.env_, "fun_callscope");
493 : }
494 13223025 : }
495 :
496 : static void
497 13223025 : fun_trace(JSTracer *trc, JSObject *obj)
498 : {
499 13223025 : obj->toFunction()->trace(trc);
500 13223025 : }
501 :
502 : /*
503 : * Reserve two slots in all function objects for XPConnect. Note that this
504 : * does not bloat every instance, only those on which reserved slots are set,
505 : * and those on which ad-hoc properties are defined.
506 : */
507 : JS_FRIEND_DATA(Class) js::FunctionClass = {
508 : js_Function_str,
509 : JSCLASS_NEW_RESOLVE | JSCLASS_IMPLEMENTS_BARRIERS |
510 : JSCLASS_HAS_CACHED_PROTO(JSProto_Function),
511 : JS_PropertyStub, /* addProperty */
512 : JS_PropertyStub, /* delProperty */
513 : JS_PropertyStub, /* getProperty */
514 : JS_StrictPropertyStub, /* setProperty */
515 : fun_enumerate,
516 : (JSResolveOp)fun_resolve,
517 : JS_ConvertStub,
518 : NULL, /* finalize */
519 : NULL, /* checkAccess */
520 : NULL, /* call */
521 : NULL, /* construct */
522 : fun_hasInstance,
523 : fun_trace
524 : };
525 :
526 : JSString *
527 267039 : fun_toStringHelper(JSContext *cx, JSObject *obj, unsigned indent)
528 : {
529 267039 : if (!obj->isFunction()) {
530 9 : if (IsFunctionProxy(obj))
531 9 : return Proxy::fun_toString(cx, obj, indent);
532 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
533 : JSMSG_INCOMPATIBLE_PROTO,
534 : js_Function_str, js_toString_str,
535 0 : "object");
536 0 : return NULL;
537 : }
538 :
539 267030 : JSFunction *fun = obj->toFunction();
540 267030 : if (!fun)
541 0 : return NULL;
542 :
543 267030 : if (!indent && !cx->compartment->toSourceCache.empty()) {
544 255717 : ToSourceCache::Ptr p = cx->compartment->toSourceCache.ref().lookup(fun);
545 255717 : if (p)
546 255501 : return p->value;
547 : }
548 :
549 11529 : JSString *str = JS_DecompileFunction(cx, fun, indent);
550 11529 : if (!str)
551 0 : return NULL;
552 :
553 11529 : if (!indent) {
554 774 : Maybe<ToSourceCache> &lazy = cx->compartment->toSourceCache;
555 :
556 774 : if (lazy.empty()) {
557 558 : lazy.construct();
558 558 : if (!lazy.ref().init())
559 0 : return NULL;
560 : }
561 :
562 774 : if (!lazy.ref().put(fun, str))
563 0 : return NULL;
564 : }
565 :
566 11529 : return str;
567 : }
568 :
569 : static JSBool
570 256275 : fun_toString(JSContext *cx, unsigned argc, Value *vp)
571 : {
572 256275 : JS_ASSERT(IsFunctionObject(vp[0]));
573 256275 : uint32_t indent = 0;
574 :
575 256275 : if (argc != 0 && !ToUint32(cx, vp[2], &indent))
576 0 : return false;
577 :
578 256275 : JSObject *obj = ToObject(cx, &vp[1]);
579 256275 : if (!obj)
580 0 : return false;
581 :
582 256275 : JSString *str = fun_toStringHelper(cx, obj, indent);
583 256275 : if (!str)
584 0 : return false;
585 :
586 256275 : vp->setString(str);
587 256275 : return true;
588 : }
589 :
590 : #if JS_HAS_TOSOURCE
591 : static JSBool
592 10755 : fun_toSource(JSContext *cx, unsigned argc, Value *vp)
593 : {
594 10755 : JS_ASSERT(IsFunctionObject(vp[0]));
595 :
596 10755 : JSObject *obj = ToObject(cx, &vp[1]);
597 10755 : if (!obj)
598 0 : return false;
599 :
600 10755 : JSString *str = fun_toStringHelper(cx, obj, JS_DONT_PRETTY_PRINT);
601 10755 : if (!str)
602 0 : return false;
603 :
604 10755 : vp->setString(str);
605 10755 : return true;
606 : }
607 : #endif
608 :
609 : JSBool
610 38988 : js_fun_call(JSContext *cx, unsigned argc, Value *vp)
611 : {
612 38988 : Value fval = vp[1];
613 :
614 38988 : if (!js_IsCallable(fval)) {
615 18 : ReportIncompatibleMethod(cx, CallReceiverFromVp(vp), &FunctionClass);
616 18 : return false;
617 : }
618 :
619 38970 : Value *argv = vp + 2;
620 : Value thisv;
621 38970 : if (argc == 0) {
622 387 : thisv.setUndefined();
623 : } else {
624 38583 : thisv = argv[0];
625 :
626 38583 : argc--;
627 38583 : argv++;
628 : }
629 :
630 : /* Allocate stack space for fval, obj, and the args. */
631 77940 : InvokeArgsGuard args;
632 38970 : if (!cx->stack.pushInvokeArgs(cx, argc, &args))
633 0 : return JS_FALSE;
634 :
635 : /* Push fval, thisv, and the args. */
636 38970 : args.calleev() = fval;
637 38970 : args.thisv() = thisv;
638 38970 : PodCopy(args.array(), argv, argc);
639 :
640 38970 : bool ok = Invoke(cx, args);
641 38970 : *vp = args.rval();
642 38970 : return ok;
643 : }
644 :
645 : /* ES5 15.3.4.3 */
646 : JSBool
647 462207 : js_fun_apply(JSContext *cx, unsigned argc, Value *vp)
648 : {
649 : /* Step 1. */
650 462207 : Value fval = vp[1];
651 462207 : if (!js_IsCallable(fval)) {
652 8 : ReportIncompatibleMethod(cx, CallReceiverFromVp(vp), &FunctionClass);
653 8 : return false;
654 : }
655 :
656 : /* Step 2. */
657 462199 : if (argc < 2 || vp[3].isNullOrUndefined())
658 225 : return js_fun_call(cx, (argc > 0) ? 1 : 0, vp);
659 :
660 923948 : InvokeArgsGuard args;
661 461974 : if (vp[3].isMagic(JS_OPTIMIZED_ARGUMENTS)) {
662 : /*
663 : * Pretend we have been passed the 'arguments' object for the current
664 : * function and read actuals out of the frame.
665 : *
666 : * N.B. Changes here need to be propagated to stubs::SplatApplyArgs.
667 : */
668 : /* Steps 4-6. */
669 144060 : unsigned length = cx->fp()->numActualArgs();
670 144060 : JS_ASSERT(length <= StackSpace::ARGS_LENGTH_MAX);
671 :
672 144060 : if (!cx->stack.pushInvokeArgs(cx, length, &args))
673 0 : return false;
674 :
675 : /* Push fval, obj, and aobj's elements as args. */
676 144060 : args.calleev() = fval;
677 144060 : args.thisv() = vp[2];
678 :
679 : /* Steps 7-8. */
680 144060 : cx->fp()->forEachCanonicalActualArg(CopyTo(args.array()));
681 : } else {
682 : /* Step 3. */
683 317914 : if (!vp[3].isObject()) {
684 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_APPLY_ARGS, js_apply_str);
685 0 : return false;
686 : }
687 :
688 : /*
689 : * Steps 4-5 (note erratum removing steps originally numbered 5 and 7 in
690 : * original version of ES5).
691 : */
692 317914 : JSObject *aobj = &vp[3].toObject();
693 : uint32_t length;
694 317914 : if (!js_GetLengthProperty(cx, aobj, &length))
695 0 : return false;
696 :
697 : /* Step 6. */
698 317914 : if (length > StackSpace::ARGS_LENGTH_MAX) {
699 22 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TOO_MANY_FUN_APPLY_ARGS);
700 22 : return false;
701 : }
702 :
703 317892 : if (!cx->stack.pushInvokeArgs(cx, length, &args))
704 0 : return false;
705 :
706 : /* Push fval, obj, and aobj's elements as args. */
707 317892 : args.calleev() = fval;
708 317892 : args.thisv() = vp[2];
709 :
710 : /* Steps 7-8. */
711 317892 : if (!GetElements(cx, aobj, length, args.array()))
712 0 : return false;
713 : }
714 :
715 : /* Step 9. */
716 461952 : if (!Invoke(cx, args))
717 4016 : return false;
718 :
719 457936 : *vp = args.rval();
720 457936 : return true;
721 : }
722 :
723 : namespace js {
724 :
725 : JSBool
726 : CallOrConstructBoundFunction(JSContext *cx, unsigned argc, Value *vp);
727 :
728 : }
729 :
730 : static const uint32_t JSSLOT_BOUND_FUNCTION_THIS = 0;
731 : static const uint32_t JSSLOT_BOUND_FUNCTION_ARGS_COUNT = 1;
732 :
733 : static const uint32_t BOUND_FUNCTION_RESERVED_SLOTS = 2;
734 :
735 : inline bool
736 2235 : JSFunction::initBoundFunction(JSContext *cx, const Value &thisArg,
737 : const Value *args, unsigned argslen)
738 : {
739 2235 : JS_ASSERT(isFunction());
740 :
741 : /*
742 : * Convert to a dictionary to set the BOUND_FUNCTION flag and increase
743 : * the slot span to cover the arguments and additional slots for the 'this'
744 : * value and arguments count.
745 : */
746 2235 : if (!toDictionaryMode(cx))
747 0 : return false;
748 :
749 2235 : if (!setFlag(cx, BaseShape::BOUND_FUNCTION))
750 0 : return false;
751 :
752 2235 : if (!setSlotSpan(cx, BOUND_FUNCTION_RESERVED_SLOTS + argslen))
753 0 : return false;
754 :
755 2235 : setSlot(JSSLOT_BOUND_FUNCTION_THIS, thisArg);
756 2235 : setSlot(JSSLOT_BOUND_FUNCTION_ARGS_COUNT, PrivateUint32Value(argslen));
757 :
758 2235 : initSlotRange(BOUND_FUNCTION_RESERVED_SLOTS, args, argslen);
759 :
760 2235 : return true;
761 : }
762 :
763 : inline JSObject *
764 489 : JSFunction::getBoundFunctionTarget() const
765 : {
766 489 : JS_ASSERT(isFunction());
767 489 : JS_ASSERT(isBoundFunction());
768 :
769 : /* Bound functions abuse |parent| to store their target function. */
770 489 : return getParent();
771 : }
772 :
773 : inline const js::Value &
774 489 : JSFunction::getBoundFunctionThis() const
775 : {
776 489 : JS_ASSERT(isFunction());
777 489 : JS_ASSERT(isBoundFunction());
778 :
779 489 : return getSlot(JSSLOT_BOUND_FUNCTION_THIS);
780 : }
781 :
782 : inline const js::Value &
783 378 : JSFunction::getBoundFunctionArgument(unsigned which) const
784 : {
785 378 : JS_ASSERT(isFunction());
786 378 : JS_ASSERT(isBoundFunction());
787 378 : JS_ASSERT(which < getBoundFunctionArgumentCount());
788 :
789 378 : return getSlot(BOUND_FUNCTION_RESERVED_SLOTS + which);
790 : }
791 :
792 : inline size_t
793 867 : JSFunction::getBoundFunctionArgumentCount() const
794 : {
795 867 : JS_ASSERT(isFunction());
796 867 : JS_ASSERT(isBoundFunction());
797 :
798 867 : return getSlot(JSSLOT_BOUND_FUNCTION_ARGS_COUNT).toPrivateUint32();
799 : }
800 :
801 : namespace js {
802 :
803 : /* ES5 15.3.4.5.1 and 15.3.4.5.2. */
804 : JSBool
805 489 : CallOrConstructBoundFunction(JSContext *cx, unsigned argc, Value *vp)
806 : {
807 489 : JSFunction *fun = vp[0].toObject().toFunction();
808 489 : JS_ASSERT(fun->isBoundFunction());
809 :
810 489 : bool constructing = IsConstructing(vp);
811 :
812 : /* 15.3.4.5.1 step 1, 15.3.4.5.2 step 3. */
813 489 : unsigned argslen = fun->getBoundFunctionArgumentCount();
814 :
815 489 : if (argc + argslen > StackSpace::ARGS_LENGTH_MAX) {
816 0 : js_ReportAllocationOverflow(cx);
817 0 : return false;
818 : }
819 :
820 : /* 15.3.4.5.1 step 3, 15.3.4.5.2 step 1. */
821 489 : JSObject *target = fun->getBoundFunctionTarget();
822 :
823 : /* 15.3.4.5.1 step 2. */
824 489 : const Value &boundThis = fun->getBoundFunctionThis();
825 :
826 978 : InvokeArgsGuard args;
827 489 : if (!cx->stack.pushInvokeArgs(cx, argc + argslen, &args))
828 0 : return false;
829 :
830 : /* 15.3.4.5.1, 15.3.4.5.2 step 4. */
831 867 : for (unsigned i = 0; i < argslen; i++)
832 378 : args[i] = fun->getBoundFunctionArgument(i);
833 489 : PodCopy(args.array() + argslen, vp + 2, argc);
834 :
835 : /* 15.3.4.5.1, 15.3.4.5.2 step 5. */
836 489 : args.calleev().setObject(*target);
837 :
838 489 : if (!constructing)
839 273 : args.thisv() = boundThis;
840 :
841 489 : if (constructing ? !InvokeConstructor(cx, args) : !Invoke(cx, args))
842 9 : return false;
843 :
844 480 : *vp = args.rval();
845 480 : return true;
846 : }
847 :
848 : }
849 :
850 : #if JS_HAS_GENERATORS
851 : static JSBool
852 0 : fun_isGenerator(JSContext *cx, unsigned argc, Value *vp)
853 : {
854 : JSFunction *fun;
855 0 : if (!IsFunctionObject(vp[1], &fun)) {
856 0 : JS_SET_RVAL(cx, vp, BooleanValue(false));
857 0 : return true;
858 : }
859 :
860 0 : bool result = false;
861 0 : if (fun->isInterpreted()) {
862 0 : JSScript *script = fun->script();
863 0 : JS_ASSERT(script->length != 0);
864 0 : result = script->code[0] == JSOP_GENERATOR;
865 : }
866 :
867 0 : JS_SET_RVAL(cx, vp, BooleanValue(result));
868 0 : return true;
869 : }
870 : #endif
871 :
872 : /* ES5 15.3.4.5. */
873 : static JSBool
874 2234 : fun_bind(JSContext *cx, unsigned argc, Value *vp)
875 : {
876 2234 : CallArgs args = CallArgsFromVp(argc, vp);
877 :
878 : /* Step 1. */
879 2234 : Value &thisv = args.thisv();
880 :
881 : /* Step 2. */
882 2234 : if (!js_IsCallable(thisv)) {
883 0 : ReportIncompatibleMethod(cx, args, &FunctionClass);
884 0 : return false;
885 : }
886 :
887 4468 : RootedVarObject target(cx);
888 2234 : target = &thisv.toObject();
889 :
890 : /* Step 3. */
891 2234 : Value *boundArgs = NULL;
892 2234 : unsigned argslen = 0;
893 2234 : if (args.length() > 1) {
894 1854 : boundArgs = args.array() + 1;
895 1854 : argslen = args.length() - 1;
896 : }
897 :
898 : /* Steps 7-9. */
899 2234 : Value thisArg = args.length() >= 1 ? args[0] : UndefinedValue();
900 :
901 2234 : JSObject *boundFunction = js_fun_bind(cx, target, thisArg, boundArgs, argslen);
902 2234 : if (!boundFunction)
903 0 : return false;
904 :
905 : /* Step 22. */
906 2234 : args.rval().setObject(*boundFunction);
907 2234 : return true;
908 : }
909 :
910 : JSObject*
911 2235 : js_fun_bind(JSContext *cx, HandleObject target, Value thisArg,
912 : Value *boundArgs, unsigned argslen)
913 : {
914 : /* Steps 15-16. */
915 2235 : unsigned length = 0;
916 2235 : if (target->isFunction()) {
917 2235 : unsigned nargs = target->toFunction()->nargs;
918 2235 : if (nargs > argslen)
919 27 : length = nargs - argslen;
920 : }
921 :
922 : /* Step 4-6, 10-11. */
923 2235 : JSAtom *name = target->isFunction() ? target->toFunction()->atom.get() : NULL;
924 :
925 : JSObject *funobj =
926 : js_NewFunction(cx, NULL, CallOrConstructBoundFunction, length,
927 2235 : JSFUN_CONSTRUCTOR, target, name);
928 2235 : if (!funobj)
929 0 : return NULL;
930 :
931 : /* NB: Bound functions abuse |parent| to store their target. */
932 2235 : if (!funobj->setParent(cx, target))
933 0 : return NULL;
934 :
935 2235 : if (!funobj->toFunction()->initBoundFunction(cx, thisArg, boundArgs, argslen))
936 0 : return NULL;
937 :
938 : /* Steps 17, 19-21 are handled by fun_resolve. */
939 : /* Step 18 is the default for new functions. */
940 2235 : return funobj;
941 : }
942 :
943 : /*
944 : * Report "malformed formal parameter" iff no illegal char or similar scanner
945 : * error was already reported.
946 : */
947 : static bool
948 9 : OnBadFormal(JSContext *cx, TokenKind tt)
949 : {
950 9 : if (tt != TOK_ERROR)
951 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_FORMAL);
952 : else
953 9 : JS_ASSERT(cx->isExceptionPending());
954 9 : return false;
955 : }
956 :
957 : namespace js {
958 :
959 : JSFunctionSpec function_methods[] = {
960 : #if JS_HAS_TOSOURCE
961 : JS_FN(js_toSource_str, fun_toSource, 0,0),
962 : #endif
963 : JS_FN(js_toString_str, fun_toString, 0,0),
964 : JS_FN(js_apply_str, js_fun_apply, 2,0),
965 : JS_FN(js_call_str, js_fun_call, 1,0),
966 : JS_FN("bind", fun_bind, 1,0),
967 : #if JS_HAS_GENERATORS
968 : JS_FN("isGenerator", fun_isGenerator,0,0),
969 : #endif
970 : JS_FS_END
971 : };
972 :
973 : JSBool
974 10612 : Function(JSContext *cx, unsigned argc, Value *vp)
975 : {
976 10612 : CallArgs args = CallArgsFromVp(argc, vp);
977 :
978 : /* Block this call if security callbacks forbid it. */
979 21224 : RootedVar<GlobalObject*> global(cx);
980 10612 : global = &args.callee().global();
981 10612 : if (!global->isRuntimeCodeGenEnabled(cx)) {
982 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CSP_BLOCKED_FUNCTION);
983 0 : return false;
984 : }
985 :
986 21224 : Bindings bindings(cx);
987 :
988 : const char *filename;
989 : unsigned lineno;
990 : JSPrincipals *originPrincipals;
991 10612 : CurrentScriptFileLineOrigin(cx, &filename, &lineno, &originPrincipals);
992 10612 : JSPrincipals *principals = PrincipalsForCompiledCode(args, cx);
993 :
994 10612 : unsigned n = args.length() ? args.length() - 1 : 0;
995 10612 : if (n > 0) {
996 : /*
997 : * Collect the function-argument arguments into one string, separated
998 : * by commas, then make a tokenstream from that string, and scan it to
999 : * get the arguments. We need to throw the full scanner at the
1000 : * problem, because the argument string can legitimately contain
1001 : * comments and linefeeds. XXX It might be better to concatenate
1002 : * everything up into a function definition and pass it to the
1003 : * compiler, but doing it this way is less of a delta from the old
1004 : * code. See ECMA 15.3.2.1.
1005 : */
1006 4907 : size_t args_length = 0;
1007 9823 : for (unsigned i = 0; i < n; i++) {
1008 : /* Collect the lengths for all the function-argument arguments. */
1009 4916 : JSString *arg = ToString(cx, args[i]);
1010 4916 : if (!arg)
1011 0 : return false;
1012 4916 : args[i].setString(arg);
1013 :
1014 : /*
1015 : * Check for overflow. The < test works because the maximum
1016 : * JSString length fits in 2 fewer bits than size_t has.
1017 : */
1018 4916 : size_t old_args_length = args_length;
1019 4916 : args_length = old_args_length + arg->length();
1020 4916 : if (args_length < old_args_length) {
1021 0 : js_ReportAllocationOverflow(cx);
1022 0 : return false;
1023 : }
1024 : }
1025 :
1026 : /* Add 1 for each joining comma and check for overflow (two ways). */
1027 4907 : size_t old_args_length = args_length;
1028 4907 : args_length = old_args_length + n - 1;
1029 4907 : if (args_length < old_args_length ||
1030 : args_length >= ~(size_t)0 / sizeof(jschar)) {
1031 0 : js_ReportAllocationOverflow(cx);
1032 0 : return false;
1033 : }
1034 :
1035 : /*
1036 : * Allocate a string to hold the concatenated arguments, including room
1037 : * for a terminating 0. Mark cx->tempLifeAlloc for later release, to
1038 : * free collected_args and its tokenstream in one swoop.
1039 : */
1040 9814 : LifoAllocScope las(&cx->tempLifoAlloc());
1041 4907 : jschar *cp = cx->tempLifoAlloc().newArray<jschar>(args_length + 1);
1042 4907 : if (!cp) {
1043 0 : js_ReportOutOfMemory(cx);
1044 0 : return false;
1045 : }
1046 4907 : jschar *collected_args = cp;
1047 :
1048 : /*
1049 : * Concatenate the arguments into the new string, separated by commas.
1050 : */
1051 9823 : for (unsigned i = 0; i < n; i++) {
1052 4916 : JSString *arg = args[i].toString();
1053 4916 : size_t arg_length = arg->length();
1054 4916 : const jschar *arg_chars = arg->getChars(cx);
1055 4916 : if (!arg_chars)
1056 0 : return false;
1057 4916 : (void) js_strncpy(cp, arg_chars, arg_length);
1058 4916 : cp += arg_length;
1059 :
1060 : /* Add separating comma or terminating 0. */
1061 4916 : *cp++ = (i + 1 < n) ? ',' : 0;
1062 : }
1063 :
1064 : /* Initialize a tokenstream that reads from the given string. */
1065 9814 : TokenStream ts(cx, principals, originPrincipals);
1066 4907 : if (!ts.init(collected_args, args_length, filename, lineno, cx->findVersion()))
1067 0 : return false;
1068 :
1069 : /* The argument string may be empty or contain no tokens. */
1070 4907 : TokenKind tt = ts.getToken();
1071 4907 : if (tt != TOK_EOF) {
1072 18 : for (;;) {
1073 : /*
1074 : * Check that it's a name. This also implicitly guards against
1075 : * TOK_ERROR, which was already reported.
1076 : */
1077 4925 : if (tt != TOK_NAME)
1078 9 : return OnBadFormal(cx, tt);
1079 :
1080 : /* Check for a duplicate parameter name. */
1081 4916 : PropertyName *name = ts.currentToken().name();
1082 4916 : if (bindings.hasBinding(cx, name)) {
1083 0 : JSAutoByteString bytes;
1084 0 : if (!js_AtomToPrintableString(cx, name, &bytes))
1085 0 : return false;
1086 0 : if (!ReportCompileErrorNumber(cx, &ts, NULL,
1087 : JSREPORT_WARNING | JSREPORT_STRICT,
1088 0 : JSMSG_DUPLICATE_FORMAL, bytes.ptr()))
1089 : {
1090 0 : return false;
1091 : }
1092 : }
1093 :
1094 : uint16_t dummy;
1095 4916 : if (!bindings.addArgument(cx, name, &dummy))
1096 0 : return false;
1097 :
1098 : /*
1099 : * Get the next token. Stop on end of stream. Otherwise
1100 : * insist on a comma, get another name, and iterate.
1101 : */
1102 4916 : tt = ts.getToken();
1103 4916 : if (tt == TOK_EOF)
1104 : break;
1105 18 : if (tt != TOK_COMMA)
1106 0 : return OnBadFormal(cx, tt);
1107 18 : tt = ts.getToken();
1108 : }
1109 : }
1110 : }
1111 :
1112 21206 : JS::Anchor<JSString *> strAnchor(NULL);
1113 : const jschar *chars;
1114 : size_t length;
1115 :
1116 10603 : if (args.length()) {
1117 10396 : JSString *str = ToString(cx, args[args.length() - 1]);
1118 10396 : if (!str)
1119 0 : return false;
1120 10396 : strAnchor.set(str);
1121 10396 : chars = str->getChars(cx);
1122 10396 : length = str->length();
1123 : } else {
1124 207 : chars = cx->runtime->emptyString->chars();
1125 207 : length = 0;
1126 : }
1127 :
1128 : /*
1129 : * NB: (new Function) is not lexically closed by its caller, it's just an
1130 : * anonymous function in the top-level scope that its constructor inhabits.
1131 : * Thus 'var x = 42; f = new Function("return x"); print(f())' prints 42,
1132 : * and so would a call to f from another top-level's script or function.
1133 : */
1134 : JSFunction *fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_LAMBDA | JSFUN_INTERPRETED,
1135 10603 : global, cx->runtime->atomState.anonymousAtom);
1136 10603 : if (!fun)
1137 0 : return false;
1138 :
1139 : bool ok = frontend::CompileFunctionBody(cx, fun, principals, originPrincipals,
1140 : &bindings, chars, length, filename, lineno,
1141 10603 : cx->findVersion());
1142 10603 : args.rval().setObject(*fun);
1143 10603 : return ok;
1144 : }
1145 :
1146 : bool
1147 10612 : IsBuiltinFunctionConstructor(JSFunction *fun)
1148 : {
1149 10612 : return fun->maybeNative() == Function;
1150 : }
1151 :
1152 : const Shape *
1153 0 : LookupInterpretedFunctionPrototype(JSContext *cx, JSObject *funobj)
1154 : {
1155 : #ifdef DEBUG
1156 0 : JSFunction *fun = funobj->toFunction();
1157 0 : JS_ASSERT(fun->isInterpreted());
1158 0 : JS_ASSERT(!fun->isFunctionPrototype());
1159 0 : JS_ASSERT(!funobj->isBoundFunction());
1160 : #endif
1161 :
1162 0 : jsid id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
1163 0 : const Shape *shape = funobj->nativeLookup(cx, id);
1164 0 : if (!shape) {
1165 0 : if (!ResolveInterpretedFunctionPrototype(cx, funobj))
1166 0 : return NULL;
1167 0 : shape = funobj->nativeLookup(cx, id);
1168 : }
1169 0 : JS_ASSERT(!shape->configurable());
1170 0 : JS_ASSERT(shape->isDataDescriptor());
1171 0 : JS_ASSERT(shape->hasSlot());
1172 0 : return shape;
1173 : }
1174 :
1175 : } /* namespace js */
1176 :
1177 : JSFunction *
1178 7075280 : js_NewFunction(JSContext *cx, JSObject *funobj, Native native, unsigned nargs,
1179 : unsigned flags, HandleObject parent, JSAtom *atom, js::gc::AllocKind kind)
1180 : {
1181 7075280 : JS_ASSERT(kind == JSFunction::FinalizeKind || kind == JSFunction::ExtendedFinalizeKind);
1182 7075280 : JS_ASSERT(sizeof(JSFunction) <= gc::Arena::thingSize(JSFunction::FinalizeKind));
1183 7075280 : JS_ASSERT(sizeof(FunctionExtended) <= gc::Arena::thingSize(JSFunction::ExtendedFinalizeKind));
1184 :
1185 : JSFunction *fun;
1186 :
1187 7075280 : if (funobj) {
1188 70887 : JS_ASSERT(funobj->isFunction());
1189 70887 : JS_ASSERT(funobj->getParent() == parent);
1190 : } else {
1191 7004393 : funobj = NewObjectWithClassProto(cx, &FunctionClass, NULL, SkipScopeParent(parent), kind);
1192 7004393 : if (!funobj)
1193 0 : return NULL;
1194 : }
1195 7075280 : fun = static_cast<JSFunction *>(funobj);
1196 :
1197 : /* Initialize all function members. */
1198 7075280 : fun->nargs = uint16_t(nargs);
1199 7075280 : fun->flags = flags & (JSFUN_FLAGS_MASK | JSFUN_KINDMASK);
1200 7075280 : if ((flags & JSFUN_KINDMASK) >= JSFUN_INTERPRETED) {
1201 191130 : JS_ASSERT(!native);
1202 191130 : fun->script().init(NULL);
1203 191130 : fun->initEnvironment(parent);
1204 : } else {
1205 6884150 : fun->u.native = native;
1206 6884150 : JS_ASSERT(fun->u.native);
1207 : }
1208 7075280 : if (kind == JSFunction::ExtendedFinalizeKind) {
1209 623184 : fun->flags |= JSFUN_EXTENDED;
1210 623184 : fun->initializeExtended();
1211 : }
1212 7075280 : fun->atom.init(atom);
1213 :
1214 7075280 : if (native && !fun->setSingletonType(cx))
1215 0 : return NULL;
1216 :
1217 7075280 : return fun;
1218 : }
1219 :
1220 : JSFunction * JS_FASTCALL
1221 971430 : js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent,
1222 : JSObject *proto, gc::AllocKind kind)
1223 : {
1224 971430 : JS_ASSERT(parent);
1225 971430 : JS_ASSERT(proto);
1226 :
1227 971430 : JSObject *cloneobj = NewObjectWithClassProto(cx, &FunctionClass, NULL, SkipScopeParent(parent), kind);
1228 971430 : if (!cloneobj)
1229 0 : return NULL;
1230 971430 : JSFunction *clone = static_cast<JSFunction *>(cloneobj);
1231 :
1232 971430 : clone->nargs = fun->nargs;
1233 971430 : clone->flags = fun->flags & ~JSFUN_EXTENDED;
1234 971430 : if (fun->isInterpreted()) {
1235 971430 : clone->initScript(fun->script());
1236 971430 : clone->initEnvironment(parent);
1237 : } else {
1238 0 : clone->u.native = fun->native();
1239 : }
1240 971430 : clone->atom.init(fun->atom);
1241 :
1242 971430 : if (kind == JSFunction::ExtendedFinalizeKind) {
1243 0 : clone->flags |= JSFUN_EXTENDED;
1244 0 : clone->initializeExtended();
1245 : }
1246 :
1247 971430 : if (cx->compartment == fun->compartment()) {
1248 : /*
1249 : * We can use the same type as the original function provided that (a)
1250 : * its prototype is correct, and (b) its type is not a singleton. The
1251 : * first case will hold in all compileAndGo code, and the second case
1252 : * will have been caught by CloneFunctionObject coming from function
1253 : * definitions or read barriers, so will not get here.
1254 : */
1255 969061 : if (fun->getProto() == proto && !fun->hasSingletonType())
1256 968291 : clone->setType(fun->type());
1257 : } else {
1258 : /*
1259 : * Across compartments we have to clone the script for interpreted
1260 : * functions.
1261 : */
1262 2369 : if (clone->isInterpreted()) {
1263 2369 : JSScript *script = clone->script();
1264 2369 : JS_ASSERT(script);
1265 2369 : JS_ASSERT(script->compartment() == fun->compartment());
1266 2369 : JS_ASSERT(script->compartment() != cx->compartment);
1267 :
1268 2369 : clone->script().init(NULL);
1269 2369 : JSScript *cscript = CloneScript(cx, script);
1270 2369 : if (!cscript)
1271 0 : return NULL;
1272 :
1273 2369 : cscript->globalObject = &clone->global();
1274 2369 : clone->setScript(cscript);
1275 2369 : if (!cscript->typeSetFunction(cx, clone))
1276 0 : return NULL;
1277 :
1278 2369 : js_CallNewScriptHook(cx, clone->script(), clone);
1279 2369 : Debugger::onNewScript(cx, clone->script(), NULL);
1280 : }
1281 : }
1282 971430 : return clone;
1283 : }
1284 :
1285 : JSFunction *
1286 5600397 : js_DefineFunction(JSContext *cx, HandleObject obj, jsid id, Native native,
1287 : unsigned nargs, unsigned attrs, AllocKind kind)
1288 : {
1289 11200794 : RootId idRoot(cx, &id);
1290 :
1291 : PropertyOp gop;
1292 : StrictPropertyOp sop;
1293 :
1294 11200794 : RootedVarFunction fun(cx);
1295 :
1296 5600397 : if (attrs & JSFUN_STUB_GSOPS) {
1297 : /*
1298 : * JSFUN_STUB_GSOPS is a request flag only, not stored in fun->flags or
1299 : * the defined property's attributes. This allows us to encode another,
1300 : * internal flag using the same bit, JSFUN_EXPR_CLOSURE -- see jsfun.h
1301 : * for more on this.
1302 : */
1303 5505494 : attrs &= ~JSFUN_STUB_GSOPS;
1304 5505494 : gop = JS_PropertyStub;
1305 5505494 : sop = JS_StrictPropertyStub;
1306 : } else {
1307 94903 : gop = NULL;
1308 94903 : sop = NULL;
1309 : }
1310 :
1311 : fun = js_NewFunction(cx, NULL, native, nargs,
1312 : attrs & (JSFUN_FLAGS_MASK),
1313 : obj,
1314 5600397 : JSID_IS_ATOM(id) ? JSID_TO_ATOM(id) : NULL,
1315 5600397 : kind);
1316 5600397 : if (!fun)
1317 0 : return NULL;
1318 :
1319 5600397 : if (!obj->defineGeneric(cx, id, ObjectValue(*fun), gop, sop, attrs & ~JSFUN_FLAGS_MASK))
1320 0 : return NULL;
1321 :
1322 5600397 : return fun;
1323 : }
1324 :
1325 : JS_STATIC_ASSERT((JSV2F_CONSTRUCT & JSV2F_SEARCH_STACK) == 0);
1326 :
1327 : JSFunction *
1328 4519 : js_ValueToFunction(JSContext *cx, const Value *vp, unsigned flags)
1329 : {
1330 : JSFunction *fun;
1331 4519 : if (!IsFunctionObject(*vp, &fun)) {
1332 1809 : js_ReportIsNotFunction(cx, vp, flags);
1333 1809 : return NULL;
1334 : }
1335 2710 : return fun;
1336 : }
1337 :
1338 : JSObject *
1339 21188 : js_ValueToCallableObject(JSContext *cx, Value *vp, unsigned flags)
1340 : {
1341 21188 : if (vp->isObject()) {
1342 21188 : JSObject *callable = &vp->toObject();
1343 21188 : if (callable->isCallable())
1344 21179 : return callable;
1345 : }
1346 :
1347 9 : js_ReportIsNotFunction(cx, vp, flags);
1348 9 : return NULL;
1349 : }
1350 :
1351 : void
1352 2593 : js_ReportIsNotFunction(JSContext *cx, const Value *vp, unsigned flags)
1353 : {
1354 2593 : const char *name = NULL, *source = NULL;
1355 5186 : AutoValueRooter tvr(cx);
1356 2593 : unsigned error = (flags & JSV2F_CONSTRUCT) ? JSMSG_NOT_CONSTRUCTOR : JSMSG_NOT_FUNCTION;
1357 :
1358 : /*
1359 : * We try to the print the code that produced vp if vp is a value in the
1360 : * most recent interpreted stack frame. Note that additional values, not
1361 : * directly produced by the script, may have been pushed onto the frame's
1362 : * expression stack (e.g. by pushInvokeArgs) thereby incrementing sp past
1363 : * the depth simulated by ReconstructPCStack.
1364 : *
1365 : * Conversely, values may have been popped from the stack in preparation
1366 : * for a call (e.g., by SplatApplyArgs). Since we must pass an offset from
1367 : * the top of the simulated stack to js_ReportValueError3, we do bounds
1368 : * checking using the minimum of both the simulated and actual stack depth.
1369 : */
1370 2593 : ptrdiff_t spindex = 0;
1371 :
1372 2593 : FrameRegsIter i(cx);
1373 2593 : if (!i.done()) {
1374 2593 : unsigned depth = js_ReconstructStackDepth(cx, i.fp()->script(), i.pc());
1375 2593 : Value *simsp = i.fp()->base() + depth;
1376 2593 : if (i.fp()->base() <= vp && vp < Min(simsp, i.sp()))
1377 757 : spindex = vp - simsp;
1378 : }
1379 :
1380 2593 : if (!spindex)
1381 1836 : spindex = ((flags & JSV2F_SEARCH_STACK) ? JSDVG_SEARCH_STACK : JSDVG_IGNORE_STACK);
1382 :
1383 2593 : js_ReportValueError3(cx, error, spindex, *vp, NULL, name, source);
1384 2593 : }
|