home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / js / src / jsfun.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  26.5 KB  |  1,095 lines

  1. /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19. /*
  20.  * JS function support.
  21.  */
  22. #include <string.h>
  23. #include "prtypes.h"
  24. #include "prlog.h"
  25. #include "jsapi.h"
  26. #include "jsarray.h"
  27. #include "jsatom.h"
  28. #include "jscntxt.h"
  29. #include "jsconfig.h"
  30. #include "jsfun.h"
  31. #include "jsgc.h"
  32. #include "jsinterp.h"
  33. #include "jslock.h"
  34. #include "jsnum.h"
  35. #include "jsobj.h"
  36. #include "jsopcode.h"
  37. #include "jsparse.h"
  38. #include "jsscan.h"
  39. #include "jsscope.h"
  40. #include "jsscript.h"
  41. #include "jsstr.h"
  42.  
  43. static char js_arguments_str[] = "arguments";
  44. static char js_arity_str[]     = "arity";
  45. static char js_callee_str[]    = "callee";
  46. static char js_caller_str[]    = "caller";
  47.  
  48. #if JS_HAS_CALL_OBJECT
  49.  
  50. JSObject *
  51. js_GetCallObject(JSContext *cx, JSStackFrame *fp, JSObject *parent)
  52. {
  53.     JSObject *callobj, *funobj;
  54.  
  55.     PR_ASSERT(fp->fun);
  56.     callobj = fp->object;
  57.     if (callobj)
  58.     return callobj;
  59.     funobj = fp->fun->object;
  60.     if (!parent)
  61.     parent = OBJ_GET_PARENT(funobj);
  62.     callobj = js_NewObject(cx, &js_CallClass, NULL, parent);
  63.     if (!callobj || !JS_SetPrivate(cx, callobj, fp)) {
  64.     cx->newborn[GCX_OBJECT] = NULL;
  65.     return NULL;
  66.     }
  67.     fp->object = callobj;
  68.     if (fp->scopeChain == funobj)
  69.     fp->scopeChain = callobj;
  70.     return callobj;
  71. }
  72.  
  73. static JSBool
  74. call_enumerate(JSContext *cx, JSObject *obj);
  75.  
  76. JSBool
  77. js_PutCallObject(JSContext *cx, JSStackFrame *fp)
  78. {
  79.     JSObject *callobj;
  80.     JSBool ok;
  81.     jsval junk;
  82.  
  83.     /*
  84.      * Reuse call_enumerate here to reflect all actual args and vars into the
  85.      * call object from fp.
  86.      */
  87.     callobj = fp->object;
  88.     ok = call_enumerate(cx, callobj);
  89.  
  90.     /*
  91.      * Now get the prototype properties so we snapshot fp->fun, fp->down, and
  92.      * fp->argc before fp goes away.  But the predefined call.length will not
  93.      * override a formal arg or var named length -- the compiler redefines the
  94.      * name, getter, and setter.
  95.      */
  96.     ok &= JS_GetProperty(cx, callobj, js_callee_str, &junk);
  97.     ok &= JS_GetProperty(cx, callobj, js_caller_str, &junk);
  98.     ok &= JS_GetProperty(cx, callobj, js_length_str, &junk);
  99.  
  100.     /*
  101.      * Clear the private pointer to fp, which is about to go away (in Call).
  102.      * Do this last because the js_GetProperty calls above need to follow the
  103.      * private slot to find fp, in order to copy back args and locals.
  104.      */
  105.     ok &= JS_SetPrivate(cx, callobj, NULL);
  106.     return ok;
  107. }
  108.  
  109. JSBool
  110. js_GetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  111. {
  112.     JSBool ok;
  113.     JSStackFrame *fp;
  114.     JSSymbol *sym;
  115.  
  116.     ok = JS_TRUE;
  117.     fp = JS_GetPrivate(cx, obj);
  118.     if (fp && fp->script) {
  119.     JS_LOCK(cx);
  120.     for (sym = fp->script->vars; sym; sym = sym->next) {
  121.         if (sym_property(sym)->id == id) {
  122.         if (!js_GetProperty(cx, obj, sym_id(sym), vp))
  123.             ok = JS_FALSE;
  124.         break;
  125.         }
  126.     }
  127.     JS_UNLOCK(cx);
  128.     }
  129.     return ok;
  130. }
  131.  
  132. JSBool
  133. js_SetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  134. {
  135.     JSBool ok;
  136.     JSStackFrame *fp;
  137.     JSSymbol *sym;
  138.  
  139.     ok = JS_TRUE;
  140.     fp = JS_GetPrivate(cx, obj);
  141.     if (fp && fp->script) {
  142.     JS_LOCK(cx);
  143.     for (sym = fp->script->vars; sym; sym = sym->next) {
  144.         if (sym_property(sym)->id == id) {
  145.         if (!js_SetProperty(cx, obj, sym_id(sym), vp))
  146.             ok = JS_FALSE;
  147.         break;
  148.         }
  149.     }
  150.     JS_UNLOCK(cx);
  151.     }
  152.     return ok;
  153. }
  154.  
  155. static JSBool
  156. Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  157. {
  158.     return JS_TRUE;
  159. }
  160.  
  161. enum call_tinyid {
  162.     CALL_ARGUMENTS = -1,
  163.     CALL_CALLEE    = -2,
  164.     CALL_CALLER    = -3,
  165.     CALL_LENGTH    = -4
  166. };
  167.  
  168. static JSPropertySpec call_props[] = {
  169.     {js_arguments_str,  CALL_ARGUMENTS, JSPROP_READONLY},
  170.     {js_callee_str,     CALL_CALLEE,    JSPROP_READONLY},
  171.     {js_caller_str,     CALL_CALLER,    JSPROP_READONLY},
  172.     {js_length_str,     CALL_LENGTH,    JSPROP_READONLY},
  173.     {0}
  174. };
  175.  
  176. #define IS_CALL_TINYID(slot)    (CALL_LENGTH <= slot && slot <= CALL_ARGUMENTS)
  177. #define CALL_TINYID_NAME(slot)  call_props[-(slot)-1].name
  178.  
  179. static JSBool
  180. call_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  181. {
  182.     JSStackFrame *fp;
  183.     JSObject *callobj;
  184.     jsint slot;
  185.  
  186.     if (!JSVAL_IS_INT(id))
  187.     return JS_TRUE;
  188.  
  189.     fp = JS_GetPrivate(cx, obj);
  190.     slot = JSVAL_TO_INT(id);
  191.  
  192.     /* XXX don't pollute call scope with unqualified call property names */
  193.     if (fp &&
  194.     fp->pc &&
  195.     (js_CodeSpec[*fp->pc].format & JOF_MODEMASK) == JOF_NAME &&
  196.     IS_CALL_TINYID(slot)) {
  197.     return JS_GetProperty(cx, fp->fun->object, CALL_TINYID_NAME(slot), vp);
  198.     }
  199.  
  200.     switch (slot) {
  201.       case CALL_ARGUMENTS:
  202.     *vp = OBJECT_TO_JSVAL(obj);
  203.     break;
  204.  
  205.       case CALL_CALLEE:
  206.     if (fp)
  207.         *vp = OBJECT_TO_JSVAL(fp->fun->object);
  208.     break;
  209.  
  210.       case CALL_CALLER:
  211.     if (fp && fp->down && fp->down->fun) {
  212.         callobj = js_GetCallObject(cx, fp->down, NULL);
  213.         if (!callobj)
  214.         return JS_FALSE;
  215.         *vp = OBJECT_TO_JSVAL(callobj);
  216.     }
  217.     break;
  218.  
  219.       case CALL_LENGTH:
  220.     if (fp)
  221.         *vp = INT_TO_JSVAL((jsint)fp->argc);
  222.     break;
  223.  
  224.       default:
  225.     if (fp && (uintN)slot < fp->argc)
  226.         *vp = fp->argv[slot];
  227.     break;
  228.     }
  229.     return JS_TRUE;
  230. }
  231.  
  232. static JSBool
  233. call_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  234. {
  235.     JSStackFrame *fp;
  236.     jsint slot;
  237.  
  238.     if (!JSVAL_IS_INT(id))
  239.     return JS_TRUE;
  240.  
  241.     fp = JS_GetPrivate(cx, obj);
  242.     if (fp) {
  243.     slot = JSVAL_TO_INT(id);
  244.     if ((uintN)slot < fp->argc)
  245.         fp->argv[slot] = *vp;
  246.     }
  247.     return JS_TRUE;
  248. }
  249.  
  250. static JSBool
  251. call_getVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  252. {
  253.     JSStackFrame *fp;
  254.  
  255.     PR_ASSERT(JSVAL_IS_INT(id));
  256.     fp = JS_GetPrivate(cx, obj);
  257.     if (fp) {
  258.     /* XXX no jsint slot commoning here to avoid MSVC1.52 crashes */
  259.     if ((uintN)JSVAL_TO_INT(id) < fp->nvars)
  260.         *vp = fp->vars[JSVAL_TO_INT(id)];
  261.     }
  262.     return JS_TRUE;
  263. }
  264.  
  265. static JSBool
  266. call_setVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  267. {
  268.     JSStackFrame *fp;
  269.  
  270.     PR_ASSERT(JSVAL_IS_INT(id));
  271.     fp = JS_GetPrivate(cx, obj);
  272.     if (fp) {
  273.     /* XXX jsint slot is block-local here to avoid MSVC1.52 crashes */
  274.     jsint slot = JSVAL_TO_INT(id);
  275.     if ((uintN)slot < fp->nvars)
  276.         fp->vars[slot] = *vp;
  277.     }
  278.     return JS_TRUE;
  279. }
  280.  
  281. static JSBool
  282. call_reflectArgument(JSContext *cx, JSObject *obj, JSStackFrame *fp,
  283.              JSSymbol *sym)
  284. {
  285.     uintN slot;
  286.     JSProperty *prop;
  287.     JSScope *scope;
  288.     JSBool ok;
  289.  
  290.     JS_LOCK(cx);
  291.     slot = (uintN)JSVAL_TO_INT(sym_property(sym)->id);
  292.     prop = js_DefineProperty(cx, obj, (jsval)sym_atom(sym),
  293.                  (slot < fp->argc) ? fp->argv[slot] : JSVAL_VOID,
  294.                  NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT);
  295.     if (!prop) {
  296.     ok = JS_FALSE;
  297.     } else {
  298.     prop->id = INT_TO_JSVAL(slot);
  299.     scope = (JSScope *)obj->map;
  300.     ok = (scope->ops->add(cx, scope, INT_TO_JSVAL(slot), prop) != NULL);
  301.     }
  302.     JS_UNLOCK(cx);
  303.     return ok;
  304. }
  305.  
  306. static JSBool
  307. call_reflectVariable(JSContext *cx, JSObject *obj, JSStackFrame *fp,
  308.              JSSymbol *sym)
  309. {
  310.     uintN slot;
  311.     JSProperty *prop;
  312.     JSBool ok;
  313.  
  314.     slot = (uintN)JSVAL_TO_INT(sym_property(sym)->id);
  315.     JS_LOCK(cx);
  316.     prop = js_DefineProperty(cx, obj, (jsval)sym_atom(sym),
  317.                  (slot < fp->nvars) ? fp->vars[slot] : JSVAL_VOID,
  318.                  call_getVariable, call_setVariable,
  319.                  JSPROP_PERMANENT);
  320.     ok = (prop != NULL);
  321.     if (ok)
  322.     prop->id = INT_TO_JSVAL(slot);
  323.     JS_UNLOCK(cx);
  324.     return ok;
  325. }
  326.  
  327. static JSBool
  328. call_enumerate(JSContext *cx, JSObject *obj)
  329. {
  330.     JSStackFrame *fp;
  331.     JSFunction *fun;
  332.     JSSymbol *sym;
  333.     JSBool ok;
  334.     JSProperty *prop;
  335.     uintN slot;
  336.  
  337.     fp = JS_GetPrivate(cx, obj);
  338.     if (!fp)
  339.     return JS_TRUE;
  340.     fun = fp->fun;
  341.     if (!fun->script)
  342.     return JS_TRUE;
  343.  
  344.     ok = JS_TRUE;
  345.     JS_LOCK(cx);
  346.  
  347.     /* First, reflect actuals that match formal arguments. */
  348.     if (fun->script) {
  349.     for (sym = fun->script->args; sym; sym = sym->next) {
  350.         ok = js_LookupProperty(cx, obj, sym_id(sym), &obj, &prop);
  351.         if (!ok)
  352.         goto out;
  353.     }
  354.     }
  355.  
  356.     /* Next, copy any actual arguments that lacked formals. */
  357.     for (slot = fun->nargs; slot < fp->argc; slot++) {
  358.     if (!js_SetProperty(cx, obj, INT_TO_JSVAL((jsint)slot),
  359.                 &fp->argv[slot])) {
  360.         ok = JS_FALSE;
  361.         goto out;
  362.     }
  363.     }
  364.  
  365.     /* Finally, reflect variables. */
  366.     if (fun->script) {
  367.     for (sym = fun->script->vars; sym; sym = sym->next) {
  368.         ok = js_LookupProperty(cx, obj, sym_id(sym), &obj, &prop);
  369.         if (!ok)
  370.         goto out;
  371.     }
  372.     }
  373.  
  374. out:
  375.     JS_UNLOCK(cx);
  376.     return ok;
  377. }
  378.  
  379. static JSBool
  380. call_resolve(JSContext *cx, JSObject *obj, jsval id, JSObject **objp)
  381. {
  382.     JSStackFrame *fp;
  383.     JSString *str;
  384.     JSAtom *atom;
  385.     JSBool ok;
  386.     JSProperty *prop;
  387.     JSSymbol *sym;
  388.  
  389.     fp = JS_GetPrivate(cx, obj);
  390.     if (!fp)
  391.     return JS_TRUE;
  392.  
  393.     if (!JSVAL_IS_STRING(id))
  394.     return JS_TRUE;
  395.  
  396.     str = JSVAL_TO_STRING(id);
  397.     JS_LOCK(cx);
  398.     atom = js_AtomizeString(cx, str, 0);
  399.     if (!atom) {
  400.     ok = JS_FALSE;
  401.     } else {
  402.     ok = js_LookupProperty(cx, fp->fun->object, (jsval)atom, &obj, &prop);
  403.     js_DropAtom(cx, atom);
  404.     }
  405.     JS_UNLOCK(cx);
  406.     if (!ok)
  407.     return JS_FALSE;
  408.  
  409.     if (prop) {
  410.     sym = prop->symbols;
  411.     if (prop->getter == js_GetArgument) {
  412.         if (!call_reflectArgument(cx, obj, fp, sym))
  413.         return JS_FALSE;
  414.         *objp = obj;
  415.     } else if (prop->getter == js_GetLocalVariable) {
  416.         if (!call_reflectVariable(cx, obj, fp, sym))
  417.         return JS_FALSE;
  418.         *objp = obj;
  419.     }
  420.     }
  421.     return JS_TRUE;
  422. }
  423.  
  424. static JSBool
  425. call_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
  426. {
  427.     JSStackFrame *fp;
  428.  
  429.     if (type == JSTYPE_FUNCTION) {
  430.     fp = JS_GetPrivate(cx, obj);
  431.     if (fp)
  432.         *vp = OBJECT_TO_JSVAL(fp->fun->object);
  433.     }
  434.     return JS_TRUE;
  435. }
  436.  
  437. JSClass js_CallClass = {
  438.     "Call",
  439.     JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE,
  440.     JS_PropertyStub,  JS_PropertyStub,
  441.     call_getProperty, call_setProperty,
  442.     call_enumerate,   (JSResolveOp)call_resolve,
  443.     call_convert,     JS_FinalizeStub
  444. };
  445.  
  446. /* Forward declaration. */
  447. static JSBool
  448. fun_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
  449.          jsval *rval);
  450.  
  451. static JSBool
  452. call_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
  453.           jsval *rval)
  454. {
  455.     JSStackFrame *fp;
  456.  
  457.     if (!JS_InstanceOf(cx, obj, &js_CallClass, argv))
  458.     return JS_FALSE;
  459.     fp = JS_GetPrivate(cx, obj);
  460.     if (fp)
  461.     return fun_toString(cx, fp->fun->object, argc, argv, rval);
  462.     return js_obj_toString(cx, obj, argc, argv, rval);
  463. }
  464.  
  465. static JSBool
  466. call_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
  467.          jsval *rval)
  468. {
  469.     if (!JS_InstanceOf(cx, obj, &js_CallClass, argv))
  470.     return JS_FALSE;
  471.     return JS_GetProperty(cx, obj, js_callee_str, rval);
  472. }
  473.  
  474. static JSFunctionSpec call_methods[] = {
  475.     {js_toString_str,    call_toString,    1},
  476.     {js_valueOf_str,    call_valueOf,    0},
  477.     {0}
  478. };
  479.  
  480. #endif /* !JS_HAS_CALL_OBJECT */
  481.  
  482. #if JS_HAS_LEXICAL_CLOSURE
  483. static JSBool
  484. Closure(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  485. {
  486.     JSStackFrame *caller;
  487.     JSObject *varParent, *closureParent;
  488.     JSFunction *fun;
  489.  
  490.     if (!JS_InstanceOf(cx, obj, &js_ClosureClass, argv))
  491.     return JS_FALSE;
  492.     if (!(caller = cx->fp->down) || !caller->scopeChain)
  493.     return JS_TRUE;
  494.  
  495.     JS_LOCK_VOID(cx, varParent = js_FindVariableScope(cx, &fun));
  496.     if (!varParent)
  497.     return JS_FALSE;
  498.  
  499.     closureParent = caller->scopeChain;
  500.     if (argc != 0) {
  501.     fun = JS_ValueToFunction(cx, argv[0]);
  502.     if (!fun)
  503.         return JS_FALSE;
  504.     OBJ_SET_PROTO(obj, fun->object);
  505.     if (argc > 1) {
  506.         if (!JS_ValueToObject(cx, argv[1], &closureParent))
  507.         return JS_FALSE;
  508.     }
  509.     }
  510.     OBJ_SET_PARENT(obj, closureParent);
  511.  
  512.     /* Make sure constructor is not inherited from fun->object. */
  513.     if (!js_DefineProperty(cx, obj,
  514.                (jsval)cx->runtime->atomState.constructorAtom,
  515.                argv[-2], NULL, NULL,
  516.                JSPROP_READONLY | JSPROP_PERMANENT)) {
  517.     return JS_FALSE;
  518.     }
  519.     return JS_TRUE;
  520. }
  521.  
  522. static JSBool
  523. closure_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
  524. {
  525.     JSObject *proto;
  526.  
  527.     if (type == JSTYPE_FUNCTION) {
  528.     proto = JS_GetPrototype(cx, obj);
  529.     if (proto) {
  530.         PR_ASSERT(proto->map->clasp == &js_FunctionClass);
  531.         *vp = OBJECT_TO_JSVAL(proto);
  532.     }
  533.     }
  534.     return JS_TRUE;
  535. }
  536.  
  537. JSClass js_ClosureClass = {
  538.     "Closure",
  539.     0,
  540.     JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
  541.     JS_EnumerateStub, JS_ResolveStub,   closure_convert,  JS_FinalizeStub
  542. };
  543. #endif /* JS_HAS_LEXICAL_CLOSURE */
  544.  
  545. enum fun_tinyid {
  546.     FUN_ARGUMENTS = -1,         /* actual arguments for this call to fun */
  547.     FUN_CALLER    = -2,         /* bogus, the function that called fun */
  548.     FUN_ARITY     = -3          /* number of formals if inactive, else argc */
  549. };
  550.  
  551. static JSPropertySpec function_props[] = {
  552.     {js_arguments_str,  FUN_ARGUMENTS,  JSPROP_READONLY | JSPROP_PERMANENT},
  553.     {js_caller_str,     FUN_CALLER,     JSPROP_READONLY | JSPROP_PERMANENT},
  554.     {js_arity_str,      FUN_ARITY,      JSPROP_READONLY | JSPROP_PERMANENT},
  555.     {0}
  556. };
  557.  
  558. static JSBool
  559. fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  560. {
  561.     JSFunction *fun;
  562.     JSStackFrame *fp, *bestfp;
  563.     jsint slot;
  564. #if defined XP_PC && defined _MSC_VER &&_MSC_VER <= 800
  565.     /* MSVC1.5 coredumps */
  566.     jsval bogus = *vp;
  567. #endif
  568.  
  569.     if (!JSVAL_IS_INT(id))
  570.     return JS_TRUE;
  571.  
  572.     fun = JS_GetPrivate(cx, obj);
  573.     if (!fun)
  574.     return JS_TRUE;
  575.  
  576.     /* Find fun's top-most frame, or the top-most non-native frame. */
  577.     bestfp = NULL;
  578.     for (fp = cx->fp; fp && fp->fun && fp->fun != fun; fp = fp->down) {
  579.     if (fp->fun->call)
  580.         continue;
  581.     if (!bestfp)
  582.         bestfp = fp;
  583.     }
  584.  
  585.     /* Use bestfp only if this property reference is unqualified. */
  586.     if ((!fp || !fp->fun) &&
  587.     bestfp &&
  588.     bestfp->pc &&
  589.     (js_CodeSpec[*bestfp->pc].format & JOF_MODEMASK) == JOF_NAME) {
  590.     fp = bestfp;
  591.     }
  592.  
  593.     slot = (jsint)JSVAL_TO_INT(id);
  594.     switch (slot) {
  595. #if JS_HAS_CALL_OBJECT
  596.       case FUN_ARGUMENTS:
  597.     if (fp && fp->fun) {
  598.         JSObject *callobj = js_GetCallObject(cx, fp, NULL);
  599.         if (!callobj)
  600.         return JS_FALSE;
  601.         *vp = OBJECT_TO_JSVAL(callobj);
  602.     } else {
  603.         *vp = JSVAL_NULL;
  604.     }
  605.     break;
  606. #else  /* !JS_HAS_CALL_OBJECT */
  607.       case FUN_ARGUMENTS:
  608.     *vp = OBJECT_TO_JSVAL(fp ? obj : NULL);
  609.     break;
  610. #endif /* !JS_HAS_CALL_OBJECT */
  611.  
  612.       case FUN_CALLER:
  613.     if (fp && fp->down && fp->down->fun)
  614.         *vp = OBJECT_TO_JSVAL(fp->down->fun->object);
  615.     else
  616.         *vp = JSVAL_NULL;
  617.     break;
  618.  
  619.       case FUN_ARITY:
  620.     if (cx->version < JSVERSION_1_2)
  621.         *vp = INT_TO_JSVAL((jsint)(fp && fp->fun ? fp->argc : fun->nargs));
  622.     else
  623.         *vp = INT_TO_JSVAL((jsint)fun->nargs);
  624.     break;
  625.  
  626.       default:
  627.     /* XXX fun[0] and fun.arguments[0] are equivalent. */
  628.     if (fp && fp->fun && (uintN)slot < fp->argc)
  629. #if defined XP_PC && defined _MSC_VER &&_MSC_VER <= 800
  630.       /* MSVC1.5 coredumps */
  631.       if (bogus == *vp)
  632. #endif
  633.         *vp = fp->argv[slot];
  634.     break;
  635.     }
  636.  
  637.     return JS_TRUE;
  638. }
  639.  
  640. static JSBool
  641. fun_resolve(JSContext *cx, JSObject *obj, jsval id, JSObject **objp)
  642. {
  643.     JSFunction *fun;
  644.     JSString *str;
  645.  
  646.     if (!JSVAL_IS_STRING(id))
  647.     return JS_TRUE;
  648.  
  649.     fun = JS_GetPrivate(cx, obj);
  650.     if (!fun)
  651.     return JS_TRUE;
  652.  
  653.     str = JSVAL_TO_STRING(id);
  654.     if (str == ATOM_TO_STRING(cx->runtime->atomState.classPrototypeAtom)) {
  655.     JSObject *proto;
  656.  
  657.     proto = js_NewObject(cx, &js_ObjectClass, NULL, NULL);
  658.     if (!proto)
  659.         return JS_FALSE;
  660.     if (!js_SetClassPrototype(cx, fun, proto)) {
  661.         cx->newborn[GCX_OBJECT] = NULL;
  662.         return JS_FALSE;
  663.     }
  664.     *objp = obj;
  665.     return JS_TRUE;
  666.     }
  667.  
  668.     return JS_TRUE;
  669. }
  670.  
  671. static JSBool
  672. fun_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
  673. {
  674.     switch (type) {
  675.       case JSTYPE_OBJECT:
  676.       case JSTYPE_FUNCTION:
  677.     *vp = OBJECT_TO_JSVAL(obj);
  678.     break;
  679.       default:;
  680.     }
  681.     return JS_TRUE;
  682. }
  683.  
  684. static void
  685. fun_finalize(JSContext *cx, JSObject *obj)
  686. {
  687.     JSFunction *fun;
  688.  
  689.     fun = JS_GetPrivate(cx, obj);
  690.     if (!fun)
  691.     return;
  692.     if (fun->atom)
  693.     JS_LOCK_VOID(cx, js_DropAtom(cx, fun->atom));
  694.     if (fun->script)
  695.     js_DestroyScript(cx, fun->script);
  696.     JS_free(cx, fun);
  697. }
  698.  
  699. JSClass js_FunctionClass = {
  700.     "Function",
  701.     JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE,
  702.     JS_PropertyStub,  JS_PropertyStub,
  703.     fun_getProperty,  JS_PropertyStub,
  704.     JS_EnumerateStub, (JSResolveOp)fun_resolve,
  705.     fun_convert,      fun_finalize
  706. };
  707.  
  708. static JSBool
  709. fun_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  710. {
  711.     JSFunction *fun;
  712.     jsdouble d;
  713.     JSString *str;
  714.  
  715.     fun = JS_GetInstancePrivate(cx, obj, &js_FunctionClass, argv);
  716.     if (!fun)
  717.     return JS_FALSE;
  718.     d = 0;
  719.     if (argc && !JS_ValueToNumber(cx, argv[0], &d))
  720.     return JS_FALSE;
  721.     str = JS_DecompileFunction(cx, fun, (uintN)d);
  722.     if (!str)
  723.     return JS_FALSE;
  724.     *rval = STRING_TO_JSVAL(str);
  725.     return JS_TRUE;
  726. }
  727.  
  728. #if JS_HAS_APPLY_FUNCTION
  729. static JSBool
  730. fun_apply(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  731. {
  732.     jsval fval, *nargv;
  733.     uintN i, nargc, nargvlen;
  734.     JSObject *aobj;
  735.     jsint index, length;
  736.     JSBool ok;
  737.  
  738.     if (!obj->map->clasp->convert(cx, obj, JSTYPE_FUNCTION, &fval))
  739.     return JS_FALSE;
  740.  
  741.     if (argc == 0) {
  742.     /* Call fun with its parent as the 'this' parameter if no args. */
  743.     obj = OBJ_GET_PARENT(obj);
  744.     } else {
  745.     /* Otherwise convert the first arg to 'this' and skip over it. */
  746.     if (!JS_ValueToObject(cx, argv[0], &obj))
  747.         return JS_FALSE;
  748.     argc--;
  749.     argv++;
  750.     }
  751.  
  752.     nargv = NULL;
  753.     nargc = nargvlen = 0;
  754.     for (i = 0; i < argc; i++) {
  755.     if (JSVAL_IS_OBJECT(argv[i])) {
  756.         aobj = JSVAL_TO_OBJECT(argv[i]);
  757.         if (aobj && JS_HasLengthProperty(cx, aobj)) {
  758.         ok = JS_GetArrayLength(cx, aobj, &length);
  759.         if (!ok)
  760.             goto out;
  761.         if (length == 0)
  762.             continue;
  763.         if (!nargv)
  764.             nargvlen = argc;
  765.         nargvlen += (uintN)(length - 1);
  766.         nargv = nargv
  767.             ? JS_realloc(cx, nargv, nargvlen * sizeof(jsval))
  768.             : JS_malloc(cx, nargvlen * sizeof(jsval));
  769.         if (!nargv)
  770.             return JS_FALSE;
  771.         for (index = 0; index < length; index++) {
  772.             ok = JS_GetElement(cx, aobj, index, &nargv[nargc]);
  773.             if (!ok)
  774.             goto out;
  775.             nargc++;
  776.         }
  777.         continue;
  778.         }
  779.     }
  780.  
  781.     if (!nargv) {
  782.         nargvlen = argc;
  783.         nargv = JS_malloc(cx, nargvlen * sizeof(jsval));
  784.         if (!nargv)
  785.         return JS_FALSE;
  786.     }
  787.     nargv[nargc] = argv[i];
  788.     nargc++;
  789.     }
  790.  
  791.     ok = js_Call(cx, obj, fval, nargc, nargv, rval);
  792. out:
  793.     if (nargv)
  794.     JS_free(cx, nargv);
  795.     return ok;
  796. }
  797. #endif /* JS_HAS_APPLY_FUNCTION */
  798.  
  799. static JSFunctionSpec function_methods[] = {
  800.     {js_toString_str,    fun_toString,    1},
  801. #if JS_HAS_APPLY_FUNCTION
  802.     {"apply",        fun_apply,    1},
  803. #endif
  804.     {0}
  805. };
  806.  
  807. static JSFunction *
  808. NewFunction(JSContext *cx, JSObject *obj, JSNative call, uintN nargs,
  809.         uintN flags, JSObject *parent, JSAtom *atom)
  810. {
  811.     JSFunction *fun;
  812.  
  813.     /* Allocate a function struct. */
  814.     fun = JS_malloc(cx, sizeof *fun);
  815.     if (!fun)
  816.     return NULL;
  817.  
  818.     /* If obj is null, allocate an object for it. */
  819.     if (!obj) {
  820.     obj = js_NewObject(cx, &js_FunctionClass, NULL, parent);
  821.     if (!obj) {
  822.         JS_free(cx, fun);
  823.         return NULL;
  824.     }
  825.     } else {
  826.     OBJ_SET_PARENT(obj, parent);
  827.     }
  828.  
  829.     /* Link fun to obj and vice versa. */
  830.     fun->object = obj;
  831.     if (!JS_SetPrivate(cx, obj, fun)) {
  832.     cx->newborn[GCX_OBJECT] = NULL;
  833.     JS_free(cx, fun);
  834.     return NULL;
  835.     }
  836.  
  837.     /* Initialize remaining function members. */
  838.     fun->call = call;
  839.     fun->nargs = nargs;
  840.     fun->flags = flags;
  841.     fun->nvars = 0;
  842.     if (atom)
  843.     JS_LOCK_VOID(cx, fun->atom = js_HoldAtom(cx, atom));
  844.     else
  845.     fun->atom = NULL;
  846.     fun->script = NULL;
  847.     return fun;
  848. }
  849.  
  850. JSBool
  851. js_IsIdentifier(JSString *str)
  852. {
  853.     size_t n;
  854.     jschar *s, c;
  855.  
  856.     n = str->length;
  857.     s = str->chars;
  858.     c = *s;
  859.     if (n == 0 || !JS_ISIDENT(c))
  860.     return JS_FALSE;
  861.     for (n--; n != 0; n--) {
  862.     c = *++s;
  863.     if (!JS_ISIDENT2(c))
  864.         return JS_FALSE;
  865.     }
  866.     return JS_TRUE;
  867. }
  868.  
  869. static JSBool
  870. Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  871. {
  872.     JSFunction *fun;
  873.     JSObject *parent;
  874.     uintN i, n, lineno;
  875.     JSAtom *atom;
  876.     const char *filename;
  877.     JSProperty *prop;
  878.     JSSymbol *arg, *args, **argp;
  879.     JSString *str;
  880.     JSStackFrame *fp;
  881.     JSTokenStream *ts;
  882.     JSBool ok;
  883.     JSPrincipals *principals;
  884.  
  885.     if (obj->map->clasp != &js_FunctionClass) {
  886.     obj = js_NewObject(cx, &js_FunctionClass, NULL, NULL);
  887.     if (!obj)
  888.         return JS_FALSE;
  889.     }
  890.     fun = JS_GetPrivate(cx, obj);
  891.     if (fun)
  892.     return JS_TRUE;
  893.  
  894. #if JS_HAS_CALL_OBJECT
  895.     /*
  896.      * NB: (new Function) is not lexically closed by its caller, it's just an
  897.      * anonymous function in the top-level scope that its constructor inhabits.
  898.      * Thus 'var x = 42; f = new Function("return x"); print(f())' prints 42,
  899.      * and so would a call to f from another top-level's script or function.
  900.      *
  901.      * In older versions, before call objects, a new Function was adopted by
  902.      * its running context's globalObject, which might be different from the
  903.      * top-level reachable from scopeChain (in HTML frames, e.g.).
  904.      */
  905.     parent = argv ? OBJ_GET_PARENT(JSVAL_TO_OBJECT(argv[-2])) : NULL;
  906. #else
  907.     /* Set up for dynamic parenting (see Call in jsinterp.c). */
  908.     parent = NULL;
  909. #endif
  910.  
  911.     fun = NewFunction(cx, obj, NULL, 0, 0, parent,
  912.               (cx->version >= JSVERSION_1_2)
  913.               ? NULL
  914.               : cx->runtime->atomState.anonymousAtom);
  915.     if (!fun)
  916.     return JS_FALSE;
  917.  
  918.     args = NULL;
  919.     argp = &args;
  920.     n = argc ? argc - 1 : 0;
  921.  
  922.     JS_LOCK(cx);
  923.     for (i = 0; i < n; i++) {
  924.     atom = js_ValueToStringAtom(cx, argv[i]);
  925.     if (!atom) {
  926.         ok = JS_FALSE;
  927.         goto out;
  928.     }
  929.     str = ATOM_TO_STRING(atom);
  930.     if (!js_IsIdentifier(str)) {
  931.         JS_ReportError(cx, "illegal formal argument name %s",
  932.                JS_GetStringBytes(str));
  933.         js_DropAtom(cx, atom);
  934.         ok = JS_FALSE;
  935.         goto out;
  936.     }
  937.  
  938.     if (!js_LookupProperty(cx, obj, (jsval)atom, &obj, &prop)) {
  939.         js_DropAtom(cx, atom);
  940.         ok = JS_FALSE;
  941.         goto out;
  942.     }
  943.     if (prop && prop->object == obj) {
  944.         PR_ASSERT(prop->getter == js_GetArgument);
  945.         JS_ReportError(cx, "duplicate formal argument %s",
  946.                ATOM_BYTES(atom));
  947.         js_DropAtom(cx, atom);
  948.         ok = JS_FALSE;
  949.         goto out;
  950.     }
  951.     prop = js_DefineProperty(cx, obj, (jsval)atom, JSVAL_VOID,
  952.                  js_GetArgument, js_SetArgument,
  953.                  JSPROP_ENUMERATE | JSPROP_PERMANENT);
  954.     js_DropAtom(cx, atom);
  955.     if (!prop) {
  956.         ok = JS_FALSE;
  957.         goto out;
  958.     }
  959.     prop->id = INT_TO_JSVAL(fun->nargs++);
  960.     arg = prop->symbols;
  961.     *argp = arg;
  962.     argp = &arg->next;
  963.     }
  964.  
  965.     if (argc) {
  966.     str = js_ValueToString(cx, argv[argc-1]);
  967.     if (!str) {
  968.         ok = JS_FALSE;
  969.         goto out;
  970.     }
  971.     js_LockGCThing(cx, str);
  972.     } else {
  973.     /* Can't use cx->runtime->emptyString because we're called too early. */
  974.     str = js_NewStringCopyZ(cx, js_empty_ucstr, GCF_LOCK);
  975.     }
  976.     if ((fp = cx->fp) && (fp = fp->down) && fp->script) {
  977.     filename = fp->script->filename;
  978.     lineno = js_PCToLineNumber(fp->script, fp->pc);
  979.         principals = fp->script->principals;
  980.     } else {
  981.     filename = NULL;
  982.     lineno = 0;
  983.         principals = NULL;
  984.     }
  985.     ts = js_NewTokenStream(cx, str->chars, str->length, filename, lineno,
  986.                            principals);
  987.     if (ts) {
  988.     ok = js_ParseFunctionBody(cx, ts, fun, args) &&
  989.          js_CloseTokenStream(cx, ts);
  990.     } else {
  991.     ok = JS_FALSE;
  992.     }
  993.     js_UnlockGCThing(cx, str);
  994. out:
  995.     JS_UNLOCK(cx);
  996.     if (!ok)
  997.     return JS_FALSE;
  998.     *rval = OBJECT_TO_JSVAL(obj);
  999.     return JS_TRUE;
  1000. }
  1001.  
  1002. JSObject *
  1003. js_InitFunctionClass(JSContext *cx, JSObject *obj)
  1004. {
  1005.     JSObject *proto;
  1006.     jsval rval;
  1007.  
  1008.     proto = JS_InitClass(cx, obj, NULL, &js_FunctionClass, Function, 1,
  1009.              function_props, function_methods, NULL, NULL);
  1010.     if (!proto || !Function(cx, proto, 0, NULL, &rval))
  1011.     return NULL;
  1012.  
  1013.     /* Define fun.length as an alias of fun.arity iff < JS1.2. */
  1014.     if (cx->version < JSVERSION_1_2 &&
  1015.     !JS_AliasProperty(cx, proto, js_arity_str, js_length_str)) {
  1016.     return NULL;
  1017.     }
  1018.     return proto;
  1019. }
  1020.  
  1021. JSBool
  1022. js_InitCallAndClosureClasses(JSContext *cx, JSObject *obj,
  1023.                  JSObject *arrayProto)
  1024. {
  1025. #if JS_HAS_CALL_OBJECT
  1026.     if (!JS_InitClass(cx, obj, arrayProto, &js_CallClass, Call, 0,
  1027.               call_props, call_methods, NULL, NULL)) {
  1028.     return JS_FALSE;
  1029.     }
  1030. #endif
  1031.  
  1032. #if JS_HAS_LEXICAL_CLOSURE
  1033.     if (!JS_InitClass(cx, obj, NULL, &js_ClosureClass, Closure, 0,
  1034.               NULL, NULL, NULL, NULL)) {
  1035.     return JS_FALSE;
  1036.     }
  1037. #endif
  1038.  
  1039.     return JS_TRUE;
  1040. }
  1041.  
  1042. JSFunction *
  1043. js_NewFunction(JSContext *cx, JSNative call, uintN nargs, uintN flags,
  1044.            JSObject *parent, JSAtom *atom)
  1045. {
  1046.     return NewFunction(cx, NULL, call, nargs, flags, parent, atom);
  1047. }
  1048.  
  1049. JSFunction *
  1050. js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative call,
  1051.           uintN nargs, uintN flags)
  1052. {
  1053.     JSFunction *fun;
  1054.  
  1055.     fun = js_NewFunction(cx, call, nargs, flags, obj, atom);
  1056.     if (!fun)
  1057.     return NULL;
  1058.     if (!js_DefineProperty(cx, obj, (jsval)atom, OBJECT_TO_JSVAL(fun->object),
  1059.                NULL, NULL, flags)) {
  1060.     return NULL;
  1061.     }
  1062.     return fun;
  1063. }
  1064.  
  1065. JSFunction *
  1066. js_ValueToFunction(JSContext *cx, jsval v)
  1067. {
  1068.     JSObject *obj;
  1069.     jsval rval;
  1070.     JSString *str;
  1071.  
  1072.     obj = NULL;
  1073.     if (JSVAL_IS_OBJECT(v)) {
  1074.     obj = JSVAL_TO_OBJECT(v);
  1075.     if (obj && obj->map->clasp != &js_FunctionClass) {
  1076.         rval = JSVAL_VOID;
  1077.         if (!obj->map->clasp->convert(cx, obj, JSTYPE_FUNCTION, &rval))
  1078.         return NULL;
  1079.         if (JSVAL_IS_FUNCTION(rval))
  1080.         obj = JSVAL_TO_OBJECT(rval);
  1081.         else
  1082.         obj = NULL;
  1083.     }
  1084.     }
  1085.     if (!obj) {
  1086.     str = js_ValueToSource(cx, v);
  1087.     if (str) {
  1088.         JS_ReportError(cx, "%s is not a function",
  1089.                JS_GetStringBytes(str));
  1090.     }
  1091.     return NULL;
  1092.     }
  1093.     return JS_GetPrivate(cx, obj);
  1094. }
  1095.