ArrayPrototype.cpp 48.2 KB
Newer Older
1 2
/*
 *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3
 *  Copyright (C) 2003, 2007, 2008, 2009, 2011 Apple Inc. All rights reserved.
4
 *  Copyright (C) 2003 Peter Kelly (pmk@post.com)
ap's avatar
ap committed
5
 *  Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
19 20
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
 *  USA
21
 *
22 23
 */

mjs's avatar
mjs committed
24
#include "config.h"
darin@apple.com's avatar
darin@apple.com committed
25
#include "ArrayPrototype.h"
darin's avatar
darin committed
26

27
#include "ButterflyInlineMethods.h"
28
#include "CachedCall.h"
29
#include "CodeBlock.h"
30
#include "CopiedSpaceInlineMethods.h"
ggaren@apple.com's avatar
ggaren@apple.com committed
31
#include "Interpreter.h"
32
#include "JIT.h"
33
#include "JSStringBuilder.h"
34
#include "JSStringJoiner.h"
35
#include "Lookup.h"
36
#include "ObjectPrototype.h"
37
#include "Operations.h"
38
#include "StringRecursionChecker.h"
39
#include <algorithm>
ggaren's avatar
ggaren committed
40 41
#include <wtf/Assertions.h>
#include <wtf/HashSet.h>
eric@webkit.org's avatar
eric@webkit.org committed
42

