home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / js / src / jsarray.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  28.9 KB  |  1,238 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 array class.
  21.  */
  22. #include <stdlib.h>
  23. #include <string.h>
  24. #include "prtypes.h"
  25. #include "prlog.h"
  26. #include "prprf.h"
  27. #include "jsapi.h"
  28. #include "jsarray.h"
  29. #include "jsatom.h"
  30. #include "jscntxt.h"
  31. #include "jsconfig.h"
  32. #include "jsfun.h"
  33. #include "jsgc.h"
  34. #include "jsinterp.h"
  35. #include "jslock.h"
  36. #include "jsnum.h"
  37. #include "jsobj.h"
  38. #include "jsopcode.h"
  39. #include "jsscope.h"
  40. #include "jsstr.h"
  41.  
  42. static JSBool
  43. ValueToLength(JSContext *cx, jsval v, jsint *lengthp)
  44. {
  45.     jsint i;
  46.     jsdouble d;
  47.  
  48.     if (JSVAL_IS_INT(v)) {
  49.     i = JSVAL_TO_INT(v);
  50.     *lengthp = (i < 0) ? 0 : i;
  51.     } else {
  52.     if (JSVAL_IS_DOUBLE(v)) {
  53.         d = *JSVAL_TO_DOUBLE(v);
  54.     } else {
  55.         if (!js_ValueToNumber(cx, v, &d))
  56.         return JS_FALSE;
  57.     }
  58.     *lengthp = (d < 0 || d > JSVAL_INT_MAX || JSDOUBLE_IS_NaN(d))
  59.            ? 0
  60.            : (jsint)d;
  61.     }
  62.     return JS_TRUE;
  63. }
  64.  
  65. static JSProperty *
  66. GetLengthProperty(JSContext *cx, JSObject *obj, jsint *lengthp)
  67. {
  68.     JSProperty *prop;
  69.     jsval v;
  70.  
  71.     PR_ASSERT(JS_IS_LOCKED(cx));
  72.     prop = js_GetProperty(cx, obj,
  73.               (jsval)cx->runtime->atomState.lengthAtom,
  74.               &v);
  75.     if (!prop || !ValueToLength(cx, v, lengthp))
  76.     return NULL;
  77.     return prop;
  78. }
  79.  
  80. static JSProperty *
  81. SetLengthProperty(JSContext *cx, JSObject *obj, jsint length)
  82. {
  83.     jsval v;
  84.  
  85.     PR_ASSERT(JS_IS_LOCKED(cx));
  86.     v = INT_TO_JSVAL(length);
  87.     return js_SetProperty(cx, obj,
  88.               (jsval)cx->runtime->atomState.lengthAtom,
  89.               &v);
  90. }
  91.  
  92. static JSBool
  93. array_length_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  94. {
  95.     JSBool ok;
  96.     jsint newlen, oldlen, slot;
  97.     jsval junk;
  98.  
  99.     JS_LOCK(cx);
  100.     ok = ValueToLength(cx, *vp, &newlen);
  101.     if (!ok)
  102.     goto out;
  103.     if (!GetLengthProperty(cx, obj, &oldlen)) {
  104.     ok = JS_FALSE;
  105.     goto out;
  106.     }
  107.     for (slot = newlen; slot < oldlen; slot++) {
  108.     ok = js_DeleteProperty(cx, obj, INT_TO_JSVAL(slot), &junk);
  109.     if (!ok)
  110.         break;
  111.     }
  112. out:
  113.     JS_UNLOCK(cx);
  114.     return ok;
  115. }
  116.  
  117. static JSBool
  118. array_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  119. {
  120.     jsint index, length;
  121.     JSProperty *prop;
  122.  
  123.     if (!JSVAL_IS_INT(id))
  124.     return JS_TRUE;
  125.     index = JSVAL_TO_INT(id);
  126.     JS_LOCK(cx);
  127.     prop = GetLengthProperty(cx, obj, &length);
  128.     if (prop && index >= length) {
  129.     length = index + 1;
  130.     if (prop->object == obj && prop->setter == array_length_setter)
  131.         obj->slots[prop->slot] = INT_TO_JSVAL(length);
  132.     else
  133.         prop = SetLengthProperty(cx, obj, length);
  134.     }
  135.     JS_UNLOCK(cx);
  136.     return (prop != NULL);
  137. }
  138.  
  139. static JSBool
  140. array_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  141. {
  142.     jsint index, length;
  143.     JSProperty *prop;
  144.  
  145.     if (!JSVAL_IS_INT(id))
  146.     return JS_TRUE;
  147.     index = JSVAL_TO_INT(id);
  148.     JS_LOCK(cx);
  149.     prop = GetLengthProperty(cx, obj, &length);
  150.     if (prop && index == length - 1) {
  151.     length = index;
  152.     if (prop->object == obj && prop->setter == array_length_setter)
  153.         obj->slots[prop->slot] = INT_TO_JSVAL(length);
  154.     else
  155.         prop = SetLengthProperty(cx, obj, length);
  156.     }
  157.     JS_UNLOCK(cx);
  158.     return (prop != NULL);
  159. }
  160.  
  161. static JSBool
  162. array_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
  163. {
  164.     JSProperty *prop;
  165.     jsint length;
  166.  
  167.     JS_LOCK_VOID(cx, prop = GetLengthProperty(cx, obj, &length));
  168.     if (!prop)
  169.     return JS_FALSE;
  170.     switch (type) {
  171.       case JSTYPE_NUMBER:
  172.     *vp = INT_TO_JSVAL(length);
  173.     return JS_TRUE;
  174.       case JSTYPE_BOOLEAN:
  175.     *vp = BOOLEAN_TO_JSVAL(length > 0);
  176.     return JS_TRUE;
  177.       default:
  178.     return JS_TRUE;
  179.     }
  180. }
  181.  
  182. JSClass js_ArrayClass = {
  183.     "Array",
  184.     0,
  185.     array_addProperty, array_delProperty, JS_PropertyStub,   JS_PropertyStub,
  186.     JS_EnumerateStub,  JS_ResolveStub,    array_convert,     JS_FinalizeStub
  187. };
  188.  
  189. static JSBool
  190. array_join_sub(JSContext *cx, JSObject *obj, JSString *sep, JSBool literalize,
  191.            jsval *rval)
  192. {
  193.     JSProperty *prop;
  194.     jsval v;
  195.     jsint length, index;
  196.     jschar *chars;
  197.     size_t nchars, growth, seplen;
  198.     const jschar *sepstr;
  199.     const char *quote;
  200.     JSBool ok;
  201.     JSString *str;
  202. #if JS_HAS_SHARP_VARS
  203.     PRHashEntry *he;
  204. #else
  205.     jsval lenval;
  206. #endif
  207.  
  208.     PR_ASSERT(JS_IS_LOCKED(cx));
  209.     prop = GetLengthProperty(cx, obj, &length);
  210.     if (!prop)
  211.     return JS_FALSE;
  212. #if !JS_HAS_SHARP_VARS
  213.     lenval = INT_TO_JSVAL(length);
  214. #endif
  215.  
  216.     ok = JS_TRUE;
  217.     if (literalize) {
  218. #if JS_HAS_SHARP_VARS
  219.     he = js_EnterSharpObject(cx, obj, &chars);
  220.     if (!he)
  221.         return JS_FALSE;
  222.     if (IS_SHARP(he)) {
  223.         nchars = js_strlen(chars);
  224.         goto make_string;
  225.     }
  226.     MAKE_SHARP(he);
  227. #else
  228.     chars = NULL;
  229. #endif
  230.     /*
  231.      * Allocate 1 + 3 + 1 for "[", the worst-case closing ", ]", and the
  232.      * terminating 0.
  233.      */
  234.     growth = (1 + 3 + 1) * sizeof(jschar);
  235.     if (!chars) {
  236.         nchars = 0;
  237.         chars = malloc(growth);
  238.     } else {
  239.         nchars = js_strlen(chars);
  240.         chars = realloc(chars, nchars * sizeof(jschar) + growth);
  241.     }
  242.     if (!chars)
  243.         goto done;
  244.     chars[nchars++] = '[';
  245.     } else {
  246.     if (length == 0) {
  247.         *rval = JS_GetEmptyStringValue(cx);
  248.         return ok;
  249.     }
  250.     chars = NULL;
  251.     nchars = 0;
  252.     }
  253.     sepstr = NULL;
  254.     seplen = sep->length;
  255.  
  256.     v = JSVAL_NULL;
  257.     for (index = 0; index < length; index++) {
  258.     ok = JS_GetElement(cx, obj, index, &v);
  259.     if (!ok)
  260.         goto done;
  261.  
  262.     /* If v is a string, it needs to be quoted and escaped. */
  263.     quote = NULL;
  264.     if (JSVAL_IS_VOID(v)) {
  265.         str = cx->runtime->emptyString;
  266.     } else {
  267. #if !JS_HAS_SHARP_VARS
  268.         /* Avoid recursive divergence by setting length to 0. */
  269.         obj->slots[prop->slot] = JSVAL_ZERO;
  270. #endif
  271.         str = js_ValueToString(cx, v);
  272. #if !JS_HAS_SHARP_VARS
  273.         obj->slots[prop->slot] = lenval;
  274. #endif
  275.         if (!str) {
  276.         ok = JS_FALSE;
  277.         goto done;
  278.         }
  279.         if (literalize && JSVAL_IS_STRING(v)) {
  280.         quote = "\"";
  281.         str = js_EscapeString(cx, str, *quote);
  282.         if (!str) {
  283.             ok = JS_FALSE;
  284.             goto done;
  285.         }
  286.         }
  287.     }
  288.  
  289.     /* Allocate 3 + 1 at end for ", ", closing bracket, and zero. */
  290.     growth = (nchars + (sepstr ? seplen : 0) +
  291.           (quote ? 2 : 0) + str->length +
  292.           3 + 1) * sizeof(jschar);
  293.     chars = chars ? realloc(chars, growth) : malloc(growth);
  294.     if (!chars)
  295.         goto done;
  296.  
  297.     if (sepstr) {
  298.         js_strncpy(&chars[nchars], sepstr, seplen);
  299.         nchars += seplen;
  300.     }
  301.     sepstr = sep->chars;
  302.  
  303.     if (quote)
  304.         chars[nchars++] = *quote;
  305.     js_strncpy(&chars[nchars], str->chars, str->length);
  306.     nchars += str->length;
  307.     if (quote)
  308.         chars[nchars++] = *quote;
  309.     }
  310.  
  311. done:
  312.     if (literalize) {
  313.     if (chars) {
  314.         if (JSVAL_IS_VOID(v)) {
  315.         chars[nchars++] = ',';
  316.         chars[nchars++] = ' ';
  317.         }
  318.         chars[nchars++] = ']';
  319.     }
  320. #if JS_HAS_SHARP_VARS
  321.     js_LeaveSharpObject(cx);
  322. #endif
  323.     }
  324.     if (!ok) {
  325.     if (chars)
  326.         free(chars);
  327.     return ok;
  328.     }
  329.  
  330. #if JS_HAS_SHARP_VARS
  331.   make_string:
  332. #endif
  333.     if (!chars) {
  334.     JS_ReportOutOfMemory(cx);
  335.     return JS_FALSE;
  336.     }
  337.     chars[nchars] = 0;
  338.     str = js_NewString(cx, chars, nchars, 0);
  339.     if (!str) {
  340.     free(chars);
  341.     return JS_FALSE;
  342.     }
  343.     *rval = STRING_TO_JSVAL(str);
  344.     return JS_TRUE;
  345. }
  346.  
  347. static jschar   comma_space_ucstr[] = {',', ' ', 0};
  348. static jschar   comma_ucstr[]       = {',', 0};
  349. static JSString comma_space         = {2, comma_space_ucstr};
  350. static JSString comma               = {1, comma_ucstr};
  351.  
  352. static JSBool
  353. array_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
  354.            jsval *rval)
  355. {
  356.     JSBool literalize;
  357.  
  358.     /*
  359.      * JS1.2 arrays convert to array literals, with a comma followed by a space
  360.      * between each element.
  361.      */
  362.     literalize = (cx->version >= JSVERSION_1_2);
  363.     JS_LOCK_AND_RETURN_BOOL(cx,
  364.     array_join_sub(cx, obj,
  365.                literalize ? &comma_space : &comma,
  366.                literalize, rval));
  367. }
  368.  
  369. static JSBool
  370. array_join(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  371. {
  372.     JSString *str;
  373.     JSBool ok;
  374.  
  375.     JS_LOCK(cx);
  376.     if (argc == 0) {
  377.     ok = array_join_sub(cx, obj, &comma, JS_FALSE, rval);
  378.     } else {
  379.     str = js_ValueToString(cx, argv[0]);
  380.     if (!str) {
  381.         ok = JS_FALSE;
  382.     } else {
  383.         argv[0] = STRING_TO_JSVAL(str);
  384.         ok = array_join_sub(cx, obj, str, JS_FALSE, rval);
  385.     }
  386.     }
  387.     JS_UNLOCK(cx);
  388.     return ok;
  389. }
  390.  
  391. #if !JS_HAS_MORE_PERL_FUN
  392. static JSBool
  393. array_nyi(JSContext *cx, const char *what)
  394. {
  395.     JS_ReportError(cx, "sorry, Array.prototype.%s is not yet implemented",
  396.                    what);
  397.     return JS_FALSE;
  398. }
  399. #endif
  400.  
  401. static JSBool
  402. InitArrayObject(JSContext *cx, JSObject *obj, jsint length, jsval *vector)
  403. {
  404.     jsint index;
  405.  
  406.     PR_ASSERT(JS_IS_LOCKED(cx));
  407.     if (!js_DefineProperty(cx, obj,
  408.                (jsval)cx->runtime->atomState.lengthAtom,
  409.                INT_TO_JSVAL(length),
  410.                JS_PropertyStub, array_length_setter,
  411.                JSPROP_PERMANENT)) {
  412.     return JS_FALSE;
  413.     }
  414.     if (!vector)
  415.     return JS_TRUE;
  416.     for (index = 0; index < length; index++) {
  417.     if (!js_DefineProperty(cx, obj, INT_TO_JSVAL(index), vector[index],
  418.                    JS_PropertyStub, JS_PropertyStub,
  419.                    JSPROP_ENUMERATE)) {
  420.         return JS_FALSE;
  421.     }
  422.     }
  423.     return JS_TRUE;
  424. }
  425.  
  426. static JSBool
  427. array_reverse(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
  428.           jsval *rval)
  429. {
  430.     jsint len, i;
  431.     jsval *vec, *vp;
  432.     JSBool ok;
  433.     JSProperty *prop;
  434.  
  435.     JS_LOCK(cx);
  436.     if (!GetLengthProperty(cx, obj, &len) ||
  437.     !(vec = JS_malloc(cx, (size_t) len * sizeof *vec))) {
  438.     ok = JS_FALSE;
  439.     goto out;
  440.     }
  441.  
  442.     for (i = 0; i < len; i++)
  443.     vec[i] = JSVAL_VOID;
  444.     for (prop = obj->map->props; prop; prop = prop->next) {
  445.     if (!(prop->flags & JSPROP_TINYIDHACK) &&
  446.         JSVAL_IS_INT(prop->id) &&
  447.         (jsuint)(i = JSVAL_TO_INT(prop->id)) < (jsuint)len) {
  448.         vp = &vec[len - i - 1];
  449.         *vp = prop->object->slots[prop->slot];
  450.     }
  451.     }
  452.  
  453.     ok = InitArrayObject(cx, obj, len, vec);
  454.     if (ok)
  455.     *rval = OBJECT_TO_JSVAL(obj);
  456.     JS_free(cx, vec);
  457. out:
  458.     JS_UNLOCK(cx);
  459.     return ok;
  460. }
  461.  
  462. typedef struct QSortArgs {
  463.     void         *vec;
  464.     size_t       elsize;
  465.     void         *pivot;
  466.     JSComparator cmp;
  467.     void         *arg;
  468. } QSortArgs;
  469.  
  470. static void
  471. js_qsort_r(QSortArgs *qa, int lo, int hi)
  472. {
  473.     void *pivot, *a, *b;
  474.     int i, j;
  475.  
  476.     pivot = qa->pivot;
  477.     while (lo < hi) {
  478.     i = lo;
  479.     j = hi;
  480.     a = (char *)qa->vec + i * qa->elsize;
  481.     memmove(pivot, a, qa->elsize);
  482.     while (i < j) {
  483.         for (;;) {
  484.         b = (char *)qa->vec + j * qa->elsize;
  485.         if ((*qa->cmp)(b, pivot, qa->arg) <= 0)
  486.             break;
  487.         j--;
  488.         }
  489.         memmove(a, b, qa->elsize);
  490.         while (i < j && (*qa->cmp)(a, pivot, qa->arg) <= 0) {
  491.         i++;
  492.         a = (char *)qa->vec + i * qa->elsize;
  493.         }
  494.         memmove(b, a, qa->elsize);
  495.     }
  496.     memmove(a, pivot, qa->elsize);
  497.     if (i - lo < hi - i) {
  498.         js_qsort_r(qa, lo, i - 1);
  499.         lo = i + 1;
  500.     } else {
  501.         js_qsort_r(qa, i + 1, hi);
  502.         hi = i - 1;
  503.     }
  504.     }
  505. }
  506.  
  507. PRBool
  508. js_qsort(void *vec, size_t nel, size_t elsize, JSComparator cmp, void *arg)
  509. {
  510.     void *pivot;
  511.     QSortArgs qa;
  512.  
  513.     pivot = malloc(elsize);
  514.     if (!pivot)
  515.     return PR_FALSE;
  516.     qa.vec = vec;
  517.     qa.elsize = elsize;
  518.     qa.pivot = pivot;
  519.     qa.cmp = cmp;
  520.     qa.arg = arg;
  521.     js_qsort_r(&qa, 0, (int)(nel - 1));
  522.     free(pivot);
  523.     return PR_TRUE;
  524. }
  525.  
  526. typedef struct CompareArgs {
  527.     JSContext  *context;
  528.     jsval      fval;
  529.     JSBool     status;
  530. } CompareArgs;
  531.  
  532. static int
  533. sort_compare(const void *a, const void *b, void *arg)
  534. {
  535.     const jsval *avp = a, *bvp = b;
  536.     CompareArgs *ca = arg;
  537.     JSContext *cx = ca->context;
  538.     jsdouble cmp = -1;
  539.     jsval fval, argv[2], rval;
  540.     JSBool ok;
  541.  
  542.     fval = ca->fval;
  543.     if (fval == JSVAL_NULL) {
  544.     JSString *astr, *bstr;
  545.  
  546.     if (*avp == *bvp) {
  547.         cmp = 0;
  548.     } else if (*avp == JSVAL_VOID || *bvp == JSVAL_VOID) {
  549.         /* Put undefined properties at the end. */
  550.         cmp = (*avp == JSVAL_VOID) ? 1 : -1;
  551.     } else if ((astr = js_ValueToString(cx, *avp)) &&
  552.            (bstr = js_ValueToString(cx, *bvp))) {
  553.         cmp = js_CompareStrings(astr, bstr);
  554.     } else {
  555.         ca->status = JS_FALSE;
  556.     }
  557.     } else {
  558.     argv[0] = *avp;
  559.     argv[1] = *bvp;
  560.     ok = js_Call(cx, OBJ_GET_PARENT(JSVAL_TO_OBJECT(fval)), fval, 2, argv,
  561.              &rval);
  562.     if (ok)
  563.         ok = js_ValueToNumber(cx, rval, &cmp);
  564.     if (!ok)
  565.         ca->status = ok;
  566.     }
  567.     return (int)cmp;
  568. }
  569.  
  570. static JSBool
  571. array_sort(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  572. {
  573.     jsval fval;
  574.     CompareArgs ca;
  575.     jsint len, i;
  576.     jsval *vec;
  577.     JSProperty *prop;
  578.  
  579.     if (argc > 0) {
  580.     if (JS_TypeOfValue(cx, argv[0]) != JSTYPE_FUNCTION) {
  581.         /* XXX JS_ConvertValue shouldn't convert closure */
  582.         if (!JS_ConvertValue(cx, argv[0], JSTYPE_FUNCTION, &argv[0]))
  583.         return JS_FALSE;
  584.     }
  585.     fval = argv[0];
  586.     } else {
  587.     fval = JSVAL_NULL;
  588.     }
  589.  
  590.     JS_LOCK(cx);
  591.     if (!GetLengthProperty(cx, obj, &len) ||
  592.     !(vec = JS_malloc(cx, (size_t) len * sizeof *vec))) {
  593.     ca.status = JS_FALSE;
  594.     goto out;
  595.     }
  596.  
  597.     for (i = 0; i < len; i++)
  598.     vec[i] = JSVAL_VOID;
  599.     for (prop = obj->map->props; prop; prop = prop->next) {
  600.     if (!(prop->flags & JSPROP_TINYIDHACK) &&
  601.         JSVAL_IS_INT(prop->id) &&
  602.         (jsuint)(i = JSVAL_TO_INT(prop->id)) < (jsuint)len) {
  603.         vec[i] = prop->object->slots[prop->slot];
  604.     }
  605.     }
  606.  
  607.     ca.context = cx;
  608.     ca.fval = fval;
  609.     ca.status = JS_TRUE;
  610.     if (!js_qsort(vec, (size_t) len, (size_t) sizeof *vec, sort_compare, &ca)) {
  611.     JS_ReportOutOfMemory(cx);
  612.     ca.status = JS_FALSE;
  613.     }
  614.  
  615.     if (ca.status) {
  616.     ca.status = InitArrayObject(cx, obj, len, vec);
  617.     if (ca.status)
  618.         *rval = OBJECT_TO_JSVAL(obj);
  619.     }
  620.     JS_free(cx, vec);
  621. out:
  622.     JS_UNLOCK(cx);
  623.     return ca.status;
  624. }
  625.  
  626. #ifdef NOTYET
  627. /*
  628.  * From "Programming perl", Larry Wall and Randall L. Schwartz, Copyright XXX
  629.  * O'Reilly & Associates, Inc., but with Java primitive type sizes for i, l,
  630.  * and so on:
  631.  *
  632.  *  a   An ASCII string, will be null padded.
  633.  *  A   An ASCII string, will be space padded.
  634.  *  b   A bit string, low-to-high order.
  635.  *  B   A bit string, high-to-low order.
  636.  *  h   A hexadecimal string, low nybble first.
  637.  *  H   A hexadecimal string, high nybble first.
  638.  *  c   A signed char value.
  639.  *  C   An unsigned char value.
  640.  *  s   A signed short (16-bit) value.
  641.  *  S   An unsigned short (16-bit) value.
  642.  *  i   A signed integer (32-bit) value.
  643.  *  I   An unsigned integer (32-bit) value.
  644.  *  l   A signed long (64-bit) value.
  645.  *  L   An unsigned long (64-bit) value.
  646.  *  n   A short in "network" byte order.
  647.  *  N   An integer in "network" byte order.
  648.  *  f   A single-precision float in IEEE format.
  649.  *  d   A double-precision float in IEEE format.
  650.  *  p   A pointer to a string.
  651.  *  x   A null byte.
  652.  *  X   Back up one byte.
  653.  *  @   Null-fill to absolute position.
  654.  *  u   A uuencoded string.
  655.  *
  656.  * Each letter may be followed by a number giving the repeat count.  Together
  657.  * the letter and repeat count make a field specifier.  Field specifiers may
  658.  * be separated by whitespace, which will be ignored.
  659.  */
  660. static JSBool
  661. array_pack(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  662. {
  663. #if JS_HAS_MORE_PERL_FUN
  664. #else
  665.     return array_nyi(cx, "pack");
  666. #endif
  667. }
  668. #endif /* NOTYET */
  669.  
  670. static JSBool
  671. array_push(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  672. {
  673. #if JS_HAS_MORE_PERL_FUN
  674.     jsint length;
  675.     uintN i;
  676.     jsval id;
  677.     JSBool ok;
  678.  
  679.     JS_LOCK(cx);
  680.     if (!GetLengthProperty(cx, obj, &length))
  681.     return JS_FALSE;
  682.     for (i = 0; i < argc; i++) {
  683.     id = INT_TO_JSVAL(length + i);
  684.     if (!js_SetProperty(cx, obj, id, &argv[i])) {
  685.         ok = JS_FALSE;
  686.         goto out;
  687.     }
  688.     }
  689.  
  690.     /*
  691.      * If JS1.2, follow Perl4 by returning the last thing pushed.  Otherwise,
  692.      * return the new array length.
  693.      */
  694.     length += argc;
  695.     *rval = (cx->version == JSVERSION_1_2)
  696.         ? (argc ? argv[argc-1] : JSVAL_VOID)
  697.         : INT_TO_JSVAL(length);
  698.     ok = SetLengthProperty(cx, obj, length) != NULL;
  699. out:
  700.     JS_UNLOCK(cx);
  701.     return ok;
  702. #else
  703.     return array_nyi(cx, "push");
  704. #endif
  705. }
  706.  
  707. static JSBool
  708. array_pop(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  709. {
  710. #if JS_HAS_MORE_PERL_FUN
  711.     jsint index;
  712.     JSBool ok;
  713.     jsval id, junk;
  714.     JSProperty *prop;
  715.  
  716.     JS_LOCK(cx);
  717.     if (!GetLengthProperty(cx, obj, &index)) {
  718.     ok = JS_FALSE;
  719.     } else if (index == 0) {
  720.     ok = JS_TRUE;
  721.     } else {
  722.     index--;
  723.     id = INT_TO_JSVAL(index);
  724.  
  725.     /* Get the to-be-deleted property's value into rval ASAP. */
  726.     prop = js_GetProperty(cx, obj, id, rval);
  727.     if (!prop) {
  728.         ok = JS_FALSE;
  729.         goto out;
  730.     }
  731.  
  732.     ok = js_DeleteProperty2(cx, obj, prop, id, &junk);
  733.     if (!ok)
  734.         goto out;
  735.     if (!SetLengthProperty(cx, obj, index))
  736.         ok = JS_FALSE;
  737.     }
  738. out:
  739.     JS_UNLOCK(cx);
  740.     return ok;
  741. #else
  742.     return array_nyi(cx, "pop");
  743. #endif
  744. }
  745.  
  746. static JSBool
  747. array_shift(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  748. {
  749. #if JS_HAS_MORE_PERL_FUN
  750.     jsint length, i;
  751.     JSBool ok;
  752.     jsval id, id2, v, junk;
  753.     JSProperty *prop;
  754.  
  755.     JS_LOCK(cx);
  756.     if (!GetLengthProperty(cx, obj, &length)) {
  757.     ok = JS_FALSE;
  758.     } else if (length == 0) {
  759.     ok = JS_TRUE;
  760.     } else {
  761.     length--;
  762.     id = JSVAL_ZERO;
  763.  
  764.     /* Get the to-be-deleted property's value into rval ASAP. */
  765.     prop = js_GetProperty(cx, obj, id, rval);
  766.     if (!prop) {
  767.         ok = JS_FALSE;
  768.         goto out;
  769.     }
  770.  
  771.     /*
  772.      * Slide down the array above the first element.  Leave prop and
  773.      * id set to point to the last element.
  774.      */
  775.     if (length > 0) {
  776.         for (i = 1; i <= length; i++) {
  777.         id = INT_TO_JSVAL(i);
  778.         id2 = INT_TO_JSVAL(i - 1);
  779.         prop = js_GetProperty(cx, obj, id, &v);
  780.         if (!prop || !js_SetProperty(cx, obj, id2, &v)) {
  781.             ok = JS_FALSE;
  782.             goto out;
  783.         }
  784.         }
  785.     }
  786.  
  787.     /* Delete the only or last element. */
  788.     ok = js_DeleteProperty2(cx, obj, prop, id, &junk);
  789.     if (!ok)
  790.         goto out;
  791.     if (!SetLengthProperty(cx, obj, length))
  792.         ok = JS_FALSE;
  793.     }
  794. out:
  795.     JS_UNLOCK(cx);
  796.     return ok;
  797. #else
  798.     return array_nyi(cx, "shift");
  799. #endif
  800. }
  801.  
  802. static JSBool
  803. array_unshift(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
  804.           jsval *rval)
  805. {
  806. #if JS_HAS_MORE_PERL_FUN
  807.     jsint length, last;
  808.     uintN i;
  809.     jsval id, id2, v;
  810.     JSProperty *prop;
  811.     JSBool ok;
  812.  
  813.     JS_LOCK(cx);
  814.     if (!GetLengthProperty(cx, obj, &length)) {
  815.     ok = JS_FALSE;
  816.     goto out;
  817.     }
  818.     if (argc > 0) {
  819.     /* Slide up the array to make room for argc at the bottom. */
  820.     if (length > 0) {
  821.         for (last = length - 1; last >= 0; last--) {
  822.         id = INT_TO_JSVAL(last);
  823.         id2 = INT_TO_JSVAL(last + argc);
  824.         prop = js_GetProperty(cx, obj, id, &v);
  825.         if (!prop || !js_SetProperty(cx, obj, id2, &v)) {
  826.             ok = JS_FALSE;
  827.             goto out;
  828.         }
  829.         }
  830.     }
  831.  
  832.     /* Copy from argv to the bottom of the array. */
  833.     for (i = 0; i < argc; i++) {
  834.         id = INT_TO_JSVAL(i);
  835.         if (!js_SetProperty(cx, obj, id, &argv[i])) {
  836.         ok = JS_FALSE;
  837.         goto out;
  838.         }
  839.     }
  840.  
  841.     /* Follow Perl by returning the new array length. */
  842.     length += argc;
  843.     if (!SetLengthProperty(cx, obj, length)) {
  844.         ok = JS_FALSE;
  845.         goto out;
  846.     }
  847.     }
  848.     *rval = INT_TO_JSVAL(length);
  849.     ok = JS_TRUE;
  850. out:
  851.     JS_UNLOCK(cx);
  852.     return ok;
  853. #else
  854.     return array_nyi(cx, "unshift");
  855. #endif
  856. }
  857.  
  858. static JSBool
  859. array_splice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  860. {
  861. #if JS_HAS_MORE_PERL_FUN
  862.     jsint length, begin, end, count, delta, last;
  863.     uintN i;
  864.     JSBool ok;
  865.     jsdouble d;
  866.     jsval id, id2, v;
  867.     JSObject *obj2;
  868.     JSProperty *prop;
  869.  
  870.     /* Nothing to do if no args.  Otherwise lock and load length. */
  871.     if (argc == 0)
  872.     return JS_TRUE;
  873.     JS_LOCK(cx);
  874.     if (!GetLengthProperty(cx, obj, &length)) {
  875.     ok = JS_FALSE;
  876.     goto out;
  877.     }
  878.  
  879.     /* Convert the first argument into a starting index. */
  880.     ok = js_ValueToNumber(cx, *argv, &d);
  881.     if (!ok)
  882.     goto out;
  883.     begin = (jsint)d;
  884.     if (begin < 0) {
  885.     begin += length;
  886.     if (begin < 0)
  887.         begin = 0;
  888.     } else if (begin > length) {
  889.     begin = length;
  890.     }
  891.     argc--;
  892.     argv++;
  893.  
  894.     /* Convert the second argument from a count into a fencepost index. */
  895.     delta = length - begin;
  896.     if (argc == 0) {
  897.     count = delta;
  898.     end = length;
  899.     } else {
  900.     ok = js_ValueToNumber(cx, *argv, &d);
  901.     if (!ok)
  902.         goto out;
  903.     count = (jsint)d;
  904.     if (count < 0)
  905.         count = 0;
  906.     else if (count > delta)
  907.         count = delta;
  908.     end = begin + count;
  909.     argc--;
  910.     argv++;
  911.     }
  912.  
  913.     /* If there are elements to remove, put them into the return value. */
  914.     if (count > 0) {
  915.     if (count == 1 && cx->version == JSVERSION_1_2) {
  916.         /*
  917.          * JS lacks "list context", whereby in Perl one turns the single
  918.          * scalar that's spliced out into an array just by assigning it to
  919.          * @single instead of $single, or by using it as Perl push's first
  920.          * argument, for instance.
  921.          *
  922.          * JS1.2 emulated Perl too closely and returned a non-Array for
  923.          * the single-splice-out case, requiring callers to test and wrap
  924.          * in [] if necessary.  So JS1.3, default, and other versions all
  925.          * return an array of length 1 for uniformity.
  926.          */
  927.         id = INT_TO_JSVAL(begin);
  928.         if (!js_GetProperty(cx, obj, id, rval)) {
  929.         ok = JS_FALSE;
  930.         goto out;
  931.         }
  932.     } else {
  933.         obj2 = js_NewArrayObject(cx, 0, NULL);
  934.         if (!obj2) {
  935.         ok = JS_FALSE;
  936.         goto out;
  937.         }
  938.         *rval = OBJECT_TO_JSVAL(obj2);
  939.         for (last = begin; last < end; last++) {
  940.         id = INT_TO_JSVAL(last);
  941.         id2 = INT_TO_JSVAL(last - begin);
  942.         if (!js_GetProperty(cx, obj, id, &v) ||
  943.             !js_SetProperty(cx, obj2, id2, &v)) {
  944.             ok = JS_FALSE;
  945.             goto out;
  946.         }
  947.         }
  948.     }
  949.     }
  950.  
  951.     /* Find the direction (up or down) to copy and make way for argv. */
  952.     delta = (jsint)argc - count;
  953.     if (delta > 0) {
  954.     for (last = length - 1; last >= end; last--) {
  955.         id = INT_TO_JSVAL(last);
  956.         id2 = INT_TO_JSVAL(last + delta);
  957.         prop = js_GetProperty(cx, obj, id, &v);
  958.         if (!prop || !js_SetProperty(cx, obj, id2, &v)) {
  959.         ok = JS_FALSE;
  960.         goto out;
  961.         }
  962.     }
  963.     } else if (delta < 0) {
  964.     for (last = end; last < length; last++) {
  965.         id = INT_TO_JSVAL(last);
  966.         id2 = INT_TO_JSVAL(last + delta);
  967.         prop = js_GetProperty(cx, obj, id, &v);
  968.         if (!prop || !js_SetProperty(cx, obj, id2, &v)) {
  969.         ok = JS_FALSE;
  970.         goto out;
  971.         }
  972.     }
  973.     }
  974.  
  975.     /* Copy from argv into the hole to complete the splice. */
  976.     for (i = 0; i < argc; i++) {
  977.     id = INT_TO_JSVAL(begin + i);
  978.     if (!js_SetProperty(cx, obj, id, &argv[i])) {
  979.         ok = JS_FALSE;
  980.         goto out;
  981.     }
  982.     }
  983.  
  984.     /* Update length in case we deleted elements from the end. */
  985.     if (!SetLengthProperty(cx, obj, length + delta))
  986.     ok = JS_FALSE;
  987. out:
  988.     JS_UNLOCK(cx);
  989.     return ok;
  990. #else
  991.     return array_nyi(cx, "splice");
  992. #endif
  993. }
  994.  
  995. #if JS_HAS_SEQUENCE_OPS
  996. /*
  997.  * Python-esque sequence operations.
  998.  */
  999. static JSBool
  1000. array_concat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  1001. {
  1002.     JSObject *nobj, *aobj;
  1003.     JSBool ok;
  1004.     jsint slot, length, alength;
  1005.     jsval v, id, id2, lv, *vp;
  1006.     JSProperty *prop;
  1007.     uintN i;
  1008.  
  1009.     nobj = JS_NewArrayObject(cx, 0, NULL);
  1010.     if (!nobj)
  1011.     return JS_FALSE;
  1012.  
  1013.     ok = JS_TRUE;
  1014.     JS_LOCK(cx);
  1015.     if (!GetLengthProperty(cx, obj, &length)) {
  1016.     ok = JS_FALSE;
  1017.     goto out;
  1018.     }
  1019.     for (slot = 0; slot < length; slot++) {
  1020.     id = INT_TO_JSVAL(slot);
  1021.     prop = js_GetProperty(cx, obj, id, &v);
  1022.     if (!prop || !js_SetProperty(cx, nobj, id, &v)) {
  1023.         ok = JS_FALSE;
  1024.         goto out;
  1025.     }
  1026.     }
  1027.  
  1028.     for (i = 0; i < argc; i++) {
  1029.     v = argv[i];
  1030.     if (JSVAL_IS_OBJECT(v)) {
  1031.         aobj = JSVAL_TO_OBJECT(v);
  1032.         if (aobj && (prop = js_HasLengthProperty(cx, aobj))) {
  1033.         vp = &aobj->slots[prop->slot];
  1034.         lv = *vp;
  1035.         ok = prop->getter(cx, aobj,
  1036.                   (jsval)cx->runtime->atomState.lengthAtom,
  1037.                   &lv);
  1038.         if (!ok)
  1039.             goto out;
  1040.         *vp = lv;
  1041.         ok = ValueToLength(cx, lv, &alength);
  1042.         if (!ok)
  1043.             goto out;
  1044.         for (slot = 0; slot < alength; slot++) {
  1045.             id  = INT_TO_JSVAL(slot);
  1046.             id2 = INT_TO_JSVAL(length + slot);
  1047.             prop = js_GetProperty(cx, aobj, id, &v);
  1048.             if (!prop || !js_SetProperty(cx, nobj, id2, &v)) {
  1049.             ok = JS_FALSE;
  1050.             goto out;
  1051.             }
  1052.         }
  1053.         length += alength;
  1054.         continue;
  1055.         }
  1056.     }
  1057.  
  1058.     id = INT_TO_JSVAL(length);
  1059.     if (!js_SetProperty(cx, nobj, id, &v)) {
  1060.         ok = JS_FALSE;
  1061.         goto out;
  1062.     }
  1063.     length++;
  1064.     }
  1065.  
  1066.     *rval = OBJECT_TO_JSVAL(nobj);
  1067. out:
  1068.     JS_UNLOCK(cx);
  1069.     return ok;
  1070. }
  1071.  
  1072. static JSBool
  1073. array_slice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  1074. {
  1075.     JSObject *nobj;
  1076.     JSBool ok;
  1077.     jsint length, begin, end, slot;
  1078.     jsdouble d;
  1079.     jsval v, id, id2;
  1080.     JSProperty *prop;
  1081.  
  1082.     nobj = JS_NewArrayObject(cx, 0, NULL);
  1083.     if (!nobj)
  1084.     return JS_FALSE;
  1085.  
  1086.     ok = JS_TRUE;
  1087.     JS_LOCK(cx);
  1088.     if (!GetLengthProperty(cx, obj, &length)) {
  1089.     ok = JS_FALSE;
  1090.     goto out;
  1091.     }
  1092.     begin = 0;
  1093.     end = length;
  1094.  
  1095.     if (argc > 0) {
  1096.     ok = js_ValueToNumber(cx, argv[0], &d);
  1097.     if (!ok)
  1098.         goto out;
  1099.     begin = (jsint)d;
  1100.     if (begin < 0) {
  1101.         begin += length;
  1102.         if (begin < 0)
  1103.         begin = 0;
  1104.     } else if (begin > length) {
  1105.         begin = length;
  1106.     }
  1107.  
  1108.     if (argc > 1) {
  1109.         ok = js_ValueToNumber(cx, argv[1], &d);
  1110.         if (!ok)
  1111.         goto out;
  1112.         end = (jsint)d;
  1113.         if (end < 0) {
  1114.         end += length;
  1115.         if (end < 0)
  1116.             end = 0;
  1117.         } else if (end > length) {
  1118.         end = length;
  1119.         }
  1120.     }
  1121.     }
  1122.  
  1123.     for (slot = begin; slot < end; slot++) {
  1124.     id  = INT_TO_JSVAL(slot);
  1125.     id2 = INT_TO_JSVAL(slot - begin);
  1126.     prop = js_GetProperty(cx, obj, id, &v);
  1127.     if (!prop || !js_SetProperty(cx, nobj, id2, &v)) {
  1128.         ok = JS_FALSE;
  1129.         goto out;
  1130.     }
  1131.     }
  1132.     *rval = OBJECT_TO_JSVAL(nobj);
  1133. out:
  1134.     JS_UNLOCK(cx);
  1135.     return ok;
  1136. }
  1137. #endif /* JS_HAS_SEQUENCE_OPS */
  1138.  
  1139. static JSFunctionSpec array_methods[] = {
  1140.     {js_toString_str,   array_toString,         0},
  1141.  
  1142.     /* Perl-ish methods. */
  1143.     {"join",            array_join,             1},
  1144.     {"reverse",         array_reverse,          0},
  1145.     {"sort",            array_sort,             1},
  1146. #ifdef NOTYET
  1147.     {"pack",            array_pack,             1},
  1148. #endif
  1149.     {"push",            array_push,             1},
  1150.     {"pop",             array_pop,              0},
  1151.     {"shift",           array_shift,            0},
  1152.     {"unshift",         array_unshift,          1},
  1153.     {"splice",          array_splice,           1},
  1154.  
  1155.     /* Python-esque sequence methods. */
  1156. #if JS_HAS_SEQUENCE_OPS
  1157.     {"concat",          array_concat,           0},
  1158.     {"slice",           array_slice,            0},
  1159. #endif
  1160.  
  1161.     {0}
  1162. };
  1163.  
  1164. static JSBool
  1165. Array(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  1166. {
  1167.     jsint length;
  1168.     jsval *vector;
  1169.     JSBool ok;
  1170.  
  1171.     if (argc == 0) {
  1172.     length = 0;
  1173.     vector = NULL;
  1174.     } else if (cx->version < JSVERSION_1_2 &&
  1175.            argc == 1 && JSVAL_IS_INT(argv[0])) {
  1176.     length = JSVAL_TO_INT(argv[0]);
  1177.     vector = NULL;
  1178.     } else {
  1179.     length = argc;
  1180.     vector = argv;
  1181.     }
  1182.     JS_LOCK_VOID(cx, ok = InitArrayObject(cx, obj, length, vector));
  1183.     if (ok)
  1184.     *rval = OBJECT_TO_JSVAL(obj);
  1185.     return ok;
  1186. }
  1187.  
  1188. JSObject *
  1189. js_InitArrayClass(JSContext *cx, JSObject *obj)
  1190. {
  1191.     return JS_InitClass(cx, obj, NULL, &js_ArrayClass, Array, 1,
  1192.             NULL, array_methods, NULL, NULL);
  1193. }
  1194.  
  1195. JSObject *
  1196. js_NewArrayObject(JSContext *cx, jsint length, jsval *vector)
  1197. {
  1198.     JSObject *obj;
  1199.  
  1200.     obj = js_NewObject(cx, &js_ArrayClass, NULL, NULL);
  1201.     if (!obj)
  1202.     return NULL;
  1203.     if (!InitArrayObject(cx, obj, length, vector)) {
  1204.     cx->newborn[GCX_OBJECT] = NULL;
  1205.     return NULL;
  1206.     }
  1207.     return obj;
  1208. }
  1209.  
  1210. JSBool
  1211. js_GetArrayLength(JSContext *cx, JSObject *obj, jsint *lengthp)
  1212. {
  1213.     return GetLengthProperty(cx, obj, lengthp) != NULL;
  1214. }
  1215.  
  1216. JSBool
  1217. js_SetArrayLength(JSContext *cx, JSObject *obj, jsint length)
  1218. {
  1219.     return SetLengthProperty(cx, obj, length) != NULL;
  1220. }
  1221.  
  1222. JSProperty *
  1223. js_HasLengthProperty(JSContext *cx, JSObject *obj)
  1224. {
  1225.     JSErrorReporter older;
  1226.     JSObject *pobj;
  1227.     JSProperty *prop;
  1228.     JSBool ok;
  1229.  
  1230.     PR_ASSERT(JS_IS_LOCKED(cx));
  1231.     older = JS_SetErrorReporter(cx, NULL);
  1232.     pobj = NULL;
  1233.     ok = js_LookupProperty(cx, obj, (jsval)cx->runtime->atomState.lengthAtom,
  1234.                &pobj, &prop);
  1235.     JS_SetErrorReporter(cx, older);
  1236.     return (ok && prop && prop->object == obj) ? prop : NULL;
  1237. }
  1238.