home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / js / src / jsobj.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  42.8 KB  |  1,680 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 object implementation.
  21.  */
  22. #include <stdlib.h>
  23. #include <string.h>
  24. #include "prtypes.h"
  25. #ifndef NSPR20
  26. #include "prarena.h"
  27. #else
  28. #include "plarena.h"
  29. #endif
  30. #include "prlog.h"
  31. #ifndef NSPR20
  32. #include "prhash.h"
  33. #else
  34. #include "plhash.h"
  35. #endif
  36. #include "prprf.h"
  37. #include "jsapi.h"
  38. #include "jsatom.h"
  39. #include "jsbool.h"
  40. #include "jscntxt.h"
  41. #include "jsconfig.h"
  42. #include "jsfun.h"
  43. #include "jsgc.h"
  44. #include "jsinterp.h"
  45. #include "jslock.h"
  46. #include "jsnum.h"
  47. #include "jsobj.h"
  48. #include "jsopcode.h"
  49. #include "jsscope.h"
  50. #include "jsscript.h"
  51. #include "jsstr.h"
  52.  
  53. #if JS_HAS_OBJ_WATCHPOINT
  54. #include "jsdbgapi.h"
  55.  
  56. extern JSProperty *
  57. js_FindWatchPoint(JSRuntime *rt, JSObject *obj, jsval userid);
  58.  
  59. extern JSBool PR_CALLBACK
  60. js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
  61. #endif
  62.  
  63. JSClass js_ObjectClass = {
  64.     js_Object_str,
  65.     0,
  66.     JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
  67.     JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub
  68. };
  69.  
  70. #if JS_HAS_OBJ_PROTO_PROP
  71.  
  72. static JSBool
  73. obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
  74.  
  75. PR_STATIC_CALLBACK(JSBool)
  76. obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
  77.  
  78. static JSBool
  79. obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
  80.  
  81. static JSPropertySpec object_props[] = {
  82.     /* These two must come first; see object_props[slot].name usage below. */
  83.     {js_proto_str, JSSLOT_PROTO,  JSPROP_PERMANENT, obj_getSlot,  obj_setSlot},
  84.     {js_parent_str,JSSLOT_PARENT, JSPROP_PERMANENT, obj_getSlot,  obj_setSlot},
  85.     {js_count_str, 0,             JSPROP_PERMANENT, obj_getCount, obj_getCount},
  86.     {0}
  87. };
  88.  
  89. static JSBool
  90. obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  91. {
  92.     JS_LOCK_VOID(cx, *vp = js_GetSlot(cx, obj, JSVAL_TO_INT(id)));
  93.     return JS_TRUE;
  94. }
  95.  
  96. PR_STATIC_CALLBACK(JSBool)
  97. obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  98. {
  99.     JSObject *obj2;
  100.     jsint slot;
  101.     JSBool ok;
  102.  
  103.     if (!JSVAL_IS_OBJECT(*vp))
  104.     return JS_TRUE;
  105.     obj2 = JSVAL_TO_OBJECT(*vp);
  106.     slot = JSVAL_TO_INT(id);
  107.     JS_LOCK(cx);
  108.     while (obj2) {
  109.     if (obj2 == obj) {
  110.         JS_ReportError(cx, "cyclic %s value", object_props[slot].name);
  111.         ok = JS_FALSE;
  112.         goto out;
  113.     }
  114.     obj2 = JSVAL_TO_OBJECT(js_GetSlot(cx, obj2, slot));
  115.     }
  116.     ok = js_SetSlot(cx, obj, slot, *vp);
  117. out:
  118.     JS_UNLOCK(cx);
  119.     return ok;
  120. }
  121.  
  122. static JSBool
  123. obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  124. {
  125.     jsint count;
  126.     JSProperty *prop;
  127.  
  128.     count = 0;
  129.     JS_LOCK(cx);
  130.     for (prop = obj->map->props; prop; prop = prop->next)
  131.     if (prop->flags & JSPROP_ENUMERATE)
  132.         count++;
  133.     JS_UNLOCK(cx);
  134.     *vp = INT_TO_JSVAL(count);
  135.     return JS_TRUE;
  136. }
  137.  
  138. #else  /* !JS_HAS_OBJ_PROTO_PROP */
  139.  
  140. #define object_props NULL
  141.  
  142. #endif /* !JS_HAS_OBJ_PROTO_PROP */
  143.  
  144. #if JS_HAS_SHARP_VARS
  145.  
  146. PR_STATIC_CALLBACK(PRHashNumber)
  147. js_hash_object(const void *key)
  148. {
  149.     return (PRHashNumber)key >> JSVAL_TAGBITS;
  150. }
  151.  
  152. static PRHashEntry *
  153. MarkSharpObjects(JSContext *cx, JSObject *obj)
  154. {
  155.     JSSharpObjectMap *map;
  156.     PRHashTable *table;
  157.     PRHashNumber hash;
  158.     PRHashEntry **hep, *he;
  159.     jsatomid sharpid;
  160.     JSProperty *prop;
  161.     jsval val;
  162.  
  163.     map = &cx->sharpObjectMap;
  164.     table = map->table;
  165.     hash = js_hash_object(obj);
  166.     hep = PR_HashTableRawLookup(table, hash, obj);
  167.     he = *hep;
  168.     if (!he) {
  169.     sharpid = 0;
  170.     he = PR_HashTableRawAdd(table, hep, hash, obj, (void *)sharpid);
  171.     if (!he) {
  172.         JS_ReportOutOfMemory(cx);
  173.         return NULL;
  174.     }
  175.     for (prop = obj->map->props; prop; prop = prop->next) {
  176.         if (!prop->symbols || !(prop->flags & JSPROP_ENUMERATE))
  177.         continue;
  178.         val = prop->object->slots[prop->slot];
  179.         if (JSVAL_IS_OBJECT(val) && val != JSVAL_NULL)
  180.         (void) MarkSharpObjects(cx, JSVAL_TO_OBJECT(val));
  181.     }
  182.     } else {
  183.     sharpid = (jsatomid) he->value;
  184.     if (sharpid == 0) {
  185.         sharpid = ++map->sharpgen << 1;
  186.         he->value = (void *) sharpid;
  187.     }
  188.     }
  189.     return he;
  190. }
  191.  
  192. PRHashEntry *
  193. js_EnterSharpObject(JSContext *cx, JSObject *obj, jschar **sp)
  194. {
  195.     JSSharpObjectMap *map;
  196.     PRHashTable *table;
  197.     PRHashNumber hash;
  198.     PRHashEntry *he;
  199.     jsatomid sharpid;
  200.     char buf[20];
  201.     size_t len;
  202.  
  203.     map = &cx->sharpObjectMap;
  204.     table = map->table;
  205.     if (!table) {
  206.     table = PR_NewHashTable(8, js_hash_object, PR_CompareValues,
  207.                 PR_CompareValues, NULL, NULL);
  208.     if (!table) {
  209.         JS_ReportOutOfMemory(cx);
  210.         return NULL;
  211.     }
  212.     map->table = table;
  213.     }
  214.  
  215.     if (map->depth == 0) {
  216.     he = MarkSharpObjects(cx, obj);
  217.     if (!he)
  218.         return NULL;
  219.     } else {
  220.     hash = js_hash_object(obj);
  221.     he = *PR_HashTableRawLookup(table, hash, obj);
  222.     PR_ASSERT(he);
  223.     }
  224.  
  225.     sharpid = (jsatomid) he->value;
  226.     if (sharpid == 0) {
  227.     *sp = NULL;
  228.     } else {
  229.     len = PR_snprintf(buf, sizeof buf, "#%u%c",
  230.               sharpid >> 1, (sharpid & SHARP_BIT) ? '#' : '=');
  231.     *sp = js_InflateString(cx, buf, len);
  232.     if (!*sp)
  233.         return NULL;
  234.     }
  235.  
  236.     if ((sharpid & SHARP_BIT) == 0)
  237.     map->depth++;
  238.     return he;
  239. }
  240.  
  241. void
  242. js_LeaveSharpObject(JSContext *cx)
  243. {
  244.     JSSharpObjectMap *map;
  245.  
  246.     map = &cx->sharpObjectMap;
  247.     PR_ASSERT(map->depth > 0);
  248.     if (--map->depth == 0) {
  249.     map->sharpgen = 0;
  250.     PR_HashTableDestroy(map->table);
  251.     map->table = NULL;
  252.     }
  253. }
  254.  
  255. #endif /* JS_HAS_SHARP_VARS */
  256.  
  257. #define OBJ_TOSTRING_NARGS    2    /* for 2 GC roots */
  258.  
  259. JSBool
  260. js_obj_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
  261.         jsval *rval)
  262. {
  263.     jschar *chars;
  264.     size_t nchars;
  265.     char *clazz, *prefix;
  266.     JSString *str;
  267.  
  268. #if JS_HAS_OBJECT_LITERAL
  269.     if (cx->version >= JSVERSION_1_2) {
  270.     JSProperty *list, *prop;
  271.     JSBool ok;
  272.     char *comma, *idquote, *valquote;
  273.     jsval id, val;
  274.     JSString *idstr, *valstr;
  275. #if JS_HAS_SHARP_VARS
  276.     PRHashEntry *he;
  277. #endif
  278.  
  279.     /* Early returns after this must unlock, or goto done with ok set. */
  280.     JS_LOCK(cx);
  281.     list = obj->map->props;
  282.  
  283. #if JS_HAS_SHARP_VARS
  284.     he = js_EnterSharpObject(cx, obj, &chars);
  285.     if (!he) {
  286.         JS_UNLOCK(cx);
  287.         return JS_FALSE;
  288.     }
  289.     if (IS_SHARP(he)) {    /* we didn't enter -- obj is already sharp */
  290.         JS_UNLOCK(cx);
  291.         nchars = js_strlen(chars);
  292.         goto make_string;
  293.     }
  294.     MAKE_SHARP(he);
  295. #else
  296.     /* Shut off recursion by temporarily clearing obj's property list. */
  297.     obj->map->props = NULL;
  298.     chars = NULL;
  299. #endif
  300.     ok = JS_TRUE;
  301.  
  302.     /* Allocate 2 + 1 for "{}" and the terminator. */
  303.     if (!chars) {
  304.         chars = malloc((2 + 1) * sizeof(jschar));
  305.         nchars = 0;
  306.     } else {
  307.         nchars = js_strlen(chars);
  308.         chars = realloc(chars, (nchars + 2 + 1) * sizeof(jschar));
  309.     }
  310.     if (!chars)
  311.         goto done;
  312.     chars[nchars++] = '{';
  313.  
  314.     comma = NULL;
  315.  
  316.     for (prop = list; prop; prop = prop->next) {
  317.         if (!prop->symbols || !(prop->flags & JSPROP_ENUMERATE))
  318.         continue;
  319.  
  320.         /* Get strings for id and val and GC-root them via argv. */
  321.         id = js_IdToValue(sym_id(prop->symbols));
  322.         idstr = js_ValueToString(cx, id);
  323.         if (idstr)
  324.         argv[0] = STRING_TO_JSVAL(idstr);
  325.         val = prop->object->slots[prop->slot];
  326.         valstr = js_ValueToString(cx, val);
  327.         if (!idstr || !valstr) {
  328.         ok = JS_FALSE;
  329.         goto done;
  330.         }
  331.         argv[1] = STRING_TO_JSVAL(valstr);
  332.  
  333.         /* If id is a non-identifier string, it needs to be quoted. */
  334.         if (JSVAL_IS_STRING(id) && !js_IsIdentifier(idstr)) {
  335.         idquote = "'";
  336.         idstr = js_EscapeString(cx, idstr, *idquote);
  337.         if (!idstr) {
  338.             ok = JS_FALSE;
  339.             goto done;
  340.         }
  341.         argv[0] = STRING_TO_JSVAL(idstr);
  342.         } else {
  343.         idquote = NULL;
  344.         }
  345.  
  346.         /* Same for val, except it can't be an identifier. */
  347.         if (JSVAL_IS_STRING(val)) {
  348.         valquote = "\"";
  349.         valstr = js_EscapeString(cx, valstr, *valquote);
  350.         if (!valstr) {
  351.             ok = JS_FALSE;
  352.             goto done;
  353.         }
  354.         argv[1] = STRING_TO_JSVAL(valstr);
  355.         } else {
  356.         valquote = NULL;
  357.         }
  358.  
  359.         /* Allocate 1 + 1 at end for closing brace and terminating 0. */
  360.         chars = realloc(chars,
  361.                 (nchars + (comma ? 2 : 0) +
  362.                  (idquote ? 2 : 0) + idstr->length + 1 +
  363.                  (valquote ? 2 : 0) + valstr->length +
  364.                  1 + 1) * sizeof(jschar));
  365.         if (!chars)
  366.         goto done;
  367.  
  368.         if (comma) {
  369.         chars[nchars++] = comma[0];
  370.         chars[nchars++] = comma[1];
  371.         }
  372.         comma = ", ";
  373.  
  374.         if (idquote)
  375.         chars[nchars++] = *idquote;
  376.         js_strncpy(&chars[nchars], idstr->chars, idstr->length);
  377.         nchars += idstr->length;
  378.         if (idquote)
  379.         chars[nchars++] = *idquote;
  380.  
  381.         chars[nchars++] = ':';
  382.  
  383.         if (valquote)
  384.         chars[nchars++] = *valquote;
  385.         js_strncpy(&chars[nchars], valstr->chars, valstr->length);
  386.         nchars += valstr->length;
  387.         if (valquote)
  388.         chars[nchars++] = *valquote;
  389.     }
  390.  
  391.       done:
  392.     if (chars) {
  393.         chars[nchars++] = '}';
  394.         chars[nchars] = 0;
  395.     }
  396. #if JS_HAS_SHARP_VARS
  397.     js_LeaveSharpObject(cx);
  398. #else
  399.     /* Restore obj's property list before bailing on error. */
  400.     obj->map->props = list;
  401. #endif
  402.  
  403.     JS_UNLOCK(cx);
  404.     if (!ok) {
  405.         if (chars)
  406.         free(chars);
  407.         return ok;
  408.     }
  409.     } else
  410. #endif /* JS_HAS_OBJECT_LITERAL */
  411.     {
  412.     clazz = obj->map->clasp->name;
  413.     nchars = 9 + strlen(clazz);        /* 9 for "[object ]" */
  414.     chars = malloc((nchars + 1) * sizeof(jschar));
  415.     if (chars) {
  416.         prefix = "[object ";
  417.         nchars = 0;
  418.         while ((chars[nchars] = (jschar)*prefix) != 0)
  419.         nchars++, prefix++;
  420.         while ((chars[nchars] = (jschar)*clazz) != 0)
  421.         nchars++, clazz++;
  422.         chars[nchars++] = ']';
  423.         chars[nchars] = 0;
  424.     }
  425.     }
  426.  
  427. #if JS_HAS_SHARP_VARS
  428.   make_string:
  429. #endif
  430.     if (!chars) {
  431.     JS_ReportOutOfMemory(cx);
  432.     return JS_FALSE;
  433.     }
  434.     str = js_NewString(cx, chars, nchars, 0);
  435.     if (!str) {
  436.     free(chars);
  437.     return JS_FALSE;
  438.     }
  439.     *rval = STRING_TO_JSVAL(str);
  440.     return JS_TRUE;
  441. }
  442.  
  443. static JSBool
  444. obj_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  445. {
  446.     *rval = OBJECT_TO_JSVAL(obj);
  447.     return JS_TRUE;
  448. }
  449.  
  450. static JSBool
  451. obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  452. {
  453.     JSStackFrame *fp, *caller;
  454.     JSBool ok;
  455.     JSString *str;
  456.     const char *file;
  457.     uintN line;
  458.     JSPrincipals *principals;
  459.     JSScript *script;
  460. #if JS_HAS_EVAL_THIS_SCOPE
  461.     JSObject *callerScopeChain;
  462.     JSBool implicitWith;
  463. #endif
  464.  
  465.     if (!JSVAL_IS_STRING(argv[0])) {
  466.     *rval = argv[0];
  467.     return JS_TRUE;
  468.     }
  469.  
  470.     fp = cx->fp;
  471.     caller = fp->down;
  472. #if !JS_BUG_EVAL_THIS_FUN
  473.     /* Ensure that this flows into eval from the calling function, if any. */
  474.     fp->thisp = caller->thisp;
  475. #endif
  476. #if JS_HAS_SHARP_VARS
  477.     fp->sharpArray = caller->sharpArray;
  478. #endif
  479.  
  480. #if JS_HAS_EVAL_THIS_SCOPE
  481.     /* If obj.eval(str), emulate 'with (obj) eval(str)' in the calling frame. */
  482.     callerScopeChain = caller->scopeChain;
  483.     implicitWith = (callerScopeChain != obj &&
  484.             (callerScopeChain->map->clasp != &js_WithClass ||
  485.              OBJ_GET_PROTO(callerScopeChain) != obj));
  486.     if (implicitWith) {
  487.     obj = js_NewObject(cx, &js_WithClass, obj, callerScopeChain);
  488.     if (!obj)
  489.         return JS_FALSE;
  490.     caller->scopeChain = obj;
  491.     }
  492. #endif
  493.  
  494. #if !JS_BUG_EVAL_THIS_SCOPE
  495.     /* Compile using caller's current scope object (might be a function). */
  496.     obj = caller->scopeChain;
  497. #endif
  498.  
  499.     str = JSVAL_TO_STRING(argv[0]);
  500.     if (caller->script) {
  501.     file = caller->script->filename;
  502.     line = js_PCToLineNumber(caller->script, caller->pc);
  503.     principals = caller->script->principals;
  504.     } else {
  505.     file = NULL;
  506.     line = 0;
  507.     principals = NULL;
  508.     }
  509.     script = JS_CompileUCScriptForPrincipals(cx, obj, principals,
  510.                          str->chars, str->length,
  511.                          file, line);
  512.     if (!script) {
  513.     ok = JS_FALSE;
  514.     goto out;
  515.     }
  516.  
  517. #if !JS_BUG_EVAL_THIS_SCOPE
  518.     /* Interpret using caller's new scope object (might be a Call object). */
  519.     obj = caller->scopeChain;
  520. #endif
  521.     ok = js_Execute(cx, obj, script, fp, rval);
  522.     JS_DestroyScript(cx, script);
  523.  
  524. out:
  525. #if JS_HAS_EVAL_THIS_SCOPE
  526.     if (implicitWith) {
  527.     /* Restore OBJ_GET_PARENT(obj) not callerScopeChain in case of Call. */
  528.     caller->scopeChain = OBJ_GET_PARENT(obj);
  529.     }
  530. #endif
  531.     return ok;
  532. }
  533.  
  534. #if JS_HAS_OBJ_WATCHPOINT
  535.  
  536. static JSBool
  537. obj_watch_handler(JSContext *cx, JSObject *obj, jsval id, jsval old, jsval *nvp,
  538.           void *closure)
  539. {
  540.     JSObject *funobj;
  541.     jsval argv[3];
  542.  
  543.     PR_ASSERT(JS_IS_LOCKED(cx));
  544.     funobj = closure;
  545.     argv[0] = id;
  546.     argv[1] = old;
  547.     argv[2] = *nvp;
  548.     return js_Call(cx, obj, OBJECT_TO_JSVAL(funobj), 3, argv, nvp);
  549. }
  550.  
  551. static JSBool
  552. obj_watch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  553. {
  554.     JSFunction *fun;
  555.     jsval userid, symid, propid, value;
  556.     JSAtom *atom;
  557.     JSRuntime *rt;
  558.     JSBool ok;
  559.     JSProperty *prop;
  560.     JSPropertyOp getter, setter;
  561.     JSClass *clasp;
  562.  
  563.     fun = JS_ValueToFunction(cx, argv[1]);
  564.     if (!fun)
  565.     return JS_FALSE;
  566.     argv[1] = OBJECT_TO_JSVAL(fun->object);
  567.  
  568.     /*
  569.      * Lock the world while we look in obj.  Be sure to return via goto out
  570.      * on error, so we unlock.
  571.      */
  572.     rt = cx->runtime;
  573.     JS_LOCK_RUNTIME(rt);
  574.  
  575.     /* Compute the unique int/atom symbol id needed by js_LookupProperty. */
  576.     userid = argv[0];
  577.     if (JSVAL_IS_INT(userid)) {
  578.     symid = userid;
  579.     atom = NULL;
  580.     } else {
  581.     atom = js_ValueToStringAtom(cx, userid);
  582.     if (!atom) {
  583.         ok = JS_FALSE;
  584.         goto out;
  585.     }
  586.     symid = (jsval)atom;
  587.     }
  588.  
  589.     ok = js_LookupProperty(cx, obj, symid, &obj, &prop);
  590.     if (atom)
  591.     js_DropAtom(cx, atom);
  592.     if (!ok)
  593.     goto out;
  594.  
  595.     /* Set propid from the property, in case it has a tinyid. */
  596.     if (prop) {
  597.     propid = prop->id;
  598.     getter = prop->getter;
  599.     setter = prop->setter;
  600.     value = prop->object->slots[prop->slot];
  601.     } else {
  602.     propid = userid;
  603.     clasp = obj->map->clasp;
  604.     getter = clasp->getProperty;
  605.     setter = clasp->setProperty;
  606.     value = JSVAL_VOID;
  607.     }
  608.  
  609.     /*
  610.      * Security policy is implemented in getters and setters, so we have to
  611.      * call get and set here to let the JS API client check for a watchpoint
  612.      * that crosses a trust boundary.
  613.      * XXX assumes get and set are idempotent, should use a clasp->watch hook
  614.      */
  615.     ok = getter(cx, obj, propid, &value) && setter(cx, obj, propid, &value);
  616.     if (!ok)
  617.     goto out;
  618.  
  619.     /* Finally, call into jsdbgapi.c to set the watchpoint on userid. */
  620.     ok = JS_SetWatchPoint(cx, obj, userid, obj_watch_handler, fun->object);
  621.  
  622. out:
  623.     JS_UNLOCK_RUNTIME(rt);
  624.     return ok;
  625. }
  626.  
  627. static JSBool
  628. obj_unwatch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  629. {
  630.     JS_LOCK_VOID(cx, JS_ClearWatchPoint(cx, obj, argv[0], NULL, NULL));
  631.     return JS_TRUE;
  632. }
  633.  
  634. #endif /* JS_HAS_OBJ_WATCHPOINT */
  635.  
  636. static JSFunctionSpec object_methods[] = {
  637.     {js_toString_str,    js_obj_toString,    OBJ_TOSTRING_NARGS},
  638.     {js_valueOf_str,    obj_valueOf,        0},
  639.     {js_eval_str,    obj_eval,        1},
  640. #if JS_HAS_OBJ_WATCHPOINT
  641.     {"watch",           obj_watch,              2},
  642.     {"unwatch",         obj_unwatch,            1},
  643. #endif
  644.     {0}
  645. };
  646.  
  647. static JSBool
  648. Object(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  649. {
  650.     if (argc != 0) {
  651.     if (!JS_ValueToObject(cx, argv[0], &obj))
  652.         return JS_FALSE;
  653.     if (!obj)
  654.         return JS_TRUE;
  655.     }
  656.     *rval = OBJECT_TO_JSVAL(obj);
  657.     return JS_TRUE;
  658. }
  659.  
  660. /*
  661.  * Class for with-statement stack objects.
  662.  */
  663. static JSBool
  664. with_resolve(JSContext *cx, JSObject *obj, jsval id, JSObject **objp)
  665. {
  666.     *objp = OBJ_GET_PROTO(obj);
  667.     return JS_TRUE;
  668. }
  669.  
  670. JSClass js_WithClass = {
  671.     "With",
  672.     JSCLASS_NEW_RESOLVE,
  673.     JS_PropertyStub,  JS_PropertyStub,
  674.     JS_PropertyStub,  JS_PropertyStub,
  675.     JS_EnumerateStub, (JSResolveOp)with_resolve,
  676.     JS_ConvertStub,   JS_FinalizeStub
  677. };
  678.  
  679. #if JS_HAS_OBJ_PROTO_PROP
  680. static JSBool
  681. With(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  682. {
  683.     JSObject *parent, *proto;
  684.     jsval v;
  685.  
  686.     if (!JS_InstanceOf(cx, obj, &js_WithClass, argv))
  687.     return JS_FALSE;
  688.  
  689.     parent = cx->fp->scopeChain;
  690.     if (argc > 0) {
  691.     if (!JS_ValueToObject(cx, argv[0], &proto))
  692.         return JS_FALSE;
  693.     v = OBJECT_TO_JSVAL(proto);
  694.     if (!obj_setSlot(cx, obj, INT_TO_JSVAL(JSSLOT_PROTO), &v))
  695.         return JS_FALSE;
  696.     if (argc > 1) {
  697.         if (!JS_ValueToObject(cx, argv[1], &parent))
  698.         return JS_FALSE;
  699.     }
  700.     }
  701.     v = OBJECT_TO_JSVAL(parent);
  702.     return obj_setSlot(cx, obj, INT_TO_JSVAL(JSSLOT_PARENT), &v);
  703. }
  704. #endif
  705.  
  706. JSObject *
  707. js_InitObjectClass(JSContext *cx, JSObject *obj)
  708. {
  709.     JSObject *proto;
  710.  
  711. #if JS_HAS_SHARP_VARS
  712.     PR_ASSERT(sizeof(jsatomid) * PR_BITS_PER_BYTE >= ATOM_INDEX_LIMIT_LOG2 + 1);
  713. #endif
  714.  
  715.     proto = JS_InitClass(cx, obj, NULL, &js_ObjectClass, Object, 0,
  716.              object_props, object_methods, NULL, NULL);
  717. #if JS_HAS_OBJ_PROTO_PROP
  718.     if (!JS_InitClass(cx, obj, NULL, &js_WithClass, With, 0,
  719.               NULL, NULL, NULL, NULL)) {
  720.     return NULL;
  721.     }
  722. #endif
  723.     return proto;
  724. }
  725.  
  726. JSObject *
  727. js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent)
  728. {
  729.     JSObject *obj;
  730.     JSScope *scope;
  731.  
  732.     /* Allocate an object from the GC heap and zero it. */
  733.     obj = js_AllocGCThing(cx, GCX_OBJECT);
  734.     if (!obj)
  735.     return NULL;
  736.  
  737.     /* Bootstrap the ur-object, and make it the default prototype object. */
  738.     if (!proto) {
  739.     if (!js_GetClassPrototype(cx, clasp, &proto) ||
  740.         (!proto && !js_GetClassPrototype(cx, &js_ObjectClass, &proto))) {
  741.         cx->newborn[GCX_OBJECT] = NULL;
  742.         return NULL;
  743.     }
  744.     }
  745.  
  746.     /* Share the given prototype's scope (create one if necessary). */
  747.     if (proto && proto->map->clasp == clasp) {
  748.     scope = (JSScope *)proto->map;
  749.     JS_LOCK_VOID(cx, obj->map = (JSObjectMap *)js_HoldScope(cx, scope));
  750.     } else {
  751.     scope = js_NewScope(cx, clasp, obj);
  752.     if (!scope) {
  753.         cx->newborn[GCX_OBJECT] = NULL;
  754.         return NULL;
  755.     }
  756.     scope->map.nrefs = 1;
  757.     obj->map = &scope->map;
  758.     }
  759.  
  760.     /* Set the prototype and parent properties. */
  761.     if (!js_SetSlot(cx, obj, JSSLOT_PROTO, OBJECT_TO_JSVAL(proto)) ||
  762.     !js_SetSlot(cx, obj, JSSLOT_PARENT, OBJECT_TO_JSVAL(parent))) {
  763.     cx->newborn[GCX_OBJECT] = NULL;
  764.     return NULL;
  765.     }
  766.     return obj;
  767. }
  768.  
  769. static JSBool
  770. FindConstructor(JSContext *cx, JSClass *clasp, jsval *vp)
  771. {
  772.     JSAtom *atom;
  773.     JSObject *obj, *tmp;
  774.     JSBool ok;
  775.  
  776.     PR_ASSERT(JS_IS_LOCKED(cx));
  777.  
  778.     /* XXX pre-atomize in JS_InitClass! */
  779.     atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0);
  780.     if (!atom)
  781.     return JS_FALSE;
  782.  
  783.     if (cx->fp && (tmp = cx->fp->scopeChain)) {
  784.     /* Find the topmost object in the scope chain. */
  785.     do {
  786.         obj = tmp;
  787.         tmp = OBJ_GET_PARENT(obj);
  788.     } while (tmp);
  789.     } else {
  790.     obj = cx->globalObject;
  791.     if (!obj) {
  792.         *vp = JSVAL_VOID;
  793.         return JS_TRUE;
  794.     }
  795.     }
  796.  
  797.     ok = (js_GetProperty(cx, obj, (jsval)atom, vp) != NULL);
  798.     js_DropAtom(cx, atom);
  799.     return JS_TRUE;
  800. }
  801.  
  802. JSObject *
  803. js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto,
  804.            JSObject *parent)
  805. {
  806.     JSObject *obj;
  807.     JSBool ok;
  808.     jsval ctor, rval;
  809.  
  810.     obj = js_NewObject(cx, clasp, proto, parent);
  811.     if (!obj)
  812.     return NULL;
  813.     JS_LOCK_VOID(cx, ok = FindConstructor(cx, clasp, &ctor));
  814.     if (!ok || !js_Call(cx, obj, ctor, 0, NULL, &rval)) {
  815.     cx->newborn[GCX_OBJECT] = NULL;
  816.     return NULL;
  817.     }
  818.     return JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : obj;
  819. }
  820.  
  821. void
  822. js_FinalizeObject(JSContext *cx, JSObject *obj)
  823. {
  824.     JSScope *scope;
  825.  
  826.     PR_ASSERT(JS_IS_LOCKED(cx));
  827.  
  828.     /* Cope with stillborn objects that have no scope. */
  829.     scope = (JSScope *)obj->map;
  830.     if (!scope)
  831.     return;
  832.  
  833. #if JS_HAS_OBJ_WATCHPOINT
  834.     /* Remove all watchpoints with weak links to obj. */
  835.     JS_ClearWatchPointsForObject(cx, obj);
  836. #endif
  837.  
  838.     /* Finalize obj first, in case it needs map and slots. */
  839.     scope->map.clasp->finalize(cx, obj);
  840.     if (scope->object == obj)
  841.     scope->object = NULL;
  842.  
  843.     /* Drop scope and free slots. */
  844.     js_DropScope(cx, scope);
  845.     obj->map = NULL;
  846.     JS_free(cx, obj->slots);
  847.     obj->slots = NULL;
  848. }
  849.  
  850. jsval
  851. js_GetSlot(JSContext *cx, JSObject *obj, uint32 slot)
  852. {
  853.     if (!obj->slots || slot >= obj->map->freeslot)
  854.     return JSVAL_NULL;
  855.     return obj->slots[slot];
  856. }
  857.  
  858. JSBool
  859. js_SetSlot(JSContext *cx, JSObject *obj, uint32 slot, jsval value)
  860. {
  861.     uint32 i;
  862.  
  863.     PR_ASSERT(slot < JS_INITIAL_NSLOTS);
  864.     if (!obj->slots) {
  865.     obj->slots = JS_malloc(cx, JS_INITIAL_NSLOTS * sizeof(jsval));
  866.     if (!obj->slots)
  867.         return JS_FALSE;
  868.     for (i = 0; i < (uint32)JS_INITIAL_NSLOTS; i++) {
  869.         if (i == slot)
  870.         continue;
  871.         obj->slots[i] = JSVAL_VOID;
  872.     }
  873.     if (obj->map->nslots == 0)
  874.         obj->map->nslots = JS_INITIAL_NSLOTS;
  875.     }
  876.     if (slot >= obj->map->freeslot)
  877.     obj->map->freeslot = slot + 1;
  878.     obj->slots[slot] = value;
  879.     return JS_TRUE;
  880. }
  881.  
  882. JSBool
  883. js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp)
  884. {
  885.     JSObjectMap *map;
  886.     uint32 nslots;
  887.     size_t nbytes;
  888.     jsval *newslots;
  889.  
  890.     PR_ASSERT(JS_IS_LOCKED(cx));
  891.     map = obj->map;
  892.     PR_ASSERT(((JSScope *)map)->object == obj);
  893.  
  894.     nslots = map->nslots;
  895.     if (map->freeslot >= nslots) {
  896.     nslots = PR_MAX(map->freeslot, nslots);
  897.     if (nslots < JS_INITIAL_NSLOTS)
  898.         nslots = JS_INITIAL_NSLOTS;
  899.     else
  900.         nslots += (nslots + 1) / 2;
  901.  
  902.     nbytes = (size_t)nslots * sizeof(jsval);
  903. #if defined(XP_PC) && defined _MSC_VER && _MSC_VER <= 800
  904.     if (nbytes > 60000U) {
  905.         JS_ReportOutOfMemory(cx);
  906.         return JS_FALSE;
  907.     }
  908. #endif
  909.  
  910.     if (obj->slots)
  911.         newslots = JS_realloc(cx, obj->slots, nbytes);
  912.     else
  913.         newslots = JS_malloc(cx, nbytes);
  914.     if (!newslots)
  915.         return JS_FALSE;
  916.     obj->slots = newslots;
  917.     map->nslots = nslots;
  918.     }
  919.  
  920. #ifdef TOO_MUCH_GC
  921.     obj->slots[map->freeslot] = JSVAL_VOID;
  922. #endif
  923.     *slotp = map->freeslot++;
  924.     return JS_TRUE;
  925. }
  926.  
  927. void
  928. js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot)
  929. {
  930.     JSObjectMap *map;
  931.     uint32 nslots;
  932.     size_t nbytes;
  933.     jsval *newslots;
  934.  
  935.     PR_ASSERT(JS_IS_LOCKED(cx));
  936.     map = obj->map;
  937.     PR_ASSERT(((JSScope *)map)->object == obj);
  938.     if (map->freeslot == slot + 1)
  939.     map->freeslot = slot;
  940.     nslots = map->nslots;
  941.     if (nslots > JS_INITIAL_NSLOTS && map->freeslot < nslots / 2) {
  942.     nslots = map->freeslot;
  943.     nslots += nslots / 2;
  944.     nbytes = (size_t)nslots * sizeof(jsval);
  945.     newslots = JS_realloc(cx, obj->slots, nbytes);
  946.     if (!newslots)
  947.         return;
  948.     obj->slots = newslots;
  949.     map->nslots = nslots;
  950.     }
  951. }
  952.  
  953. #if JS_BUG_EMPTY_INDEX_ZERO
  954. #define CHECK_FOR_EMPTY_INDEX(id)                                             \
  955.     PR_BEGIN_MACRO                                                            \
  956.     if (_str->length == 0)                                                \
  957.         id = JSVAL_ZERO;                                                  \
  958.     PR_END_MACRO
  959. #else
  960. #define CHECK_FOR_EMPTY_INDEX(id) /* nothing */
  961. #endif
  962.  
  963. #define CHECK_FOR_FUNNY_INDEX(id)                                             \
  964.     PR_BEGIN_MACRO                                                            \
  965.     if (!JSVAL_IS_INT(id)) {                                              \
  966.         JSAtom *_atom = (JSAtom *)id;                                     \
  967.         JSString *_str = ATOM_TO_STRING(_atom);                           \
  968.         const jschar *_cp = _str->chars;                                  \
  969.         if (JS7_ISDEC(*_cp)) {                                            \
  970.         jsint _index = JS7_UNDEC(*_cp);                               \
  971.         _cp++;                                                        \
  972.         if (_index != 0) {                                            \
  973.             while (JS7_ISDEC(*_cp)) {                                 \
  974.             _index = 10 * _index + JS7_UNDEC(*_cp);               \
  975.             _cp++;                                                \
  976.             }                                                         \
  977.         }                                                             \
  978.         if (*_cp == 0 && INT_FITS_IN_JSVAL(_index))                   \
  979.             id = INT_TO_JSVAL(_index);                                \
  980.         } else {                                                          \
  981.         CHECK_FOR_EMPTY_INDEX(id);                                    \
  982.         }                                                                 \
  983.     }                                                                     \
  984.     PR_END_MACRO
  985.  
  986. JSProperty *
  987. js_DefineProperty(JSContext *cx, JSObject *obj, jsval id, jsval value,
  988.           JSPropertyOp getter, JSPropertyOp setter, uintN flags)
  989. {
  990.     JSRuntime *rt;
  991.     JSScope *scope;
  992.     JSProperty *prop;
  993.  
  994.     rt = cx->runtime;
  995.     PR_ASSERT(JS_IS_RUNTIME_LOCKED(rt));
  996.  
  997.     /* Handle old bug that treated empty string as zero index. */
  998.     CHECK_FOR_FUNNY_INDEX(id);
  999.  
  1000.     /* Use the object's class getter and setter by default. */
  1001.     if (!getter)
  1002.     getter = obj->map->clasp->getProperty;
  1003.     if (!setter)
  1004.     setter = obj->map->clasp->setProperty;
  1005.  
  1006.     /* Find a sharable scope, or get a new one for obj. */
  1007.     scope = js_MutateScope(cx, obj, id, getter, setter, flags, &prop);
  1008.     if (!scope)
  1009.     return NULL;
  1010.  
  1011.     /* Add the property only if MutateScope didn't find a shared scope. */
  1012.     if (!prop) {
  1013.     prop = js_NewProperty(cx, scope, id, getter, setter, flags);
  1014.     if (!prop)
  1015.         return NULL;
  1016.     if (!obj->map->clasp->addProperty(cx, obj, prop->id, &value) ||
  1017.         !scope->ops->add(cx, scope, id, prop)) {
  1018.         js_DestroyProperty(cx, prop);
  1019.         return NULL;
  1020.     }
  1021.     PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id, prop);
  1022.     }
  1023.  
  1024.     PR_ASSERT(prop->slot < obj->map->freeslot);
  1025.     obj->slots[prop->slot] = value;
  1026.     return prop;
  1027. }
  1028.  
  1029. JS_FRIEND_API(JSBool)
  1030. js_LookupProperty(JSContext *cx, JSObject *obj, jsval id, JSObject **objp,
  1031.           JSProperty **propp)
  1032. {
  1033.     JSObject *pobj;
  1034.     PRHashNumber hash;
  1035.     JSScope *prevscope, *scope;
  1036.     JSSymbol *sym;
  1037.     JSClass *clasp;
  1038.     JSResolveOp resolve;
  1039.     JSNewResolveOp newresolve;
  1040.  
  1041.     PR_ASSERT(JS_IS_LOCKED(cx));
  1042.  
  1043.     /* Handle old bug that treated empty string as zero index. */
  1044.     CHECK_FOR_FUNNY_INDEX(id);
  1045.  
  1046.     /* Search scopes starting with obj and following the prototype link. */
  1047.     hash = js_HashValue(id);
  1048.     prevscope = NULL;
  1049.     do {
  1050.     scope = (JSScope *)obj->map;
  1051.     if (scope == prevscope)
  1052.         continue;
  1053.     sym = scope->ops->lookup(cx, scope, id, hash);
  1054.     if (!sym && objp) {
  1055.         clasp = scope->map.clasp;
  1056.         resolve = clasp->resolve;
  1057.         if (resolve != JS_ResolveStub) {
  1058.         if (clasp->flags & JSCLASS_NEW_RESOLVE) {
  1059.             newresolve = (JSNewResolveOp)resolve;
  1060.             pobj = NULL;
  1061.             if (!newresolve(cx, obj, js_IdToValue(id), &pobj))
  1062.             return JS_FALSE;
  1063.             if (pobj) {
  1064.             *objp = pobj;
  1065.             scope = (JSScope *)pobj->map;
  1066.             sym = scope->ops->lookup(cx, scope, id, hash);
  1067.             }
  1068.         } else {
  1069.             if (!resolve(cx, obj, js_IdToValue(id)))
  1070.             return JS_FALSE;
  1071.             scope = (JSScope *)obj->map;
  1072.             sym = scope->ops->lookup(cx, scope, id, hash);
  1073.         }
  1074.         }
  1075.     }
  1076.     if (sym) {
  1077.         *propp = sym_property(sym);
  1078.         return JS_TRUE;
  1079.     }
  1080.     prevscope = scope;
  1081.     } while ((obj = OBJ_GET_PROTO(obj)) != NULL);
  1082.     *propp = NULL;
  1083.     return JS_TRUE;
  1084. }
  1085.  
  1086. JSBool
  1087. js_FindProperty(JSContext *cx, jsval id, JSObject **objp, JSProperty **propp)
  1088. {
  1089.     JSRuntime *rt;
  1090.     JSObject *obj, *parent, *lastobj;
  1091.     JSProperty *prop;
  1092.  
  1093.     rt = cx->runtime;
  1094.     PR_ASSERT(JS_IS_RUNTIME_LOCKED(rt));
  1095.  
  1096.     for (obj = cx->fp->scopeChain; obj; obj = parent) {
  1097.     /* Try the property cache and return immediately on cache hit. */
  1098.     PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, prop);
  1099.     if (PROP_FOUND(prop)) {
  1100.         *objp = obj;
  1101.         *propp = prop;
  1102.         return JS_TRUE;
  1103.     }
  1104.  
  1105.     /*
  1106.      * Set parent here, after cache hit to minimize cycles in that case,
  1107.      * but before js_LookupProperty, which might change obj.
  1108.      */
  1109.     parent = OBJ_GET_PARENT(obj);
  1110.  
  1111.     /* If cache miss (not cached-as-not-found), take the slow path. */
  1112.     if (!prop) {
  1113.         if (!js_LookupProperty(cx, obj, id, &obj, &prop))
  1114.         return JS_FALSE;
  1115.         if (prop) {
  1116.         PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id, prop);
  1117.         *objp = obj;
  1118.         *propp = prop;
  1119.         return JS_TRUE;
  1120.         }
  1121.  
  1122.         /* No such property -- cache obj[id] as not-found. */
  1123.         PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id,
  1124.                 PROP_NOT_FOUND);
  1125.     }
  1126.     lastobj = obj;
  1127.     }
  1128.     *objp = lastobj;
  1129.     *propp = NULL;
  1130.     return JS_TRUE;
  1131. }
  1132.  
  1133. JSBool
  1134. js_FindVariable(JSContext *cx, jsval id, JSObject **objp, JSProperty **propp)
  1135. {
  1136.     JSRuntime *rt;
  1137.     JSObject *obj;
  1138.     JSProperty *prop;
  1139.  
  1140.     rt = cx->runtime;
  1141.     PR_ASSERT(JS_IS_RUNTIME_LOCKED(rt));
  1142.  
  1143.     /*
  1144.      * First look for id's property along the "with" statement and the
  1145.      * statically-linked scope chains.
  1146.      */
  1147.     if (!js_FindProperty(cx, id, objp, propp))
  1148.     return JS_FALSE;
  1149.     if (*propp)
  1150.     return JS_TRUE;
  1151.  
  1152.     /*
  1153.      * Use the top-level scope from the scope chain, which won't end in the
  1154.      * same scope as cx->globalObject's for cross-context function calls.
  1155.      */
  1156.     obj = *objp;
  1157.     PR_ASSERT(obj);
  1158.  
  1159.     /*
  1160.      * Make a top-level variable.
  1161.      */
  1162.     prop = js_DefineProperty(cx, obj, id, JSVAL_VOID, NULL, NULL,
  1163.                  JSPROP_ENUMERATE);
  1164.     if (!prop)
  1165.     return JS_FALSE;
  1166.     PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id, prop);
  1167.     *propp = prop;
  1168.     return JS_TRUE;
  1169. }
  1170.  
  1171. JSObject *
  1172. js_FindVariableScope(JSContext *cx, JSFunction **funp)
  1173. {
  1174.     JSStackFrame *fp;
  1175.     JSObject *obj, *parent, *withobj;
  1176.     JSClass *clasp;
  1177.     JSFunction *fun;
  1178.  
  1179.     PR_ASSERT(JS_IS_LOCKED(cx));
  1180.  
  1181.     fp = cx->fp;
  1182.     for (obj = fp->scopeChain, withobj = NULL; ; obj = parent) {
  1183.     parent = OBJ_GET_PARENT(obj);
  1184.     clasp = obj->map->clasp;
  1185.     if (!parent || clasp != &js_WithClass)
  1186.         break;
  1187.     withobj = obj;
  1188.     }
  1189.  
  1190.     fun = (clasp == &js_FunctionClass) ? JS_GetPrivate(cx, obj) : NULL;
  1191. #if JS_HAS_CALL_OBJECT
  1192.     if (fun && fun->script) {
  1193.     for (; fp && fp->fun != fun; fp = fp->down)
  1194.         ;
  1195.     if (fp) {
  1196.         obj = js_GetCallObject(cx, fp, parent);
  1197.         if (withobj)
  1198.         OBJ_SET_PARENT(withobj, obj);
  1199.     }
  1200.     }
  1201. #endif
  1202.  
  1203.     *funp = fun;
  1204.     return obj;
  1205. }
  1206.  
  1207. JSProperty *
  1208. js_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  1209. {
  1210.     JSProperty *prop;
  1211.     JSObject *obj2;
  1212.     jsint slot;
  1213.  
  1214.     PR_ASSERT(JS_IS_LOCKED(cx));
  1215.  
  1216.     if (!js_LookupProperty(cx, obj, id, &obj, &prop))
  1217.     return NULL;
  1218.     if (!prop) {
  1219.     prop = js_DefineProperty(cx, obj, id,
  1220. #if JS_BUG_NULL_INDEX_PROPS
  1221.                  (JSVAL_IS_INT(id) && JSVAL_TO_INT(id) >= 0)
  1222.                  ? JSVAL_NULL
  1223.                  : JSVAL_VOID,
  1224. #else
  1225.                  JSVAL_VOID,
  1226. #endif
  1227.                  NULL, NULL, 0);
  1228.     if (!prop)
  1229.         return NULL;
  1230.     }
  1231.     obj2 = prop->object;
  1232.     slot = prop->slot;
  1233.     *vp = obj2->slots[slot];
  1234.     if (!prop->getter(cx, obj, prop->id, vp))
  1235.     return NULL;
  1236.     obj2->slots[slot] = *vp;
  1237.     return prop;
  1238. }
  1239.  
  1240. JSProperty *
  1241. js_SetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  1242. {
  1243.     JSRuntime *rt;
  1244.     JSScope *scope, *protoScope;
  1245.     JSProperty *prop, *protoProp;
  1246.     PRHashNumber hash;
  1247.     JSSymbol *sym, *protoSym;
  1248.     JSObject *proto, *assignobj;
  1249.     jsval pval, aval, rval;
  1250.     jsint slot;
  1251.     JSErrorReporter older;
  1252.     JSString *str;
  1253.  
  1254.     rt = cx->runtime;
  1255.     PR_ASSERT(JS_IS_RUNTIME_LOCKED(rt));
  1256.  
  1257. #ifdef SCOPE_TABLE_NOTYET
  1258.     /* XXX hash recompute */
  1259.     /* XXX protoProp->getter, protoProp->setter, protoProp->flags */
  1260.     scope = js_MutateScope(cx, obj, id, getter, setter, flags, &prop);
  1261. #endif
  1262.     scope = js_GetMutableScope(cx, obj);
  1263.     if (!scope)
  1264.     return NULL;
  1265.  
  1266.     /* Handle old bug that treated empty string as zero index. */
  1267.     CHECK_FOR_FUNNY_INDEX(id);
  1268.  
  1269.     hash = js_HashValue(id);
  1270.     sym = scope->ops->lookup(cx, scope, id, hash);
  1271.     if (sym) {
  1272.     prop = sym_property(sym);
  1273. #if JS_HAS_OBJ_WATCHPOINT
  1274.     if (!prop) {
  1275.         /*
  1276.          * Deleted property place-holder, could have a watchpoint that
  1277.          * holds the deleted-but-watched property.
  1278.          */
  1279.         prop = js_FindWatchPoint(rt, obj, js_IdToValue(id));
  1280.     }
  1281. #endif
  1282.     } else {
  1283.     prop = NULL;
  1284.     }
  1285.  
  1286.     if (!prop) {
  1287.     /* Find a prototype property with the same id. */
  1288.     proto = OBJ_GET_PROTO(obj);
  1289.     protoProp = NULL;
  1290.     while (proto) {
  1291.         protoScope = (JSScope *)proto->map;
  1292.         protoSym = protoScope->ops->lookup(cx, protoScope, id, hash);
  1293.         if (protoSym) {
  1294.         protoProp = sym_property(protoSym);
  1295.         if (protoProp)
  1296.             break;
  1297.         }
  1298.         proto = OBJ_GET_PROTO(proto);
  1299.     }
  1300.  
  1301.     /* Make a new property descriptor with the right heritage. */
  1302.     if (protoProp) {
  1303.         prop = js_NewProperty(cx, scope, id,
  1304.                   protoProp->getter, protoProp->setter,
  1305.                   protoProp->flags | JSPROP_ENUMERATE);
  1306.         prop->id = protoProp->id;
  1307.     } else {
  1308.         prop = js_NewProperty(cx, scope, id,
  1309.                   scope->map.clasp->getProperty,
  1310.                   scope->map.clasp->setProperty,
  1311.                   JSPROP_ENUMERATE);
  1312.     }
  1313.     if (!prop)
  1314.         return NULL;
  1315.  
  1316.     if (!obj->map->clasp->addProperty(cx, obj, prop->id, vp)) {
  1317.         js_DestroyProperty(cx, prop);
  1318.         return NULL;
  1319.     }
  1320.  
  1321.     /* Initialize new properties to undefined. */
  1322.     obj->slots[prop->slot] = JSVAL_VOID;
  1323.  
  1324.     if (sym) {
  1325.         /* Null-valued symbol left behind from a delete operation. */
  1326.         sym->entry.value = js_HoldProperty(cx, prop);
  1327.     }
  1328.     }
  1329.  
  1330.     if (!sym) {
  1331.     /* Need a new symbol as well as a new property. */
  1332.     sym = scope->ops->add(cx, scope, id, prop);
  1333.     if (!sym)
  1334.         return NULL;
  1335. #if JS_BUG_AUTO_INDEX_PROPS
  1336.     {
  1337.         jsval id2 = INT_TO_JSVAL(prop->slot - JSSLOT_START);
  1338.         if (!scope->ops->add(cx, scope, id2, prop)) {
  1339.         scope->ops->remove(cx, scope, id);
  1340.         return NULL;
  1341.         }
  1342.         PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id2, prop);
  1343.     }
  1344. #endif
  1345.     PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id, prop);
  1346.     }
  1347.  
  1348.     /* Get the current property value from its slot. */
  1349.     PR_ASSERT(prop->slot < obj->map->freeslot);
  1350.     slot = prop->slot;
  1351.     pval = obj->slots[slot];
  1352.  
  1353.     /* Evil overloaded operator assign() hack. */
  1354.     if (JSVAL_IS_OBJECT(pval)) {
  1355.     assignobj = JSVAL_TO_OBJECT(pval);
  1356.     if (assignobj) {
  1357.         older = JS_SetErrorReporter(cx, NULL);
  1358.         if (js_GetProperty(cx, assignobj, (jsval)rt->atomState.assignAtom,
  1359.                    &aval) &&
  1360.         JSVAL_IS_FUNCTION(aval) &&
  1361.         js_Call(cx, assignobj, aval, 1, vp, &rval)) {
  1362.         *vp = rval;
  1363.         JS_SetErrorReporter(cx, older);
  1364.         prop->flags |= JSPROP_ASSIGNHACK;
  1365.         return prop;
  1366.         }
  1367.         JS_SetErrorReporter(cx, older);
  1368.     }
  1369.     }
  1370.  
  1371.     /* Check for readonly *after* the assign() hack. */
  1372.     if (prop->flags & JSPROP_READONLY) {
  1373.     if (!JSVERSION_IS_ECMA(cx->version)) {
  1374.         str = js_ValueToSource(cx, js_IdToValue(id));
  1375.         if (str) {
  1376.         JS_ReportError(cx, "%s is read-only",
  1377.                    JS_GetStringBytes(str));
  1378.         }
  1379.     }
  1380.     return NULL;
  1381.     }
  1382.  
  1383.     /* Let the setter modify vp before copying from it to obj->slots[slot]. */
  1384.     if (!prop->setter(cx, obj, prop->id, vp))
  1385.     return NULL;
  1386.     GC_POKE(cx, pval);
  1387.     obj->slots[slot] = *vp;
  1388.  
  1389.     /* Setting a property makes it enumerable. */
  1390.     prop->flags |= JSPROP_ENUMERATE;
  1391.     return prop;
  1392. }
  1393.  
  1394. JSBool
  1395. js_DeleteProperty(JSContext *cx, JSObject *obj, jsval id, jsval *rval)
  1396. {
  1397. #if JS_HAS_PROP_DELETE
  1398.     JSProperty *prop;
  1399.  
  1400.     PR_ASSERT(JS_IS_LOCKED(cx));
  1401.  
  1402.     if (!js_LookupProperty(cx, obj, id, NULL, &prop))
  1403.     return JS_FALSE;
  1404.     if (!prop)
  1405.     return JS_TRUE;
  1406.     return js_DeleteProperty2(cx, obj, prop, id, rval);
  1407. #else
  1408.     jsval null = JSVAL_NULL;
  1409.  
  1410.     *rval = JSVAL_VOID;
  1411.     return (js_SetProperty(cx, obj, id, &null) != NULL);
  1412. #endif
  1413. }
  1414.  
  1415. JSBool
  1416. js_DeleteProperty2(JSContext *cx, JSObject *obj, JSProperty *prop, jsval id,
  1417.            jsval *rval)
  1418. {
  1419. #if JS_HAS_PROP_DELETE
  1420.     JSRuntime *rt;
  1421.     JSString *str;
  1422.     JSScope *scope;
  1423.     JSObject *proto;
  1424.     PRHashNumber hash;
  1425.     JSSymbol *sym;
  1426.  
  1427.     rt = cx->runtime;
  1428.     PR_ASSERT(JS_IS_RUNTIME_LOCKED(rt));
  1429.  
  1430.     *rval = JSVERSION_IS_ECMA(cx->version) ? JSVAL_TRUE : JSVAL_VOID;
  1431.  
  1432.     if (prop->flags & JSPROP_PERMANENT) {
  1433.     if (JSVERSION_IS_ECMA(cx->version)) {
  1434.         *rval = JSVAL_FALSE;
  1435.         return JS_TRUE;
  1436.     }
  1437.     str = js_ValueToSource(cx, js_IdToValue(id));
  1438.     if (str)
  1439.         JS_ReportError(cx, "%s is permanent", JS_GetStringBytes(str));
  1440.     return JS_FALSE;
  1441.     }
  1442.  
  1443.     if (!obj->map->clasp->delProperty(cx, obj, prop->id,
  1444.                       &prop->object->slots[prop->slot])) {
  1445.     return JS_FALSE;
  1446.     }
  1447.  
  1448.     /* Handle old bug that treated empty string as zero index. */
  1449.     CHECK_FOR_FUNNY_INDEX(id);
  1450.  
  1451.     GC_POKE(cx, prop->object->slots[prop->slot]);
  1452.     scope = (JSScope *)obj->map;
  1453.     proto = scope->object;
  1454.     if (proto == obj) {
  1455.     /* The object has its own scope, so remove id if it was found here. */
  1456.     if (prop->object == obj) {
  1457.         /* Purge cache only if prop is not about to be destroyed. */
  1458.         if (prop->nrefs != 1) {
  1459.         PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id,
  1460.                     PROP_NOT_FOUND);
  1461.         }
  1462. #if JS_HAS_OBJ_WATCHPOINT
  1463.         if (prop->setter == js_watch_set) {
  1464.         /*
  1465.          * Keep the symbol around with null value in case of re-set.
  1466.          * The watchpoint will hold the "deleted" property until it
  1467.          * is removed by obj_unwatch or a native JS_ClearWatchPoint.
  1468.          * See js_SetProperty for the re-set logic.
  1469.          */
  1470.         for (sym = prop->symbols; sym; sym = sym->next) {
  1471.             if (sym_id(sym) == id) {
  1472.             sym->entry.value = NULL;
  1473.             prop = js_DropProperty(cx, prop);
  1474.             PR_ASSERT(prop);
  1475.             return JS_TRUE;
  1476.             }
  1477.         }
  1478.         }
  1479. #endif
  1480.         scope->ops->remove(cx, scope, id);
  1481.     }
  1482.     proto = OBJ_GET_PROTO(obj);
  1483.     if (!proto)
  1484.         return JS_TRUE;
  1485.     }
  1486.  
  1487.     /* Search shared prototype scopes for an inherited property to hide. */
  1488.     hash = js_HashValue(id);
  1489.     do {
  1490.     scope = (JSScope *)proto->map;
  1491.     sym = scope->ops->lookup(cx, scope, id, hash);
  1492.     if (sym) {
  1493.         /* Add a null-valued symbol to hide the prototype property. */
  1494.         scope = js_GetMutableScope(cx, obj);
  1495.         if (!scope)
  1496.         return JS_FALSE;
  1497.         if (!scope->ops->add(cx, scope, id, NULL))
  1498.         return JS_FALSE;
  1499.         PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id,
  1500.                 PROP_NOT_FOUND);
  1501.         return JS_TRUE;
  1502.     }
  1503.     proto = OBJ_GET_PROTO(proto);
  1504.     } while (proto);
  1505.     return JS_TRUE;
  1506. #else
  1507.     jsval null = JSVAL_NULL;
  1508.     return (js_SetProperty(cx, obj, id, &null) != NULL);
  1509. #endif
  1510. }
  1511.  
  1512. JSBool
  1513. js_GetClassPrototype(JSContext *cx, JSClass *clasp, JSObject **protop)
  1514. {
  1515.     JSBool ok;
  1516.     JSObject *ctor;
  1517.     jsval pval;
  1518.     JSProperty *prop;
  1519.  
  1520.     *protop = NULL;
  1521.     JS_LOCK(cx);
  1522.     ok = FindConstructor(cx, clasp, &pval);
  1523.     if (!ok || !JSVAL_IS_FUNCTION(pval))
  1524.     goto out;
  1525.     ctor = JSVAL_TO_OBJECT(pval);
  1526.     if (!js_LookupProperty(cx, ctor,
  1527.                (jsval)cx->runtime->atomState.classPrototypeAtom,
  1528.                NULL, &prop)) {
  1529.     ok = JS_FALSE;
  1530.     goto out;
  1531.     }
  1532.     if (prop) {
  1533.     pval = prop->object->slots[prop->slot];
  1534.     if (JSVAL_IS_OBJECT(pval))
  1535.         *protop = JSVAL_TO_OBJECT(pval);
  1536.     }
  1537. out:
  1538.     JS_UNLOCK(cx);
  1539.     return ok;
  1540. }
  1541.  
  1542. JSBool
  1543. js_SetClassPrototype(JSContext *cx, JSFunction *fun, JSObject *obj)
  1544. {
  1545.     JSBool ok;
  1546.  
  1547.     ok = JS_TRUE;
  1548.     JS_LOCK(cx);
  1549.     if (!js_DefineProperty(cx, fun->object,
  1550.                (jsval)cx->runtime->atomState.classPrototypeAtom,
  1551.                OBJECT_TO_JSVAL(obj), NULL, NULL,
  1552.                JSPROP_READONLY | JSPROP_PERMANENT)) {
  1553.     ok = JS_FALSE;
  1554.     goto out;
  1555.     }
  1556.     if (!js_DefineProperty(cx, obj,
  1557.                (jsval)cx->runtime->atomState.constructorAtom,
  1558.                OBJECT_TO_JSVAL(fun->object), NULL, NULL,
  1559.                JSPROP_READONLY | JSPROP_PERMANENT)) {
  1560.     ok = JS_FALSE;
  1561.     goto out;
  1562.     }
  1563. out:
  1564.     JS_UNLOCK(cx);
  1565.     return ok;
  1566. }
  1567.  
  1568. JSString *
  1569. js_ObjectToString(JSContext *cx, JSObject *obj)
  1570. {
  1571.     jsval v, *mark, *argv;
  1572.     JSString *str;
  1573.  
  1574.     if (!obj)
  1575.     return ATOM_TO_STRING(cx->runtime->atomState.nullAtom);
  1576.     v = JSVAL_VOID;
  1577.     if (!obj->map->clasp->convert(cx, obj, JSTYPE_STRING, &v))
  1578.     return NULL;
  1579.     if (JSVAL_IS_STRING(v))
  1580.     return JSVAL_TO_STRING(v);
  1581.  
  1582.     /* Try the toString method if it's defined. */
  1583.     js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, NULL, &v);
  1584.     if (JSVAL_IS_STRING(v))
  1585.     return JSVAL_TO_STRING(v);
  1586. #if JS_BUG_EAGER_TOSTRING
  1587.     js_TryValueOf(cx, obj, JSTYPE_STRING, &v);
  1588.     if (JSVAL_IS_STRING(v))
  1589.     return JSVAL_TO_STRING(v);
  1590. #endif
  1591.     mark = PR_ARENA_MARK(&cx->stackPool);
  1592.     PR_ARENA_ALLOCATE(argv, &cx->stackPool, OBJ_TOSTRING_NARGS * sizeof(jsval));
  1593.     if (!argv || !js_obj_toString(cx, obj, OBJ_TOSTRING_NARGS, argv, &v))
  1594.     str = NULL;
  1595.     else
  1596.     str = JSVAL_TO_STRING(v);
  1597.     PR_ARENA_RELEASE(&cx->stackPool, mark);
  1598.     return str;
  1599. }
  1600.  
  1601. JSBool
  1602. js_ValueToObject(JSContext *cx, jsval v, JSObject **objp)
  1603. {
  1604.     JSObject *obj;
  1605.  
  1606.     if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) {
  1607.     obj = NULL;
  1608.     } else if (JSVAL_IS_OBJECT(v)) {
  1609.     obj = JSVAL_TO_OBJECT(v);
  1610.     if (!obj->map->clasp->convert(cx, obj, JSTYPE_OBJECT, &v))
  1611.         return JS_FALSE;
  1612.     if (JSVAL_IS_OBJECT(v))
  1613.         obj = JSVAL_TO_OBJECT(v);
  1614.     } else {
  1615.     if (JSVAL_IS_STRING(v)) {
  1616.         obj = js_StringToObject(cx, JSVAL_TO_STRING(v));
  1617.     } else if (JSVAL_IS_INT(v)) {
  1618.         obj = js_NumberToObject(cx, (jsdouble)JSVAL_TO_INT(v));
  1619.     } else if (JSVAL_IS_DOUBLE(v)) {
  1620.         obj = js_NumberToObject(cx, *JSVAL_TO_DOUBLE(v));
  1621.     } else {
  1622.         PR_ASSERT(JSVAL_IS_BOOLEAN(v));
  1623.         obj = js_BooleanToObject(cx, JSVAL_TO_BOOLEAN(v));
  1624.     }
  1625.     if (!obj)
  1626.         return JS_FALSE;
  1627.     }
  1628.     *objp = obj;
  1629.     return JS_TRUE;
  1630. }
  1631.  
  1632. JSObject *
  1633. js_ValueToNonNullObject(JSContext *cx, jsval v)
  1634. {
  1635.     JSObject *obj;
  1636.     JSString *name;
  1637.  
  1638.     if (!js_ValueToObject(cx, v, &obj))
  1639.     return NULL;
  1640.     if (!obj) {
  1641.     name = js_ValueToSource(cx, v);
  1642.     if (name) {
  1643.         JS_ReportError(cx, "%s has no properties",
  1644.                JS_GetStringBytes(name));
  1645.     }
  1646.     }
  1647.     return obj;
  1648. }
  1649.  
  1650. void
  1651. js_TryValueOf(JSContext *cx, JSObject *obj, JSType type, jsval *rval)
  1652. {
  1653. #if JS_HAS_VALUEOF_HINT
  1654.     jsval argv[1];
  1655.  
  1656.     argv[0] = ATOM_KEY(cx->runtime->atomState.typeAtoms[type]);
  1657.     js_TryMethod(cx, obj, cx->runtime->atomState.valueOfAtom, 1, argv, rval);
  1658. #else
  1659.     js_TryMethod(cx, obj, cx->runtime->atomState.valueOfAtom, 0, NULL, rval);
  1660. #endif
  1661. }
  1662.  
  1663. void
  1664. js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom,
  1665.          uintN argc, jsval *argv, jsval *rval)
  1666. {
  1667.     JSErrorReporter older;
  1668.     JSProperty *prop;
  1669.     jsval fval;
  1670.  
  1671.     older = JS_SetErrorReporter(cx, NULL);
  1672.     JS_LOCK_VOID(cx, prop = js_GetProperty(cx, obj, (jsval)atom, &fval));
  1673.     if (prop &&
  1674.     JSVAL_IS_OBJECT(fval) &&
  1675.     fval != JSVAL_NULL) {
  1676.     (void) js_Call(cx, obj, fval, argc, argv, rval);
  1677.     }
  1678.     JS_SetErrorReporter(cx, older);
  1679. }
  1680.