43
namespace JSC {
darin's avatar
darin committed
44

ggaren@apple.com's avatar
ggaren@apple.com committed
45 46
ASSERT_CLASS_FITS_IN_CELL(ArrayPrototype);

47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncPush(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncShift(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncSort(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncUnShift(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncEvery(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncForEach(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncSome(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncFilter(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncMap(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncReduce(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncReduceRight(ExecState*);
static EncodedJSValue JSC_HOST_CALL arrayProtoFuncLastIndexOf(ExecState*);
darin@apple.com's avatar
darin@apple.com committed
68 69 70 71 72

}

#include "ArrayPrototype.lut.h"

73
namespace JSC {
darin@apple.com's avatar
darin@apple.com committed
74

75
static inline bool isNumericCompareFunction(ExecState* exec, CallType callType, const CallData& callData)
ggaren@apple.com's avatar
ggaren@apple.com committed
76 77 78
{
    if (callType != CallTypeJS)
        return false;
79

80 81
    FunctionExecutable* executable = callData.js.functionExecutable;

82
    JSObject* error = executable->compileForCall(exec, callData.js.scope);
83
    if (error)
84
        return false;
85

86
    return executable->generatedBytecodeForCall().isNumericCompareFunction();
ggaren@apple.com's avatar
ggaren@apple.com committed
87 88
}

darin's avatar
darin committed
89
// ------------------------------ ArrayPrototype ----------------------------
90

91
const ClassInfo ArrayPrototype::s_info = {"Array", &JSArray::s_info, 0, ExecState::arrayPrototypeTable, CREATE_METHOD_TABLE(ArrayPrototype)};
92

darin@apple.com's avatar
darin@apple.com committed
93
/* Source for ArrayPrototype.lut.h
94
@begin arrayPrototypeTable 16
weinig@apple.com's avatar
weinig@apple.com committed
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
  toString       arrayProtoFuncToString       DontEnum|Function 0
  toLocaleString arrayProtoFuncToLocaleString DontEnum|Function 0
  concat         arrayProtoFuncConcat         DontEnum|Function 1
  join           arrayProtoFuncJoin           DontEnum|Function 1
  pop            arrayProtoFuncPop            DontEnum|Function 0
  push           arrayProtoFuncPush           DontEnum|Function 1
  reverse        arrayProtoFuncReverse        DontEnum|Function 0
  shift          arrayProtoFuncShift          DontEnum|Function 0
  slice          arrayProtoFuncSlice          DontEnum|Function 2
  sort           arrayProtoFuncSort           DontEnum|Function 1
  splice         arrayProtoFuncSplice         DontEnum|Function 2
  unshift        arrayProtoFuncUnShift        DontEnum|Function 1
  every          arrayProtoFuncEvery          DontEnum|Function 1
  forEach        arrayProtoFuncForEach        DontEnum|Function 1
  some           arrayProtoFuncSome           DontEnum|Function 1
  indexOf        arrayProtoFuncIndexOf        DontEnum|Function 1
  lastIndexOf    arrayProtoFuncLastIndexOf    DontEnum|Function 1
  filter         arrayProtoFuncFilter         DontEnum|Function 1
113
  reduce         arrayProtoFuncReduce         DontEnum|Function 1
114
  reduceRight    arrayProtoFuncReduceRight    DontEnum|Function 1
weinig@apple.com's avatar
weinig@apple.com committed
115
  map            arrayProtoFuncMap            DontEnum|Function 1
116 117
@end
*/
118

119 120 121 122 123 124 125 126
ArrayPrototype* ArrayPrototype::create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure)
{
    Butterfly* butterfly = createArrayButterfly(exec->globalData(), 0);
    ArrayPrototype* prototype = new (NotNull, allocateCell<ArrayPrototype>(*exec->heap())) ArrayPrototype(globalObject, structure, butterfly);
    prototype->finishCreation(globalObject);
    return prototype;
}

127
// ECMA 15.4.4
128 129
ArrayPrototype::ArrayPrototype(JSGlobalObject* globalObject, Structure* structure, Butterfly* butterfly)
    : JSArray(globalObject->globalData(), structure, butterfly)
130
{
131 132 133 134
}

void ArrayPrototype::finishCreation(JSGlobalObject* globalObject)
{
135 136
    JSGlobalData& globalData = globalObject->globalData();
    Base::finishCreation(globalData);
137
    ASSERT(inherits(&s_info));
138
    notifyUsedAsPrototype(globalData);
139 140
}

141
bool ArrayPrototype::getOwnPropertySlot(JSCell* cell, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
142
{
143
    return getStaticFunctionSlot<JSArray>(exec, ExecState::arrayPrototypeTable(exec), jsCast<ArrayPrototype*>(cell), propertyName, slot);
144 145
}

146
bool ArrayPrototype::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor)
147
{
148
    return getStaticFunctionDescriptor<JSArray>(exec, ExecState::arrayPrototypeTable(exec), jsCast<ArrayPrototype*>(object), propertyName, descriptor);
149 150
}

weinig@apple.com's avatar
weinig@apple.com committed
151
// ------------------------------ Array Functions ----------------------------
152

weinig@apple.com's avatar
weinig@apple.com committed
153
// Helper function
ggaren@apple.com's avatar
ggaren@apple.com committed
154
static JSValue getProperty(ExecState* exec, JSObject* obj, unsigned index)
darin's avatar
darin committed
155
{
darin@apple.com's avatar
darin@apple.com committed
156
    PropertySlot slot(obj);
darin's avatar
darin committed
157
    if (!obj->getPropertySlot(exec, index, slot))
ggaren@apple.com's avatar
ggaren@apple.com committed
158
        return JSValue();
darin@apple.com's avatar
darin@apple.com committed
159
    return slot.getValue(exec, index);
darin's avatar
darin committed
160 161
}

162
static void putProperty(ExecState* exec, JSObject* obj, PropertyName propertyName, JSValue value)
weinig@apple.com's avatar
weinig@apple.com committed
163 164
{
    PutPropertySlot slot;
mhahnenberg@apple.com's avatar
mhahnenberg@apple.com committed
165
    obj->methodTable()->put(obj, exec, propertyName, value, slot);
weinig@apple.com's avatar
weinig@apple.com committed
166 167
}

168 169 170 171 172 173 174 175 176 177 178 179 180 181
static unsigned argumentClampedIndexFromStartOrEnd(ExecState* exec, int argument, unsigned length, unsigned undefinedValue = 0)
{
    JSValue value = exec->argument(argument);
    if (value.isUndefined())
        return undefinedValue;

    double indexDouble = value.toInteger(exec);
    if (indexDouble < 0) {
        indexDouble += length;
        return indexDouble < 0 ? 0 : static_cast<unsigned>(indexDouble);
    }
    return indexDouble > length ? length : static_cast<unsigned>(indexDouble);
}

182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204

// The shift/unshift function implement the shift/unshift behaviour required
// by the corresponding array prototype methods, and by splice. In both cases,
// the methods are operating an an array or array like object.
//
//  header  currentCount  (remainder)
// [------][------------][-----------]
//  header  resultCount  (remainder)
// [------][-----------][-----------]
//
// The set of properties in the range 'header' must be unchanged. The set of
// properties in the range 'remainder' (where remainder = length - header -
// currentCount) will be shifted to the left or right as appropriate; in the
// case of shift this must be removing values, in the case of unshift this
// must be introducing new values.
static inline void shift(ExecState* exec, JSObject* thisObj, unsigned header, unsigned currentCount, unsigned resultCount, unsigned length)
{
    ASSERT(currentCount > resultCount);
    unsigned count = currentCount - resultCount;

    ASSERT(header <= length);
    ASSERT(currentCount <= (length - header));

205
    if (isJSArray(thisObj)) {
206
        JSArray* array = asArray(thisObj);
207
        if (array->length() == length && asArray(thisObj)->shiftCount(exec, header, count))
208 209
            return;
    }
210 211 212 213 214 215 216 217 218 219 220 221 222

    for (unsigned k = header; k < length - currentCount; ++k) {
        unsigned from = k + currentCount;
        unsigned to = k + resultCount;
        PropertySlot slot(thisObj);
        if (thisObj->getPropertySlot(exec, from, slot)) {
            JSValue value = slot.getValue(exec, from);
            if (exec->hadException())
                return;
            thisObj->methodTable()->putByIndex(thisObj, exec, to, value, true);
            if (exec->hadException())
                return;
        } else if (!thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, to)) {
223
            throwTypeError(exec, ASCIILiteral("Unable to delete property."));
224 225 226 227 228
            return;
        }
    }
    for (unsigned k = length; k > length - count; --k) {
        if (!thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, k - 1)) {
229
            throwTypeError(exec, ASCIILiteral("Unable to delete property."));
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
            return;
        }
    }
}
static inline void unshift(ExecState* exec, JSObject* thisObj, unsigned header, unsigned currentCount, unsigned resultCount, unsigned length)
{
    ASSERT(resultCount > currentCount);
    unsigned count = resultCount - currentCount;

    ASSERT(header <= length);
    ASSERT(currentCount <= (length - header));

    // Guard against overflow.
    if (count > (UINT_MAX - length)) {
        throwOutOfMemoryError(exec);
        return;
    }

248
    if (isJSArray(thisObj)) {
249
        JSArray* array = asArray(thisObj);
250
        if (array->length() == length && array->unshiftCount(exec, header, count))
251 252
            return;
    }
253 254 255 256 257 258 259 260 261 262 263

    for (unsigned k = length - currentCount; k > header; --k) {
        unsigned from = k + currentCount - 1;
        unsigned to = k + resultCount - 1;
        PropertySlot slot(thisObj);
        if (thisObj->getPropertySlot(exec, from, slot)) {
            JSValue value = slot.getValue(exec, from);
            if (exec->hadException())
                return;
            thisObj->methodTable()->putByIndex(thisObj, exec, to, value, true);
        } else if (!thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, to)) {
264
            throwTypeError(exec, ASCIILiteral("Unable to delete property."));
265 266 267 268 269 270 271
            return;
        }
        if (exec->hadException())
            return;
    }
}

272
EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState* exec)
273
{
274
    JSValue thisValue = exec->hostThisValue();
275

276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
    // 1. Let array be the result of calling ToObject on the this value.
    JSObject* thisObject = thisValue.toObject(exec);
    if (exec->hadException())
        return JSValue::encode(jsUndefined());
    
    // 2. Let func be the result of calling the [[Get]] internal method of array with argument "join".
    JSValue function = JSValue(thisObject).get(exec, exec->propertyNames().join);

    // 3. If IsCallable(func) is false, then let func be the standard built-in method Object.prototype.toString (15.2.4.2).
    if (!function.isCell())
        return JSValue::encode(jsMakeNontrivialString(exec, "[object ", thisObject->methodTable()->className(thisObject), "]"));
    CallData callData;
    CallType callType = getCallData(function, callData);
    if (callType == CallTypeNone)
        return JSValue::encode(jsMakeNontrivialString(exec, "[object ", thisObject->methodTable()->className(thisObject), "]"));

    // 4. Return the result of calling the [[Call]] internal method of func providing array as the this value and an empty arguments list.
    if (!isJSArray(thisObject) || callType != CallTypeHost || callData.native.function != arrayProtoFuncJoin)
        return JSValue::encode(call(exec, function, callType, callData, thisObject, exec->emptyList()));

    ASSERT(isJSArray(thisValue));
297
    JSArray* thisObj = asArray(thisValue);
298
    
299 300 301 302
    unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
    if (exec->hadException())
        return JSValue::encode(jsUndefined());

303
    StringRecursionChecker checker(exec, thisObj);
304 305
    if (JSValue earlyReturnValue = checker.earlyReturnValue())
        return JSValue::encode(earlyReturnValue);
weinig@apple.com's avatar
weinig@apple.com committed
306

307
    unsigned totalSize = length ? length - 1 : 0;
308
    Vector<RefPtr<StringImpl>, 256> strBuffer(length);
309
    bool allStrings8Bit = true;
310

darin@apple.com's avatar
darin@apple.com committed
311
    for (unsigned k = 0; k < length; k++) {
312
        JSValue element;
313 314
        if (thisObj->canGetIndexQuickly(k))
            element = thisObj->getIndexQuickly(k);
315 316 317
        else
            element = thisObj->get(exec, k);
        
weinig@apple.com's avatar
weinig@apple.com committed
318
        if (element.isUndefinedOrNull())
weinig@apple.com's avatar
weinig@apple.com committed
319
            continue;
320
        
321
        String str = element.toWTFString(exec);
322
        strBuffer[k] = str.impl();
323
        totalSize += str.length();
324
        allStrings8Bit = allStrings8Bit && str.is8Bit();
325
        
ap@webkit.org's avatar
ap@webkit.org committed
326
        if (!strBuffer.data()) {
327
            throwOutOfMemoryError(exec);
weinig@apple.com's avatar
weinig@apple.com committed
328
        }
329
        
weinig@apple.com's avatar
weinig@apple.com committed
330 331 332
        if (exec->hadException())
            break;
    }
333
    if (!totalSize)
334
        return JSValue::encode(jsEmptyString(exec));
335 336 337 338 339 340 341 342 343 344 345 346 347 348

    if (allStrings8Bit) {
        Vector<LChar> buffer;
        buffer.reserveCapacity(totalSize);
        if (!buffer.data())
            return JSValue::encode(throwOutOfMemoryError(exec));
        
        for (unsigned i = 0; i < length; i++) {
            if (i)
                buffer.append(',');
            if (RefPtr<StringImpl> rep = strBuffer[i])
                buffer.append(rep->characters8(), rep->length());
        }
        ASSERT(buffer.size() == totalSize);
349
        return JSValue::encode(jsString(exec, String::adopt(buffer)));
350 351
    }

352 353 354
    Vector<UChar> buffer;
    buffer.reserveCapacity(totalSize);
    if (!buffer.data())
355
        return JSValue::encode(throwOutOfMemoryError(exec));
356 357 358 359
        
    for (unsigned i = 0; i < length; i++) {
        if (i)
            buffer.append(',');
360
        if (RefPtr<StringImpl> rep = strBuffer[i])
361
            buffer.append(rep->characters(), rep->length());
362 363
    }
    ASSERT(buffer.size() == totalSize);
364
    return JSValue::encode(jsString(exec, String::adopt(buffer)));
weinig@apple.com's avatar
weinig@apple.com committed
365 366
}

367
EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState* exec)
weinig@apple.com's avatar
weinig@apple.com committed
368
{
369
    JSValue thisValue = exec->hostThisValue();
370

371 372 373
    if (!thisValue.inherits(&JSArray::s_info))
        return throwVMTypeError(exec);
    JSObject* thisObj = asArray(thisValue);
374

375 376 377 378
    unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
    if (exec->hadException())
        return JSValue::encode(jsUndefined());

379
    StringRecursionChecker checker(exec, thisObj);
380 381
    if (JSValue earlyReturnValue = checker.earlyReturnValue())
        return JSValue::encode(earlyReturnValue);
382

383
    String separator(",", String::ConstructFromLiteral);
384
    JSStringJoiner stringJoiner(separator, length);
darin@apple.com's avatar
darin@apple.com committed
385
    for (unsigned k = 0; k < length; k++) {
ggaren@apple.com's avatar
ggaren@apple.com committed
386
        JSValue element = thisObj->get(exec, k);
387 388
        if (exec->hadException())
            return JSValue::encode(jsUndefined());
389 390 391
        if (!element.isUndefinedOrNull()) {
            JSObject* o = element.toObject(exec);
            JSValue conversionFunction = o->get(exec, exec->propertyNames().toLocaleString);
392 393
            if (exec->hadException())
                return JSValue::encode(jsUndefined());
394
            String str;
395
            CallData callData;
396
            CallType callType = getCallData(conversionFunction, callData);
397
            if (callType != CallTypeNone)
398
                str = call(exec, conversionFunction, callType, callData, element, exec->emptyList()).toWTFString(exec);
399
            else
400
                str = element.toWTFString(exec);
401 402
            if (exec->hadException())
                return JSValue::encode(jsUndefined());
403
            stringJoiner.append(str);
404
        }
405
    }
406

407
    return JSValue::encode(stringJoiner.build(exec));
weinig@apple.com's avatar
weinig@apple.com committed
408 409
}

410
EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState* exec)
weinig@apple.com's avatar
weinig@apple.com committed
411
{
412
    JSObject* thisObj = exec->hostThisValue().toObject(exec);
413 414 415
    unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
    if (exec->hadException())
        return JSValue::encode(jsUndefined());
darin@apple.com's avatar
darin@apple.com committed
416

417
    StringRecursionChecker checker(exec, thisObj);
418 419
    if (JSValue earlyReturnValue = checker.earlyReturnValue())
        return JSValue::encode(earlyReturnValue);
weinig@apple.com's avatar
weinig@apple.com committed
420

421
    String separator;
422
    if (!exec->argument(0).isUndefined())
423
        separator = exec->argument(0).toWTFString(exec);
424
    if (separator.isNull())
425
        separator = String(",", String::ConstructFromLiteral);
426 427

    JSStringJoiner stringJoiner(separator, length);
weinig@apple.com's avatar
weinig@apple.com committed
428

429
    unsigned k = 0;
430
    if (isJSArray(thisObj)) {
431
        JSArray* array = asArray(thisObj);
432

433
        for (; k < length; k++) {
434
            if (!array->canGetIndexQuickly(k))
435 436
                break;

437
            JSValue element = array->getIndexQuickly(k);
438
            if (!element.isUndefinedOrNull())
439
                stringJoiner.append(element.toWTFStringInline(exec));
440
            else
441
                stringJoiner.append(String());
weinig@apple.com's avatar
weinig@apple.com committed
442
        }
443
    }
weinig@apple.com's avatar
weinig@apple.com committed
444

445
    for (; k < length; k++) {
ggaren@apple.com's avatar
ggaren@apple.com committed
446
        JSValue element = thisObj->get(exec, k);
447
        if (!element.isUndefinedOrNull())
448
            stringJoiner.append(element.toWTFStringInline(exec));
449
        else
450
            stringJoiner.append(String());
weinig@apple.com's avatar
weinig@apple.com committed
451
    }
452

453
    return JSValue::encode(stringJoiner.build(exec));
weinig@apple.com's avatar
weinig@apple.com committed
454 455
}

456
EncodedJSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState* exec)
weinig@apple.com's avatar
weinig@apple.com committed
457
{
458
    JSValue thisValue = exec->hostThisValue();
darin@apple.com's avatar
darin@apple.com committed
459
    JSArray* arr = constructEmptyArray(exec);
460
    unsigned n = 0;
461 462 463
    JSValue curArg = thisValue.toObject(exec);
    if (exec->hadException())
        return JSValue::encode(jsUndefined());
464 465
    size_t i = 0;
    size_t argCount = exec->argumentCount();
eric@webkit.org's avatar
eric@webkit.org committed
466
    while (1) {
467
        if (curArg.inherits(&JSArray::s_info)) {
oliver@apple.com's avatar
oliver@apple.com committed
468 469
            unsigned length = curArg.get(exec, exec->propertyNames().length).toUInt32(exec);
            JSObject* curObject = curArg.toObject(exec);
darin@apple.com's avatar
darin@apple.com committed
470
            for (unsigned k = 0; k < length; ++k) {
471 472 473 474
                JSValue v = getProperty(exec, curObject, k);
                if (exec->hadException())
                    return JSValue::encode(jsUndefined());
                if (v)
475
                    arr->putDirectIndex(exec, n, v);
weinig@apple.com's avatar
weinig@apple.com committed
476 477 478
                n++;
            }
        } else {
479
            arr->putDirectIndex(exec, n, curArg);
weinig@apple.com's avatar
weinig@apple.com committed
480
            n++;
481
        }
482
        if (i == argCount)
weinig@apple.com's avatar
weinig@apple.com committed
483
            break;
484 485
        curArg = (exec->argument(i));
        ++i;
486
    }
487
    arr->setLength(exec, n);
488
    return JSValue::encode(arr);
weinig@apple.com's avatar
weinig@apple.com committed
489
}
490

491
EncodedJSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState* exec)
weinig@apple.com's avatar
weinig@apple.com committed
492
{
493
    JSValue thisValue = exec->hostThisValue();
494

495
    if (isJSArray(thisValue))
496
        return JSValue::encode(asArray(thisValue)->pop(exec));
497

498
    JSObject* thisObj = thisValue.toObject(exec);
weinig@apple.com's avatar
weinig@apple.com committed
499
    unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
500 501 502 503
    if (exec->hadException())
        return JSValue::encode(jsUndefined());

    JSValue result;
504
    if (length == 0) {
505
        putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length));
eric@webkit.org's avatar
eric@webkit.org committed
506
        result = jsUndefined();
507
    } else {
eric@webkit.org's avatar
eric@webkit.org committed
508
        result = thisObj->get(exec, length - 1);
509 510 511
        if (exec->hadException())
            return JSValue::encode(jsUndefined());
        if (!thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, length - 1)) {
512
            throwTypeError(exec, ASCIILiteral("Unable to delete property."));
513 514
            return JSValue::encode(jsUndefined());
        }
515
        putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length - 1));
