| /* |
| * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) |
| * Copyright (C) 2003, 2007, 2008, 2009, 2011 Apple Inc. All rights reserved. |
| * Copyright (C) 2003 Peter Kelly (pmk@post.com) |
| * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com) |
| * |
| * 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 |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 |
| * USA |
| * |
| */ |
| |
| #include "config.h" |
| #include "ArrayPrototype.h" |
| |
| #include "ButterflyInlines.h" |
| #include "CachedCall.h" |
| #include "CodeBlock.h" |
| #include "CopiedSpaceInlines.h" |
| #include "Interpreter.h" |
| #include "JIT.h" |
| #include "JSStringBuilder.h" |
| #include "JSStringJoiner.h" |
| #include "Lookup.h" |
| #include "ObjectPrototype.h" |
| #include "Operations.h" |
| #include "StringRecursionChecker.h" |
| #include <algorithm> |
| #include <wtf/Assertions.h> |
| #include <wtf/HashSet.h> |
| |
| namespace JSC { |
| |
| 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*); |
| |
| } |
| |
| #include "ArrayPrototype.lut.h" |
| |
| namespace JSC { |
| |
| static inline bool isNumericCompareFunction(ExecState* exec, CallType callType, const CallData& callData) |
| { |
| if (callType != CallTypeJS) |
| return false; |
| |
| FunctionExecutable* executable = callData.js.functionExecutable; |
| |
| JSObject* error = executable->compileForCall(exec, callData.js.scope); |
| if (error) |
| return false; |
| |
| return executable->generatedBytecodeForCall().isNumericCompareFunction(); |
| } |
| |
| // ------------------------------ ArrayPrototype ---------------------------- |
| |
| const ClassInfo ArrayPrototype::s_info = {"Array", JSArray::s_classinfo(), 0, ExecState::arrayPrototypeTable, CREATE_METHOD_TABLE(ArrayPrototype)}; |
| |
| /* Source for ArrayPrototype.lut.h |
| @begin arrayPrototypeTable 16 |
| 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 |
| reduce arrayProtoFuncReduce DontEnum|Function 1 |
| reduceRight arrayProtoFuncReduceRight DontEnum|Function 1 |
| map arrayProtoFuncMap DontEnum|Function 1 |
| @end |
| */ |
| |
| ArrayPrototype* ArrayPrototype::create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure) |
| { |
| ArrayPrototype* prototype = new (NotNull, allocateCell<ArrayPrototype>(*exec->heap())) ArrayPrototype(globalObject, structure); |
| prototype->finishCreation(globalObject); |
| return prototype; |
| } |
| |
| // ECMA 15.4.4 |
| ArrayPrototype::ArrayPrototype(JSGlobalObject* globalObject, Structure* structure) |
| : JSArray(globalObject->globalData(), structure, 0) |
| { |
| } |
| |
| void ArrayPrototype::finishCreation(JSGlobalObject* globalObject) |
| { |
| JSGlobalData& globalData = globalObject->globalData(); |
| Base::finishCreation(globalData); |
| ASSERT(inherits(&s_info)); |
| notifyUsedAsPrototype(globalData); |
| } |
| |
| bool ArrayPrototype::getOwnPropertySlot(JSCell* cell, ExecState* exec, PropertyName propertyName, PropertySlot& slot) |
| { |
| return getStaticFunctionSlot<JSArray>(exec, ExecState::arrayPrototypeTable(exec), jsCast<ArrayPrototype*>(cell), propertyName, slot); |
| } |
| |
| bool ArrayPrototype::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor) |
| { |
| return getStaticFunctionDescriptor<JSArray>(exec, ExecState::arrayPrototypeTable(exec), jsCast<ArrayPrototype*>(object), propertyName, descriptor); |
| } |
| |
| // ------------------------------ Array Functions ---------------------------- |
| |
| // Helper function |
| static JSValue getProperty(ExecState* exec, JSObject* obj, unsigned index) |
| { |
| PropertySlot slot(obj); |
| if (!obj->getPropertySlot(exec, index, slot)) |
| return JSValue(); |
| return slot.getValue(exec, index); |
| } |
| |
| static void putProperty(ExecState* exec, JSObject* obj, PropertyName propertyName, JSValue value) |
| { |
| PutPropertySlot slot; |
| obj->methodTable()->put(obj, exec, propertyName, value, slot); |
| } |
| |
| 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); |
| } |
| |
| |
| // 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. |
| template<JSArray::ShiftCountMode shiftCountMode> |
| 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)); |
| |
| if (isJSArray(thisObj)) { |
| JSArray* array = asArray(thisObj); |
| if (array->length() == length && asArray(thisObj)->shiftCount<shiftCountMode>(exec, header, count)) |
| return; |
| } |
| |
| 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)) { |
| throwTypeError(exec, ASCIILiteral("Unable to delete property.")); |
| return; |
| } |
| } |
| for (unsigned k = length; k > length - count; --k) { |
| if (!thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, k - 1)) { |
| throwTypeError(exec, ASCIILiteral("Unable to delete property.")); |
| return; |
| } |
| } |
| } |
| template<JSArray::ShiftCountMode shiftCountMode> |
| 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; |
| } |
| |
| if (isJSArray(thisObj)) { |
| JSArray* array = asArray(thisObj); |
| if (array->length() == length && array->unshiftCount<shiftCountMode>(exec, header, count)) |
| return; |
| } |
| |
| 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)) { |
| throwTypeError(exec, ASCIILiteral("Unable to delete property.")); |
| return; |
| } |
| if (exec->hadException()) |
| return; |
| } |
| } |
| |
| EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState* exec) |
| { |
| JSValue thisValue = exec->hostThisValue(); |
| |
| // 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)); |
| JSArray* thisObj = asArray(thisValue); |
| |
| unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| |
| StringRecursionChecker checker(exec, thisObj); |
| if (JSValue earlyReturnValue = checker.earlyReturnValue()) |
| return JSValue::encode(earlyReturnValue); |
| |
| unsigned totalSize = length ? length - 1 : 0; |
| Vector<RefPtr<StringImpl>, 256> strBuffer(length); |
| bool allStrings8Bit = true; |
| |
| for (unsigned k = 0; k < length; k++) { |
| JSValue element; |
| if (thisObj->canGetIndexQuickly(k)) |
| element = thisObj->getIndexQuickly(k); |
| else |
| element = thisObj->get(exec, k); |
| |
| if (element.isUndefinedOrNull()) |
| continue; |
| |
| String str = element.toWTFString(exec); |
| strBuffer[k] = str.impl(); |
| totalSize += str.length(); |
| allStrings8Bit = allStrings8Bit && str.is8Bit(); |
| |
| if (!strBuffer.data()) { |
| throwOutOfMemoryError(exec); |
| } |
| |
| if (exec->hadException()) |
| break; |
| } |
| if (!totalSize) |
| return JSValue::encode(jsEmptyString(exec)); |
| |
| 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); |
| return JSValue::encode(jsString(exec, String::adopt(buffer))); |
| } |
| |
| Vector<UChar> 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->characters(), rep->length()); |
| } |
| ASSERT(buffer.size() == totalSize); |
| return JSValue::encode(jsString(exec, String::adopt(buffer))); |
| } |
| |
| EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState* exec) |
| { |
| JSValue thisValue = exec->hostThisValue(); |
| |
| if (!thisValue.inherits(JSArray::s_classinfo())) |
| return throwVMTypeError(exec); |
| JSObject* thisObj = asArray(thisValue); |
| |
| unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| |
| StringRecursionChecker checker(exec, thisObj); |
| if (JSValue earlyReturnValue = checker.earlyReturnValue()) |
| return JSValue::encode(earlyReturnValue); |
| |
| String separator(",", String::ConstructFromLiteral); |
| JSStringJoiner stringJoiner(separator, length); |
| for (unsigned k = 0; k < length; k++) { |
| JSValue element = thisObj->get(exec, k); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| if (!element.isUndefinedOrNull()) { |
| JSObject* o = element.toObject(exec); |
| JSValue conversionFunction = o->get(exec, exec->propertyNames().toLocaleString); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| String str; |
| CallData callData; |
| CallType callType = getCallData(conversionFunction, callData); |
| if (callType != CallTypeNone) |
| str = call(exec, conversionFunction, callType, callData, element, exec->emptyList()).toWTFString(exec); |
| else |
| str = element.toWTFString(exec); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| stringJoiner.append(str); |
| } |
| } |
| |
| return JSValue::encode(stringJoiner.build(exec)); |
| } |
| |
| EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState* exec) |
| { |
| JSObject* thisObj = exec->hostThisValue().toObject(exec); |
| unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| |
| StringRecursionChecker checker(exec, thisObj); |
| if (JSValue earlyReturnValue = checker.earlyReturnValue()) |
| return JSValue::encode(earlyReturnValue); |
| |
| String separator; |
| if (!exec->argument(0).isUndefined()) |
| separator = exec->argument(0).toWTFString(exec); |
| if (separator.isNull()) |
| separator = String(",", String::ConstructFromLiteral); |
| |
| JSStringJoiner stringJoiner(separator, length); |
| |
| unsigned k = 0; |
| if (isJSArray(thisObj)) { |
| JSArray* array = asArray(thisObj); |
| |
| for (; k < length; k++) { |
| if (!array->canGetIndexQuickly(k)) |
| break; |
| |
| JSValue element = array->getIndexQuickly(k); |
| if (!element.isUndefinedOrNull()) |
| stringJoiner.append(element.toWTFStringInline(exec)); |
| else |
| stringJoiner.append(String()); |
| } |
| } |
| |
| for (; k < length; k++) { |
| JSValue element = thisObj->get(exec, k); |
| if (!element.isUndefinedOrNull()) |
| stringJoiner.append(element.toWTFStringInline(exec)); |
| else |
| stringJoiner.append(String()); |
| } |
| |
| return JSValue::encode(stringJoiner.build(exec)); |
| } |
| |
| EncodedJSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState* exec) |
| { |
| JSValue thisValue = exec->hostThisValue(); |
| JSArray* arr = constructEmptyArray(exec, 0); |
| unsigned n = 0; |
| JSValue curArg = thisValue.toObject(exec); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| size_t i = 0; |
| size_t argCount = exec->argumentCount(); |
| while (1) { |
| if (curArg.inherits(JSArray::s_classinfo())) { |
| unsigned length = curArg.get(exec, exec->propertyNames().length).toUInt32(exec); |
| JSObject* curObject = curArg.toObject(exec); |
| for (unsigned k = 0; k < length; ++k) { |
| JSValue v = getProperty(exec, curObject, k); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| if (v) |
| arr->putDirectIndex(exec, n, v); |
| n++; |
| } |
| } else { |
| arr->putDirectIndex(exec, n, curArg); |
| n++; |
| } |
| if (i == argCount) |
| break; |
| curArg = (exec->argument(i)); |
| ++i; |
| } |
| arr->setLength(exec, n); |
| return JSValue::encode(arr); |
| } |
| |
| EncodedJSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState* exec) |
| { |
| JSValue thisValue = exec->hostThisValue(); |
| |
| if (isJSArray(thisValue)) |
| return JSValue::encode(asArray(thisValue)->pop(exec)); |
| |
| JSObject* thisObj = thisValue.toObject(exec); |
| unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| |
| JSValue result; |
| if (length == 0) { |
| putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length)); |
| result = jsUndefined(); |
| } else { |
| result = thisObj->get(exec, length - 1); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| if (!thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, length - 1)) { |
| throwTypeError(exec, ASCIILiteral("Unable to delete property.")); |
| return JSValue::encode(jsUndefined()); |
| } |
| putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length - 1)); |
| } |
| return JSValue::encode(result); |
| } |
| |
| EncodedJSValue JSC_HOST_CALL arrayProtoFuncPush(ExecState* exec) |
| { |
| JSValue thisValue = exec->hostThisValue(); |
| |
| if (isJSArray(thisValue) && exec->argumentCount() == 1) { |
| JSArray* array = asArray(thisValue); |
| array->push(exec, exec->argument(0)); |
| return JSValue::encode(jsNumber(array->length())); |
| } |
| |
| JSObject* thisObj = thisValue.toObject(exec); |
| unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| |
| 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) |
| thisObj->methodTable()->putByIndex(thisObj, exec, length + n, exec->argument(n), true); |
| else { |
| PutPropertySlot slot; |
| Identifier propertyName(exec, JSValue(static_cast<int64_t>(length) + static_cast<int64_t>(n)).toWTFString(exec)); |
| thisObj->methodTable()->put(thisObj, exec, propertyName, exec->argument(n), slot); |
| } |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| } |
| |
| JSValue newLength(static_cast<int64_t>(length) + static_cast<int64_t>(exec->argumentCount())); |
| putProperty(exec, thisObj, exec->propertyNames().length, newLength); |
| return JSValue::encode(newLength); |
| } |
| |
| EncodedJSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState* exec) |
| { |
| JSObject* thisObj = exec->hostThisValue().toObject(exec); |
| unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| |
| unsigned middle = length / 2; |
| for (unsigned k = 0; k < middle; k++) { |
| unsigned lk1 = length - k - 1; |
| JSValue obj2 = getProperty(exec, thisObj, lk1); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| JSValue obj = getProperty(exec, thisObj, k); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| |
| if (obj2) { |
| thisObj->methodTable()->putByIndex(thisObj, exec, k, obj2, true); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| } else if (!thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, k)) { |
| throwTypeError(exec, ASCIILiteral("Unable to delete property.")); |
| return JSValue::encode(jsUndefined()); |
| } |
| |
| if (obj) { |
| thisObj->methodTable()->putByIndex(thisObj, exec, lk1, obj, true); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| } else if (!thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, lk1)) { |
| throwTypeError(exec, ASCIILiteral("Unable to delete property.")); |
| return JSValue::encode(jsUndefined()); |
| } |
| } |
| return JSValue::encode(thisObj); |
| } |
| |
| EncodedJSValue JSC_HOST_CALL arrayProtoFuncShift(ExecState* exec) |
| { |
| JSObject* thisObj = exec->hostThisValue().toObject(exec); |
| unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| |
| JSValue result; |
| if (length == 0) { |
| putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length)); |
| result = jsUndefined(); |
| } else { |
| result = thisObj->get(exec, 0); |
| shift<JSArray::ShiftCountForShift>(exec, thisObj, 0, 1, 0, length); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length - 1)); |
| } |
| return JSValue::encode(result); |
| } |
| |
| EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState* exec) |
| { |
| // http://developer.netscape.com/docs/manuals/js/client/jsref/array.htm#1193713 or 15.4.4.10 |
| JSObject* thisObj = exec->hostThisValue().toObject(exec); |
| unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| |
| // We return a new array |
| JSArray* resObj = constructEmptyArray(exec, 0); |
| JSValue result = resObj; |
| |
| unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length); |
| unsigned end = argumentClampedIndexFromStartOrEnd(exec, 1, length, length); |
| |
| unsigned n = 0; |
| for (unsigned k = begin; k < end; k++, n++) { |
| JSValue v = getProperty(exec, thisObj, k); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| if (v) |
| resObj->putDirectIndex(exec, n, v); |
| } |
| resObj->setLength(exec, n); |
| return JSValue::encode(result); |
| } |
| |
| 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(); |
| } |
| |
| EncodedJSValue JSC_HOST_CALL arrayProtoFuncSort(ExecState* exec) |
| { |
| JSObject* thisObj = exec->hostThisValue().toObject(exec); |
| unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); |
| if (!length || exec->hadException()) |
| return JSValue::encode(thisObj); |
| |
| JSValue function = exec->argument(0); |
| CallData callData; |
| CallType callType = getCallData(function, callData); |
| |
| if (thisObj->classInfo() == JSArray::s_classinfo() && !asArray(thisObj)->hasSparseMap() && !shouldUseSlowPut(thisObj->structure()->indexingType())) { |
| if (isNumericCompareFunction(exec, callType, callData)) |
| asArray(thisObj)->sortNumeric(exec, function, callType, callData); |
| else if (callType != CallTypeNone) |
| asArray(thisObj)->sort(exec, function, callType, callData); |
| else |
| asArray(thisObj)->sort(exec); |
| return JSValue::encode(thisObj); |
| } |
| |
| // "Min" sort. Not the fastest, but definitely less code than heapsort |
| // or quicksort, and much less swapping than bubblesort/insertionsort. |
| for (unsigned i = 0; i < length - 1; ++i) { |
| JSValue iObj = getOrHole(thisObj, exec, i); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| unsigned themin = i; |
| JSValue minObj = iObj; |
| for (unsigned j = i + 1; j < length; ++j) { |
| JSValue jObj = getOrHole(thisObj, exec, j); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| double compareResult; |
| if (!jObj) |
| compareResult = 1; |
| else if (!minObj) |
| compareResult = -1; |
| else if (jObj.isUndefined()) |
| compareResult = 1; // don't check minObj because there's no need to differentiate == (0) from > (1) |
| else if (minObj.isUndefined()) |
| compareResult = -1; |
| else if (callType != CallTypeNone) { |
| MarkedArgumentBuffer l; |
| l.append(jObj); |
| l.append(minObj); |
| compareResult = call(exec, function, callType, callData, jsUndefined(), l).toNumber(exec); |
| } else |
| compareResult = codePointCompareLessThan(jObj.toWTFStringInline(exec), minObj.toWTFStringInline(exec)) ? -1 : 1; |
| |
| if (compareResult < 0) { |
| themin = j; |
| minObj = jObj; |
| } |
| } |
| // Swap themin and i |
| if (themin > i) { |
| 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."); |
| return JSValue::encode(jsUndefined()); |
| } |
| 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."); |
| return JSValue::encode(jsUndefined()); |
| } |
| } |
| } |
| return JSValue::encode(thisObj); |
| } |
| |
| EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState* exec) |
| { |
| // 15.4.4.12 |
| |
| JSObject* thisObj = exec->hostThisValue().toObject(exec); |
| unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| |
| if (!exec->argumentCount()) |
| return JSValue::encode(constructEmptyArray(exec, 0)); |
| |
| unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length); |
| |
| unsigned deleteCount = length - begin; |
| if (exec->argumentCount() > 1) { |
| double deleteDouble = exec->argument(1).toInteger(exec); |
| if (deleteDouble < 0) |
| deleteCount = 0; |
| else if (deleteDouble > length - begin) |
| deleteCount = length - begin; |
| else |
| deleteCount = static_cast<unsigned>(deleteDouble); |
| } |
| |
| JSArray* resObj = JSArray::tryCreateUninitialized(exec->globalData(), exec->lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(ArrayWithUndecided), deleteCount); |
| if (!resObj) |
| return JSValue::encode(throwOutOfMemoryError(exec)); |
| |
| JSValue result = resObj; |
| JSGlobalData& globalData = exec->globalData(); |
| for (unsigned k = 0; k < deleteCount; k++) { |
| JSValue v = getProperty(exec, thisObj, k + begin); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| resObj->initializeIndex(globalData, k, v); |
| } |
| |
| unsigned additionalArgs = std::max<int>(exec->argumentCount() - 2, 0); |
| if (additionalArgs < deleteCount) { |
| shift<JSArray::ShiftCountForSplice>(exec, thisObj, begin, deleteCount, additionalArgs, length); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| } else if (additionalArgs > deleteCount) { |
| unshift<JSArray::ShiftCountForSplice>(exec, thisObj, begin, deleteCount, additionalArgs, length); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| } |
| for (unsigned k = 0; k < additionalArgs; ++k) { |
| thisObj->methodTable()->putByIndex(thisObj, exec, k + begin, exec->argument(k + 2), true); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| } |
| |
| putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length - deleteCount + additionalArgs)); |
| return JSValue::encode(result); |
| } |
| |
| EncodedJSValue JSC_HOST_CALL arrayProtoFuncUnShift(ExecState* exec) |
| { |
| // 15.4.4.13 |
| |
| JSObject* thisObj = exec->hostThisValue().toObject(exec); |
| unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| |
| unsigned nrArgs = exec->argumentCount(); |
| if (nrArgs) { |
| unshift<JSArray::ShiftCountForShift>(exec, thisObj, 0, 0, nrArgs, length); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| } |
| for (unsigned k = 0; k < nrArgs; ++k) { |
| thisObj->methodTable()->putByIndex(thisObj, exec, k, exec->argument(k), true); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| } |
| JSValue result = jsNumber(length + nrArgs); |
| putProperty(exec, thisObj, exec->propertyNames().length, result); |
| return JSValue::encode(result); |
| } |
| |
| EncodedJSValue JSC_HOST_CALL arrayProtoFuncFilter(ExecState* exec) |
| { |
| JSObject* thisObj = exec->hostThisValue().toObject(exec); |
| unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| |
| JSValue function = exec->argument(0); |
| CallData callData; |
| CallType callType = getCallData(function, callData); |
| if (callType == CallTypeNone) |
| return throwVMTypeError(exec); |
| |
| JSValue applyThis = exec->argument(1); |
| JSArray* resultArray = constructEmptyArray(exec, 0); |
| |
| unsigned filterIndex = 0; |
| unsigned k = 0; |
| if (callType == CallTypeJS && isJSArray(thisObj)) { |
| JSFunction* f = jsCast<JSFunction*>(function); |
| JSArray* array = asArray(thisObj); |
| CachedCall cachedCall(exec, f, 3); |
| for (; k < length && !exec->hadException(); ++k) { |
| if (!array->canGetIndexQuickly(k)) |
| break; |
| JSValue v = array->getIndexQuickly(k); |
| cachedCall.setThis(applyThis); |
| cachedCall.setArgument(0, v); |
| cachedCall.setArgument(1, jsNumber(k)); |
| cachedCall.setArgument(2, thisObj); |
| |
| JSValue result = cachedCall.call(); |
| if (result.toBoolean(exec)) |
| resultArray->putDirectIndex(exec, filterIndex++, v); |
| } |
| if (k == length) |
| return JSValue::encode(resultArray); |
| } |
| for (; k < length && !exec->hadException(); ++k) { |
| PropertySlot slot(thisObj); |
| if (!thisObj->getPropertySlot(exec, k, slot)) |
| continue; |
| JSValue v = slot.getValue(exec, k); |
| |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| |
| MarkedArgumentBuffer eachArguments; |
| eachArguments.append(v); |
| eachArguments.append(jsNumber(k)); |
| eachArguments.append(thisObj); |
| |
| JSValue result = call(exec, function, callType, callData, applyThis, eachArguments); |
| if (result.toBoolean(exec)) |
| resultArray->putDirectIndex(exec, filterIndex++, v); |
| } |
| return JSValue::encode(resultArray); |
| } |
| |
| EncodedJSValue JSC_HOST_CALL arrayProtoFuncMap(ExecState* exec) |
| { |
| JSObject* thisObj = exec->hostThisValue().toObject(exec); |
| unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| |
| JSValue function = exec->argument(0); |
| CallData callData; |
| CallType callType = getCallData(function, callData); |
| if (callType == CallTypeNone) |
| return throwVMTypeError(exec); |
| |
| JSValue applyThis = exec->argument(1); |
| |
| JSArray* resultArray = constructEmptyArray(exec, 0, length); |
| unsigned k = 0; |
| if (callType == CallTypeJS && isJSArray(thisObj)) { |
| JSFunction* f = jsCast<JSFunction*>(function); |
| JSArray* array = asArray(thisObj); |
| CachedCall cachedCall(exec, f, 3); |
| for (; k < length && !exec->hadException(); ++k) { |
| if (UNLIKELY(!array->canGetIndexQuickly(k))) |
| break; |
| |
| cachedCall.setThis(applyThis); |
| cachedCall.setArgument(0, array->getIndexQuickly(k)); |
| cachedCall.setArgument(1, jsNumber(k)); |
| cachedCall.setArgument(2, thisObj); |
| |
| resultArray->putDirectIndex(exec, k, cachedCall.call()); |
| } |
| } |
| for (; k < length && !exec->hadException(); ++k) { |
| PropertySlot slot(thisObj); |
| if (!thisObj->getPropertySlot(exec, k, slot)) |
| continue; |
| JSValue v = slot.getValue(exec, k); |
| |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| |
| MarkedArgumentBuffer eachArguments; |
| eachArguments.append(v); |
| eachArguments.append(jsNumber(k)); |
| eachArguments.append(thisObj); |
| |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| |
| JSValue result = call(exec, function, callType, callData, applyThis, eachArguments); |
| resultArray->putDirectIndex(exec, k, result); |
| } |
| |
| return JSValue::encode(resultArray); |
| } |
| |
| // Documentation for these three is available at: |
| // http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:every |
| // http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:forEach |
| // http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:some |
| |
| EncodedJSValue JSC_HOST_CALL arrayProtoFuncEvery(ExecState* exec) |
| { |
| JSObject* thisObj = exec->hostThisValue().toObject(exec); |
| unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| |
| JSValue function = exec->argument(0); |
| CallData callData; |
| CallType callType = getCallData(function, callData); |
| if (callType == CallTypeNone) |
| return throwVMTypeError(exec); |
| |
| JSValue applyThis = exec->argument(1); |
| |
| JSValue result = jsBoolean(true); |
| |
| unsigned k = 0; |
| if (callType == CallTypeJS && isJSArray(thisObj)) { |
| JSFunction* f = jsCast<JSFunction*>(function); |
| JSArray* array = asArray(thisObj); |
| CachedCall cachedCall(exec, f, 3); |
| for (; k < length && !exec->hadException(); ++k) { |
| if (UNLIKELY(!array->canGetIndexQuickly(k))) |
| break; |
| |
| cachedCall.setThis(applyThis); |
| cachedCall.setArgument(0, array->getIndexQuickly(k)); |
| cachedCall.setArgument(1, jsNumber(k)); |
| cachedCall.setArgument(2, thisObj); |
| JSValue result = cachedCall.call(); |
| if (!result.toBoolean(exec)) |
| return JSValue::encode(jsBoolean(false)); |
| } |
| } |
| for (; k < length && !exec->hadException(); ++k) { |
| PropertySlot slot(thisObj); |
| if (!thisObj->getPropertySlot(exec, k, slot)) |
| continue; |
| |
| MarkedArgumentBuffer eachArguments; |
| eachArguments.append(slot.getValue(exec, k)); |
| eachArguments.append(jsNumber(k)); |
| eachArguments.append(thisObj); |
| |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| |
| bool predicateResult = call(exec, function, callType, callData, applyThis, eachArguments).toBoolean(exec); |
| if (!predicateResult) { |
| result = jsBoolean(false); |
| break; |
| } |
| } |
| |
| return JSValue::encode(result); |
| } |
| |
| EncodedJSValue JSC_HOST_CALL arrayProtoFuncForEach(ExecState* exec) |
| { |
| JSObject* thisObj = exec->hostThisValue().toObject(exec); |
| unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| |
| JSValue function = exec->argument(0); |
| CallData callData; |
| CallType callType = getCallData(function, callData); |
| if (callType == CallTypeNone) |
| return throwVMTypeError(exec); |
| |
| JSValue applyThis = exec->argument(1); |
| |
| unsigned k = 0; |
| if (callType == CallTypeJS && isJSArray(thisObj)) { |
| JSFunction* f = jsCast<JSFunction*>(function); |
| JSArray* array = asArray(thisObj); |
| CachedCall cachedCall(exec, f, 3); |
| for (; k < length && !exec->hadException(); ++k) { |
| if (UNLIKELY(!array->canGetIndexQuickly(k))) |
| break; |
| |
| cachedCall.setThis(applyThis); |
| cachedCall.setArgument(0, array->getIndexQuickly(k)); |
| cachedCall.setArgument(1, jsNumber(k)); |
| cachedCall.setArgument(2, thisObj); |
| |
| cachedCall.call(); |
| } |
| } |
| for (; k < length && !exec->hadException(); ++k) { |
| PropertySlot slot(thisObj); |
| if (!thisObj->getPropertySlot(exec, k, slot)) |
| continue; |
| |
| MarkedArgumentBuffer eachArguments; |
| eachArguments.append(slot.getValue(exec, k)); |
| eachArguments.append(jsNumber(k)); |
| eachArguments.append(thisObj); |
| |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| |
| call(exec, function, callType, callData, applyThis, eachArguments); |
| } |
| return JSValue::encode(jsUndefined()); |
| } |
| |
| EncodedJSValue JSC_HOST_CALL arrayProtoFuncSome(ExecState* exec) |
| { |
| JSObject* thisObj = exec->hostThisValue().toObject(exec); |
| unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| |
| JSValue function = exec->argument(0); |
| CallData callData; |
| CallType callType = getCallData(function, callData); |
| if (callType == CallTypeNone) |
| return throwVMTypeError(exec); |
| |
| JSValue applyThis = exec->argument(1); |
| |
| JSValue result = jsBoolean(false); |
| |
| unsigned k = 0; |
| if (callType == CallTypeJS && isJSArray(thisObj)) { |
| JSFunction* f = jsCast<JSFunction*>(function); |
| JSArray* array = asArray(thisObj); |
| CachedCall cachedCall(exec, f, 3); |
| for (; k < length && !exec->hadException(); ++k) { |
| if (UNLIKELY(!array->canGetIndexQuickly(k))) |
| break; |
| |
| cachedCall.setThis(applyThis); |
| cachedCall.setArgument(0, array->getIndexQuickly(k)); |
| cachedCall.setArgument(1, jsNumber(k)); |
| cachedCall.setArgument(2, thisObj); |
| JSValue result = cachedCall.call(); |
| if (result.toBoolean(exec)) |
| return JSValue::encode(jsBoolean(true)); |
| } |
| } |
| for (; k < length && !exec->hadException(); ++k) { |
| PropertySlot slot(thisObj); |
| if (!thisObj->getPropertySlot(exec, k, slot)) |
| continue; |
| |
| MarkedArgumentBuffer eachArguments; |
| eachArguments.append(slot.getValue(exec, k)); |
| eachArguments.append(jsNumber(k)); |
| eachArguments.append(thisObj); |
| |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| |
| bool predicateResult = call(exec, function, callType, callData, applyThis, eachArguments).toBoolean(exec); |
| if (predicateResult) { |
| result = jsBoolean(true); |
| break; |
| } |
| } |
| return JSValue::encode(result); |
| } |
| |
| EncodedJSValue JSC_HOST_CALL arrayProtoFuncReduce(ExecState* exec) |
| { |
| JSObject* thisObj = exec->hostThisValue().toObject(exec); |
| unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| |
| JSValue function = exec->argument(0); |
| CallData callData; |
| CallType callType = getCallData(function, callData); |
| if (callType == CallTypeNone) |
| return throwVMTypeError(exec); |
| |
| unsigned i = 0; |
| JSValue rv; |
| if (!length && exec->argumentCount() == 1) |
| return throwVMTypeError(exec); |
| |
| JSArray* array = 0; |
| if (isJSArray(thisObj)) |
| array = asArray(thisObj); |
| |
| if (exec->argumentCount() >= 2) |
| rv = exec->argument(1); |
| else if (array && array->canGetIndexQuickly(0)) { |
| rv = array->getIndexQuickly(0); |
| i = 1; |
| } else { |
| for (i = 0; i < length; i++) { |
| rv = getProperty(exec, thisObj, i); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| if (rv) |
| break; |
| } |
| if (!rv) |
| return throwVMTypeError(exec); |
| i++; |
| } |
| |
| if (callType == CallTypeJS && array) { |
| CachedCall cachedCall(exec, jsCast<JSFunction*>(function), 4); |
| for (; i < length && !exec->hadException(); ++i) { |
| cachedCall.setThis(jsUndefined()); |
| cachedCall.setArgument(0, rv); |
| JSValue v; |
| if (LIKELY(array->canGetIndexQuickly(i))) |
| v = array->getIndexQuickly(i); |
| else |
| break; // length has been made unsafe while we enumerate fallback to slow path |
| cachedCall.setArgument(1, v); |
| cachedCall.setArgument(2, jsNumber(i)); |
| cachedCall.setArgument(3, array); |
| rv = cachedCall.call(); |
| } |
| if (i == length) // only return if we reached the end of the array |
| return JSValue::encode(rv); |
| } |
| |
| for (; i < length && !exec->hadException(); ++i) { |
| JSValue prop = getProperty(exec, thisObj, i); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| if (!prop) |
| continue; |
| |
| MarkedArgumentBuffer eachArguments; |
| eachArguments.append(rv); |
| eachArguments.append(prop); |
| eachArguments.append(jsNumber(i)); |
| eachArguments.append(thisObj); |
| |
| rv = call(exec, function, callType, callData, jsUndefined(), eachArguments); |
| } |
| return JSValue::encode(rv); |
| } |
| |
| EncodedJSValue JSC_HOST_CALL arrayProtoFuncReduceRight(ExecState* exec) |
| { |
| JSObject* thisObj = exec->hostThisValue().toObject(exec); |
| unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| |
| JSValue function = exec->argument(0); |
| CallData callData; |
| CallType callType = getCallData(function, callData); |
| if (callType == CallTypeNone) |
| return throwVMTypeError(exec); |
| |
| unsigned i = 0; |
| JSValue rv; |
| if (!length && exec->argumentCount() == 1) |
| return throwVMTypeError(exec); |
| |
| JSArray* array = 0; |
| if (isJSArray(thisObj)) |
| array = asArray(thisObj); |
| |
| if (exec->argumentCount() >= 2) |
| rv = exec->argument(1); |
| else if (array && array->canGetIndexQuickly(length - 1)) { |
| rv = array->getIndexQuickly(length - 1); |
| i = 1; |
| } else { |
| for (i = 0; i < length; i++) { |
| rv = getProperty(exec, thisObj, length - i - 1); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| if (rv) |
| break; |
| } |
| if (!rv) |
| return throwVMTypeError(exec); |
| i++; |
| } |
| |
| if (callType == CallTypeJS && array) { |
| CachedCall cachedCall(exec, jsCast<JSFunction*>(function), 4); |
| for (; i < length && !exec->hadException(); ++i) { |
| unsigned idx = length - i - 1; |
| cachedCall.setThis(jsUndefined()); |
| cachedCall.setArgument(0, rv); |
| if (UNLIKELY(!array->canGetIndexQuickly(idx))) |
| break; // length has been made unsafe while we enumerate fallback to slow path |
| cachedCall.setArgument(1, array->getIndexQuickly(idx)); |
| cachedCall.setArgument(2, jsNumber(idx)); |
| cachedCall.setArgument(3, array); |
| rv = cachedCall.call(); |
| } |
| if (i == length) // only return if we reached the end of the array |
| return JSValue::encode(rv); |
| } |
| |
| for (; i < length && !exec->hadException(); ++i) { |
| unsigned idx = length - i - 1; |
| JSValue prop = getProperty(exec, thisObj, idx); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| if (!prop) |
| continue; |
| |
| MarkedArgumentBuffer eachArguments; |
| eachArguments.append(rv); |
| eachArguments.append(prop); |
| eachArguments.append(jsNumber(idx)); |
| eachArguments.append(thisObj); |
| |
| rv = call(exec, function, callType, callData, jsUndefined(), eachArguments); |
| } |
| return JSValue::encode(rv); |
| } |
| |
| EncodedJSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState* exec) |
| { |
| // 15.4.4.14 |
| JSObject* thisObj = exec->hostThisValue().toObject(exec); |
| unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| |
| unsigned index = argumentClampedIndexFromStartOrEnd(exec, 1, length); |
| JSValue searchElement = exec->argument(0); |
| for (; index < length; ++index) { |
| JSValue e = getProperty(exec, thisObj, index); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| if (!e) |
| continue; |
| if (JSValue::strictEqual(exec, searchElement, e)) |
| return JSValue::encode(jsNumber(index)); |
| } |
| |
| return JSValue::encode(jsNumber(-1)); |
| } |
| |
| EncodedJSValue JSC_HOST_CALL arrayProtoFuncLastIndexOf(ExecState* exec) |
| { |
| // 15.4.4.15 |
| JSObject* thisObj = exec->hostThisValue().toObject(exec); |
| unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); |
| if (!length) |
| return JSValue::encode(jsNumber(-1)); |
| |
| unsigned index = length - 1; |
| if (exec->argumentCount() >= 2) { |
| JSValue fromValue = exec->argument(1); |
| double fromDouble = fromValue.toInteger(exec); |
| if (fromDouble < 0) { |
| fromDouble += length; |
| if (fromDouble < 0) |
| return JSValue::encode(jsNumber(-1)); |
| } |
| if (fromDouble < length) |
| index = static_cast<unsigned>(fromDouble); |
| } |
| |
| JSValue searchElement = exec->argument(0); |
| do { |
| ASSERT(index < length); |
| JSValue e = getProperty(exec, thisObj, index); |
| if (exec->hadException()) |
| return JSValue::encode(jsUndefined()); |
| if (!e) |
| continue; |
| if (JSValue::strictEqual(exec, searchElement, e)) |
| return JSValue::encode(jsNumber(index)); |
| } while (index--); |
| |
| return JSValue::encode(jsNumber(-1)); |
| } |
| |
| } // namespace JSC |