PageRenderTime 402ms CodeModel.GetById 141ms app.highlight 202ms RepoModel.GetById 39ms app.codeStats 1ms

/js/lib/Socket.IO-node/support/expresso/deps/jscoverage/js/jsarray.cpp

http://github.com/onedayitwillmake/RealtimeMultiplayerNodeJs
C++ | 2159 lines | 1567 code | 251 blank | 341 comment | 398 complexity | df71ecc54001a084e456bc2e9727708b MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
   2 * vim: set sw=4 ts=8 et tw=78:
   3 *
   4 * ***** BEGIN LICENSE BLOCK *****
   5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
   6 *
   7 * The contents of this file are subject to the Mozilla Public License Version
   8 * 1.1 (the "License"); you may not use this file except in compliance with
   9 * the License. You may obtain a copy of the License at
  10 * http://www.mozilla.org/MPL/
  11 *
  12 * Software distributed under the License is distributed on an "AS IS" basis,
  13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  14 * for the specific language governing rights and limitations under the
  15 * License.
  16 *
  17 * The Original Code is Mozilla Communicator client code, released
  18 * March 31, 1998.
  19 *
  20 * The Initial Developer of the Original Code is
  21 * Netscape Communications Corporation.
  22 * Portions created by the Initial Developer are Copyright (C) 1998
  23 * the Initial Developer. All Rights Reserved.
  24 *
  25 * Contributor(s):
  26 *
  27 * Alternatively, the contents of this file may be used under the terms of
  28 * either of the GNU General Public License Version 2 or later (the "GPL"),
  29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  30 * in which case the provisions of the GPL or the LGPL are applicable instead
  31 * of those above. If you wish to allow use of your version of this file only
  32 * under the terms of either the GPL or the LGPL, and not to allow others to
  33 * use your version of this file under the terms of the MPL, indicate your
  34 * decision by deleting the provisions above and replace them with the notice
  35 * and other provisions required by the GPL or the LGPL. If you do not delete
  36 * the provisions above, a recipient may use your version of this file under
  37 * the terms of any one of the MPL, the GPL or the LGPL.
  38 *
  39 * ***** END LICENSE BLOCK ***** */
  40
  41/*
  42 * JS array class.
  43 *
  44 * Array objects begin as "dense" arrays, optimized for numeric-only property
  45 * access over a vector of slots (obj->dslots) with high load factor.  Array
  46 * methods optimize for denseness by testing that the object's class is
  47 * &js_ArrayClass, and can then directly manipulate the slots for efficiency.
  48 *
  49 * We track these pieces of metadata for arrays in dense mode:
  50 *  - the array's length property as a uint32, in JSSLOT_ARRAY_LENGTH,
  51 *  - the number of indices that are filled (non-holes), in JSSLOT_ARRAY_COUNT,
  52 *  - the net number of slots starting at dslots (DENSELEN), in dslots[-1] if
  53 *    dslots is non-NULL.
  54 *
  55 * In dense mode, holes in the array are represented by JSVAL_HOLE.  The final
  56 * slot in fslots (JSSLOT_ARRAY_LOOKUP_HOLDER) is used to store the single jsid
  57 * "in use" by a lookupProperty caller.
  58 *
  59 * Arrays are converted to use js_SlowArrayClass when any of these conditions
  60 * are met:
  61 *  - the load factor (COUNT / DENSELEN) is less than 0.25, and there are
  62 *    more than MIN_SPARSE_INDEX slots total
  63 *  - a property is set that is non-numeric (and not "length"); or
  64 *  - a hole is filled below DENSELEN (possibly implicitly through methods like
  65 *    |reverse| or |splice|).
  66 *
  67 * In the latter two cases, property creation order is no longer index order,
  68 * which necessitates use of a structure that keeps track of property creation
  69 * order.  (ES4, due to expectations baked into web script, requires that
  70 * enumeration order be the order in which properties were created.)
  71 *
  72 * An alternative in the latter case (out-of-order index set) would be to
  73 * maintain the scope to track property enumeration order, but still use
  74 * the fast slot access.  That would have the same memory cost as just using
  75 * a js_SlowArrayClass, but have the same performance characteristics as
  76 * a dense array for slot accesses, at some cost in code complexity.
  77 */
  78#include "jsstddef.h"
  79#include <stdlib.h>
  80#include <string.h>
  81#include "jstypes.h"
  82#include "jsutil.h" /* Added by JSIFY */
  83#include "jsapi.h"
  84#include "jsarray.h"
  85#include "jsatom.h"
  86#include "jsbit.h"
  87#include "jsbool.h"
  88#include "jsbuiltins.h"
  89#include "jscntxt.h"
  90#include "jsversion.h"
  91#include "jsdbgapi.h" /* for js_TraceWatchPoints */
  92#include "jsdtoa.h"
  93#include "jsfun.h"
  94#include "jsgc.h"
  95#include "jsinterp.h"
  96#include "jslock.h"
  97#include "jsnum.h"
  98#include "jsobj.h"
  99#include "jsscope.h"
 100#include "jsstr.h"
 101#include "jsstaticcheck.h"
 102
 103/* 2^32 - 1 as a number and a string */
 104#define MAXINDEX 4294967295u
 105#define MAXSTR   "4294967295"
 106
 107/* Small arrays are dense, no matter what. */
 108#define MIN_SPARSE_INDEX 32
 109
 110#define INDEX_TOO_BIG(index) ((index) > JS_BIT(29) - 1)
 111#define INDEX_TOO_SPARSE(array, index)                                         \
 112    (INDEX_TOO_BIG(index) ||                                                   \
 113     ((index) > ARRAY_DENSE_LENGTH(array) && (index) >= MIN_SPARSE_INDEX &&    \
 114      (index) > (uint32)((array)->fslots[JSSLOT_ARRAY_COUNT] + 1) * 4))
 115
 116JS_STATIC_ASSERT(sizeof(JSScopeProperty) > 4 * sizeof(jsval));
 117
 118#define ENSURE_SLOW_ARRAY(cx, obj)                                             \
 119    (OBJ_GET_CLASS(cx, obj) == &js_SlowArrayClass || js_MakeArraySlow(cx, obj))
 120
 121/*
 122 * Determine if the id represents an array index or an XML property index.
 123 *
 124 * An id is an array index according to ECMA by (15.4):
 125 *
 126 * "Array objects give special treatment to a certain class of property names.
 127 * A property name P (in the form of a string value) is an array index if and
 128 * only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal
 129 * to 2^32-1."
 130 *
 131 * In our implementation, it would be sufficient to check for JSVAL_IS_INT(id)
 132 * except that by using signed 32-bit integers we miss the top half of the
 133 * valid range. This function checks the string representation itself; note
 134 * that calling a standard conversion routine might allow strings such as
 135 * "08" or "4.0" as array indices, which they are not.
 136 */
 137JSBool
 138js_IdIsIndex(jsval id, jsuint *indexp)
 139{
 140    JSString *str;
 141    jschar *cp;
 142
 143    if (JSVAL_IS_INT(id)) {
 144        jsint i;
 145        i = JSVAL_TO_INT(id);
 146        if (i < 0)
 147            return JS_FALSE;
 148        *indexp = (jsuint)i;
 149        return JS_TRUE;
 150    }
 151
 152    /* NB: id should be a string, but jsxml.c may call us with an object id. */
 153    if (!JSVAL_IS_STRING(id))
 154        return JS_FALSE;
 155
 156    str = JSVAL_TO_STRING(id);
 157    cp = JSSTRING_CHARS(str);
 158    if (JS7_ISDEC(*cp) && JSSTRING_LENGTH(str) < sizeof(MAXSTR)) {
 159        jsuint index = JS7_UNDEC(*cp++);
 160        jsuint oldIndex = 0;
 161        jsuint c = 0;
 162        if (index != 0) {
 163            while (JS7_ISDEC(*cp)) {
 164                oldIndex = index;
 165                c = JS7_UNDEC(*cp);
 166                index = 10*index + c;
 167                cp++;
 168            }
 169        }
 170
 171        /* Ensure that all characters were consumed and we didn't overflow. */
 172        if (*cp == 0 &&
 173             (oldIndex < (MAXINDEX / 10) ||
 174              (oldIndex == (MAXINDEX / 10) && c < (MAXINDEX % 10))))
 175        {
 176            *indexp = index;
 177            return JS_TRUE;
 178        }
 179    }
 180    return JS_FALSE;
 181}
 182
 183static jsuint
 184ValueIsLength(JSContext *cx, jsval* vp)
 185{
 186    jsint i;
 187    jsdouble d;
 188    jsuint length;
 189
 190    if (JSVAL_IS_INT(*vp)) {
 191        i = JSVAL_TO_INT(*vp);
 192        if (i < 0)
 193            goto error;
 194        return (jsuint) i;
 195    }
 196
 197    d = js_ValueToNumber(cx, vp);
 198    if (JSVAL_IS_NULL(*vp))
 199        goto error;
 200
 201    if (JSDOUBLE_IS_NaN(d))
 202        goto error;
 203    length = (jsuint) d;
 204    if (d != (jsdouble) length)
 205        goto error;
 206    return length;
 207
 208  error:
 209    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
 210                         JSMSG_BAD_ARRAY_LENGTH);
 211    *vp = JSVAL_NULL;
 212    return 0;
 213}
 214
 215JSBool
 216js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp)
 217{
 218    JSTempValueRooter tvr;
 219    jsid id;
 220    JSBool ok;
 221    jsint i;
 222
 223    if (OBJ_IS_ARRAY(cx, obj)) {
 224        *lengthp = obj->fslots[JSSLOT_ARRAY_LENGTH];
 225        return JS_TRUE;
 226    }
 227
 228    JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);
 229    id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
 230    ok = OBJ_GET_PROPERTY(cx, obj, id, &tvr.u.value);
 231    if (ok) {
 232        if (JSVAL_IS_INT(tvr.u.value)) {
 233            i = JSVAL_TO_INT(tvr.u.value);
 234            *lengthp = (jsuint)i;       /* jsuint cast does ToUint32 */
 235        } else {
 236            *lengthp = js_ValueToECMAUint32(cx, &tvr.u.value);
 237            ok = !JSVAL_IS_NULL(tvr.u.value);
 238        }
 239    }
 240    JS_POP_TEMP_ROOT(cx, &tvr);
 241    return ok;
 242}
 243
 244static JSBool
 245IndexToValue(JSContext *cx, jsuint index, jsval *vp)
 246{
 247    if (index <= JSVAL_INT_MAX) {
 248        *vp = INT_TO_JSVAL(index);
 249        return JS_TRUE;
 250    }
 251    return JS_NewDoubleValue(cx, (jsdouble)index, vp);
 252}
 253
 254JSBool JS_FASTCALL
 255js_IndexToId(JSContext *cx, jsuint index, jsid *idp)
 256{
 257    JSString *str;
 258
 259    if (index <= JSVAL_INT_MAX) {
 260        *idp = INT_TO_JSID(index);
 261        return JS_TRUE;
 262    }
 263    str = js_NumberToString(cx, index);
 264    if (!str)
 265        return JS_FALSE;
 266    return js_ValueToStringId(cx, STRING_TO_JSVAL(str), idp);
 267}
 268
 269static JSBool
 270BigIndexToId(JSContext *cx, JSObject *obj, jsuint index, JSBool createAtom,
 271             jsid *idp)
 272{
 273    jschar buf[10], *start;
 274    JSClass *clasp;
 275    JSAtom *atom;
 276    JS_STATIC_ASSERT((jsuint)-1 == 4294967295U);
 277
 278    JS_ASSERT(index > JSVAL_INT_MAX);
 279
 280    start = JS_ARRAY_END(buf);
 281    do {
 282        --start;
 283        *start = (jschar)('0' + index % 10);
 284        index /= 10;
 285    } while (index != 0);
 286
 287    /*
 288     * Skip the atomization if the class is known to store atoms corresponding
 289     * to big indexes together with elements. In such case we know that the
 290     * array does not have an element at the given index if its atom does not
 291     * exist.  Fast arrays (clasp == &js_ArrayClass) don't use atoms for
 292     * any indexes, though it would be rare to see them have a big index
 293     * in any case.
 294     */
 295    if (!createAtom &&
 296        ((clasp = OBJ_GET_CLASS(cx, obj)) == &js_SlowArrayClass ||
 297         clasp == &js_ArgumentsClass ||
 298         clasp == &js_ObjectClass)) {
 299        atom = js_GetExistingStringAtom(cx, start, JS_ARRAY_END(buf) - start);
 300        if (!atom) {
 301            *idp = JSVAL_VOID;
 302            return JS_TRUE;
 303        }
 304    } else {
 305        atom = js_AtomizeChars(cx, start, JS_ARRAY_END(buf) - start, 0);
 306        if (!atom)
 307            return JS_FALSE;
 308    }
 309
 310    *idp = ATOM_TO_JSID(atom);
 311    return JS_TRUE;
 312}
 313
 314static JSBool
 315ResizeSlots(JSContext *cx, JSObject *obj, uint32 oldlen, uint32 len)
 316{
 317    jsval *slots, *newslots;
 318
 319    if (len == 0) {
 320        if (obj->dslots) {
 321            JS_free(cx, obj->dslots - 1);
 322            obj->dslots = NULL;
 323        }
 324        return JS_TRUE;
 325    }
 326
 327    if (len > ~(uint32)0 / sizeof(jsval)) {
 328        js_ReportAllocationOverflow(cx);
 329        return JS_FALSE;
 330    }
 331
 332    slots = obj->dslots ? obj->dslots - 1 : NULL;
 333    newslots = (jsval *) JS_realloc(cx, slots, sizeof (jsval) * (len + 1));
 334    if (!newslots)
 335        return JS_FALSE;
 336
 337    obj->dslots = newslots + 1;
 338    ARRAY_SET_DENSE_LENGTH(obj, len);
 339
 340    for (slots = obj->dslots + oldlen; slots < obj->dslots + len; slots++)
 341        *slots = JSVAL_HOLE;
 342
 343    return JS_TRUE;
 344}
 345
 346static JSBool
 347EnsureLength(JSContext *cx, JSObject *obj, uint32 len)
 348{
 349    uint32 oldlen = ARRAY_DENSE_LENGTH(obj);
 350
 351    if (len > oldlen) {
 352        return ResizeSlots(cx, obj, oldlen,
 353                           len + ARRAY_GROWBY - (len % ARRAY_GROWBY));
 354    }
 355    return JS_TRUE;
 356}
 357
 358/*
 359 * If the property at the given index exists, get its value into location
 360 * pointed by vp and set *hole to false. Otherwise set *hole to true and *vp
 361 * to JSVAL_VOID. This function assumes that the location pointed by vp is
 362 * properly rooted and can be used as GC-protected storage for temporaries.
 363 */
 364static JSBool
 365GetArrayElement(JSContext *cx, JSObject *obj, jsuint index, JSBool *hole,
 366                jsval *vp)
 367{
 368    jsid id;
 369    JSObject *obj2;
 370    JSProperty *prop;
 371
 372    if (OBJ_IS_DENSE_ARRAY(cx, obj) && index < ARRAY_DENSE_LENGTH(obj) &&
 373        (*vp = obj->dslots[index]) != JSVAL_HOLE) {
 374        *hole = JS_FALSE;
 375        return JS_TRUE;
 376    }
 377
 378    if (index <= JSVAL_INT_MAX) {
 379        id = INT_TO_JSID(index);
 380    } else {
 381        if (!BigIndexToId(cx, obj, index, JS_FALSE, &id))
 382            return JS_FALSE;
 383        if (JSVAL_IS_VOID(id)) {
 384            *hole = JS_TRUE;
 385            *vp = JSVAL_VOID;
 386            return JS_TRUE;
 387        }
 388    }
 389
 390    if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
 391        return JS_FALSE;
 392    if (!prop) {
 393        *hole = JS_TRUE;
 394        *vp = JSVAL_VOID;
 395    } else {
 396        OBJ_DROP_PROPERTY(cx, obj2, prop);
 397        if (!OBJ_GET_PROPERTY(cx, obj, id, vp))
 398            return JS_FALSE;
 399        *hole = JS_FALSE;
 400    }
 401    return JS_TRUE;
 402}
 403
 404/*
 405 * Set the value of the property at the given index to v assuming v is rooted.
 406 */
 407static JSBool
 408SetArrayElement(JSContext *cx, JSObject *obj, jsuint index, jsval v)
 409{
 410    jsid id;
 411
 412    if (OBJ_IS_DENSE_ARRAY(cx, obj)) {
 413        /* Predicted/prefeched code should favor the remains-dense case. */
 414        if (!INDEX_TOO_SPARSE(obj, index)) {
 415            if (!EnsureLength(cx, obj, index + 1))
 416                return JS_FALSE;
 417            if (index >= (uint32)obj->fslots[JSSLOT_ARRAY_LENGTH])
 418                obj->fslots[JSSLOT_ARRAY_LENGTH] = index + 1;
 419            if (obj->dslots[index] == JSVAL_HOLE)
 420                obj->fslots[JSSLOT_ARRAY_COUNT]++;
 421            obj->dslots[index] = v;
 422            return JS_TRUE;
 423        }
 424
 425        if (!js_MakeArraySlow(cx, obj))
 426            return JS_FALSE;
 427    }
 428
 429    if (index <= JSVAL_INT_MAX) {
 430        id = INT_TO_JSID(index);
 431    } else {
 432        if (!BigIndexToId(cx, obj, index, JS_TRUE, &id))
 433            return JS_FALSE;
 434        JS_ASSERT(!JSVAL_IS_VOID(id));
 435    }
 436    return OBJ_SET_PROPERTY(cx, obj, id, &v);
 437}
 438
 439static JSBool
 440DeleteArrayElement(JSContext *cx, JSObject *obj, jsuint index)
 441{
 442    jsid id;
 443    jsval junk;
 444
 445    if (OBJ_IS_DENSE_ARRAY(cx, obj)) {
 446        if (index < ARRAY_DENSE_LENGTH(obj)) {
 447            if (obj->dslots[index] != JSVAL_HOLE)
 448                obj->fslots[JSSLOT_ARRAY_COUNT]--;
 449            obj->dslots[index] = JSVAL_HOLE;
 450        }
 451        return JS_TRUE;
 452    }
 453
 454    if (index <= JSVAL_INT_MAX) {
 455        id = INT_TO_JSID(index);
 456    } else {
 457        if (!BigIndexToId(cx, obj, index, JS_FALSE, &id))
 458            return JS_FALSE;
 459        if (JSVAL_IS_VOID(id))
 460            return JS_TRUE;
 461    }
 462    return OBJ_DELETE_PROPERTY(cx, obj, id, &junk);
 463}
 464
 465/*
 466 * When hole is true, delete the property at the given index. Otherwise set
 467 * its value to v assuming v is rooted.
 468 */
 469static JSBool
 470SetOrDeleteArrayElement(JSContext *cx, JSObject *obj, jsuint index,
 471                        JSBool hole, jsval v)
 472{
 473    if (hole) {
 474        JS_ASSERT(JSVAL_IS_VOID(v));
 475        return DeleteArrayElement(cx, obj, index);
 476    }
 477    return SetArrayElement(cx, obj, index, v);
 478}
 479
 480JSBool
 481js_SetLengthProperty(JSContext *cx, JSObject *obj, jsuint length)
 482{
 483    jsval v;
 484    jsid id;
 485
 486    if (!IndexToValue(cx, length, &v))
 487        return JS_FALSE;
 488    id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
 489    return OBJ_SET_PROPERTY(cx, obj, id, &v);
 490}
 491
 492JSBool
 493js_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp)
 494{
 495    JSErrorReporter older;
 496    JSTempValueRooter tvr;
 497    jsid id;
 498    JSBool ok;
 499
 500    older = JS_SetErrorReporter(cx, NULL);
 501    JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);
 502    id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
 503    ok = OBJ_GET_PROPERTY(cx, obj, id, &tvr.u.value);
 504    JS_SetErrorReporter(cx, older);
 505    if (ok) {
 506        *lengthp = ValueIsLength(cx, &tvr.u.value);
 507        ok = !JSVAL_IS_NULL(tvr.u.value);
 508    }
 509    JS_POP_TEMP_ROOT(cx, &tvr);
 510    return ok;
 511}
 512
 513JSBool
 514js_IsArrayLike(JSContext *cx, JSObject *obj, JSBool *answerp, jsuint *lengthp)
 515{
 516    JSClass *clasp;
 517
 518    clasp = OBJ_GET_CLASS(cx, obj);
 519    *answerp = (clasp == &js_ArgumentsClass || clasp == &js_ArrayClass ||
 520                clasp == &js_SlowArrayClass);
 521    if (!*answerp) {
 522        *lengthp = 0;
 523        return JS_TRUE;
 524    }
 525    return js_GetLengthProperty(cx, obj, lengthp);
 526}
 527
 528/*
 529 * The 'length' property of all native Array instances is a shared permanent
 530 * property of Array.prototype, so it appears to be a direct property of each
 531 * array instance delegating to that Array.prototype. It accesses the private
 532 * slot reserved by js_ArrayClass.
 533 *
 534 * Since SpiderMonkey supports cross-class prototype-based delegation, we have
 535 * to be careful about the length getter and setter being called on an object
 536 * not of Array class. For the getter, we search obj's prototype chain for the
 537 * array that caused this getter to be invoked. In the setter case to overcome
 538 * the JSPROP_SHARED attribute, we must define a shadowing length property.
 539 */
 540static JSBool
 541array_length_getter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
 542{
 543    do {
 544        if (OBJ_IS_ARRAY(cx, obj))
 545            return IndexToValue(cx, obj->fslots[JSSLOT_ARRAY_LENGTH], vp);
 546    } while ((obj = OBJ_GET_PROTO(cx, obj)) != NULL);
 547    return JS_TRUE;
 548}
 549
 550static JSBool
 551array_length_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
 552{
 553    jsuint newlen, oldlen, gap, index;
 554    jsval junk;
 555    JSObject *iter;
 556    JSTempValueRooter tvr;
 557    JSBool ok;
 558
 559    if (!OBJ_IS_ARRAY(cx, obj)) {
 560        jsid lengthId = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
 561
 562        return OBJ_DEFINE_PROPERTY(cx, obj, lengthId, *vp, NULL, NULL,
 563                                   JSPROP_ENUMERATE, NULL);
 564    }
 565
 566    newlen = ValueIsLength(cx, vp);
 567    if (JSVAL_IS_NULL(*vp))
 568        return JS_FALSE;
 569    oldlen = obj->fslots[JSSLOT_ARRAY_LENGTH];
 570
 571    if (oldlen == newlen)
 572        return JS_TRUE;
 573
 574    if (!IndexToValue(cx, newlen, vp))
 575        return JS_FALSE;
 576
 577    if (oldlen < newlen) {
 578        obj->fslots[JSSLOT_ARRAY_LENGTH] = newlen;
 579        return JS_TRUE;
 580    }
 581
 582    if (OBJ_IS_DENSE_ARRAY(cx, obj)) {
 583        if (ARRAY_DENSE_LENGTH(obj) && !ResizeSlots(cx, obj, oldlen, newlen))
 584            return JS_FALSE;
 585    } else if (oldlen - newlen < (1 << 24)) {
 586        do {
 587            --oldlen;
 588            if (!JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) ||
 589                !DeleteArrayElement(cx, obj, oldlen)) {
 590                return JS_FALSE;
 591            }
 592        } while (oldlen != newlen);
 593    } else {
 594        /*
 595         * We are going to remove a lot of indexes in a presumably sparse
 596         * array. So instead of looping through indexes between newlen and
 597         * oldlen, we iterate through all properties and remove those that
 598         * correspond to indexes in the half-open range [newlen, oldlen).  See
 599         * bug 322135.
 600         */
 601        iter = JS_NewPropertyIterator(cx, obj);
 602        if (!iter)
 603            return JS_FALSE;
 604
 605        /* Protect iter against GC in OBJ_DELETE_PROPERTY. */
 606        JS_PUSH_TEMP_ROOT_OBJECT(cx, iter, &tvr);
 607        gap = oldlen - newlen;
 608        for (;;) {
 609            ok = (JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) &&
 610                  JS_NextProperty(cx, iter, &id));
 611            if (!ok)
 612                break;
 613            if (JSVAL_IS_VOID(id))
 614                break;
 615            if (js_IdIsIndex(id, &index) && index - newlen < gap) {
 616                ok = OBJ_DELETE_PROPERTY(cx, obj, id, &junk);
 617                if (!ok)
 618                    break;
 619            }
 620        }
 621        JS_POP_TEMP_ROOT(cx, &tvr);
 622        if (!ok)
 623            return JS_FALSE;
 624    }
 625
 626    obj->fslots[JSSLOT_ARRAY_LENGTH] = newlen;
 627    return JS_TRUE;
 628}
 629
 630static JSBool
 631array_lookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
 632                     JSProperty **propp)
 633{
 634    uint32 i;
 635    union { JSProperty *p; jsval *v; } u;
 636
 637    if (!OBJ_IS_DENSE_ARRAY(cx, obj))
 638        return js_LookupProperty(cx, obj, id, objp, propp);
 639
 640    /*
 641     * We have only indexed properties up to DENSELEN (excepting holes), plus
 642     * the length property. For all else, we delegate to the prototype.
 643     */
 644    if (id != ATOM_TO_JSID(cx->runtime->atomState.lengthAtom) &&
 645        (!js_IdIsIndex(id, &i) ||
 646         obj->fslots[JSSLOT_ARRAY_LENGTH] == 0 ||
 647         i >= ARRAY_DENSE_LENGTH(obj) ||
 648         obj->dslots[i] == JSVAL_HOLE))
 649    {
 650        JSObject *proto = STOBJ_GET_PROTO(obj);
 651
 652        if (!proto) {
 653            *objp = NULL;
 654            *propp = NULL;
 655            return JS_TRUE;
 656        }
 657
 658        return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp);
 659    }
 660
 661    /* FIXME 417501: threadsafety: could race with a lookup on another thread.
 662     * If we can only have a single lookup active per context, we could
 663     * pigeonhole this on the context instead. */
 664    JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_ARRAY_LOOKUP_HOLDER]));
 665    obj->fslots[JSSLOT_ARRAY_LOOKUP_HOLDER] = (jsval) id;
 666    u.v = &(obj->fslots[JSSLOT_ARRAY_LOOKUP_HOLDER]);
 667    *propp = u.p;
 668    *objp = obj;
 669    return JS_TRUE;
 670}
 671
 672static void
 673array_dropProperty(JSContext *cx, JSObject *obj, JSProperty *prop)
 674{
 675    JS_ASSERT_IF(OBJ_IS_DENSE_ARRAY(cx, obj),
 676                 !JSVAL_IS_VOID(obj->fslots[JSSLOT_ARRAY_LOOKUP_HOLDER]));
 677#ifdef DEBUG
 678    obj->fslots[JSSLOT_ARRAY_LOOKUP_HOLDER] = JSVAL_VOID;
 679#endif
 680}
 681
 682static JSBool
 683array_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
 684{
 685    uint32 i;
 686
 687    if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom))
 688        return IndexToValue(cx, obj->fslots[JSSLOT_ARRAY_LENGTH], vp);
 689
 690    if (id == ATOM_TO_JSID(cx->runtime->atomState.protoAtom)) {
 691        *vp = STOBJ_GET_SLOT(obj, JSSLOT_PROTO);
 692        return JS_TRUE;
 693    }
 694
 695    if (!OBJ_IS_DENSE_ARRAY(cx, obj))
 696        return js_GetProperty(cx, obj, id, vp);
 697
 698    if (!js_IdIsIndex(ID_TO_VALUE(id), &i) || i >= ARRAY_DENSE_LENGTH(obj) ||
 699        obj->dslots[i] == JSVAL_HOLE) {
 700        JSObject *obj2;
 701        JSProperty *prop;
 702        JSScopeProperty *sprop;
 703
 704        JSObject *proto = STOBJ_GET_PROTO(obj);
 705        if (!proto) {
 706            *vp = JSVAL_VOID;
 707            return JS_TRUE;
 708        }
 709
 710        *vp = JSVAL_VOID;
 711        if (js_LookupPropertyWithFlags(cx, proto, id, cx->resolveFlags,
 712                                       &obj2, &prop) < 0)
 713            return JS_FALSE;
 714
 715        if (prop) {
 716            if (OBJ_IS_NATIVE(obj2)) {
 717                sprop = (JSScopeProperty *) prop;
 718                if (!js_NativeGet(cx, obj, obj2, sprop, vp))
 719                    return JS_FALSE;
 720            }
 721            OBJ_DROP_PROPERTY(cx, obj2, prop);
 722        }
 723        return JS_TRUE;
 724    }
 725
 726    *vp = obj->dslots[i];
 727    return JS_TRUE;
 728}
 729
 730static JSBool
 731slowarray_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
 732{
 733    jsuint index, length;
 734
 735    if (!js_IdIsIndex(id, &index))
 736        return JS_TRUE;
 737    length = obj->fslots[JSSLOT_ARRAY_LENGTH];
 738    if (index >= length)
 739        obj->fslots[JSSLOT_ARRAY_LENGTH] = index + 1;
 740    return JS_TRUE;
 741}
 742
 743static void
 744slowarray_trace(JSTracer *trc, JSObject *obj)
 745{
 746    uint32 length = obj->fslots[JSSLOT_ARRAY_LENGTH];
 747
 748    JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_SlowArrayClass);
 749
 750    /*
 751     * Move JSSLOT_ARRAY_LENGTH aside to prevent the GC from treating
 752     * untagged integer values as objects or strings.
 753     */
 754    obj->fslots[JSSLOT_ARRAY_LENGTH] = JSVAL_VOID;
 755    js_TraceObject(trc, obj);
 756    obj->fslots[JSSLOT_ARRAY_LENGTH] = length;
 757}
 758
 759static JSObjectOps js_SlowArrayObjectOps;
 760
 761static JSObjectOps *
 762slowarray_getObjectOps(JSContext *cx, JSClass *clasp)
 763{
 764    return &js_SlowArrayObjectOps;
 765}
 766
 767static JSBool
 768array_setProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
 769{
 770    uint32 i;
 771
 772    if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom))
 773        return array_length_setter(cx, obj, id, vp);
 774
 775    if (!OBJ_IS_DENSE_ARRAY(cx, obj))
 776        return js_SetProperty(cx, obj, id, vp);
 777
 778    if (!js_IdIsIndex(id, &i) || INDEX_TOO_SPARSE(obj, i)) {
 779        if (!js_MakeArraySlow(cx, obj))
 780            return JS_FALSE;
 781        return js_SetProperty(cx, obj, id, vp);
 782    }
 783
 784    if (!EnsureLength(cx, obj, i + 1))
 785        return JS_FALSE;
 786
 787    if (i >= (uint32)obj->fslots[JSSLOT_ARRAY_LENGTH])
 788        obj->fslots[JSSLOT_ARRAY_LENGTH] = i + 1;
 789    if (obj->dslots[i] == JSVAL_HOLE)
 790        obj->fslots[JSSLOT_ARRAY_COUNT]++;
 791    obj->dslots[i] = *vp;
 792    return JS_TRUE;
 793}
 794
 795#ifdef JS_TRACER
 796JSBool FASTCALL
 797js_Array_dense_setelem(JSContext* cx, JSObject* obj, jsint i, jsval v)
 798{
 799    JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj));
 800
 801    do {
 802        jsuint length = ARRAY_DENSE_LENGTH(obj);
 803        if ((jsuint)i < length) {
 804            if (obj->dslots[i] == JSVAL_HOLE) {
 805                if (cx->runtime->anyArrayProtoHasElement)
 806                    break;
 807                if (i >= obj->fslots[JSSLOT_ARRAY_LENGTH])
 808                    obj->fslots[JSSLOT_ARRAY_LENGTH] = i + 1;
 809                obj->fslots[JSSLOT_ARRAY_COUNT]++;
 810            }
 811            obj->dslots[i] = v;
 812            return JS_TRUE;
 813        }
 814    } while (0);
 815    return OBJ_SET_PROPERTY(cx, obj, INT_TO_JSID(i), &v);
 816}
 817#endif
 818
 819static JSBool
 820array_defineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
 821                     JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
 822                     JSProperty **propp)
 823{
 824    uint32 i;
 825    JSBool isIndex;
 826
 827    if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom))
 828        return JS_TRUE;
 829
 830    isIndex = js_IdIsIndex(ID_TO_VALUE(id), &i);
 831    if (!isIndex || attrs != JSPROP_ENUMERATE) {
 832        if (!ENSURE_SLOW_ARRAY(cx, obj))
 833            return JS_FALSE;
 834        if (isIndex && STOBJ_IS_DELEGATE(obj))
 835            cx->runtime->anyArrayProtoHasElement = JS_TRUE;
 836        return js_DefineProperty(cx, obj, id, value, getter, setter, attrs, propp);
 837    }
 838
 839    return array_setProperty(cx, obj, id, &value);
 840}
 841
 842static JSBool
 843array_getAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
 844                    uintN *attrsp)
 845{
 846    *attrsp = id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)
 847        ? JSPROP_PERMANENT : JSPROP_ENUMERATE;
 848    return JS_TRUE;
 849}
 850
 851static JSBool
 852array_setAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
 853                    uintN *attrsp)
 854{
 855    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
 856                         JSMSG_CANT_SET_ARRAY_ATTRS);
 857    return JS_FALSE;
 858}
 859
 860static JSBool
 861array_deleteProperty(JSContext *cx, JSObject *obj, jsval id, jsval *rval)
 862{
 863    uint32 i;
 864
 865    if (!OBJ_IS_DENSE_ARRAY(cx, obj))
 866        return js_DeleteProperty(cx, obj, id, rval);
 867
 868    if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)) {
 869        *rval = JSVAL_FALSE;
 870        return JS_TRUE;
 871    }
 872
 873    if (js_IdIsIndex(id, &i) && i < ARRAY_DENSE_LENGTH(obj) &&
 874        obj->dslots[i] != JSVAL_HOLE) {
 875        obj->fslots[JSSLOT_ARRAY_COUNT]--;
 876        obj->dslots[i] = JSVAL_HOLE;
 877    }
 878
 879    *rval = JSVAL_TRUE;
 880    return JS_TRUE;
 881}
 882
 883/*
 884 * JSObjectOps.enumerate implementation.
 885 *
 886 * For a fast array, JSENUMERATE_INIT captures in the enumeration state both
 887 * the length of the array and the bitmap indicating the positions of holes in
 888 * the array. This ensures that adding or deleting array elements does not
 889 * affect the sequence of indexes JSENUMERATE_NEXT returns.
 890 *
 891 * For a common case of an array without holes, to represent the state we pack
 892 * the (nextEnumerationIndex, arrayLength) pair as a pseudo-boolean jsval.
 893 * This is possible when length <= PACKED_UINT_PAIR_BITS. For arrays with
 894 * greater length or holes we allocate the JSIndexIterState structure and
 895 * store it as an int-tagged private pointer jsval. For a slow array we
 896 * delegate the enumeration implementation to js_Enumerate in
 897 * slowarray_enumerate.
 898 *
 899 * Array mutations can turn a fast array into a slow one after the enumeration
 900 * starts. When this happens, slowarray_enumerate receives a state created
 901 * when the array was fast. To distinguish such fast state from a slow state,
 902 * which is an int-tagged pointer that js_Enumerate creates, we set not one
 903 * but two lowest bits when tagging a JSIndexIterState pointer -- see
 904 * INDEX_ITER_TAG usage below. Thus, when slowarray_enumerate receives a state
 905 * tagged with JSVAL_BOOLEAN or with two lowest bits set, it knows that this
 906 * is a fast state so it calls array_enumerate to continue enumerating the
 907 * indexes present in the original fast array.
 908 */
 909
 910#define PACKED_UINT_PAIR_BITS           14
 911#define PACKED_UINT_PAIR_MASK           JS_BITMASK(PACKED_UINT_PAIR_BITS)
 912
 913#define UINT_PAIR_TO_BOOLEAN_JSVAL(i,j)                                       \
 914    (JS_ASSERT((uint32) (i) <= PACKED_UINT_PAIR_MASK),                        \
 915     JS_ASSERT((uint32) (j) <= PACKED_UINT_PAIR_MASK),                        \
 916     ((jsval) (i) << (PACKED_UINT_PAIR_BITS + JSVAL_TAGBITS)) |               \
 917     ((jsval) (j) << (JSVAL_TAGBITS)) |                                       \
 918     (jsval) JSVAL_BOOLEAN)
 919
 920#define BOOLEAN_JSVAL_TO_UINT_PAIR(v,i,j)                                     \
 921    (JS_ASSERT(JSVAL_TAG(v) == JSVAL_BOOLEAN),                                \
 922     (i) = (uint32) ((v) >> (PACKED_UINT_PAIR_BITS + JSVAL_TAGBITS)),         \
 923     (j) = (uint32) ((v) >> JSVAL_TAGBITS) & PACKED_UINT_PAIR_MASK,           \
 924     JS_ASSERT((i) <= PACKED_UINT_PAIR_MASK))
 925
 926JS_STATIC_ASSERT(PACKED_UINT_PAIR_BITS * 2 + JSVAL_TAGBITS <= JS_BITS_PER_WORD);
 927
 928typedef struct JSIndexIterState {
 929    uint32          index;
 930    uint32          length;
 931    JSBool          hasHoles;
 932
 933    /*
 934     * Variable-length bitmap representing array's holes. It must not be
 935     * accessed when hasHoles is false.
 936     */
 937    jsbitmap        holes[1];
 938} JSIndexIterState;
 939
 940#define INDEX_ITER_TAG      3
 941
 942JS_STATIC_ASSERT(JSVAL_INT == 1);
 943
 944static JSBool
 945array_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
 946                jsval *statep, jsid *idp)
 947{
 948    uint32 length, i;
 949    JSIndexIterState *ii;
 950
 951    switch (enum_op) {
 952      case JSENUMERATE_INIT:
 953        JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj));
 954        length = ARRAY_DENSE_LENGTH(obj);
 955        if (idp)
 956            *idp = INT_TO_JSVAL(obj->fslots[JSSLOT_ARRAY_COUNT]);
 957        ii = NULL;
 958        for (i = 0; i != length; ++i) {
 959            if (obj->dslots[i] == JSVAL_HOLE) {
 960                if (!ii) {
 961                    ii = (JSIndexIterState *)
 962                         JS_malloc(cx, offsetof(JSIndexIterState, holes) +
 963                                   JS_BITMAP_SIZE(length));
 964                    if (!ii)
 965                        return JS_FALSE;
 966                    ii->hasHoles = JS_TRUE;
 967                    memset(ii->holes, 0, JS_BITMAP_SIZE(length));
 968                }
 969                JS_SET_BIT(ii->holes, i);
 970            }
 971        }
 972        if (!ii) {
 973            /* Array has no holes. */
 974            if (length <= PACKED_UINT_PAIR_MASK) {
 975                *statep = UINT_PAIR_TO_BOOLEAN_JSVAL(0, length);
 976                break;
 977            }
 978            ii = (JSIndexIterState *)
 979                 JS_malloc(cx, offsetof(JSIndexIterState, holes));
 980            if (!ii)
 981                return JS_FALSE;
 982            ii->hasHoles = JS_FALSE;
 983        }
 984        ii->index = 0;
 985        ii->length = length;
 986        *statep = (jsval) ii | INDEX_ITER_TAG;
 987        JS_ASSERT(*statep & JSVAL_INT);
 988        break;
 989
 990      case JSENUMERATE_NEXT:
 991        if (JSVAL_TAG(*statep) == JSVAL_BOOLEAN) {
 992            BOOLEAN_JSVAL_TO_UINT_PAIR(*statep, i, length);
 993            if (i != length) {
 994                *idp = INT_TO_JSID(i);
 995                *statep = UINT_PAIR_TO_BOOLEAN_JSVAL(i + 1, length);
 996                break;
 997            }
 998        } else {
 999            JS_ASSERT((*statep & INDEX_ITER_TAG) == INDEX_ITER_TAG);
1000            ii = (JSIndexIterState *) (*statep & ~INDEX_ITER_TAG);
1001            i = ii->index;
1002            if (i != ii->length) {
1003                /* Skip holes if any. */
1004                if (ii->hasHoles) {
1005                    while (JS_TEST_BIT(ii->holes, i) && ++i != ii->length)
1006                        continue;
1007                }
1008                if (i != ii->length) {
1009                    ii->index = i + 1;
1010                    return js_IndexToId(cx, i, idp);
1011                }
1012            }
1013        }
1014        /* FALL THROUGH */
1015
1016      case JSENUMERATE_DESTROY:
1017        if (JSVAL_TAG(*statep) != JSVAL_BOOLEAN) {
1018            JS_ASSERT((*statep & INDEX_ITER_TAG) == INDEX_ITER_TAG);
1019            ii = (JSIndexIterState *) (*statep & ~INDEX_ITER_TAG);
1020            JS_free(cx, ii);
1021        }
1022        *statep = JSVAL_NULL;
1023        break;
1024    }
1025    return JS_TRUE;
1026}
1027
1028static JSBool
1029slowarray_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
1030                    jsval *statep, jsid *idp)
1031{
1032    JSBool ok;
1033
1034    /* Are we continuing an enumeration that started when we were dense? */
1035    if (enum_op != JSENUMERATE_INIT) {
1036        if (JSVAL_TAG(*statep) == JSVAL_BOOLEAN ||
1037            (*statep & INDEX_ITER_TAG) == INDEX_ITER_TAG) {
1038            return array_enumerate(cx, obj, enum_op, statep, idp);
1039        }
1040        JS_ASSERT((*statep & INDEX_ITER_TAG) == JSVAL_INT);
1041    }
1042    ok = js_Enumerate(cx, obj, enum_op, statep, idp);
1043    JS_ASSERT(*statep == JSVAL_NULL || (*statep & INDEX_ITER_TAG) == JSVAL_INT);
1044    return ok;
1045}
1046
1047static void
1048array_finalize(JSContext *cx, JSObject *obj)
1049{
1050    if (obj->dslots)
1051        JS_free(cx, obj->dslots - 1);
1052    obj->dslots = NULL;
1053}
1054
1055static void
1056array_trace(JSTracer *trc, JSObject *obj)
1057{
1058    uint32 length;
1059    size_t i;
1060    jsval v;
1061
1062    JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj));
1063
1064    length = ARRAY_DENSE_LENGTH(obj);
1065    for (i = 0; i < length; i++) {
1066        v = obj->dslots[i];
1067        if (JSVAL_IS_TRACEABLE(v)) {
1068            JS_SET_TRACING_INDEX(trc, "array_dslots", i);
1069            JS_CallTracer(trc, JSVAL_TO_TRACEABLE(v), JSVAL_TRACE_KIND(v));
1070        }
1071    }
1072
1073    for (i = JSSLOT_PROTO; i <= JSSLOT_PARENT; ++i) {
1074        v = STOBJ_GET_SLOT(obj, i);
1075        if (JSVAL_IS_TRACEABLE(v)) {
1076            JS_SET_TRACING_DETAILS(trc, js_PrintObjectSlotName, obj, i);
1077            JS_CallTracer(trc, JSVAL_TO_TRACEABLE(v), JSVAL_TRACE_KIND(v));
1078        }
1079    }
1080}
1081
1082static JSObjectMap *
1083array_newObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops,
1084                   JSClass *clasp, JSObject *obj)
1085{
1086#ifdef DEBUG
1087    extern JSClass js_ArrayClass;
1088    extern JSObjectOps js_ArrayObjectOps;
1089#endif
1090    JSObjectMap *map = (JSObjectMap *) JS_malloc(cx, sizeof(*map));
1091    if (!map)
1092        return NULL;
1093
1094    map->nrefs = nrefs;
1095    JS_ASSERT(ops == &js_ArrayObjectOps);
1096    map->ops = ops;
1097    JS_ASSERT(clasp == &js_ArrayClass);
1098    map->freeslot = JSSLOT_FREE(clasp);
1099
1100    return map;
1101}
1102
1103void
1104array_destroyObjectMap(JSContext *cx, JSObjectMap *map)
1105{
1106    JS_free(cx, map);
1107}
1108
1109JSObjectOps js_ArrayObjectOps = {
1110    array_newObjectMap,   array_destroyObjectMap,
1111    array_lookupProperty, array_defineProperty,
1112    array_getProperty,    array_setProperty,
1113    array_getAttributes,  array_setAttributes,
1114    array_deleteProperty, js_DefaultValue,
1115    array_enumerate,      js_CheckAccess,
1116    NULL,                 array_dropProperty,
1117    NULL,                 NULL,
1118    NULL,                 js_HasInstance,
1119    js_SetProtoOrParent,  js_SetProtoOrParent,
1120    array_trace,          NULL,
1121    NULL,                 NULL
1122};
1123
1124static JSObjectOps *
1125array_getObjectOps(JSContext *cx, JSClass *clasp)
1126{
1127    return &js_ArrayObjectOps;
1128}
1129
1130JSClass js_ArrayClass = {
1131    "Array",
1132    JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Array) |
1133    JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_NEW_ENUMERATE,
1134    JS_PropertyStub,    JS_PropertyStub,   JS_PropertyStub,   JS_PropertyStub,
1135    JS_EnumerateStub,   JS_ResolveStub,    js_TryValueOf,     array_finalize,
1136    array_getObjectOps, NULL,              NULL,              NULL,
1137    NULL,               NULL,              NULL,              NULL
1138};
1139
1140JSClass js_SlowArrayClass = {
1141    "Array",
1142    JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Array),
1143    slowarray_addProperty, JS_PropertyStub, JS_PropertyStub,  JS_PropertyStub,
1144    JS_EnumerateStub,      JS_ResolveStub,  js_TryValueOf,    JS_FinalizeStub,
1145    slowarray_getObjectOps, NULL,           NULL,             NULL,
1146    NULL,                  NULL,            NULL,             NULL
1147};
1148
1149/*
1150 * Convert an array object from fast-and-dense to slow-and-flexible.
1151 */
1152JSBool
1153js_MakeArraySlow(JSContext *cx, JSObject *obj)
1154{
1155    JSObjectMap *map, *oldmap;
1156    uint32 i, length;
1157
1158    JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_ArrayClass);
1159
1160    /* Create a native scope. */
1161    map = js_NewObjectMap(cx, obj->map->nrefs, &js_SlowArrayObjectOps,
1162                          &js_SlowArrayClass, obj);
1163    if (!map)
1164        return JS_FALSE;
1165
1166    length = ARRAY_DENSE_LENGTH(obj);
1167    if (length) {
1168        map->freeslot = STOBJ_NSLOTS(obj) + JS_INITIAL_NSLOTS;
1169        obj->dslots[-1] = JS_INITIAL_NSLOTS + length;
1170    } else {
1171        map->freeslot = STOBJ_NSLOTS(obj);
1172    }
1173
1174    /* Create new properties pointing to existing values in dslots */
1175    for (i = 0; i < length; i++) {
1176        jsid id;
1177        JSScopeProperty *sprop;
1178
1179        if (!JS_ValueToId(cx, INT_TO_JSVAL(i), &id))
1180            goto out_bad;
1181
1182        if (obj->dslots[i] == JSVAL_HOLE) {
1183            obj->dslots[i] = JSVAL_VOID;
1184            continue;
1185        }
1186
1187        sprop = js_AddScopeProperty(cx, (JSScope *)map, id, NULL, NULL,
1188                                    i + JS_INITIAL_NSLOTS, JSPROP_ENUMERATE,
1189                                    0, 0);
1190        if (!sprop)
1191            goto out_bad;
1192    }
1193
1194    /*
1195     * Render our formerly-reserved count property GC-safe. If length fits in
1196     * a jsval, set our slow/sparse COUNT to the current length as a jsval, so
1197     * we can tell when only named properties have been added to a dense array
1198     * to make it slow-but-not-sparse.
1199     */
1200    length = obj->fslots[JSSLOT_ARRAY_LENGTH];
1201    obj->fslots[JSSLOT_ARRAY_COUNT] = INT_FITS_IN_JSVAL(length)
1202                                      ? INT_TO_JSVAL(length)
1203                                      : JSVAL_VOID;
1204
1205    /* Make sure we preserve any flags borrowing bits in classword. */
1206    obj->classword ^= (jsuword) &js_ArrayClass;
1207    obj->classword |= (jsuword) &js_SlowArrayClass;
1208
1209    /* Swap in our new map. */
1210    oldmap = obj->map;
1211    obj->map = map;
1212    array_destroyObjectMap(cx, oldmap);
1213
1214    return JS_TRUE;
1215
1216out_bad:
1217    js_DestroyObjectMap(cx, map);
1218    return JS_FALSE;
1219}
1220
1221enum ArrayToStringOp {
1222    TO_STRING,
1223    TO_LOCALE_STRING,
1224    TO_SOURCE
1225};
1226
1227/*
1228 * When op is TO_STRING or TO_LOCALE_STRING sep indicates a separator to use
1229 * or "," when sep is NULL.
1230 * When op is TO_SOURCE sep must be NULL.
1231 */
1232static JSBool
1233array_join_sub(JSContext *cx, JSObject *obj, enum ArrayToStringOp op,
1234               JSString *sep, jsval *rval)
1235{
1236    JSBool ok, hole;
1237    jsuint length, index;
1238    jschar *chars, *ochars;
1239    size_t nchars, growth, seplen, tmplen, extratail;
1240    const jschar *sepstr;
1241    JSString *str;
1242    JSHashEntry *he;
1243    JSAtom *atom;
1244
1245    JS_CHECK_RECURSION(cx, return JS_FALSE);
1246
1247    ok = js_GetLengthProperty(cx, obj, &length);
1248    if (!ok)
1249        return JS_FALSE;
1250
1251    he = js_EnterSharpObject(cx, obj, NULL, &chars);
1252    if (!he)
1253        return JS_FALSE;
1254#ifdef DEBUG
1255    growth = (size_t) -1;
1256#endif
1257
1258    if (op == TO_SOURCE) {
1259        if (IS_SHARP(he)) {
1260#if JS_HAS_SHARP_VARS
1261            nchars = js_strlen(chars);
1262#else
1263            chars[0] = '[';
1264            chars[1] = ']';
1265            chars[2] = 0;
1266            nchars = 2;
1267#endif
1268            goto make_string;
1269        }
1270
1271        /*
1272         * Always allocate 2 extra chars for closing ']' and terminating 0
1273         * and then preallocate 1 + extratail to include starting '['.
1274         */
1275        extratail = 2;
1276        growth = (1 + extratail) * sizeof(jschar);
1277        if (!chars) {
1278            nchars = 0;
1279            chars = (jschar *) malloc(growth);
1280            if (!chars)
1281                goto done;
1282        } else {
1283            MAKE_SHARP(he);
1284            nchars = js_strlen(chars);
1285            growth += nchars * sizeof(jschar);
1286            chars = (jschar *)realloc((ochars = chars), growth);
1287            if (!chars) {
1288                free(ochars);
1289                goto done;
1290            }
1291        }
1292        chars[nchars++] = '[';
1293        JS_ASSERT(sep == NULL);
1294        sepstr = NULL;  /* indicates to use ", " as separator */
1295        seplen = 2;
1296    } else {
1297        /*
1298         * Free any sharp variable definition in chars.  Normally, we would
1299         * MAKE_SHARP(he) so that only the first sharp variable annotation is
1300         * a definition, and all the rest are references, but in the current
1301         * case of (op != TO_SOURCE), we don't need chars at all.
1302         */
1303        if (chars)
1304            JS_free(cx, chars);
1305        chars = NULL;
1306        nchars = 0;
1307        extratail = 1;  /* allocate extra char for terminating 0 */
1308
1309        /* Return the empty string on a cycle as well as on empty join. */
1310        if (IS_BUSY(he) || length == 0) {
1311            js_LeaveSharpObject(cx, NULL);
1312            *rval = JS_GetEmptyStringValue(cx);
1313            return ok;
1314        }
1315
1316        /* Flag he as BUSY so we can distinguish a cycle from a join-point. */
1317        MAKE_BUSY(he);
1318
1319        if (sep) {
1320            JSSTRING_CHARS_AND_LENGTH(sep, sepstr, seplen);
1321        } else {
1322            sepstr = NULL;      /* indicates to use "," as separator */
1323            seplen = 1;
1324        }
1325    }
1326
1327    /* Use rval to locally root each element value as we loop and convert. */
1328    for (index = 0; index < length; index++) {
1329        ok = (JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) &&
1330              GetArrayElement(cx, obj, index, &hole, rval));
1331        if (!ok)
1332            goto done;
1333        if (hole ||
1334            (op != TO_SOURCE &&
1335             (JSVAL_IS_VOID(*rval) || JSVAL_IS_NULL(*rval)))) {
1336            str = cx->runtime->emptyString;
1337        } else {
1338            if (op == TO_LOCALE_STRING) {
1339                JSObject *robj;
1340
1341                atom = cx->runtime->atomState.toLocaleStringAtom;
1342                ok = js_ValueToObject(cx, *rval, &robj);
1343                if (ok) {
1344                    /* Re-use *rval to protect robj temporarily. */
1345                    *rval = OBJECT_TO_JSVAL(robj);
1346                    ok = js_TryMethod(cx, robj, atom, 0, NULL, rval);
1347                }
1348                if (!ok)
1349                    goto done;
1350                str = js_ValueToString(cx, *rval);
1351            } else if (op == TO_STRING) {
1352                str = js_ValueToString(cx, *rval);
1353            } else {
1354                JS_ASSERT(op == TO_SOURCE);
1355                str = js_ValueToSource(cx, *rval);
1356            }
1357            if (!str) {
1358                ok = JS_FALSE;
1359                goto done;
1360            }
1361        }
1362
1363        /*
1364         * Do not append separator after the last element unless it is a hole
1365         * and we are in toSource. In that case we append single ",".
1366         */
1367        if (index + 1 == length)
1368            seplen = (hole && op == TO_SOURCE) ? 1 : 0;
1369
1370        /* Allocate 1 at end for closing bracket and zero. */
1371        tmplen = JSSTRING_LENGTH(str);
1372        growth = nchars + tmplen + seplen + extratail;
1373        if (nchars > growth || tmplen > growth ||
1374            growth > (size_t)-1 / sizeof(jschar)) {
1375            if (chars) {
1376                free(chars);
1377                chars = NULL;
1378            }
1379            goto done;
1380        }
1381        growth *= sizeof(jschar);
1382        JS_COUNT_OPERATION(cx, JSOW_ALLOCATION);
1383        if (!chars) {
1384            chars = (jschar *) malloc(growth);
1385            if (!chars)
1386                goto done;
1387        } else {
1388            chars = (jschar *) realloc((ochars = chars), growth);
1389            if (!chars) {
1390                free(ochars);
1391                goto done;
1392            }
1393        }
1394
1395        js_strncpy(&chars[nchars], JSSTRING_CHARS(str), tmplen);
1396        nchars += tmplen;
1397
1398        if (seplen) {
1399            if (sepstr) {
1400                js_strncpy(&chars[nchars], sepstr, seplen);
1401            } else {
1402                JS_ASSERT(seplen == 1 || seplen == 2);
1403                chars[nchars] = ',';
1404                if (seplen == 2)
1405                    chars[nchars + 1] = ' ';
1406            }
1407            nchars += seplen;
1408        }
1409    }
1410
1411  done:
1412    if (op == TO_SOURCE) {
1413        if (chars)
1414            chars[nchars++] = ']';
1415    } else {
1416        CLEAR_BUSY(he);
1417    }
1418    js_LeaveSharpObject(cx, NULL);
1419    if (!ok) {
1420        if (chars)
1421            free(chars);
1422        return ok;
1423    }
1424
1425  make_string:
1426    if (!chars) {
1427        JS_ReportOutOfMemory(cx);
1428        return JS_FALSE;
1429    }
1430    chars[nchars] = 0;
1431    JS_ASSERT(growth == (size_t)-1 || (nchars + 1) * sizeof(jschar) == growth);
1432    str = js_NewString(cx, chars, nchars);
1433    if (!str) {
1434        free(chars);
1435        return JS_FALSE;
1436    }
1437    *rval = STRING_TO_JSVAL(str);
1438    return JS_TRUE;
1439}
1440
1441#if JS_HAS_TOSOURCE
1442static JSBool
1443array_toSource(JSContext *cx, uintN argc, jsval *vp)
1444{
1445    JSObject *obj;
1446
1447    obj = JS_THIS_OBJECT(cx, vp);
1448    if (OBJ_GET_CLASS(cx, obj) != &js_SlowArrayClass &&
1449        !JS_InstanceOf(cx, obj, &js_ArrayClass, vp + 2)) {
1450        return JS_FALSE;
1451    }
1452    return array_join_sub(cx, obj, TO_SOURCE, NULL, vp);
1453}
1454#endif
1455
1456static JSBool
1457array_toString(JSContext *cx, uintN argc, jsval *vp)
1458{
1459    JSObject *obj;
1460
1461    obj = JS_THIS_OBJECT(cx, vp);
1462    if (OBJ_GET_CLASS(cx, obj) != &js_SlowArrayClass &&
1463        !JS_InstanceOf(cx, obj, &js_ArrayClass, vp + 2)) {
1464        return JS_FALSE;
1465    }
1466    return array_join_sub(cx, obj, TO_STRING, NULL, vp);
1467}
1468
1469static JSBool
1470array_toLocaleString(JSContext *cx, uintN argc, jsval *vp)
1471{
1472    JSObject *obj;
1473
1474    obj = JS_THIS_OBJECT(cx, vp);
1475    if (OBJ_GET_CLASS(cx, obj) != &js_SlowArrayClass &&
1476        !JS_InstanceOf(cx, obj, &js_ArrayClass, vp + 2)) {
1477        return JS_FALSE;
1478    }
1479
1480    /*
1481     *  Passing comma here as the separator. Need a way to get a
1482     *  locale-specific version.
1483     */
1484    return array_join_sub(cx, obj, TO_LOCALE_STRING, NULL, vp);
1485}
1486
1487static JSBool
1488InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint end,
1489                  jsval *vector)
1490{
1491    if (OBJ_IS_DENSE_ARRAY(cx, obj)) {
1492        if (!EnsureLength(cx, obj, end))
1493            return JS_FALSE;
1494
1495        if (end > (uint32)obj->fslots[JSSLOT_ARRAY_LENGTH])
1496            obj->fslots[JSSLOT_ARRAY_LENGTH] = end;
1497
1498        memcpy(obj->dslots + start, vector, sizeof(jsval) * (end - start));
1499        return JS_TRUE;
1500    }
1501
1502    while (start != end) {
1503        if (!JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) ||
1504            !SetArrayElement(cx, obj, start++, *vector++)) {
1505            return JS_FALSE;
1506        }
1507    }
1508    return JS_TRUE;
1509}
1510
1511static JSBool
1512InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, jsval *vector,
1513                JSBool holey = JS_FALSE)
1514{
1515    JS_ASSERT(OBJ_IS_ARRAY(cx, obj));
1516
1517    obj->fslots[JSSLOT_ARRAY_LENGTH] = length;
1518
1519    if (vector) {
1520        if (!EnsureLength(cx, obj, length))
1521            return JS_FALSE;
1522
1523        jsuint count = length;
1524        if (!holey) {
1525            memcpy(obj->dslots, vector, length * sizeof (jsval));
1526        } else {
1527            for (jsuint i = 0; i < length; i++) {
1528                if (vector[i] == JSVAL_HOLE)
1529                    --count;
1530                obj->dslots[i] = vector[i];
1531            }
1532        }
1533        obj->fslots[JSSLOT_ARRAY_COUNT] = count;
1534    } else {
1535        obj->fslots[JSSLOT_ARRAY_COUNT] = 0;
1536    }
1537    return JS_TRUE;
1538}
1539
1540#ifdef JS_TRACER
1541static JSString* FASTCALL
1542Array_p_join(JSContext* cx, JSObject* obj, JSString *str)
1543{
1544    jsval v;
1545    if (!array_join_sub(cx, obj, TO_STRING, str, &v))
1546        return NULL;
1547    JS_ASSERT(JSVAL_IS_STRING(v));
1548    return JSVAL_TO_STRING(v);
1549}
1550
1551static JSString* FASTCALL
1552Array_p_toString(JSContext* cx, JSObject* obj)
1553{
1554    jsval v;
1555    if (!array_join_sub(cx, obj, TO_STRING, NULL, &v))
1556        return NULL;
1557    JS_ASSERT(JSVAL_IS_STRING(v));
1558    return JSVAL_TO_STRING(v);
1559}
1560#endif
1561
1562/*
1563 * Perl-inspired join, reverse, and sort.
1564 */
1565static JSBool
1566array_join(JSContext *cx, uintN argc, jsval *vp)
1567{
1568    JSString *str;
1569    JSObject *obj;
1570
1571    if (argc == 0 || JSVAL_IS_VOID(vp[2])) {
1572        str = NULL;
1573    } else {
1574        str = js_ValueToString(cx, vp[2]);
1575        if (!str)
1576            return JS_FALSE;
1577        vp[2] = STRING_TO_JSVAL(str);
1578    }
1579    obj = JS_THIS_OBJECT(cx, vp);
1580    return obj && array_join_sub(cx, obj, TO_STRING, str, vp);
1581}
1582
1583static JSBool
1584array_reverse(JSContext *cx, uintN argc, jsval *vp)
1585{
1586    JSObject *obj;
1587    JSTempValueRooter tvr;
1588    jsuint len, half, i;
1589    JSBool ok, hole, hole2;
1590
1591    obj = JS_THIS_OBJECT(cx, vp);
1592    if (!obj || !js_GetLengthProperty(cx, obj, &len))
1593        return JS_FALSE;
1594
1595    ok = JS_TRUE;
1596    JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);
1597    half = len / 2;
1598    for (i = 0; i < half; i++) {
1599        ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) &&
1600             GetArrayElement(cx, obj, i, &hole, &tvr.u.value) &&
1601             GetArrayElement(cx, obj, len - i - 1, &hole2, vp) &&
1602             SetOrDeleteArrayElement(cx, obj, len - i - 1, hole, tvr.u.value) &&
1603             SetOrDeleteArrayElement(cx, obj, i, hole2, *vp);
1604        if (!ok)
1605            break;
1606    }
1607    JS_POP_TEMP_ROOT(cx, &tvr);
1608
1609    *vp = OBJECT_TO_JSVAL(obj);
1610    return ok;
1611}
1612
1613typedef struct MSortArgs {
1614    size_t       elsize;
1615    JSComparator cmp;
1616    void         *arg;
1617    JSBool       fastcopy;
1618} MSortArgs;
1619
1620/* Helper function for js_MergeSort. */
1621static JSBool
1622MergeArrays(MSortArgs *msa, void *src, void *dest, size_t run1, size_t run2)
1623{
1624    void *arg, *a, *b, *c;
1625    size_t elsize, runtotal;
1626    int cmp_result;
1627    JSComparator cmp;
1628    JSBool fastcopy;
1629
1630    runtotal = run1 + run2;
1631
1632    elsize = msa->elsize;
1633    cmp = msa->cmp;
1634    arg = msa->arg;
1635    fastcopy = msa->fastcopy;
1636
1637#define CALL_CMP(a, b) \
1638    if (!cmp(arg, (a), (b), &cmp_result)) return JS_FALSE;
1639
1640    /* Copy runs already in sorted order. */
1641    b = (char *)src + run1 * elsize;
1642    a = (char *)b - elsize;
1643    CALL_CMP(a, b);
1644    if (cmp_result <= 0) {
1645        memcpy(dest, src, runtotal * elsize);
1646        return JS_TRUE;
1647    }
1648
1649#define COPY_ONE(p,q,n) \
1650    (fastcopy ? (void)(*(jsval*)…

Large files files are truncated, but you can click here to view the full file