516
    }
517
    return JSValue::encode(result);
weinig@apple.com's avatar
weinig@apple.com committed
518 519
}

520
EncodedJSValue JSC_HOST_CALL arrayProtoFuncPush(ExecState* exec)
weinig@apple.com's avatar
weinig@apple.com committed
521
{
522
    JSValue thisValue = exec->hostThisValue();
523

524
    if (isJSArray(thisValue) && exec->argumentCount() == 1) {
darin@apple.com's avatar
darin@apple.com committed
525
        JSArray* array = asArray(thisValue);
526
        array->push(exec, exec->argument(0));
527
        return JSValue::encode(jsNumber(array->length()));
528 529
    }

530
    JSObject* thisObj = thisValue.toObject(exec);
weinig@apple.com's avatar
weinig@apple.com committed
531
    unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
532 533 534
    if (exec->hadException())
        return JSValue::encode(jsUndefined());

535 536 537
    for (unsigned n = 0; n < exec->argumentCount(); n++) {
        // Check for integer overflow; where safe we can do a fast put by index.
        if (length + n >= length)
538
            thisObj->methodTable()->putByIndex(thisObj, exec, length + n, exec->argument(n), true);
539 540
        else {
            PutPropertySlot slot;
541
            Identifier propertyName(exec, JSValue(static_cast<int64_t>(length) + static_cast<int64_t>(n)).toWTFString(exec));
mhahnenberg@apple.com's avatar
mhahnenberg@apple.com committed
542
            thisObj->methodTable()->put(thisObj, exec, propertyName, exec->argument(n), slot);
543
        }
544 545
        if (exec->hadException())
            return JSValue::encode(jsUndefined());
546
    }
547
    JSValue newLength(static_cast<int64_t>(length) + static_cast<int64_t>(exec->argumentCount()));
548 549
    putProperty(exec, thisObj, exec->propertyNames().length, newLength);
    return JSValue::encode(newLength);
weinig@apple.com's avatar
weinig@apple.com committed
550
}
551

552
EncodedJSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState* exec)
weinig@apple.com's avatar
weinig@apple.com committed
553
{
554
    JSObject* thisObj = exec->hostThisValue().toObject(exec);
weinig@apple.com's avatar
weinig@apple.com committed
555
    unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
556 557
    if (exec->hadException())
        return JSValue::encode(jsUndefined());
558

559
    unsigned middle = length / 2;
darin@apple.com's avatar
darin@apple.com committed
560
    for (unsigned k = 0; k < middle; k++) {
weinig@apple.com's avatar
weinig@apple.com committed
561
        unsigned lk1 = length - k - 1;
ggaren@apple.com's avatar
ggaren@apple.com committed
562
        JSValue obj2 = getProperty(exec, thisObj, lk1);
563 564
        if (exec->hadException())
            return JSValue::encode(jsUndefined());
ggaren@apple.com's avatar
ggaren@apple.com committed
565
        JSValue obj = getProperty(exec, thisObj, k);
566 567
        if (exec->hadException())
            return JSValue::encode(jsUndefined());
mjs's avatar
mjs committed
568

569
        if (obj2) {
570
            thisObj->methodTable()->putByIndex(thisObj, exec, k, obj2, true);
571 572 573
            if (exec->hadException())
                return JSValue::encode(jsUndefined());
        } else if (!thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, k)) {
574
            throwTypeError(exec, ASCIILiteral("Unable to delete property."));
575
            return JSValue::encode(jsUndefined());
576
        }
mjs's avatar
mjs committed
577

578
        if (obj) {
579
            thisObj->methodTable()->putByIndex(thisObj, exec, lk1, obj, true);
580 581 582
            if (exec->hadException())
                return JSValue::encode(jsUndefined());
        } else if (!thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, lk1)) {
583
            throwTypeError(exec, ASCIILiteral("Unable to delete property."));
584
            return JSValue::encode(jsUndefined());
585
        }
586
    }
587
    return JSValue::encode(thisObj);
weinig@apple.com's avatar
weinig@apple.com committed
588 589
}

590
EncodedJSValue JSC_HOST_CALL arrayProtoFuncShift(ExecState* exec)
weinig@apple.com's avatar
weinig@apple.com committed
591
{
592
    JSObject* thisObj = exec->hostThisValue().toObject(exec);
weinig@apple.com's avatar
weinig@apple.com committed
593
    unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
594 595 596
    if (exec->hadException())
        return JSValue::encode(jsUndefined());

597
    JSValue result;
598
    if (length == 0) {
599
        putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length));
eric@webkit.org's avatar
eric@webkit.org committed
600
        result = jsUndefined();
601
    } else {
eric@webkit.org's avatar
eric@webkit.org committed
602
        result = thisObj->get(exec, 0);
603 604 605
        shift(exec, thisObj, 0, 1, 0, length);
        if (exec->hadException())
            return JSValue::encode(jsUndefined());
606
        putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length - 1));
607
    }
608
    return JSValue::encode(result);
weinig@apple.com's avatar
weinig@apple.com committed
609 610
}

611
EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState* exec)
weinig@apple.com's avatar
weinig@apple.com committed
612
{
613
    // http://developer.netscape.com/docs/manuals/js/client/jsref/array.htm#1193713 or 15.4.4.10
614 615 616 617
    JSObject* thisObj = exec->hostThisValue().toObject(exec);
    unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
    if (exec->hadException())
        return JSValue::encode(jsUndefined());
darin@apple.com's avatar
darin@apple.com committed
618

619
    // We return a new array
darin@apple.com's avatar
darin@apple.com committed
620
    JSArray* resObj = constructEmptyArray(exec);
ggaren@apple.com's avatar
ggaren@apple.com committed
621
    JSValue result = resObj;
622 623 624

    unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length);
    unsigned end = argumentClampedIndexFromStartOrEnd(exec, 1, length, length);
625

626 627
    unsigned n = 0;
    for (unsigned k = begin; k < end; k++, n++) {
628 629 630 631
        JSValue v = getProperty(exec, thisObj, k);
        if (exec->hadException())
            return JSValue::encode(jsUndefined());
        if (v)
632
            resObj->putDirectIndex(exec, n, v);
633
    }
634
    resObj->setLength(exec, n);
635
    return JSValue::encode(result);
weinig@apple.com's avatar
weinig@apple.com committed
636 637
}

638 639 640 641 642 643 644 645 646
inline JSValue getOrHole(JSObject* obj, ExecState* exec, unsigned propertyName)
{
    PropertySlot slot(obj);
    if (obj->getPropertySlot(exec, propertyName, slot))
        return slot.getValue(exec, propertyName);

    return JSValue();
}

647
EncodedJSValue JSC_HOST_CALL arrayProtoFuncSort(ExecState* exec)
weinig@apple.com's avatar
weinig@apple.com committed
648
{
649
    JSObject* thisObj = exec->hostThisValue().toObject(exec);
650 651 652
    unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
    if (!length || exec->hadException())
        return JSValue::encode(thisObj);
darin@apple.com's avatar
darin@apple.com committed
653

654
    JSValue function = exec->argument(0);
darin@apple.com's avatar
darin@apple.com committed
655
    CallData callData;
656
    CallType callType = getCallData(function, callData);
eric@webkit.org's avatar
eric@webkit.org committed
657

658
    if (thisObj->classInfo() == &JSArray::s_info && !asArray(thisObj)->hasSparseMap() && !shouldUseSlowPut(thisObj->structure()->indexingType())) {
659
        if (isNumericCompareFunction(exec, callType, callData))
ggaren@apple.com's avatar
ggaren@apple.com committed
660 661
            asArray(thisObj)->sortNumeric(exec, function, callType, callData);
        else if (callType != CallTypeNone)
darin@apple.com's avatar
darin@apple.com committed
662
            asArray(thisObj)->sort(exec, function, callType, callData);
eric@webkit.org's avatar
eric@webkit.org committed
663
        else
darin@apple.com's avatar
darin@apple.com committed
664
            asArray(thisObj)->sort(exec);
665
        return JSValue::encode(thisObj);
666
    }
667

668 669
    // "Min" sort. Not the fastest, but definitely less code than heapsort
    // or quicksort, and much less swapping than bubblesort/insertionsort.
darin@apple.com's avatar
darin@apple.com committed
670
    for (unsigned i = 0; i < length - 1; ++i) {
671
        JSValue iObj = getOrHole(thisObj, exec, i);
672 673
        if (exec->hadException())
            return JSValue::encode(jsUndefined());
darin@apple.com's avatar
darin@apple.com committed
674
        unsigned themin = i;
ggaren@apple.com's avatar
ggaren@apple.com committed
675
        JSValue minObj = iObj;
darin@apple.com's avatar
darin@apple.com committed
676
        for (unsigned j = i + 1; j < length; ++j) {
677
            JSValue jObj = getOrHole(thisObj, exec, j);
678 679
            if (exec->hadException())
                return JSValue::encode(jsUndefined());
eric@webkit.org's avatar
eric@webkit.org committed
680
            double compareResult;
681 682 683 684 685
            if (!jObj)
                compareResult = 1;
            else if (!minObj)
                compareResult = -1;
            else if (jObj.isUndefined())
eric@webkit.org's avatar
eric@webkit.org committed
686
                compareResult = 1; // don't check minObj because there's no need to differentiate == (0) from > (1)
weinig@apple.com's avatar
weinig@apple.com committed
687
            else if (minObj.isUndefined())
eric@webkit.org's avatar
eric@webkit.org committed
688
                compareResult = -1;
darin@apple.com's avatar
darin@apple.com committed
689
            else if (callType != CallTypeNone) {
690
                MarkedArgumentBuffer l;
691 692
                l.append(jObj);
                l.append(minObj);
693
                compareResult = call(exec, function, callType, callData, jsUndefined(), l).toNumber(exec);
eric@webkit.org's avatar
eric@webkit.org committed
694
            } else
695
                compareResult = codePointCompareLessThan(jObj.toWTFStringInline(exec), minObj.toWTFStringInline(exec)) ? -1 : 1;
eric@webkit.org's avatar
eric@webkit.org committed
696 697

            if (compareResult < 0) {
698 699
                themin = j;
                minObj = jObj;
eric@webkit.org's avatar
eric@webkit.org committed
700 701
            }
        }
702
        // Swap themin and i
eric@webkit.org's avatar
eric@webkit.org committed
703
        if (themin > i) {
704 705 706 707 708 709
            if (minObj) {
                thisObj->methodTable()->putByIndex(thisObj, exec, i, minObj, true);
                if (exec->hadException())
                    return JSValue::encode(jsUndefined());
            } else if (!thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, i)) {
                throwTypeError(exec, "Unable to delete property.");
710
                return JSValue::encode(jsUndefined());
711 712 713 714 715 716 717
            }
            if (iObj) {
                thisObj->methodTable()->putByIndex(thisObj, exec, themin, iObj, true);
                if (exec->hadException())
                    return JSValue::encode(jsUndefined());
            } else if (!thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, themin)) {
                throwTypeError(exec, "Unable to delete property.");
718
                return JSValue::encode(jsUndefined());
719
            }
eric@webkit.org's avatar
eric@webkit.org committed
720
        }
weinig@apple.com's avatar
weinig@apple.com committed
721
    }
722
    return JSValue::encode(thisObj);
weinig@apple.com's avatar
weinig@apple.com committed
723 724
}

725
EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState* exec)
weinig@apple.com's avatar
weinig@apple.com committed
726
{
eric@webkit.org's avatar
eric@webkit.org committed
727
    // 15.4.4.12
ap@apple.com's avatar
ap@apple.com committed
728

729
    JSObject* thisObj = exec->hostThisValue().toObject(exec);
730 731 732 733
    unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
    if (exec->hadException())
        return JSValue::encode(jsUndefined());