1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jsarray.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,3357 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "jsarray.h" 1.11 + 1.12 +#include "mozilla/ArrayUtils.h" 1.13 +#include "mozilla/DebugOnly.h" 1.14 +#include "mozilla/FloatingPoint.h" 1.15 +#include "mozilla/MathAlgorithms.h" 1.16 + 1.17 +#include "jsapi.h" 1.18 +#include "jsatom.h" 1.19 +#include "jscntxt.h" 1.20 +#include "jsfriendapi.h" 1.21 +#include "jsfun.h" 1.22 +#include "jsiter.h" 1.23 +#include "jsnum.h" 1.24 +#include "jsobj.h" 1.25 +#include "jstypes.h" 1.26 +#include "jsutil.h" 1.27 + 1.28 +#include "ds/Sort.h" 1.29 +#include "gc/Heap.h" 1.30 +#include "vm/ArgumentsObject.h" 1.31 +#include "vm/ForkJoin.h" 1.32 +#include "vm/Interpreter.h" 1.33 +#include "vm/NumericConversions.h" 1.34 +#include "vm/Shape.h" 1.35 +#include "vm/StringBuffer.h" 1.36 + 1.37 +#include "jsatominlines.h" 1.38 + 1.39 +#include "vm/ArgumentsObject-inl.h" 1.40 +#include "vm/ArrayObject-inl.h" 1.41 +#include "vm/Interpreter-inl.h" 1.42 +#include "vm/Runtime-inl.h" 1.43 + 1.44 +using namespace js; 1.45 +using namespace js::gc; 1.46 +using namespace js::types; 1.47 + 1.48 +using mozilla::Abs; 1.49 +using mozilla::ArrayLength; 1.50 +using mozilla::CeilingLog2; 1.51 +using mozilla::DebugOnly; 1.52 +using mozilla::IsNaN; 1.53 +using mozilla::PointerRangeSize; 1.54 + 1.55 +bool 1.56 +js::GetLengthProperty(JSContext *cx, HandleObject obj, uint32_t *lengthp) 1.57 +{ 1.58 + if (obj->is<ArrayObject>()) { 1.59 + *lengthp = obj->as<ArrayObject>().length(); 1.60 + return true; 1.61 + } 1.62 + 1.63 + if (obj->is<ArgumentsObject>()) { 1.64 + ArgumentsObject &argsobj = obj->as<ArgumentsObject>(); 1.65 + if (!argsobj.hasOverriddenLength()) { 1.66 + *lengthp = argsobj.initialLength(); 1.67 + return true; 1.68 + } 1.69 + } 1.70 + 1.71 + RootedValue value(cx); 1.72 + if (!JSObject::getProperty(cx, obj, obj, cx->names().length, &value)) 1.73 + return false; 1.74 + 1.75 + if (value.isInt32()) { 1.76 + *lengthp = uint32_t(value.toInt32()); // uint32_t cast does ToUint32 1.77 + return true; 1.78 + } 1.79 + 1.80 + return ToUint32(cx, value, lengthp); 1.81 +} 1.82 + 1.83 +/* 1.84 + * Determine if the id represents an array index. 1.85 + * 1.86 + * An id is an array index according to ECMA by (15.4): 1.87 + * 1.88 + * "Array objects give special treatment to a certain class of property names. 1.89 + * A property name P (in the form of a string value) is an array index if and 1.90 + * only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal 1.91 + * to 2^32-1." 1.92 + * 1.93 + * This means the largest allowed index is actually 2^32-2 (4294967294). 1.94 + * 1.95 + * In our implementation, it would be sufficient to check for JSVAL_IS_INT(id) 1.96 + * except that by using signed 31-bit integers we miss the top half of the 1.97 + * valid range. This function checks the string representation itself; note 1.98 + * that calling a standard conversion routine might allow strings such as 1.99 + * "08" or "4.0" as array indices, which they are not. 1.100 + * 1.101 + */ 1.102 +JS_FRIEND_API(bool) 1.103 +js::StringIsArrayIndex(JSLinearString *str, uint32_t *indexp) 1.104 +{ 1.105 + const jschar *s = str->chars(); 1.106 + uint32_t length = str->length(); 1.107 + const jschar *end = s + length; 1.108 + 1.109 + if (length == 0 || length > (sizeof("4294967294") - 1) || !JS7_ISDEC(*s)) 1.110 + return false; 1.111 + 1.112 + uint32_t c = 0, previous = 0; 1.113 + uint32_t index = JS7_UNDEC(*s++); 1.114 + 1.115 + /* Don't allow leading zeros. */ 1.116 + if (index == 0 && s != end) 1.117 + return false; 1.118 + 1.119 + for (; s < end; s++) { 1.120 + if (!JS7_ISDEC(*s)) 1.121 + return false; 1.122 + 1.123 + previous = index; 1.124 + c = JS7_UNDEC(*s); 1.125 + index = 10 * index + c; 1.126 + } 1.127 + 1.128 + /* Make sure we didn't overflow. */ 1.129 + if (previous < (MAX_ARRAY_INDEX / 10) || (previous == (MAX_ARRAY_INDEX / 10) && 1.130 + c <= (MAX_ARRAY_INDEX % 10))) { 1.131 + JS_ASSERT(index <= MAX_ARRAY_INDEX); 1.132 + *indexp = index; 1.133 + return true; 1.134 + } 1.135 + 1.136 + return false; 1.137 +} 1.138 + 1.139 +static bool 1.140 +ToId(JSContext *cx, double index, MutableHandleId id) 1.141 +{ 1.142 + if (index == uint32_t(index)) 1.143 + return IndexToId(cx, uint32_t(index), id); 1.144 + 1.145 + Value tmp = DoubleValue(index); 1.146 + return ValueToId<CanGC>(cx, HandleValue::fromMarkedLocation(&tmp), id); 1.147 +} 1.148 + 1.149 +static bool 1.150 +ToId(JSContext *cx, uint32_t index, MutableHandleId id) 1.151 +{ 1.152 + return IndexToId(cx, index, id); 1.153 +} 1.154 + 1.155 +/* 1.156 + * If the property at the given index exists, get its value into location 1.157 + * pointed by vp and set *hole to false. Otherwise set *hole to true and *vp 1.158 + * to JSVAL_VOID. This function assumes that the location pointed by vp is 1.159 + * properly rooted and can be used as GC-protected storage for temporaries. 1.160 + */ 1.161 +template<typename IndexType> 1.162 +static inline bool 1.163 +DoGetElement(JSContext *cx, HandleObject obj, HandleObject receiver, 1.164 + IndexType index, bool *hole, MutableHandleValue vp) 1.165 +{ 1.166 + RootedId id(cx); 1.167 + if (!ToId(cx, index, &id)) 1.168 + return false; 1.169 + 1.170 + RootedObject obj2(cx); 1.171 + RootedShape prop(cx); 1.172 + if (!JSObject::lookupGeneric(cx, obj, id, &obj2, &prop)) 1.173 + return false; 1.174 + 1.175 + if (!prop) { 1.176 + vp.setUndefined(); 1.177 + *hole = true; 1.178 + } else { 1.179 + if (!JSObject::getGeneric(cx, obj, receiver, id, vp)) 1.180 + return false; 1.181 + *hole = false; 1.182 + } 1.183 + return true; 1.184 +} 1.185 + 1.186 +template<typename IndexType> 1.187 +static void 1.188 +AssertGreaterThanZero(IndexType index) 1.189 +{ 1.190 + JS_ASSERT(index >= 0); 1.191 + JS_ASSERT(index == floor(index)); 1.192 +} 1.193 + 1.194 +template<> 1.195 +void 1.196 +AssertGreaterThanZero(uint32_t index) 1.197 +{ 1.198 +} 1.199 + 1.200 +template<typename IndexType> 1.201 +static bool 1.202 +GetElement(JSContext *cx, HandleObject obj, HandleObject receiver, 1.203 + IndexType index, bool *hole, MutableHandleValue vp) 1.204 +{ 1.205 + AssertGreaterThanZero(index); 1.206 + if (obj->isNative() && index < obj->getDenseInitializedLength()) { 1.207 + vp.set(obj->getDenseElement(uint32_t(index))); 1.208 + if (!vp.isMagic(JS_ELEMENTS_HOLE)) { 1.209 + *hole = false; 1.210 + return true; 1.211 + } 1.212 + } 1.213 + if (obj->is<ArgumentsObject>()) { 1.214 + if (obj->as<ArgumentsObject>().maybeGetElement(uint32_t(index), vp)) { 1.215 + *hole = false; 1.216 + return true; 1.217 + } 1.218 + } 1.219 + 1.220 + return DoGetElement(cx, obj, receiver, index, hole, vp); 1.221 +} 1.222 + 1.223 +template<typename IndexType> 1.224 +static inline bool 1.225 +GetElement(JSContext *cx, HandleObject obj, IndexType index, bool *hole, MutableHandleValue vp) 1.226 +{ 1.227 + return GetElement(cx, obj, obj, index, hole, vp); 1.228 +} 1.229 + 1.230 +static bool 1.231 +GetElementsSlow(JSContext *cx, HandleObject aobj, uint32_t length, Value *vp) 1.232 +{ 1.233 + for (uint32_t i = 0; i < length; i++) { 1.234 + if (!JSObject::getElement(cx, aobj, aobj, i, MutableHandleValue::fromMarkedLocation(&vp[i]))) 1.235 + return false; 1.236 + } 1.237 + 1.238 + return true; 1.239 +} 1.240 + 1.241 +bool 1.242 +js::GetElements(JSContext *cx, HandleObject aobj, uint32_t length, Value *vp) 1.243 +{ 1.244 + if (aobj->is<ArrayObject>() && length <= aobj->getDenseInitializedLength() && 1.245 + !ObjectMayHaveExtraIndexedProperties(aobj)) 1.246 + { 1.247 + /* No other indexed properties so hole = undefined */ 1.248 + const Value *srcbeg = aobj->getDenseElements(); 1.249 + const Value *srcend = srcbeg + length; 1.250 + const Value *src = srcbeg; 1.251 + for (Value *dst = vp; src < srcend; ++dst, ++src) 1.252 + *dst = src->isMagic(JS_ELEMENTS_HOLE) ? UndefinedValue() : *src; 1.253 + return true; 1.254 + } 1.255 + 1.256 + if (aobj->is<ArgumentsObject>()) { 1.257 + ArgumentsObject &argsobj = aobj->as<ArgumentsObject>(); 1.258 + if (!argsobj.hasOverriddenLength()) { 1.259 + if (argsobj.maybeGetElements(0, length, vp)) 1.260 + return true; 1.261 + } 1.262 + } 1.263 + 1.264 + return GetElementsSlow(cx, aobj, length, vp); 1.265 +} 1.266 + 1.267 +/* 1.268 + * Set the value of the property at the given index to v assuming v is rooted. 1.269 + */ 1.270 +static bool 1.271 +SetArrayElement(JSContext *cx, HandleObject obj, double index, HandleValue v) 1.272 +{ 1.273 + JS_ASSERT(index >= 0); 1.274 + 1.275 + if (obj->is<ArrayObject>() && !obj->isIndexed()) { 1.276 + Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>()); 1.277 + /* Predicted/prefetched code should favor the remains-dense case. */ 1.278 + JSObject::EnsureDenseResult result = JSObject::ED_SPARSE; 1.279 + do { 1.280 + if (index > uint32_t(-1)) 1.281 + break; 1.282 + uint32_t idx = uint32_t(index); 1.283 + if (idx >= arr->length() && !arr->lengthIsWritable()) { 1.284 + JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, nullptr, 1.285 + JSMSG_CANT_REDEFINE_ARRAY_LENGTH); 1.286 + return false; 1.287 + } 1.288 + result = arr->ensureDenseElements(cx, idx, 1); 1.289 + if (result != JSObject::ED_OK) 1.290 + break; 1.291 + if (idx >= arr->length()) 1.292 + arr->setLengthInt32(idx + 1); 1.293 + arr->setDenseElementWithType(cx, idx, v); 1.294 + return true; 1.295 + } while (false); 1.296 + 1.297 + if (result == JSObject::ED_FAILED) 1.298 + return false; 1.299 + JS_ASSERT(result == JSObject::ED_SPARSE); 1.300 + } 1.301 + 1.302 + RootedId id(cx); 1.303 + if (!ToId(cx, index, &id)) 1.304 + return false; 1.305 + 1.306 + RootedValue tmp(cx, v); 1.307 + return JSObject::setGeneric(cx, obj, obj, id, &tmp, true); 1.308 +} 1.309 + 1.310 +/* 1.311 + * Attempt to delete the element |index| from |obj| as if by 1.312 + * |obj.[[Delete]](index)|. 1.313 + * 1.314 + * If an error occurs while attempting to delete the element (that is, the call 1.315 + * to [[Delete]] threw), return false. 1.316 + * 1.317 + * Otherwise set *succeeded to indicate whether the deletion attempt succeeded 1.318 + * (that is, whether the call to [[Delete]] returned true or false). (Deletes 1.319 + * generally fail only when the property is non-configurable, but proxies may 1.320 + * implement different semantics.) 1.321 + */ 1.322 +static bool 1.323 +DeleteArrayElement(JSContext *cx, HandleObject obj, double index, bool *succeeded) 1.324 +{ 1.325 + JS_ASSERT(index >= 0); 1.326 + JS_ASSERT(floor(index) == index); 1.327 + 1.328 + if (obj->is<ArrayObject>() && !obj->isIndexed()) { 1.329 + if (index <= UINT32_MAX) { 1.330 + uint32_t idx = uint32_t(index); 1.331 + if (idx < obj->getDenseInitializedLength()) { 1.332 + obj->markDenseElementsNotPacked(cx); 1.333 + obj->setDenseElement(idx, MagicValue(JS_ELEMENTS_HOLE)); 1.334 + if (!js_SuppressDeletedElement(cx, obj, idx)) 1.335 + return false; 1.336 + } 1.337 + } 1.338 + 1.339 + *succeeded = true; 1.340 + return true; 1.341 + } 1.342 + 1.343 + if (index <= UINT32_MAX) 1.344 + return JSObject::deleteElement(cx, obj, uint32_t(index), succeeded); 1.345 + 1.346 + return JSObject::deleteByValue(cx, obj, DoubleValue(index), succeeded); 1.347 +} 1.348 + 1.349 +/* ES6 20130308 draft 9.3.5 */ 1.350 +static bool 1.351 +DeletePropertyOrThrow(JSContext *cx, HandleObject obj, double index) 1.352 +{ 1.353 + bool succeeded; 1.354 + if (!DeleteArrayElement(cx, obj, index, &succeeded)) 1.355 + return false; 1.356 + if (succeeded) 1.357 + return true; 1.358 + 1.359 + RootedId id(cx); 1.360 + RootedValue indexv(cx, NumberValue(index)); 1.361 + if (!ValueToId<CanGC>(cx, indexv, &id)) 1.362 + return false; 1.363 + return obj->reportNotConfigurable(cx, id, JSREPORT_ERROR); 1.364 +} 1.365 + 1.366 +bool 1.367 +js::SetLengthProperty(JSContext *cx, HandleObject obj, double length) 1.368 +{ 1.369 + RootedValue v(cx, NumberValue(length)); 1.370 + return JSObject::setProperty(cx, obj, obj, cx->names().length, &v, true); 1.371 +} 1.372 + 1.373 +/* 1.374 + * Since SpiderMonkey supports cross-class prototype-based delegation, we have 1.375 + * to be careful about the length getter and setter being called on an object 1.376 + * not of Array class. For the getter, we search obj's prototype chain for the 1.377 + * array that caused this getter to be invoked. In the setter case to overcome 1.378 + * the JSPROP_SHARED attribute, we must define a shadowing length property. 1.379 + */ 1.380 +static bool 1.381 +array_length_getter(JSContext *cx, HandleObject obj_, HandleId id, MutableHandleValue vp) 1.382 +{ 1.383 + RootedObject obj(cx, obj_); 1.384 + do { 1.385 + if (obj->is<ArrayObject>()) { 1.386 + vp.setNumber(obj->as<ArrayObject>().length()); 1.387 + return true; 1.388 + } 1.389 + if (!JSObject::getProto(cx, obj, &obj)) 1.390 + return false; 1.391 + } while (obj); 1.392 + return true; 1.393 +} 1.394 + 1.395 +static bool 1.396 +array_length_setter(JSContext *cx, HandleObject obj, HandleId id, bool strict, MutableHandleValue vp) 1.397 +{ 1.398 + if (!obj->is<ArrayObject>()) { 1.399 + return JSObject::defineProperty(cx, obj, cx->names().length, vp, 1.400 + nullptr, nullptr, JSPROP_ENUMERATE); 1.401 + } 1.402 + 1.403 + Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>()); 1.404 + MOZ_ASSERT(arr->lengthIsWritable(), 1.405 + "setter shouldn't be called if property is non-writable"); 1.406 + return ArraySetLength<SequentialExecution>(cx, arr, id, JSPROP_PERMANENT, vp, strict); 1.407 +} 1.408 + 1.409 +struct ReverseIndexComparator 1.410 +{ 1.411 + bool operator()(const uint32_t& a, const uint32_t& b, bool *lessOrEqualp) { 1.412 + MOZ_ASSERT(a != b, "how'd we get duplicate indexes?"); 1.413 + *lessOrEqualp = b <= a; 1.414 + return true; 1.415 + } 1.416 +}; 1.417 + 1.418 +template <ExecutionMode mode> 1.419 +bool 1.420 +js::CanonicalizeArrayLengthValue(typename ExecutionModeTraits<mode>::ContextType cx, 1.421 + HandleValue v, uint32_t *newLen) 1.422 +{ 1.423 + double d; 1.424 + 1.425 + if (mode == ParallelExecution) { 1.426 + if (v.isObject()) 1.427 + return false; 1.428 + 1.429 + if (!NonObjectToUint32(cx, v, newLen)) 1.430 + return false; 1.431 + 1.432 + if (!NonObjectToNumber(cx, v, &d)) 1.433 + return false; 1.434 + } else { 1.435 + if (!ToUint32(cx->asJSContext(), v, newLen)) 1.436 + return false; 1.437 + 1.438 + if (!ToNumber(cx->asJSContext(), v, &d)) 1.439 + return false; 1.440 + } 1.441 + 1.442 + if (d == *newLen) 1.443 + return true; 1.444 + 1.445 + if (cx->isJSContext()) 1.446 + JS_ReportErrorNumber(cx->asJSContext(), js_GetErrorMessage, nullptr, 1.447 + JSMSG_BAD_ARRAY_LENGTH); 1.448 + return false; 1.449 +} 1.450 + 1.451 +template bool 1.452 +js::CanonicalizeArrayLengthValue<SequentialExecution>(JSContext *cx, 1.453 + HandleValue v, uint32_t *newLen); 1.454 +template bool 1.455 +js::CanonicalizeArrayLengthValue<ParallelExecution>(ForkJoinContext *cx, 1.456 + HandleValue v, uint32_t *newLen); 1.457 + 1.458 +/* ES6 20130308 draft 8.4.2.4 ArraySetLength */ 1.459 +template <ExecutionMode mode> 1.460 +bool 1.461 +js::ArraySetLength(typename ExecutionModeTraits<mode>::ContextType cxArg, 1.462 + Handle<ArrayObject*> arr, HandleId id, 1.463 + unsigned attrs, HandleValue value, bool setterIsStrict) 1.464 +{ 1.465 + MOZ_ASSERT(cxArg->isThreadLocal(arr)); 1.466 + MOZ_ASSERT(id == NameToId(cxArg->names().length)); 1.467 + 1.468 + /* Steps 1-2 are irrelevant in our implementation. */ 1.469 + 1.470 + /* Steps 3-5. */ 1.471 + uint32_t newLen; 1.472 + if (!CanonicalizeArrayLengthValue<mode>(cxArg, value, &newLen)) 1.473 + return false; 1.474 + 1.475 + // Abort if we're being asked to change enumerability or configurability. 1.476 + // (The length property of arrays is non-configurable, so such attempts 1.477 + // must fail.) This behavior is spread throughout the ArraySetLength spec 1.478 + // algorithm, but we only need check it once as our array implementation 1.479 + // is internally so different from the spec algorithm. (ES5 and ES6 define 1.480 + // behavior by delegating to the default define-own-property algorithm -- 1.481 + // OrdinaryDefineOwnProperty in ES6, the default [[DefineOwnProperty]] in 1.482 + // ES5 -- but we reimplement all the conflict-detection bits ourselves here 1.483 + // so that we can use a customized length representation.) 1.484 + if (!(attrs & JSPROP_PERMANENT) || (attrs & JSPROP_ENUMERATE)) { 1.485 + if (!setterIsStrict) 1.486 + return true; 1.487 + // Bail for strict mode in parallel execution, as we need to go back 1.488 + // to sequential mode to throw the error. 1.489 + if (mode == ParallelExecution) 1.490 + return false; 1.491 + return Throw(cxArg->asJSContext(), id, JSMSG_CANT_REDEFINE_PROP); 1.492 + } 1.493 + 1.494 + /* Steps 6-7. */ 1.495 + bool lengthIsWritable = arr->lengthIsWritable(); 1.496 +#ifdef DEBUG 1.497 + { 1.498 + RootedShape lengthShape(cxArg, arr->nativeLookupPure(id)); 1.499 + MOZ_ASSERT(lengthShape); 1.500 + MOZ_ASSERT(lengthShape->writable() == lengthIsWritable); 1.501 + } 1.502 +#endif 1.503 + 1.504 + uint32_t oldLen = arr->length(); 1.505 + 1.506 + /* Steps 8-9 for arrays with non-writable length. */ 1.507 + if (!lengthIsWritable) { 1.508 + if (newLen == oldLen) 1.509 + return true; 1.510 + 1.511 + if (!cxArg->isJSContext()) 1.512 + return false; 1.513 + 1.514 + if (setterIsStrict) { 1.515 + return JS_ReportErrorFlagsAndNumber(cxArg->asJSContext(), 1.516 + JSREPORT_ERROR, js_GetErrorMessage, nullptr, 1.517 + JSMSG_CANT_REDEFINE_ARRAY_LENGTH); 1.518 + } 1.519 + 1.520 + return JSObject::reportReadOnly(cxArg->asJSContext(), id, 1.521 + JSREPORT_STRICT | JSREPORT_WARNING); 1.522 + } 1.523 + 1.524 + /* Step 8. */ 1.525 + bool succeeded = true; 1.526 + do { 1.527 + // The initialized length and capacity of an array only need updating 1.528 + // when non-hole elements are added or removed, which doesn't happen 1.529 + // when array length stays the same or increases. 1.530 + if (newLen >= oldLen) 1.531 + break; 1.532 + 1.533 + // Attempt to propagate dense-element optimization tricks, if possible, 1.534 + // and avoid the generic (and accordingly slow) deletion code below. 1.535 + // We can only do this if there are only densely-indexed elements. 1.536 + // Once there's a sparse indexed element, there's no good way to know, 1.537 + // save by enumerating all the properties to find it. But we *have* to 1.538 + // know in case that sparse indexed element is non-configurable, as 1.539 + // that element must prevent any deletions below it. Bug 586842 should 1.540 + // fix this inefficiency by moving indexed storage to be entirely 1.541 + // separate from non-indexed storage. 1.542 + if (!arr->isIndexed()) { 1.543 + uint32_t oldCapacity = arr->getDenseCapacity(); 1.544 + uint32_t oldInitializedLength = arr->getDenseInitializedLength(); 1.545 + MOZ_ASSERT(oldCapacity >= oldInitializedLength); 1.546 + if (oldInitializedLength > newLen) 1.547 + arr->setDenseInitializedLength(newLen); 1.548 + if (oldCapacity > newLen) 1.549 + arr->shrinkElements(cxArg, newLen); 1.550 + 1.551 + // We've done the work of deleting any dense elements needing 1.552 + // deletion, and there are no sparse elements. Thus we can skip 1.553 + // straight to defining the length. 1.554 + break; 1.555 + } 1.556 + 1.557 + // Bail from parallel execution if need to perform step 15, which is 1.558 + // unsafe and isn't a common case. 1.559 + if (mode == ParallelExecution) 1.560 + return false; 1.561 + 1.562 + JSContext *cx = cxArg->asJSContext(); 1.563 + 1.564 + // Step 15. 1.565 + // 1.566 + // Attempt to delete all elements above the new length, from greatest 1.567 + // to least. If any of these deletions fails, we're supposed to define 1.568 + // the length to one greater than the index that couldn't be deleted, 1.569 + // *with the property attributes specified*. This might convert the 1.570 + // length to be not the value specified, yet non-writable. (You may be 1.571 + // forgiven for thinking these are interesting semantics.) Example: 1.572 + // 1.573 + // var arr = 1.574 + // Object.defineProperty([0, 1, 2, 3], 1, { writable: false }); 1.575 + // Object.defineProperty(arr, "length", 1.576 + // { value: 0, writable: false }); 1.577 + // 1.578 + // will convert |arr| to an array of non-writable length two, then 1.579 + // throw a TypeError. 1.580 + // 1.581 + // We implement this behavior, in the relevant lops below, by setting 1.582 + // |succeeded| to false. Then we exit the loop, define the length 1.583 + // appropriately, and only then throw a TypeError, if necessary. 1.584 + uint32_t gap = oldLen - newLen; 1.585 + const uint32_t RemoveElementsFastLimit = 1 << 24; 1.586 + if (gap < RemoveElementsFastLimit) { 1.587 + // If we're removing a relatively small number of elements, just do 1.588 + // it exactly by the spec. 1.589 + while (newLen < oldLen) { 1.590 + /* Step 15a. */ 1.591 + oldLen--; 1.592 + 1.593 + /* Steps 15b-d. */ 1.594 + bool deleteSucceeded; 1.595 + if (!JSObject::deleteElement(cx, arr, oldLen, &deleteSucceeded)) 1.596 + return false; 1.597 + if (!deleteSucceeded) { 1.598 + newLen = oldLen + 1; 1.599 + succeeded = false; 1.600 + break; 1.601 + } 1.602 + } 1.603 + } else { 1.604 + // If we're removing a large number of elements from an array 1.605 + // that's probably sparse, try a different tack. Get all the own 1.606 + // property names, sift out the indexes in the deletion range into 1.607 + // a vector, sort the vector greatest to least, then delete the 1.608 + // indexes greatest to least using that vector. See bug 322135. 1.609 + // 1.610 + // This heuristic's kind of a huge guess -- "large number of 1.611 + // elements" and "probably sparse" are completely unprincipled 1.612 + // predictions. In the long run, bug 586842 will support the right 1.613 + // fix: store sparse elements in a sorted data structure that 1.614 + // permits fast in-reverse-order traversal and concurrent removals. 1.615 + 1.616 + Vector<uint32_t> indexes(cx); 1.617 + { 1.618 + AutoIdVector props(cx); 1.619 + if (!GetPropertyNames(cx, arr, JSITER_OWNONLY | JSITER_HIDDEN, &props)) 1.620 + return false; 1.621 + 1.622 + for (size_t i = 0; i < props.length(); i++) { 1.623 + if (!CheckForInterrupt(cx)) 1.624 + return false; 1.625 + 1.626 + uint32_t index; 1.627 + if (!js_IdIsIndex(props[i], &index)) 1.628 + continue; 1.629 + 1.630 + if (index >= newLen && index < oldLen) { 1.631 + if (!indexes.append(index)) 1.632 + return false; 1.633 + } 1.634 + } 1.635 + } 1.636 + 1.637 + uint32_t count = indexes.length(); 1.638 + { 1.639 + // We should use radix sort to be O(n), but this is uncommon 1.640 + // enough that we'll punt til someone complains. 1.641 + Vector<uint32_t> scratch(cx); 1.642 + if (!scratch.resize(count)) 1.643 + return false; 1.644 + MOZ_ALWAYS_TRUE(MergeSort(indexes.begin(), count, scratch.begin(), 1.645 + ReverseIndexComparator())); 1.646 + } 1.647 + 1.648 + uint32_t index = UINT32_MAX; 1.649 + for (uint32_t i = 0; i < count; i++) { 1.650 + MOZ_ASSERT(indexes[i] < index, "indexes should never repeat"); 1.651 + index = indexes[i]; 1.652 + 1.653 + /* Steps 15b-d. */ 1.654 + bool deleteSucceeded; 1.655 + if (!JSObject::deleteElement(cx, arr, index, &deleteSucceeded)) 1.656 + return false; 1.657 + if (!deleteSucceeded) { 1.658 + newLen = index + 1; 1.659 + succeeded = false; 1.660 + break; 1.661 + } 1.662 + } 1.663 + } 1.664 + } while (false); 1.665 + 1.666 + /* Steps 12, 16. */ 1.667 + 1.668 + // Yes, we totally drop a non-stub getter/setter from a defineProperty 1.669 + // API call on the floor here. Given that getter/setter will go away in 1.670 + // the long run, with accessors replacing them both internally and at the 1.671 + // API level, just run with this. 1.672 + RootedShape lengthShape(cxArg, mode == ParallelExecution 1.673 + ? arr->nativeLookupPure(id) 1.674 + : arr->nativeLookup(cxArg->asJSContext(), id)); 1.675 + if (!JSObject::changeProperty<mode>(cxArg, arr, lengthShape, attrs, 1.676 + JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_SHARED, 1.677 + array_length_getter, array_length_setter)) 1.678 + { 1.679 + return false; 1.680 + } 1.681 + 1.682 + if (mode == ParallelExecution) { 1.683 + // Overflowing int32 requires changing TI state. 1.684 + if (newLen > INT32_MAX) 1.685 + return false; 1.686 + arr->setLengthInt32(newLen); 1.687 + } else { 1.688 + JSContext *cx = cxArg->asJSContext(); 1.689 + arr->setLength(cx, newLen); 1.690 + } 1.691 + 1.692 + 1.693 + // All operations past here until the |!succeeded| code must be infallible, 1.694 + // so that all element fields remain properly synchronized. 1.695 + 1.696 + // Trim the initialized length, if needed, to preserve the <= length 1.697 + // invariant. (Capacity was already reduced during element deletion, if 1.698 + // necessary.) 1.699 + ObjectElements *header = arr->getElementsHeader(); 1.700 + header->initializedLength = Min(header->initializedLength, newLen); 1.701 + 1.702 + if (attrs & JSPROP_READONLY) { 1.703 + header->setNonwritableArrayLength(); 1.704 + 1.705 + // When an array's length becomes non-writable, writes to indexes 1.706 + // greater than or equal to the length don't change the array. We 1.707 + // handle this with a check for non-writable length in most places. 1.708 + // But in JIT code every check counts -- so we piggyback the check on 1.709 + // the already-required range check for |index < capacity| by making 1.710 + // capacity of arrays with non-writable length never exceed the length. 1.711 + if (arr->getDenseCapacity() > newLen) { 1.712 + arr->shrinkElements(cxArg, newLen); 1.713 + arr->getElementsHeader()->capacity = newLen; 1.714 + } 1.715 + } 1.716 + 1.717 + if (setterIsStrict && !succeeded) { 1.718 + // We can't have arrived here under ParallelExecution, as we have 1.719 + // returned from the function before step 15 above. 1.720 + JSContext *cx = cxArg->asJSContext(); 1.721 + RootedId elementId(cx); 1.722 + if (!IndexToId(cx, newLen - 1, &elementId)) 1.723 + return false; 1.724 + return arr->reportNotConfigurable(cx, elementId); 1.725 + } 1.726 + 1.727 + return true; 1.728 +} 1.729 + 1.730 +template bool 1.731 +js::ArraySetLength<SequentialExecution>(JSContext *cx, Handle<ArrayObject*> arr, 1.732 + HandleId id, unsigned attrs, HandleValue value, 1.733 + bool setterIsStrict); 1.734 +template bool 1.735 +js::ArraySetLength<ParallelExecution>(ForkJoinContext *cx, Handle<ArrayObject*> arr, 1.736 + HandleId id, unsigned attrs, HandleValue value, 1.737 + bool setterIsStrict); 1.738 + 1.739 +bool 1.740 +js::WouldDefinePastNonwritableLength(ThreadSafeContext *cx, 1.741 + HandleObject obj, uint32_t index, bool strict, 1.742 + bool *definesPast) 1.743 +{ 1.744 + if (!obj->is<ArrayObject>()) { 1.745 + *definesPast = false; 1.746 + return true; 1.747 + } 1.748 + 1.749 + Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>()); 1.750 + uint32_t length = arr->length(); 1.751 + if (index < length) { 1.752 + *definesPast = false; 1.753 + return true; 1.754 + } 1.755 + 1.756 + if (arr->lengthIsWritable()) { 1.757 + *definesPast = false; 1.758 + return true; 1.759 + } 1.760 + 1.761 + *definesPast = true; 1.762 + 1.763 + // Error in strict mode code or warn with strict option. 1.764 + unsigned flags = strict ? JSREPORT_ERROR : (JSREPORT_STRICT | JSREPORT_WARNING); 1.765 + if (cx->isForkJoinContext()) 1.766 + return cx->asForkJoinContext()->reportError(ParallelBailoutUnsupportedVM, flags); 1.767 + 1.768 + if (!cx->isJSContext()) 1.769 + return true; 1.770 + 1.771 + JSContext *ncx = cx->asJSContext(); 1.772 + 1.773 + if (!strict && !ncx->options().extraWarnings()) 1.774 + return true; 1.775 + 1.776 + // XXX include the index and maybe array length in the error message 1.777 + return JS_ReportErrorFlagsAndNumber(ncx, flags, js_GetErrorMessage, nullptr, 1.778 + JSMSG_CANT_DEFINE_PAST_ARRAY_LENGTH); 1.779 +} 1.780 + 1.781 +static bool 1.782 +array_addProperty(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp) 1.783 +{ 1.784 + Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>()); 1.785 + 1.786 + uint32_t index; 1.787 + if (!js_IdIsIndex(id, &index)) 1.788 + return true; 1.789 + 1.790 + uint32_t length = arr->length(); 1.791 + if (index >= length) { 1.792 + MOZ_ASSERT(arr->lengthIsWritable(), 1.793 + "how'd this element get added if length is non-writable?"); 1.794 + arr->setLength(cx, index + 1); 1.795 + } 1.796 + return true; 1.797 +} 1.798 + 1.799 +bool 1.800 +js::ObjectMayHaveExtraIndexedProperties(JSObject *obj) 1.801 +{ 1.802 + /* 1.803 + * Whether obj may have indexed properties anywhere besides its dense 1.804 + * elements. This includes other indexed properties in its shape hierarchy, 1.805 + * and indexed properties or elements along its prototype chain. 1.806 + */ 1.807 + 1.808 + JS_ASSERT(obj->isNative()); 1.809 + 1.810 + if (obj->isIndexed()) 1.811 + return true; 1.812 + 1.813 + /* 1.814 + * Walk up the prototype chain and see if this indexed element already 1.815 + * exists. If we hit the end of the prototype chain, it's safe to set the 1.816 + * element on the original object. 1.817 + */ 1.818 + while ((obj = obj->getProto()) != nullptr) { 1.819 + /* 1.820 + * If the prototype is a non-native object (possibly a dense array), or 1.821 + * a native object (possibly a slow array) that has indexed properties, 1.822 + * return true. 1.823 + */ 1.824 + if (!obj->isNative()) 1.825 + return true; 1.826 + if (obj->isIndexed()) 1.827 + return true; 1.828 + if (obj->getDenseInitializedLength() > 0) 1.829 + return true; 1.830 + if (obj->is<TypedArrayObject>()) 1.831 + return true; 1.832 + } 1.833 + 1.834 + return false; 1.835 +} 1.836 + 1.837 +const Class ArrayObject::class_ = { 1.838 + "Array", 1.839 + JSCLASS_HAS_CACHED_PROTO(JSProto_Array), 1.840 + array_addProperty, 1.841 + JS_DeletePropertyStub, /* delProperty */ 1.842 + JS_PropertyStub, /* getProperty */ 1.843 + JS_StrictPropertyStub, /* setProperty */ 1.844 + JS_EnumerateStub, 1.845 + JS_ResolveStub, 1.846 + JS_ConvertStub, 1.847 + nullptr, 1.848 + nullptr, /* call */ 1.849 + nullptr, /* hasInstance */ 1.850 + nullptr, /* construct */ 1.851 + nullptr /* trace */ 1.852 +}; 1.853 + 1.854 +static bool 1.855 +AddLengthProperty(ExclusiveContext *cx, HandleObject obj) 1.856 +{ 1.857 + /* 1.858 + * Add the 'length' property for a newly created array, 1.859 + * and update the elements to be an empty array owned by the object. 1.860 + * The shared emptyObjectElements singleton cannot be used for slow arrays, 1.861 + * as accesses to 'length' will use the elements header. 1.862 + */ 1.863 + 1.864 + RootedId lengthId(cx, NameToId(cx->names().length)); 1.865 + JS_ASSERT(!obj->nativeLookup(cx, lengthId)); 1.866 + 1.867 + return JSObject::addProperty(cx, obj, lengthId, array_length_getter, array_length_setter, 1.868 + SHAPE_INVALID_SLOT, JSPROP_PERMANENT | JSPROP_SHARED, 0, 1.869 + /* allowDictionary = */ false); 1.870 +} 1.871 + 1.872 +#if JS_HAS_TOSOURCE 1.873 +MOZ_ALWAYS_INLINE bool 1.874 +IsArray(HandleValue v) 1.875 +{ 1.876 + return v.isObject() && v.toObject().is<ArrayObject>(); 1.877 +} 1.878 + 1.879 +MOZ_ALWAYS_INLINE bool 1.880 +array_toSource_impl(JSContext *cx, CallArgs args) 1.881 +{ 1.882 + JS_ASSERT(IsArray(args.thisv())); 1.883 + 1.884 + Rooted<JSObject*> obj(cx, &args.thisv().toObject()); 1.885 + RootedValue elt(cx); 1.886 + 1.887 + AutoCycleDetector detector(cx, obj); 1.888 + if (!detector.init()) 1.889 + return false; 1.890 + 1.891 + StringBuffer sb(cx); 1.892 + 1.893 + if (detector.foundCycle()) { 1.894 + if (!sb.append("[]")) 1.895 + return false; 1.896 + goto make_string; 1.897 + } 1.898 + 1.899 + if (!sb.append('[')) 1.900 + return false; 1.901 + 1.902 + uint32_t length; 1.903 + if (!GetLengthProperty(cx, obj, &length)) 1.904 + return false; 1.905 + 1.906 + for (uint32_t index = 0; index < length; index++) { 1.907 + bool hole; 1.908 + if (!CheckForInterrupt(cx) || 1.909 + !GetElement(cx, obj, index, &hole, &elt)) { 1.910 + return false; 1.911 + } 1.912 + 1.913 + /* Get element's character string. */ 1.914 + JSString *str; 1.915 + if (hole) { 1.916 + str = cx->runtime()->emptyString; 1.917 + } else { 1.918 + str = ValueToSource(cx, elt); 1.919 + if (!str) 1.920 + return false; 1.921 + } 1.922 + 1.923 + /* Append element to buffer. */ 1.924 + if (!sb.append(str)) 1.925 + return false; 1.926 + if (index + 1 != length) { 1.927 + if (!sb.append(", ")) 1.928 + return false; 1.929 + } else if (hole) { 1.930 + if (!sb.append(',')) 1.931 + return false; 1.932 + } 1.933 + } 1.934 + 1.935 + /* Finalize the buffer. */ 1.936 + if (!sb.append(']')) 1.937 + return false; 1.938 + 1.939 + make_string: 1.940 + JSString *str = sb.finishString(); 1.941 + if (!str) 1.942 + return false; 1.943 + 1.944 + args.rval().setString(str); 1.945 + return true; 1.946 +} 1.947 + 1.948 +static bool 1.949 +array_toSource(JSContext *cx, unsigned argc, Value *vp) 1.950 +{ 1.951 + JS_CHECK_RECURSION(cx, return false); 1.952 + CallArgs args = CallArgsFromVp(argc, vp); 1.953 + return CallNonGenericMethod<IsArray, array_toSource_impl>(cx, args); 1.954 +} 1.955 +#endif 1.956 + 1.957 +struct EmptySeparatorOp 1.958 +{ 1.959 + bool operator()(JSContext *, StringBuffer &sb) { return true; } 1.960 +}; 1.961 + 1.962 +struct CharSeparatorOp 1.963 +{ 1.964 + jschar sep; 1.965 + CharSeparatorOp(jschar sep) : sep(sep) {}; 1.966 + bool operator()(JSContext *, StringBuffer &sb) { return sb.append(sep); } 1.967 +}; 1.968 + 1.969 +struct StringSeparatorOp 1.970 +{ 1.971 + const jschar *sepchars; 1.972 + size_t seplen; 1.973 + 1.974 + StringSeparatorOp(const jschar *sepchars, size_t seplen) 1.975 + : sepchars(sepchars), seplen(seplen) {}; 1.976 + 1.977 + bool operator()(JSContext *cx, StringBuffer &sb) { 1.978 + return sb.append(sepchars, seplen); 1.979 + } 1.980 +}; 1.981 + 1.982 +template <bool Locale, typename SeparatorOp> 1.983 +static bool 1.984 +ArrayJoinKernel(JSContext *cx, SeparatorOp sepOp, HandleObject obj, uint32_t length, 1.985 + StringBuffer &sb) 1.986 +{ 1.987 + uint32_t i = 0; 1.988 + 1.989 + if (!Locale && obj->is<ArrayObject>() && !ObjectMayHaveExtraIndexedProperties(obj)) { 1.990 + // This loop handles all elements up to initializedLength. If 1.991 + // length > initLength we rely on the second loop to add the 1.992 + // other elements. 1.993 + uint32_t initLength = obj->getDenseInitializedLength(); 1.994 + while (i < initLength) { 1.995 + if (!CheckForInterrupt(cx)) 1.996 + return false; 1.997 + 1.998 + const Value &elem = obj->getDenseElement(i); 1.999 + 1.1000 + if (elem.isString()) { 1.1001 + if (!sb.append(elem.toString())) 1.1002 + return false; 1.1003 + } else if (elem.isNumber()) { 1.1004 + if (!NumberValueToStringBuffer(cx, elem, sb)) 1.1005 + return false; 1.1006 + } else if (elem.isBoolean()) { 1.1007 + if (!BooleanToStringBuffer(elem.toBoolean(), sb)) 1.1008 + return false; 1.1009 + } else if (elem.isObject()) { 1.1010 + /* 1.1011 + * Object stringifying could modify the initialized length or make 1.1012 + * the array sparse. Delegate it to a separate loop to keep this 1.1013 + * one tight. 1.1014 + */ 1.1015 + break; 1.1016 + } else { 1.1017 + JS_ASSERT(elem.isMagic(JS_ELEMENTS_HOLE) || elem.isNullOrUndefined()); 1.1018 + } 1.1019 + 1.1020 + if (++i != length && !sepOp(cx, sb)) 1.1021 + return false; 1.1022 + } 1.1023 + } 1.1024 + 1.1025 + if (i != length) { 1.1026 + RootedValue v(cx); 1.1027 + while (i < length) { 1.1028 + if (!CheckForInterrupt(cx)) 1.1029 + return false; 1.1030 + 1.1031 + bool hole; 1.1032 + if (!GetElement(cx, obj, i, &hole, &v)) 1.1033 + return false; 1.1034 + if (!hole && !v.isNullOrUndefined()) { 1.1035 + if (Locale) { 1.1036 + JSObject *robj = ToObject(cx, v); 1.1037 + if (!robj) 1.1038 + return false; 1.1039 + RootedId id(cx, NameToId(cx->names().toLocaleString)); 1.1040 + if (!robj->callMethod(cx, id, 0, nullptr, &v)) 1.1041 + return false; 1.1042 + } 1.1043 + if (!ValueToStringBuffer(cx, v, sb)) 1.1044 + return false; 1.1045 + } 1.1046 + 1.1047 + if (++i != length && !sepOp(cx, sb)) 1.1048 + return false; 1.1049 + } 1.1050 + } 1.1051 + 1.1052 + return true; 1.1053 +} 1.1054 + 1.1055 +template <bool Locale> 1.1056 +static bool 1.1057 +ArrayJoin(JSContext *cx, CallArgs &args) 1.1058 +{ 1.1059 + // This method is shared by Array.prototype.join and 1.1060 + // Array.prototype.toLocaleString. The steps in ES5 are nearly the same, so 1.1061 + // the annotations in this function apply to both toLocaleString and join. 1.1062 + 1.1063 + // Step 1 1.1064 + RootedObject obj(cx, ToObject(cx, args.thisv())); 1.1065 + if (!obj) 1.1066 + return false; 1.1067 + 1.1068 + AutoCycleDetector detector(cx, obj); 1.1069 + if (!detector.init()) 1.1070 + return false; 1.1071 + 1.1072 + if (detector.foundCycle()) { 1.1073 + args.rval().setString(cx->names().empty); 1.1074 + return true; 1.1075 + } 1.1076 + 1.1077 + // Steps 2 and 3 1.1078 + uint32_t length; 1.1079 + if (!GetLengthProperty(cx, obj, &length)) 1.1080 + return false; 1.1081 + 1.1082 + // Steps 4 and 5 1.1083 + RootedString sepstr(cx, nullptr); 1.1084 + const jschar *sepchars; 1.1085 + size_t seplen; 1.1086 + if (!Locale && args.hasDefined(0)) { 1.1087 + sepstr = ToString<CanGC>(cx, args[0]); 1.1088 + if (!sepstr) 1.1089 + return false; 1.1090 + sepchars = sepstr->getChars(cx); 1.1091 + if (!sepchars) 1.1092 + return false; 1.1093 + seplen = sepstr->length(); 1.1094 + } else { 1.1095 + HandlePropertyName comma = cx->names().comma; 1.1096 + sepstr = comma; 1.1097 + sepchars = comma->chars(); 1.1098 + seplen = comma->length(); 1.1099 + } 1.1100 + 1.1101 + JS::Anchor<JSString*> anchor(sepstr); 1.1102 + 1.1103 + // Step 6 is implicit in the loops below. 1.1104 + 1.1105 + // An optimized version of a special case of steps 7-11: when length==1 and 1.1106 + // the 0th element is a string, ToString() of that element is a no-op and 1.1107 + // so it can be immediately returned as the result. 1.1108 + if (length == 1 && !Locale && obj->is<ArrayObject>() && 1.1109 + obj->getDenseInitializedLength() == 1) 1.1110 + { 1.1111 + const Value &elem0 = obj->getDenseElement(0); 1.1112 + if (elem0.isString()) { 1.1113 + args.rval().setString(elem0.toString()); 1.1114 + return true; 1.1115 + } 1.1116 + } 1.1117 + 1.1118 + StringBuffer sb(cx); 1.1119 + 1.1120 + // The separator will be added |length - 1| times, reserve space for that 1.1121 + // so that we don't have to unnecessarily grow the buffer. 1.1122 + if (length > 0 && !sb.reserve(seplen * (length - 1))) 1.1123 + return false; 1.1124 + 1.1125 + // Various optimized versions of steps 7-10. 1.1126 + if (seplen == 0) { 1.1127 + EmptySeparatorOp op; 1.1128 + if (!ArrayJoinKernel<Locale>(cx, op, obj, length, sb)) 1.1129 + return false; 1.1130 + } else if (seplen == 1) { 1.1131 + CharSeparatorOp op(sepchars[0]); 1.1132 + if (!ArrayJoinKernel<Locale>(cx, op, obj, length, sb)) 1.1133 + return false; 1.1134 + } else { 1.1135 + StringSeparatorOp op(sepchars, seplen); 1.1136 + if (!ArrayJoinKernel<Locale>(cx, op, obj, length, sb)) 1.1137 + return false; 1.1138 + } 1.1139 + 1.1140 + // Step 11 1.1141 + JSString *str = sb.finishString(); 1.1142 + if (!str) 1.1143 + return false; 1.1144 + args.rval().setString(str); 1.1145 + return true; 1.1146 +} 1.1147 + 1.1148 +/* ES5 15.4.4.2. NB: The algorithm here differs from the one in ES3. */ 1.1149 +static bool 1.1150 +array_toString(JSContext *cx, unsigned argc, Value *vp) 1.1151 +{ 1.1152 + JS_CHECK_RECURSION(cx, return false); 1.1153 + 1.1154 + CallArgs args = CallArgsFromVp(argc, vp); 1.1155 + RootedObject obj(cx, ToObject(cx, args.thisv())); 1.1156 + if (!obj) 1.1157 + return false; 1.1158 + 1.1159 + RootedValue join(cx, args.calleev()); 1.1160 + if (!JSObject::getProperty(cx, obj, obj, cx->names().join, &join)) 1.1161 + return false; 1.1162 + 1.1163 + if (!js_IsCallable(join)) { 1.1164 + JSString *str = JS_BasicObjectToString(cx, obj); 1.1165 + if (!str) 1.1166 + return false; 1.1167 + args.rval().setString(str); 1.1168 + return true; 1.1169 + } 1.1170 + 1.1171 + InvokeArgs args2(cx); 1.1172 + if (!args2.init(0)) 1.1173 + return false; 1.1174 + 1.1175 + args2.setCallee(join); 1.1176 + args2.setThis(ObjectValue(*obj)); 1.1177 + 1.1178 + /* Do the call. */ 1.1179 + if (!Invoke(cx, args2)) 1.1180 + return false; 1.1181 + args.rval().set(args2.rval()); 1.1182 + return true; 1.1183 +} 1.1184 + 1.1185 +/* ES5 15.4.4.3 */ 1.1186 +static bool 1.1187 +array_toLocaleString(JSContext *cx, unsigned argc, Value *vp) 1.1188 +{ 1.1189 + JS_CHECK_RECURSION(cx, return false); 1.1190 + 1.1191 + CallArgs args = CallArgsFromVp(argc, vp); 1.1192 + return ArrayJoin<true>(cx, args); 1.1193 +} 1.1194 + 1.1195 +/* ES5 15.4.4.5 */ 1.1196 +static bool 1.1197 +array_join(JSContext *cx, unsigned argc, Value *vp) 1.1198 +{ 1.1199 + JS_CHECK_RECURSION(cx, return false); 1.1200 + 1.1201 + CallArgs args = CallArgsFromVp(argc, vp); 1.1202 + return ArrayJoin<false>(cx, args); 1.1203 +} 1.1204 + 1.1205 +static inline bool 1.1206 +InitArrayTypes(JSContext *cx, TypeObject *type, const Value *vector, unsigned count) 1.1207 +{ 1.1208 + if (!type->unknownProperties()) { 1.1209 + AutoEnterAnalysis enter(cx); 1.1210 + 1.1211 + HeapTypeSet *types = type->getProperty(cx, JSID_VOID); 1.1212 + if (!types) 1.1213 + return false; 1.1214 + 1.1215 + for (unsigned i = 0; i < count; i++) { 1.1216 + if (vector[i].isMagic(JS_ELEMENTS_HOLE)) 1.1217 + continue; 1.1218 + Type valtype = GetValueType(vector[i]); 1.1219 + types->addType(cx, valtype); 1.1220 + } 1.1221 + } 1.1222 + return true; 1.1223 +} 1.1224 + 1.1225 +enum ShouldUpdateTypes 1.1226 +{ 1.1227 + UpdateTypes = true, 1.1228 + DontUpdateTypes = false 1.1229 +}; 1.1230 + 1.1231 +/* vector must point to rooted memory. */ 1.1232 +static bool 1.1233 +InitArrayElements(JSContext *cx, HandleObject obj, uint32_t start, uint32_t count, const Value *vector, ShouldUpdateTypes updateTypes) 1.1234 +{ 1.1235 + JS_ASSERT(count <= MAX_ARRAY_INDEX); 1.1236 + 1.1237 + if (count == 0) 1.1238 + return true; 1.1239 + 1.1240 + types::TypeObject *type = obj->getType(cx); 1.1241 + if (!type) 1.1242 + return false; 1.1243 + if (updateTypes && !InitArrayTypes(cx, type, vector, count)) 1.1244 + return false; 1.1245 + 1.1246 + /* 1.1247 + * Optimize for dense arrays so long as adding the given set of elements 1.1248 + * wouldn't otherwise make the array slow or exceed a non-writable array 1.1249 + * length. 1.1250 + */ 1.1251 + do { 1.1252 + if (!obj->is<ArrayObject>()) 1.1253 + break; 1.1254 + if (ObjectMayHaveExtraIndexedProperties(obj)) 1.1255 + break; 1.1256 + 1.1257 + if (obj->shouldConvertDoubleElements()) 1.1258 + break; 1.1259 + 1.1260 + Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>()); 1.1261 + 1.1262 + if (!arr->lengthIsWritable() && start + count > arr->length()) 1.1263 + break; 1.1264 + 1.1265 + JSObject::EnsureDenseResult result = arr->ensureDenseElements(cx, start, count); 1.1266 + if (result != JSObject::ED_OK) { 1.1267 + if (result == JSObject::ED_FAILED) 1.1268 + return false; 1.1269 + JS_ASSERT(result == JSObject::ED_SPARSE); 1.1270 + break; 1.1271 + } 1.1272 + 1.1273 + uint32_t newlen = start + count; 1.1274 + if (newlen > arr->length()) 1.1275 + arr->setLengthInt32(newlen); 1.1276 + 1.1277 + JS_ASSERT(count < UINT32_MAX / sizeof(Value)); 1.1278 + arr->copyDenseElements(start, vector, count); 1.1279 + JS_ASSERT_IF(count != 0, !arr->getDenseElement(newlen - 1).isMagic(JS_ELEMENTS_HOLE)); 1.1280 + return true; 1.1281 + } while (false); 1.1282 + 1.1283 + const Value* end = vector + count; 1.1284 + while (vector < end && start <= MAX_ARRAY_INDEX) { 1.1285 + if (!CheckForInterrupt(cx) || 1.1286 + !SetArrayElement(cx, obj, start++, HandleValue::fromMarkedLocation(vector++))) { 1.1287 + return false; 1.1288 + } 1.1289 + } 1.1290 + 1.1291 + if (vector == end) 1.1292 + return true; 1.1293 + 1.1294 + JS_ASSERT(start == MAX_ARRAY_INDEX + 1); 1.1295 + RootedValue value(cx); 1.1296 + RootedId id(cx); 1.1297 + RootedValue indexv(cx); 1.1298 + double index = MAX_ARRAY_INDEX + 1; 1.1299 + do { 1.1300 + value = *vector++; 1.1301 + indexv = DoubleValue(index); 1.1302 + if (!ValueToId<CanGC>(cx, indexv, &id) || 1.1303 + !JSObject::setGeneric(cx, obj, obj, id, &value, true)) 1.1304 + { 1.1305 + return false; 1.1306 + } 1.1307 + index += 1; 1.1308 + } while (vector != end); 1.1309 + 1.1310 + return true; 1.1311 +} 1.1312 + 1.1313 +static bool 1.1314 +array_reverse(JSContext *cx, unsigned argc, Value *vp) 1.1315 +{ 1.1316 + CallArgs args = CallArgsFromVp(argc, vp); 1.1317 + RootedObject obj(cx, ToObject(cx, args.thisv())); 1.1318 + if (!obj) 1.1319 + return false; 1.1320 + 1.1321 + uint32_t len; 1.1322 + if (!GetLengthProperty(cx, obj, &len)) 1.1323 + return false; 1.1324 + 1.1325 + do { 1.1326 + if (!obj->is<ArrayObject>()) 1.1327 + break; 1.1328 + if (ObjectMayHaveExtraIndexedProperties(obj)) 1.1329 + break; 1.1330 + 1.1331 + /* An empty array or an array with no elements is already reversed. */ 1.1332 + if (len == 0 || obj->getDenseCapacity() == 0) { 1.1333 + args.rval().setObject(*obj); 1.1334 + return true; 1.1335 + } 1.1336 + 1.1337 + /* 1.1338 + * It's actually surprisingly complicated to reverse an array due to the 1.1339 + * orthogonality of array length and array capacity while handling 1.1340 + * leading and trailing holes correctly. Reversing seems less likely to 1.1341 + * be a common operation than other array mass-mutation methods, so for 1.1342 + * now just take a probably-small memory hit (in the absence of too many 1.1343 + * holes in the array at its start) and ensure that the capacity is 1.1344 + * sufficient to hold all the elements in the array if it were full. 1.1345 + */ 1.1346 + JSObject::EnsureDenseResult result = obj->ensureDenseElements(cx, len, 0); 1.1347 + if (result != JSObject::ED_OK) { 1.1348 + if (result == JSObject::ED_FAILED) 1.1349 + return false; 1.1350 + JS_ASSERT(result == JSObject::ED_SPARSE); 1.1351 + break; 1.1352 + } 1.1353 + 1.1354 + /* Fill out the array's initialized length to its proper length. */ 1.1355 + obj->ensureDenseInitializedLength(cx, len, 0); 1.1356 + 1.1357 + RootedValue origlo(cx), orighi(cx); 1.1358 + 1.1359 + uint32_t lo = 0, hi = len - 1; 1.1360 + for (; lo < hi; lo++, hi--) { 1.1361 + origlo = obj->getDenseElement(lo); 1.1362 + orighi = obj->getDenseElement(hi); 1.1363 + obj->setDenseElement(lo, orighi); 1.1364 + if (orighi.isMagic(JS_ELEMENTS_HOLE) && 1.1365 + !js_SuppressDeletedProperty(cx, obj, INT_TO_JSID(lo))) { 1.1366 + return false; 1.1367 + } 1.1368 + obj->setDenseElement(hi, origlo); 1.1369 + if (origlo.isMagic(JS_ELEMENTS_HOLE) && 1.1370 + !js_SuppressDeletedProperty(cx, obj, INT_TO_JSID(hi))) { 1.1371 + return false; 1.1372 + } 1.1373 + } 1.1374 + 1.1375 + /* 1.1376 + * Per ECMA-262, don't update the length of the array, even if the new 1.1377 + * array has trailing holes (and thus the original array began with 1.1378 + * holes). 1.1379 + */ 1.1380 + args.rval().setObject(*obj); 1.1381 + return true; 1.1382 + } while (false); 1.1383 + 1.1384 + RootedValue lowval(cx), hival(cx); 1.1385 + for (uint32_t i = 0, half = len / 2; i < half; i++) { 1.1386 + bool hole, hole2; 1.1387 + if (!CheckForInterrupt(cx) || 1.1388 + !GetElement(cx, obj, i, &hole, &lowval) || 1.1389 + !GetElement(cx, obj, len - i - 1, &hole2, &hival)) 1.1390 + { 1.1391 + return false; 1.1392 + } 1.1393 + 1.1394 + if (!hole && !hole2) { 1.1395 + if (!SetArrayElement(cx, obj, i, hival)) 1.1396 + return false; 1.1397 + if (!SetArrayElement(cx, obj, len - i - 1, lowval)) 1.1398 + return false; 1.1399 + } else if (hole && !hole2) { 1.1400 + if (!SetArrayElement(cx, obj, i, hival)) 1.1401 + return false; 1.1402 + if (!DeletePropertyOrThrow(cx, obj, len - i - 1)) 1.1403 + return false; 1.1404 + } else if (!hole && hole2) { 1.1405 + if (!DeletePropertyOrThrow(cx, obj, i)) 1.1406 + return false; 1.1407 + if (!SetArrayElement(cx, obj, len - i - 1, lowval)) 1.1408 + return false; 1.1409 + } else { 1.1410 + // No action required. 1.1411 + } 1.1412 + } 1.1413 + args.rval().setObject(*obj); 1.1414 + return true; 1.1415 +} 1.1416 + 1.1417 +namespace { 1.1418 + 1.1419 +inline bool 1.1420 +CompareStringValues(JSContext *cx, const Value &a, const Value &b, bool *lessOrEqualp) 1.1421 +{ 1.1422 + if (!CheckForInterrupt(cx)) 1.1423 + return false; 1.1424 + 1.1425 + JSString *astr = a.toString(); 1.1426 + JSString *bstr = b.toString(); 1.1427 + int32_t result; 1.1428 + if (!CompareStrings(cx, astr, bstr, &result)) 1.1429 + return false; 1.1430 + 1.1431 + *lessOrEqualp = (result <= 0); 1.1432 + return true; 1.1433 +} 1.1434 + 1.1435 +static const uint64_t powersOf10[] = { 1.1436 + 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 1000000000000ULL 1.1437 +}; 1.1438 + 1.1439 +static inline unsigned 1.1440 +NumDigitsBase10(uint32_t n) 1.1441 +{ 1.1442 + /* 1.1443 + * This is just floor_log10(n) + 1 1.1444 + * Algorithm taken from 1.1445 + * http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 1.1446 + */ 1.1447 + uint32_t log2 = CeilingLog2(n); 1.1448 + uint32_t t = log2 * 1233 >> 12; 1.1449 + return t - (n < powersOf10[t]) + 1; 1.1450 +} 1.1451 + 1.1452 +static inline bool 1.1453 +CompareLexicographicInt32(const Value &a, const Value &b, bool *lessOrEqualp) 1.1454 +{ 1.1455 + int32_t aint = a.toInt32(); 1.1456 + int32_t bint = b.toInt32(); 1.1457 + 1.1458 + /* 1.1459 + * If both numbers are equal ... trivial 1.1460 + * If only one of both is negative --> arithmetic comparison as char code 1.1461 + * of '-' is always less than any other digit 1.1462 + * If both numbers are negative convert them to positive and continue 1.1463 + * handling ... 1.1464 + */ 1.1465 + if (aint == bint) { 1.1466 + *lessOrEqualp = true; 1.1467 + } else if ((aint < 0) && (bint >= 0)) { 1.1468 + *lessOrEqualp = true; 1.1469 + } else if ((aint >= 0) && (bint < 0)) { 1.1470 + *lessOrEqualp = false; 1.1471 + } else { 1.1472 + uint32_t auint = Abs(aint); 1.1473 + uint32_t buint = Abs(bint); 1.1474 + 1.1475 + /* 1.1476 + * ... get number of digits of both integers. 1.1477 + * If they have the same number of digits --> arithmetic comparison. 1.1478 + * If digits_a > digits_b: a < b*10e(digits_a - digits_b). 1.1479 + * If digits_b > digits_a: a*10e(digits_b - digits_a) <= b. 1.1480 + */ 1.1481 + unsigned digitsa = NumDigitsBase10(auint); 1.1482 + unsigned digitsb = NumDigitsBase10(buint); 1.1483 + if (digitsa == digitsb) { 1.1484 + *lessOrEqualp = (auint <= buint); 1.1485 + } else if (digitsa > digitsb) { 1.1486 + JS_ASSERT((digitsa - digitsb) < ArrayLength(powersOf10)); 1.1487 + *lessOrEqualp = (uint64_t(auint) < uint64_t(buint) * powersOf10[digitsa - digitsb]); 1.1488 + } else { /* if (digitsb > digitsa) */ 1.1489 + JS_ASSERT((digitsb - digitsa) < ArrayLength(powersOf10)); 1.1490 + *lessOrEqualp = (uint64_t(auint) * powersOf10[digitsb - digitsa] <= uint64_t(buint)); 1.1491 + } 1.1492 + } 1.1493 + 1.1494 + return true; 1.1495 +} 1.1496 + 1.1497 +static inline bool 1.1498 +CompareSubStringValues(JSContext *cx, const jschar *s1, size_t l1, 1.1499 + const jschar *s2, size_t l2, bool *lessOrEqualp) 1.1500 +{ 1.1501 + if (!CheckForInterrupt(cx)) 1.1502 + return false; 1.1503 + 1.1504 + if (!s1 || !s2) 1.1505 + return false; 1.1506 + 1.1507 + int32_t result = CompareChars(s1, l1, s2, l2); 1.1508 + *lessOrEqualp = (result <= 0); 1.1509 + return true; 1.1510 +} 1.1511 + 1.1512 +struct SortComparatorStrings 1.1513 +{ 1.1514 + JSContext *const cx; 1.1515 + 1.1516 + SortComparatorStrings(JSContext *cx) 1.1517 + : cx(cx) {} 1.1518 + 1.1519 + bool operator()(const Value &a, const Value &b, bool *lessOrEqualp) { 1.1520 + return CompareStringValues(cx, a, b, lessOrEqualp); 1.1521 + } 1.1522 +}; 1.1523 + 1.1524 +struct SortComparatorLexicographicInt32 1.1525 +{ 1.1526 + bool operator()(const Value &a, const Value &b, bool *lessOrEqualp) { 1.1527 + return CompareLexicographicInt32(a, b, lessOrEqualp); 1.1528 + } 1.1529 +}; 1.1530 + 1.1531 +struct StringifiedElement 1.1532 +{ 1.1533 + size_t charsBegin; 1.1534 + size_t charsEnd; 1.1535 + size_t elementIndex; 1.1536 +}; 1.1537 + 1.1538 +struct SortComparatorStringifiedElements 1.1539 +{ 1.1540 + JSContext *const cx; 1.1541 + const StringBuffer &sb; 1.1542 + 1.1543 + SortComparatorStringifiedElements(JSContext *cx, const StringBuffer &sb) 1.1544 + : cx(cx), sb(sb) {} 1.1545 + 1.1546 + bool operator()(const StringifiedElement &a, const StringifiedElement &b, bool *lessOrEqualp) { 1.1547 + return CompareSubStringValues(cx, sb.begin() + a.charsBegin, a.charsEnd - a.charsBegin, 1.1548 + sb.begin() + b.charsBegin, b.charsEnd - b.charsBegin, 1.1549 + lessOrEqualp); 1.1550 + } 1.1551 +}; 1.1552 + 1.1553 +struct SortComparatorFunction 1.1554 +{ 1.1555 + JSContext *const cx; 1.1556 + const Value &fval; 1.1557 + FastInvokeGuard &fig; 1.1558 + 1.1559 + SortComparatorFunction(JSContext *cx, const Value &fval, FastInvokeGuard &fig) 1.1560 + : cx(cx), fval(fval), fig(fig) { } 1.1561 + 1.1562 + bool operator()(const Value &a, const Value &b, bool *lessOrEqualp); 1.1563 +}; 1.1564 + 1.1565 +bool 1.1566 +SortComparatorFunction::operator()(const Value &a, const Value &b, bool *lessOrEqualp) 1.1567 +{ 1.1568 + /* 1.1569 + * array_sort deals with holes and undefs on its own and they should not 1.1570 + * come here. 1.1571 + */ 1.1572 + JS_ASSERT(!a.isMagic() && !a.isUndefined()); 1.1573 + JS_ASSERT(!a.isMagic() && !b.isUndefined()); 1.1574 + 1.1575 + if (!CheckForInterrupt(cx)) 1.1576 + return false; 1.1577 + 1.1578 + InvokeArgs &args = fig.args(); 1.1579 + if (!args.init(2)) 1.1580 + return false; 1.1581 + 1.1582 + args.setCallee(fval); 1.1583 + args.setThis(UndefinedValue()); 1.1584 + args[0].set(a); 1.1585 + args[1].set(b); 1.1586 + 1.1587 + if (!fig.invoke(cx)) 1.1588 + return false; 1.1589 + 1.1590 + double cmp; 1.1591 + if (!ToNumber(cx, args.rval(), &cmp)) 1.1592 + return false; 1.1593 + 1.1594 + /* 1.1595 + * XXX eport some kind of error here if cmp is NaN? ECMA talks about 1.1596 + * 'consistent compare functions' that don't return NaN, but is silent 1.1597 + * about what the result should be. So we currently ignore it. 1.1598 + */ 1.1599 + *lessOrEqualp = (IsNaN(cmp) || cmp <= 0); 1.1600 + return true; 1.1601 +} 1.1602 + 1.1603 +struct NumericElement 1.1604 +{ 1.1605 + double dv; 1.1606 + size_t elementIndex; 1.1607 +}; 1.1608 + 1.1609 +bool 1.1610 +ComparatorNumericLeftMinusRight(const NumericElement &a, const NumericElement &b, 1.1611 + bool *lessOrEqualp) 1.1612 +{ 1.1613 + *lessOrEqualp = (a.dv <= b.dv); 1.1614 + return true; 1.1615 +} 1.1616 + 1.1617 +bool 1.1618 +ComparatorNumericRightMinusLeft(const NumericElement &a, const NumericElement &b, 1.1619 + bool *lessOrEqualp) 1.1620 +{ 1.1621 + *lessOrEqualp = (b.dv <= a.dv); 1.1622 + return true; 1.1623 +} 1.1624 + 1.1625 +typedef bool (*ComparatorNumeric)(const NumericElement &a, const NumericElement &b, 1.1626 + bool *lessOrEqualp); 1.1627 + 1.1628 +ComparatorNumeric SortComparatorNumerics[] = { 1.1629 + nullptr, 1.1630 + nullptr, 1.1631 + ComparatorNumericLeftMinusRight, 1.1632 + ComparatorNumericRightMinusLeft 1.1633 +}; 1.1634 + 1.1635 +bool 1.1636 +ComparatorInt32LeftMinusRight(const Value &a, const Value &b, bool *lessOrEqualp) 1.1637 +{ 1.1638 + *lessOrEqualp = (a.toInt32() <= b.toInt32()); 1.1639 + return true; 1.1640 +} 1.1641 + 1.1642 +bool 1.1643 +ComparatorInt32RightMinusLeft(const Value &a, const Value &b, bool *lessOrEqualp) 1.1644 +{ 1.1645 + *lessOrEqualp = (b.toInt32() <= a.toInt32()); 1.1646 + return true; 1.1647 +} 1.1648 + 1.1649 +typedef bool (*ComparatorInt32)(const Value &a, const Value &b, bool *lessOrEqualp); 1.1650 + 1.1651 +ComparatorInt32 SortComparatorInt32s[] = { 1.1652 + nullptr, 1.1653 + nullptr, 1.1654 + ComparatorInt32LeftMinusRight, 1.1655 + ComparatorInt32RightMinusLeft 1.1656 +}; 1.1657 + 1.1658 +// Note: Values for this enum must match up with SortComparatorNumerics 1.1659 +// and SortComparatorInt32s. 1.1660 +enum ComparatorMatchResult { 1.1661 + Match_Failure = 0, 1.1662 + Match_None, 1.1663 + Match_LeftMinusRight, 1.1664 + Match_RightMinusLeft 1.1665 +}; 1.1666 + 1.1667 +/* 1.1668 + * Specialize behavior for comparator functions with particular common bytecode 1.1669 + * patterns: namely, |return x - y| and |return y - x|. 1.1670 + */ 1.1671 +ComparatorMatchResult 1.1672 +MatchNumericComparator(JSContext *cx, const Value &v) 1.1673 +{ 1.1674 + if (!v.isObject()) 1.1675 + return Match_None; 1.1676 + 1.1677 + JSObject &obj = v.toObject(); 1.1678 + if (!obj.is<JSFunction>()) 1.1679 + return Match_None; 1.1680 + 1.1681 + JSFunction *fun = &obj.as<JSFunction>(); 1.1682 + if (!fun->isInterpreted()) 1.1683 + return Match_None; 1.1684 + 1.1685 + JSScript *script = fun->getOrCreateScript(cx); 1.1686 + if (!script) 1.1687 + return Match_Failure; 1.1688 + 1.1689 + jsbytecode *pc = script->code(); 1.1690 + 1.1691 + uint16_t arg0, arg1; 1.1692 + if (JSOp(*pc) != JSOP_GETARG) 1.1693 + return Match_None; 1.1694 + arg0 = GET_ARGNO(pc); 1.1695 + pc += JSOP_GETARG_LENGTH; 1.1696 + 1.1697 + if (JSOp(*pc) != JSOP_GETARG) 1.1698 + return Match_None; 1.1699 + arg1 = GET_ARGNO(pc); 1.1700 + pc += JSOP_GETARG_LENGTH; 1.1701 + 1.1702 + if (JSOp(*pc) != JSOP_SUB) 1.1703 + return Match_None; 1.1704 + pc += JSOP_SUB_LENGTH; 1.1705 + 1.1706 + if (JSOp(*pc) != JSOP_RETURN) 1.1707 + return Match_None; 1.1708 + 1.1709 + if (arg0 == 0 && arg1 == 1) 1.1710 + return Match_LeftMinusRight; 1.1711 + 1.1712 + if (arg0 == 1 && arg1 == 0) 1.1713 + return Match_RightMinusLeft; 1.1714 + 1.1715 + return Match_None; 1.1716 +} 1.1717 + 1.1718 +template<typename K, typename C> 1.1719 +static inline bool 1.1720 +MergeSortByKey(K keys, size_t len, K scratch, C comparator, AutoValueVector *vec) 1.1721 +{ 1.1722 + MOZ_ASSERT(vec->length() >= len); 1.1723 + 1.1724 + /* Sort keys. */ 1.1725 + if (!MergeSort(keys, len, scratch, comparator)) 1.1726 + return false; 1.1727 + 1.1728 + /* 1.1729 + * Reorder vec by keys in-place, going element by element. When an out-of- 1.1730 + * place element is encountered, move that element to its proper position, 1.1731 + * displacing whatever element was at *that* point to its proper position, 1.1732 + * and so on until an element must be moved to the current position. 1.1733 + * 1.1734 + * At each outer iteration all elements up to |i| are sorted. If 1.1735 + * necessary each inner iteration moves some number of unsorted elements 1.1736 + * (including |i|) directly to sorted position. Thus on completion |*vec| 1.1737 + * is sorted, and out-of-position elements have moved once. Complexity is 1.1738 + * Θ(len) + O(len) == O(2*len), with each element visited at most twice. 1.1739 + */ 1.1740 + for (size_t i = 0; i < len; i++) { 1.1741 + size_t j = keys[i].elementIndex; 1.1742 + if (i == j) 1.1743 + continue; // fixed point 1.1744 + 1.1745 + MOZ_ASSERT(j > i, "Everything less than |i| should be in the right place!"); 1.1746 + Value tv = (*vec)[j]; 1.1747 + do { 1.1748 + size_t k = keys[j].elementIndex; 1.1749 + keys[j].elementIndex = j; 1.1750 + (*vec)[j] = (*vec)[k]; 1.1751 + j = k; 1.1752 + } while (j != i); 1.1753 + 1.1754 + // We could assert the loop invariant that |i == keys[i].elementIndex| 1.1755 + // here if we synced |keys[i].elementIndex|. But doing so would render 1.1756 + // the assertion vacuous, so don't bother, even in debug builds. 1.1757 + (*vec)[i] = tv; 1.1758 + } 1.1759 + 1.1760 + return true; 1.1761 +} 1.1762 + 1.1763 +/* 1.1764 + * Sort Values as strings. 1.1765 + * 1.1766 + * To minimize #conversions, SortLexicographically() first converts all Values 1.1767 + * to strings at once, then sorts the elements by these cached strings. 1.1768 + */ 1.1769 +bool 1.1770 +SortLexicographically(JSContext *cx, AutoValueVector *vec, size_t len) 1.1771 +{ 1.1772 + JS_ASSERT(vec->length() >= len); 1.1773 + 1.1774 + StringBuffer sb(cx); 1.1775 + Vector<StringifiedElement, 0, TempAllocPolicy> strElements(cx); 1.1776 + 1.1777 + /* MergeSort uses the upper half as scratch space. */ 1.1778 + if (!strElements.reserve(2 * len)) 1.1779 + return false; 1.1780 + 1.1781 + /* Convert Values to strings. */ 1.1782 + size_t cursor = 0; 1.1783 + for (size_t i = 0; i < len; i++) { 1.1784 + if (!CheckForInterrupt(cx)) 1.1785 + return false; 1.1786 + 1.1787 + if (!ValueToStringBuffer(cx, (*vec)[i], sb)) 1.1788 + return false; 1.1789 + 1.1790 + StringifiedElement el = { cursor, sb.length(), i }; 1.1791 + strElements.infallibleAppend(el); 1.1792 + cursor = sb.length(); 1.1793 + } 1.1794 + 1.1795 + /* Resize strElements so we can perform MergeSort. */ 1.1796 + JS_ALWAYS_TRUE(strElements.resize(2 * len)); 1.1797 + 1.1798 + /* Sort Values in vec alphabetically. */ 1.1799 + return MergeSortByKey(strElements.begin(), len, strElements.begin() + len, 1.1800 + SortComparatorStringifiedElements(cx, sb), vec); 1.1801 +} 1.1802 + 1.1803 +/* 1.1804 + * Sort Values as numbers. 1.1805 + * 1.1806 + * To minimize #conversions, SortNumerically first converts all Values to 1.1807 + * numerics at once, then sorts the elements by these cached numerics. 1.1808 + */ 1.1809 +bool 1.1810 +SortNumerically(JSContext *cx, AutoValueVector *vec, size_t len, ComparatorMatchResult comp) 1.1811 +{ 1.1812 + JS_ASSERT(vec->length() >= len); 1.1813 + 1.1814 + Vector<NumericElement, 0, TempAllocPolicy> numElements(cx); 1.1815 + 1.1816 + /* MergeSort uses the upper half as scratch space. */ 1.1817 + if (!numElements.reserve(2 * len)) 1.1818 + return false; 1.1819 + 1.1820 + /* Convert Values to numerics. */ 1.1821 + for (size_t i = 0; i < len; i++) { 1.1822 + if (!CheckForInterrupt(cx)) 1.1823 + return false; 1.1824 + 1.1825 + double dv; 1.1826 + if (!ToNumber(cx, vec->handleAt(i), &dv)) 1.1827 + return false; 1.1828 + 1.1829 + NumericElement el = { dv, i }; 1.1830 + numElements.infallibleAppend(el); 1.1831 + } 1.1832 + 1.1833 + /* Resize strElements so we can perform MergeSort. */ 1.1834 + JS_ALWAYS_TRUE(numElements.resize(2 * len)); 1.1835 + 1.1836 + /* Sort Values in vec numerically. */ 1.1837 + return MergeSortByKey(numElements.begin(), len, numElements.begin() + len, 1.1838 + SortComparatorNumerics[comp], vec); 1.1839 +} 1.1840 + 1.1841 +} /* namespace anonymous */ 1.1842 + 1.1843 +bool 1.1844 +js::array_sort(JSContext *cx, unsigned argc, Value *vp) 1.1845 +{ 1.1846 + CallArgs args = CallArgsFromVp(argc, vp); 1.1847 + 1.1848 + RootedValue fvalRoot(cx); 1.1849 + Value &fval = fvalRoot.get(); 1.1850 + 1.1851 + if (args.hasDefined(0)) { 1.1852 + if (args[0].isPrimitive()) { 1.1853 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_SORT_ARG); 1.1854 + return false; 1.1855 + } 1.1856 + fval = args[0]; /* non-default compare function */ 1.1857 + } else { 1.1858 + fval.setNull(); 1.1859 + } 1.1860 + 1.1861 + RootedObject obj(cx, ToObject(cx, args.thisv())); 1.1862 + if (!obj) 1.1863 + return false; 1.1864 + 1.1865 + uint32_t len; 1.1866 + if (!GetLengthProperty(cx, obj, &len)) 1.1867 + return false; 1.1868 + if (len < 2) { 1.1869 + /* [] and [a] remain unchanged when sorted. */ 1.1870 + args.rval().setObject(*obj); 1.1871 + return true; 1.1872 + } 1.1873 + 1.1874 + /* 1.1875 + * We need a temporary array of 2 * len Value to hold the array elements 1.1876 + * and the scratch space for merge sort. Check that its size does not 1.1877 + * overflow size_t, which would allow for indexing beyond the end of the 1.1878 + * malloc'd vector. 1.1879 + */ 1.1880 +#if JS_BITS_PER_WORD == 32 1.1881 + if (size_t(len) > size_t(-1) / (2 * sizeof(Value))) { 1.1882 + js_ReportAllocationOverflow(cx); 1.1883 + return false; 1.1884 + } 1.1885 +#endif 1.1886 + 1.1887 + /* 1.1888 + * Initialize vec as a root. We will clear elements of vec one by 1.1889 + * one while increasing the rooted amount of vec when we know that the 1.1890 + * property at the corresponding index exists and its value must be rooted. 1.1891 + * 1.1892 + * In this way when sorting a huge mostly sparse array we will not 1.1893 + * access the tail of vec corresponding to properties that do not 1.1894 + * exist, allowing OS to avoiding committing RAM. See bug 330812. 1.1895 + */ 1.1896 + size_t n, undefs; 1.1897 + { 1.1898 + AutoValueVector vec(cx); 1.1899 + if (!vec.reserve(2 * size_t(len))) 1.1900 + return false; 1.1901 + 1.1902 + /* 1.1903 + * By ECMA 262, 15.4.4.11, a property that does not exist (which we 1.1904 + * call a "hole") is always greater than an existing property with 1.1905 + * value undefined and that is always greater than any other property. 1.1906 + * Thus to sort holes and undefs we simply count them, sort the rest 1.1907 + * of elements, append undefs after them and then make holes after 1.1908 + * undefs. 1.1909 + */ 1.1910 + undefs = 0; 1.1911 + bool allStrings = true; 1.1912 + bool allInts = true; 1.1913 + RootedValue v(cx); 1.1914 + for (uint32_t i = 0; i < len; i++) { 1.1915 + if (!CheckForInterrupt(cx)) 1.1916 + return false; 1.1917 + 1.1918 + /* Clear vec[newlen] before including it in the rooted set. */ 1.1919 + bool hole; 1.1920 + if (!GetElement(cx, obj, i, &hole, &v)) 1.1921 + return false; 1.1922 + if (hole) 1.1923 + continue; 1.1924 + if (v.isUndefined()) { 1.1925 + ++undefs; 1.1926 + continue; 1.1927 + } 1.1928 + vec.infallibleAppend(v); 1.1929 + allStrings = allStrings && v.isString(); 1.1930 + allInts = allInts && v.isInt32(); 1.1931 + } 1.1932 + 1.1933 + 1.1934 + /* 1.1935 + * If the array only contains holes, we're done. But if it contains 1.1936 + * undefs, those must be sorted to the front of the array. 1.1937 + */ 1.1938 + n = vec.length(); 1.1939 + if (n == 0 && undefs == 0) { 1.1940 + args.rval().setObject(*obj); 1.1941 + return true; 1.1942 + } 1.1943 + 1.1944 + /* Here len == n + undefs + number_of_holes. */ 1.1945 + if (fval.isNull()) { 1.1946 + /* 1.1947 + * Sort using the default comparator converting all elements to 1.1948 + * strings. 1.1949 + */ 1.1950 + if (allStrings) { 1.1951 + JS_ALWAYS_TRUE(vec.resize(n * 2)); 1.1952 + if (!MergeSort(vec.begin(), n, vec.begin() + n, SortComparatorStrings(cx))) 1.1953 + return false; 1.1954 + } else if (allInts) { 1.1955 + JS_ALWAYS_TRUE(vec.resize(n * 2)); 1.1956 + if (!MergeSort(vec.begin(), n, vec.begin() + n, 1.1957 + SortComparatorLexicographicInt32())) { 1.1958 + return false; 1.1959 + } 1.1960 + } else { 1.1961 + if (!SortLexicographically(cx, &vec, n)) 1.1962 + return false; 1.1963 + } 1.1964 + } else { 1.1965 + ComparatorMatchResult comp = MatchNumericComparator(cx, fval); 1.1966 + if (comp == Match_Failure) 1.1967 + return false; 1.1968 + 1.1969 + if (comp != Match_None) { 1.1970 + if (allInts) { 1.1971 + JS_ALWAYS_TRUE(vec.resize(n * 2)); 1.1972 + if (!MergeSort(vec.begin(), n, vec.begin() + n, SortComparatorInt32s[comp])) 1.1973 + return false; 1.1974 + } else { 1.1975 + if (!SortNumerically(cx, &vec, n, comp)) 1.1976 + return false; 1.1977 + } 1.1978 + } else { 1.1979 + FastInvokeGuard fig(cx, fval); 1.1980 + MOZ_ASSERT(!InParallelSection(), 1.1981 + "Array.sort() can't currently be used from parallel code"); 1.1982 + JS_ALWAYS_TRUE(vec.resize(n * 2)); 1.1983 + if (!MergeSort(vec.begin(), n, vec.begin() + n, 1.1984 + SortComparatorFunction(cx, fval, fig))) 1.1985 + { 1.1986 + return false; 1.1987 + } 1.1988 + } 1.1989 + } 1.1990 + 1.1991 + if (!InitArrayElements(cx, obj, 0, uint32_t(n), vec.begin(), DontUpdateTypes)) 1.1992 + return false; 1.1993 + } 1.1994 + 1.1995 + /* Set undefs that sorted after the rest of elements. */ 1.1996 + while (undefs != 0) { 1.1997 + --undefs; 1.1998 + if (!CheckForInterrupt(cx) || !SetArrayElement(cx, obj, n++, UndefinedHandleValue)) 1.1999 + return false; 1.2000 + } 1.2001 + 1.2002 + /* Re-create any holes that sorted to the end of the array. */ 1.2003 + while (len > n) { 1.2004 + if (!CheckForInterrupt(cx) || !DeletePropertyOrThrow(cx, obj, --len)) 1.2005 + return false; 1.2006 + } 1.2007 + args.rval().setObject(*obj); 1.2008 + return true; 1.2009 +} 1.2010 + 1.2011 +bool 1.2012 +js::NewbornArrayPush(JSContext *cx, HandleObject obj, const Value &v) 1.2013 +{ 1.2014 + Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>()); 1.2015 + 1.2016 + JS_ASSERT(!v.isMagic()); 1.2017 + JS_ASSERT(arr->lengthIsWritable()); 1.2018 + 1.2019 + uint32_t length = arr->length(); 1.2020 + JS_ASSERT(length <= arr->getDenseCapacity()); 1.2021 + 1.2022 + if (!arr->ensureElements(cx, length + 1)) 1.2023 + return false; 1.2024 + 1.2025 + arr->setDenseInitializedLength(length + 1); 1.2026 + arr->setLengthInt32(length + 1); 1.2027 + arr->initDenseElementWithType(cx, length, v); 1.2028 + return true; 1.2029 +} 1.2030 + 1.2031 +/* ES5 15.4.4.7 */ 1.2032 +bool 1.2033 +js::array_push(JSContext *cx, unsigned argc, Value *vp) 1.2034 +{ 1.2035 + CallArgs args = CallArgsFromVp(argc, vp); 1.2036 + 1.2037 + /* Step 1. */ 1.2038 + RootedObject obj(cx, ToObject(cx, args.thisv())); 1.2039 + if (!obj) 1.2040 + return false; 1.2041 + 1.2042 + /* Steps 2-3. */ 1.2043 + uint32_t length; 1.2044 + if (!GetLengthProperty(cx, obj, &length)) 1.2045 + return false; 1.2046 + 1.2047 + /* Fast path for native objects with dense elements. */ 1.2048 + do { 1.2049 + if (!obj->isNative() || obj->is<TypedArrayObject>()) 1.2050 + break; 1.2051 + 1.2052 + if (obj->is<ArrayObject>() && !obj->as<ArrayObject>().lengthIsWritable()) 1.2053 + break; 1.2054 + 1.2055 + if (ObjectMayHaveExtraIndexedProperties(obj)) 1.2056 + break; 1.2057 + 1.2058 + uint32_t argCount = args.length(); 1.2059 + JSObject::EnsureDenseResult result = obj->ensureDenseElements(cx, length, argCount); 1.2060 + if (result == JSObject::ED_FAILED) 1.2061 + return false; 1.2062 + 1.2063 + if (result == JSObject::ED_OK) { 1.2064 + for (uint32_t i = 0, index = length; i < argCount; index++, i++) 1.2065 + obj->setDenseElementWithType(cx, index, args[i]); 1.2066 + uint32_t newlength = length + argCount; 1.2067 + args.rval().setNumber(newlength); 1.2068 + if (obj->is<ArrayObject>()) { 1.2069 + obj->as<ArrayObject>().setLengthInt32(newlength); 1.2070 + return true; 1.2071 + } 1.2072 + return SetLengthProperty(cx, obj, newlength); 1.2073 + } 1.2074 + 1.2075 + MOZ_ASSERT(result == JSObject::ED_SPARSE); 1.2076 + } while (false); 1.2077 + 1.2078 + /* Steps 4-5. */ 1.2079 + if (!InitArrayElements(cx, obj, length, args.length(), args.array(), UpdateTypes)) 1.2080 + return false; 1.2081 + 1.2082 + /* Steps 6-7. */ 1.2083 + double newlength = length + double(args.length()); 1.2084 + args.rval().setNumber(newlength); 1.2085 + return SetLengthProperty(cx, obj, newlength); 1.2086 +} 1.2087 + 1.2088 +/* ES6 20130308 draft 15.4.4.6. */ 1.2089 +bool 1.2090 +js::array_pop(JSContext *cx, unsigned argc, Value *vp) 1.2091 +{ 1.2092 + CallArgs args = CallArgsFromVp(argc, vp); 1.2093 + 1.2094 + /* Step 1. */ 1.2095 + RootedObject obj(cx, ToObject(cx, args.thisv())); 1.2096 + if (!obj) 1.2097 + return false; 1.2098 + 1.2099 + /* Steps 2-3. */ 1.2100 + uint32_t index; 1.2101 + if (!GetLengthProperty(cx, obj, &index)) 1.2102 + return false; 1.2103 + 1.2104 + /* Steps 4-5. */ 1.2105 + if (index == 0) { 1.2106 + /* Step 4b. */ 1.2107 + args.rval().setUndefined(); 1.2108 + } else { 1.2109 + /* Step 5a. */ 1.2110 + index--; 1.2111 + 1.2112 + /* Step 5b, 5e. */ 1.2113 + bool hole; 1.2114 + if (!GetElement(cx, obj, index, &hole, args.rval())) 1.2115 + return false; 1.2116 + 1.2117 + /* Step 5c. */ 1.2118 + if (!hole && !DeletePropertyOrThrow(cx, obj, index)) 1.2119 + return false; 1.2120 + } 1.2121 + 1.2122 + // If this was an array, then there are no elements above the one we just 1.2123 + // deleted (if we deleted an element). Thus we can shrink the dense 1.2124 + // initialized length accordingly. (This is fine even if the array length 1.2125 + // is non-writable: length-changing occurs after element-deletion effects.) 1.2126 + // Don't do anything if this isn't an array, as any deletion above has no 1.2127 + // effect on any elements after the "last" one indicated by the "length" 1.2128 + // property. 1.2129 + if (obj->is<ArrayObject>() && obj->getDenseInitializedLength() > index) 1.2130 + obj->setDenseInitializedLength(index); 1.2131 + 1.2132 + /* Steps 4a, 5d. */ 1.2133 + return SetLengthProperty(cx, obj, index); 1.2134 +} 1.2135 + 1.2136 +void 1.2137 +js::ArrayShiftMoveElements(JSObject *obj) 1.2138 +{ 1.2139 + JS_ASSERT(obj->is<ArrayObject>()); 1.2140 + JS_ASSERT(obj->as<ArrayObject>().lengthIsWritable()); 1.2141 + 1.2142 + /* 1.2143 + * At this point the length and initialized length have already been 1.2144 + * decremented and the result fetched, so just shift the array elements 1.2145 + * themselves. 1.2146 + */ 1.2147 + uint32_t initlen = obj->getDenseInitializedLength(); 1.2148 + obj->moveDenseElementsNoPreBarrier(0, 1, initlen); 1.2149 +} 1.2150 + 1.2151 +/* ES5 15.4.4.9 */ 1.2152 +bool 1.2153 +js::array_shift(JSContext *cx, unsigned argc, Value *vp) 1.2154 +{ 1.2155 + CallArgs args = CallArgsFromVp(argc, vp); 1.2156 + 1.2157 + /* Step 1. */ 1.2158 + RootedObject obj(cx, ToObject(cx, args.thisv())); 1.2159 + if (!obj) 1.2160 + return false; 1.2161 + 1.2162 + /* Steps 2-3. */ 1.2163 + uint32_t len; 1.2164 + if (!GetLengthProperty(cx, obj, &len)) 1.2165 + return false; 1.2166 + 1.2167 + /* Step 4. */ 1.2168 + if (len == 0) { 1.2169 + /* Step 4a. */ 1.2170 + if (!SetLengthProperty(cx, obj, 0)) 1.2171 + return false; 1.2172 + 1.2173 + /* Step 4b. */ 1.2174 + args.rval().setUndefined(); 1.2175 + return true; 1.2176 + } 1.2177 + 1.2178 + uint32_t newlen = len - 1; 1.2179 + 1.2180 + /* Fast paths. */ 1.2181 + if (obj->is<ArrayObject>() && 1.2182 + obj->getDenseInitializedLength() > 0 && 1.2183 + newlen < obj->getDenseCapacity() && 1.2184 + !ObjectMayHaveExtraIndexedProperties(obj)) 1.2185 + { 1.2186 + args.rval().set(obj->getDenseElement(0)); 1.2187 + if (args.rval().isMagic(JS_ELEMENTS_HOLE)) 1.2188 + args.rval().setUndefined(); 1.2189 + 1.2190 + obj->moveDenseElements(0, 1, obj->getDenseInitializedLength() - 1); 1.2191 + obj->setDenseInitializedLength(obj->getDenseInitializedLength() - 1); 1.2192 + 1.2193 + if (!SetLengthProperty(cx, obj, newlen)) 1.2194 + return false; 1.2195 + 1.2196 + return js_SuppressDeletedProperty(cx, obj, INT_TO_JSID(newlen)); 1.2197 + } 1.2198 + 1.2199 + /* Steps 5, 10. */ 1.2200 + bool hole; 1.2201 + if (!GetElement(cx, obj, uint32_t(0), &hole, args.rval())) 1.2202 + return false; 1.2203 + 1.2204 + /* Steps 6-7. */ 1.2205 + RootedValue value(cx); 1.2206 + for (uint32_t i = 0; i < newlen; i++) { 1.2207 + if (!CheckForInterrupt(cx)) 1.2208 + return false; 1.2209 + if (!GetElement(cx, obj, i + 1, &hole, &value)) 1.2210 + return false; 1.2211 + if (hole) { 1.2212 + if (!DeletePropertyOrThrow(cx, obj, i)) 1.2213 + return false; 1.2214 + } else { 1.2215 + if (!SetArrayElement(cx, obj, i, value)) 1.2216 + return false; 1.2217 + } 1.2218 + } 1.2219 + 1.2220 + /* Step 8. */ 1.2221 + if (!DeletePropertyOrThrow(cx, obj, newlen)) 1.2222 + return false; 1.2223 + 1.2224 + /* Step 9. */ 1.2225 + return SetLengthProperty(cx, obj, newlen); 1.2226 +} 1.2227 + 1.2228 +static bool 1.2229 +array_unshift(JSContext *cx, unsigned argc, Value *vp) 1.2230 +{ 1.2231 + CallArgs args = CallArgsFromVp(argc, vp); 1.2232 + RootedObject obj(cx, ToObject(cx, args.thisv())); 1.2233 + if (!obj) 1.2234 + return false; 1.2235 + 1.2236 + uint32_t length; 1.2237 + if (!GetLengthProperty(cx, obj, &length)) 1.2238 + return false; 1.2239 + 1.2240 + double newlen = length; 1.2241 + if (args.length() > 0) { 1.2242 + /* Slide up the array to make room for all args at the bottom. */ 1.2243 + if (length > 0) { 1.2244 + bool optimized = false; 1.2245 + do { 1.2246 + if (!obj->is<ArrayObject>()) 1.2247 + break; 1.2248 + if (ObjectMayHaveExtraIndexedProperties(obj)) 1.2249 + break; 1.2250 + if (!obj->as<ArrayObject>().lengthIsWritable()) 1.2251 + break; 1.2252 + JSObject::EnsureDenseResult result = obj->ensureDenseElements(cx, length, args.length()); 1.2253 + if (result != JSObject::ED_OK) { 1.2254 + if (result == JSObject::ED_FAILED) 1.2255 + return false; 1.2256 + JS_ASSERT(result == JSObject::ED_SPARSE); 1.2257 + break; 1.2258 + } 1.2259 + obj->moveDenseElements(args.length(), 0, length); 1.2260 + for (uint32_t i = 0; i < args.length(); i++) 1.2261 + obj->setDenseElement(i, MagicValue(JS_ELEMENTS_HOLE)); 1.2262 + optimized = true; 1.2263 + } while (false); 1.2264 + 1.2265 + if (!optimized) { 1.2266 + double last = length; 1.2267 + double upperIndex = last + args.length(); 1.2268 + RootedValue value(cx); 1.2269 + do { 1.2270 + --last, --upperIndex; 1.2271 + bool hole; 1.2272 + if (!CheckForInterrupt(cx)) 1.2273 + return false; 1.2274 + if (!GetElement(cx, obj, last, &hole, &value)) 1.2275 + return false; 1.2276 + if (hole) { 1.2277 + if (!DeletePropertyOrThrow(cx, obj, upperIndex)) 1.2278 + return false; 1.2279 + } else { 1.2280 + if (!SetArrayElement(cx, obj, upperIndex, value)) 1.2281 + return false; 1.2282 + } 1.2283 + } while (last != 0); 1.2284 + } 1.2285 + } 1.2286 + 1.2287 + /* Copy from args to the bottom of the array. */ 1.2288 + if (!InitArrayElements(cx, obj, 0, args.length(), args.array(), UpdateTypes)) 1.2289 + return false; 1.2290 + 1.2291 + newlen += args.length(); 1.2292 + } 1.2293 + if (!SetLengthProperty(cx, obj, newlen)) 1.2294 + return false; 1.2295 + 1.2296 + /* Follow Perl by returning the new array length. */ 1.2297 + args.rval().setNumber(newlen); 1.2298 + return true; 1.2299 +} 1.2300 + 1.2301 +static inline void 1.2302 +TryReuseArrayType(JSObject *obj, ArrayObject *narr) 1.2303 +{ 1.2304 + /* 1.2305 + * Try to change the type of a newly created array narr to the same type 1.2306 + * as obj. This can only be performed if the original object is an array 1.2307 + * and has the same prototype. 1.2308 + */ 1.2309 + JS_ASSERT(narr->getProto()->hasNewType(&ArrayObject::class_, narr->type())); 1.2310 + 1.2311 + if (obj->is<ArrayObject>() && !obj->hasSingletonType() && obj->getProto() == narr->getProto()) 1.2312 + narr->setType(obj->type()); 1.2313 +} 1.2314 + 1.2315 +/* 1.2316 + * Returns true if this is a dense array whose |count| properties starting from 1.2317 + * |startingIndex| may be accessed (get, set, delete) directly through its 1.2318 + * contiguous vector of elements without fear of getters, setters, etc. along 1.2319 + * the prototype chain, or of enumerators requiring notification of 1.2320 + * modifications. 1.2321 + */ 1.2322 +static inline bool 1.2323 +CanOptimizeForDenseStorage(HandleObject arr, uint32_t startingIndex, uint32_t count, JSContext *cx) 1.2324 +{ 1.2325 + /* If the desired properties overflow dense storage, we can't optimize. */ 1.2326 + if (UINT32_MAX - startingIndex < count) 1.2327 + return false; 1.2328 + 1.2329 + /* There's no optimizing possible if it's not an array. */ 1.2330 + if (!arr->is<ArrayObject>()) 1.2331 + return false; 1.2332 + 1.2333 + /* 1.2334 + * Don't optimize if the array might be in the midst of iteration. We 1.2335 + * rely on this to be able to safely move dense array elements around with 1.2336 + * just a memmove (see JSObject::moveDenseArrayElements), without worrying 1.2337 + * about updating any in-progress enumerators for properties implicitly 1.2338 + * deleted if a hole is moved from one location to another location not yet 1.2339 + * visited. See bug 690622. 1.2340 + * 1.2341 + * Another potential wrinkle: what if the enumeration is happening on an 1.2342 + * object which merely has |arr| on its prototype chain? It turns out this 1.2343 + * case can't happen, because any dense array used as the prototype of 1.2344 + * another object is first slowified, for type inference's sake. 1.2345 + */ 1.2346 + types::TypeObject *arrType = arr->getType(cx); 1.2347 + if (MOZ_UNLIKELY(!arrType || arrType->hasAllFlags(OBJECT_FLAG_ITERATED))) 1.2348 + return false; 1.2349 + 1.2350 + /* 1.2351 + * Now watch out for getters and setters along the prototype chain or in 1.2352 + * other indexed properties on the object. (Note that non-writable length 1.2353 + * is subsumed by the initializedLength comparison.) 1.2354 + */ 1.2355 + return !ObjectMayHaveExtraIndexedProperties(arr) && 1.2356 + startingIndex + count <= arr->getDenseInitializedLength(); 1.2357 +} 1.2358 + 1.2359 +/* ES5 15.4.4.12. */ 1.2360 +bool 1.2361 +js::array_splice(JSContext *cx, unsigned argc, Value *vp) 1.2362 +{ 1.2363 + return array_splice_impl(cx, argc, vp, true); 1.2364 +} 1.2365 + 1.2366 +bool 1.2367 +js::array_splice_impl(JSContext *cx, unsigned argc, Value *vp, bool returnValueIsUsed) 1.2368 +{ 1.2369 + CallArgs args = CallArgsFromVp(argc, vp); 1.2370 + 1.2371 + /* Step 1. */ 1.2372 + RootedObject obj(cx, ToObject(cx, args.thisv())); 1.2373 + if (!obj) 1.2374 + return false; 1.2375 + 1.2376 + /* Steps 3-4. */ 1.2377 + uint32_t len; 1.2378 + if (!GetLengthProperty(cx, obj, &len)) 1.2379 + return false; 1.2380 + 1.2381 + /* Step 5. */ 1.2382 + double relativeStart; 1.2383 + if (!ToInteger(cx, args.get(0), &relativeStart)) 1.2384 + return false; 1.2385 + 1.2386 + /* Step 6. */ 1.2387 + uint32_t actualStart; 1.2388 + if (relativeStart < 0) 1.2389 + actualStart = Max(len + relativeStart, 0.0); 1.2390 + else 1.2391 + actualStart = Min(relativeStart, double(len)); 1.2392 + 1.2393 + /* Step 7. */ 1.2394 + uint32_t actualDeleteCount; 1.2395 + if (args.length() != 1) { 1.2396 + double deleteCountDouble; 1.2397 + RootedValue cnt(cx, args.length() >= 2 ? args[1] : Int32Value(0)); 1.2398 + if (!ToInteger(cx, cnt, &deleteCountDouble)) 1.2399 + return false; 1.2400 + actualDeleteCount = Min(Max(deleteCountDouble, 0.0), double(len - actualStart)); 1.2401 + } else { 1.2402 + /* 1.2403 + * Non-standard: if start was specified but deleteCount was omitted, 1.2404 + * delete to the end of the array. See bug 668024 for discussion. 1.2405 + */ 1.2406 + actualDeleteCount = len - actualStart; 1.2407 + } 1.2408 + 1.2409 + JS_ASSERT(len - actualStart >= actualDeleteCount); 1.2410 + 1.2411 + /* Steps 2, 8-9. */ 1.2412 + Rooted<ArrayObject*> arr(cx); 1.2413 + if (CanOptimizeForDenseStorage(obj, actualStart, actualDeleteCount, cx)) { 1.2414 + if (returnValueIsUsed) { 1.2415 + arr = NewDenseCopiedArray(cx, actualDeleteCount, obj, actualStart); 1.2416 + if (!arr) 1.2417 + return false; 1.2418 + TryReuseArrayType(obj, arr); 1.2419 + } 1.2420 + } else { 1.2421 + arr = NewDenseAllocatedArray(cx, actualDeleteCount); 1.2422 + if (!arr) 1.2423 + return false; 1.2424 + TryReuseArrayType(obj, arr); 1.2425 + 1.2426 + RootedValue fromValue(cx); 1.2427 + for (uint32_t k = 0; k < actualDeleteCount; k++) { 1.2428 + bool hole; 1.2429 + if (!CheckForInterrupt(cx) || 1.2430 + !GetElement(cx, obj, actualStart + k, &hole, &fromValue) || 1.2431 + (!hole && !JSObject::defineElement(cx, arr, k, fromValue))) 1.2432 + { 1.2433 + return false; 1.2434 + } 1.2435 + } 1.2436 + } 1.2437 + 1.2438 + /* Step 11. */ 1.2439 + uint32_t itemCount = (args.length() >= 2) ? (args.length() - 2) : 0; 1.2440 + 1.2441 + if (itemCount < actualDeleteCount) { 1.2442 + /* Step 12: the array is being shrunk. */ 1.2443 + uint32_t sourceIndex = actualStart + actualDeleteCount; 1.2444 + uint32_t targetIndex = actualStart + itemCount; 1.2445 + uint32_t finalLength = len - actualDeleteCount + itemCount; 1.2446 + 1.2447 + if (CanOptimizeForDenseStorage(obj, 0, len, cx)) { 1.2448 + /* Steps 12(a)-(b). */ 1.2449 + obj->moveDenseElements(targetIndex, sourceIndex, len - sourceIndex); 1.2450 + 1.2451 + /* 1.2452 + * Update the initialized length. Do so before shrinking so that we 1.2453 + * can apply the write barrier to the old slots. 1.2454 + */ 1.2455 + obj->setDenseInitializedLength(finalLength); 1.2456 + 1.2457 + /* Steps 12(c)-(d). */ 1.2458 + obj->shrinkElements(cx, finalLength); 1.2459 + } else { 1.2460 + /* 1.2461 + * This is all very slow if the length is very large. We don't yet 1.2462 + * have the ability to iterate in sorted order, so we just do the 1.2463 + * pessimistic thing and let CheckForInterrupt handle the 1.2464 + * fallout. 1.2465 + */ 1.2466 + 1.2467 + /* Steps 12(a)-(b). */ 1.2468 + RootedValue fromValue(cx); 1.2469 + for (uint32_t from = sourceIndex, to = targetIndex; from < len; from++, to++) { 1.2470 + if (!CheckForInterrupt(cx)) 1.2471 + return false; 1.2472 + 1.2473 + bool hole; 1.2474 + if (!GetElement(cx, obj, from, &hole, &fromValue)) 1.2475 + return false; 1.2476 + if (hole) { 1.2477 + if (!DeletePropertyOrThrow(cx, obj, to)) 1.2478 + return false; 1.2479 + } else { 1.2480 + if (!SetArrayElement(cx, obj, to, fromValue)) 1.2481 + return false; 1.2482 + } 1.2483 + } 1.2484 + 1.2485 + /* Steps 12(c)-(d). */ 1.2486 + for (uint32_t k = len; k > finalLength; k--) { 1.2487 + if (!DeletePropertyOrThrow(cx, obj, k - 1)) 1.2488 + return false; 1.2489 + } 1.2490 + } 1.2491 + } else if (itemCount > actualDeleteCount) { 1.2492 + /* Step 13. */ 1.2493 + 1.2494 + /* 1.2495 + * Optimize only if the array is already dense and we can extend it to 1.2496 + * its new length. It would be wrong to extend the elements here for a 1.2497 + * number of reasons. 1.2498 + * 1.2499 + * First, this could cause us to fall into the fast-path below. This 1.2500 + * would cause elements to be moved into places past the non-writable 1.2501 + * length. And when the dense initialized length is updated, that'll 1.2502 + * cause the |in| operator to think that those elements actually exist, 1.2503 + * even though, properly, setting them must fail. 1.2504 + * 1.2505 + * Second, extending the elements here will trigger assertions inside 1.2506 + * ensureDenseElements that the elements aren't being extended past the 1.2507 + * length of a non-writable array. This is because extending elements 1.2508 + * will extend capacity -- which might extend them past a non-writable 1.2509 + * length, violating the |capacity <= length| invariant for such 1.2510 + * arrays. And that would make the various JITted fast-path method 1.2511 + * implementations of [].push, [].unshift, and so on wrong. 1.2512 + * 1.2513 + * If the array length is non-writable, this method *will* throw. For 1.2514 + * simplicity, have the slow-path code do it. (Also note that the slow 1.2515 + * path may validly *not* throw -- if all the elements being moved are 1.2516 + * holes.) 1.2517 + */ 1.2518 + if (obj->is<ArrayObject>()) { 1.2519 + Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>()); 1.2520 + if (arr->lengthIsWritable()) { 1.2521 + JSObject::EnsureDenseResult res = 1.2522 + arr->ensureDenseElements(cx, arr->length(), itemCount - actualDeleteCount); 1.2523 + if (res == JSObject::ED_FAILED) 1.2524 + return false; 1.2525 + } 1.2526 + } 1.2527 + 1.2528 + if (CanOptimizeForDenseStorage(obj, len, itemCount - actualDeleteCount, cx)) { 1.2529 + obj->moveDenseElements(actualStart + itemCount, 1.2530 + actualStart + actualDeleteCount, 1.2531 + len - (actualStart + actualDeleteCount)); 1.2532 + obj->setDenseInitializedLength(len + itemCount - actualDeleteCount); 1.2533 + } else { 1.2534 + RootedValue fromValue(cx); 1.2535 + for (double k = len - actualDeleteCount; k > actualStart; k--) { 1.2536 + if (!CheckForInterrupt(cx)) 1.2537 + return false; 1.2538 + 1.2539 + double from = k + actualDeleteCount - 1; 1.2540 + double to = k + itemCount - 1; 1.2541 + 1.2542 + bool hole; 1.2543 + if (!GetElement(cx, obj, from, &hole, &fromValue)) 1.2544 + return false; 1.2545 + 1.2546 + if (hole) { 1.2547 + if (!DeletePropertyOrThrow(cx, obj, to)) 1.2548 + return false; 1.2549 + } else { 1.2550 + if (!SetArrayElement(cx, obj, to, fromValue)) 1.2551 + return false; 1.2552 + } 1.2553 + } 1.2554 + } 1.2555 + } 1.2556 + 1.2557 + /* Step 10. */ 1.2558 + Value *items = args.array() + 2; 1.2559 + 1.2560 + /* Steps 14-15. */ 1.2561 + for (uint32_t k = actualStart, i = 0; i < itemCount; i++, k++) { 1.2562 + if (!SetArrayElement(cx, obj, k, HandleValue::fromMarkedLocation(&items[i]))) 1.2563 + return false; 1.2564 + } 1.2565 + 1.2566 + /* Step 16. */ 1.2567 + double finalLength = double(len) - actualDeleteCount + itemCount; 1.2568 + if (!SetLengthProperty(cx, obj, finalLength)) 1.2569 + return false; 1.2570 + 1.2571 + /* Step 17. */ 1.2572 + if (returnValueIsUsed) 1.2573 + args.rval().setObject(*arr); 1.2574 + 1.2575 + return true; 1.2576 +} 1.2577 + 1.2578 +#ifdef JS_ION 1.2579 +bool 1.2580 +js::array_concat_dense(JSContext *cx, Handle<ArrayObject*> arr1, Handle<ArrayObject*> arr2, 1.2581 + Handle<ArrayObject*> result) 1.2582 +{ 1.2583 + uint32_t initlen1 = arr1->getDenseInitializedLength(); 1.2584 + JS_ASSERT(initlen1 == arr1->length()); 1.2585 + 1.2586 + uint32_t initlen2 = arr2->getDenseInitializedLength(); 1.2587 + JS_ASSERT(initlen2 == arr2->length()); 1.2588 + 1.2589 + /* No overflow here due to nelements limit. */ 1.2590 + uint32_t len = initlen1 + initlen2; 1.2591 + 1.2592 + if (!result->ensureElements(cx, len)) 1.2593 + return false; 1.2594 + 1.2595 + JS_ASSERT(!result->getDenseInitializedLength()); 1.2596 + result->setDenseInitializedLength(len); 1.2597 + 1.2598 + result->initDenseElements(0, arr1->getDenseElements(), initlen1); 1.2599 + result->initDenseElements(initlen1, arr2->getDenseElements(), initlen2); 1.2600 + result->setLengthInt32(len); 1.2601 + return true; 1.2602 +} 1.2603 +#endif /* JS_ION */ 1.2604 + 1.2605 +/* 1.2606 + * Python-esque sequence operations. 1.2607 + */ 1.2608 +bool 1.2609 +js::array_concat(JSContext *cx, unsigned argc, Value *vp) 1.2610 +{ 1.2611 + CallArgs args = CallArgsFromVp(argc, vp); 1.2612 + 1.2613 + /* Treat our |this| object as the first argument; see ECMA 15.4.4.4. */ 1.2614 + Value *p = args.array() - 1; 1.2615 + 1.2616 + /* Create a new Array object and root it using *vp. */ 1.2617 + RootedObject aobj(cx, ToObject(cx, args.thisv())); 1.2618 + if (!aobj) 1.2619 + return false; 1.2620 + 1.2621 + Rooted<ArrayObject*> narr(cx); 1.2622 + uint32_t length; 1.2623 + if (aobj->is<ArrayObject>() && !aobj->isIndexed()) { 1.2624 + length = aobj->as<ArrayObject>().length(); 1.2625 + uint32_t initlen = aobj->getDenseInitializedLength(); 1.2626 + narr = NewDenseCopiedArray(cx, initlen, aobj, 0); 1.2627 + if (!narr) 1.2628 + return false; 1.2629 + TryReuseArrayType(aobj, narr); 1.2630 + narr->setLength(cx, length); 1.2631 + args.rval().setObject(*narr); 1.2632 + if (argc == 0) 1.2633 + return true; 1.2634 + argc--; 1.2635 + p++; 1.2636 + } else { 1.2637 + narr = NewDenseEmptyArray(cx); 1.2638 + if (!narr) 1.2639 + return false; 1.2640 + args.rval().setObject(*narr); 1.2641 + length = 0; 1.2642 + } 1.2643 + 1.2644 + /* Loop over [0, argc] to concat args into narr, expanding all Arrays. */ 1.2645 + for (unsigned i = 0; i <= argc; i++) { 1.2646 + if (!CheckForInterrupt(cx)) 1.2647 + return false; 1.2648 + HandleValue v = HandleValue::fromMarkedLocation(&p[i]); 1.2649 + if (v.isObject()) { 1.2650 + RootedObject obj(cx, &v.toObject()); 1.2651 + if (ObjectClassIs(obj, ESClass_Array, cx)) { 1.2652 + uint32_t alength; 1.2653 + if (!GetLengthProperty(cx, obj, &alength)) 1.2654 + return false; 1.2655 + RootedValue tmp(cx); 1.2656 + for (uint32_t slot = 0; slot < alength; slot++) { 1.2657 + bool hole; 1.2658 + if (!CheckForInterrupt(cx) || !GetElement(cx, obj, slot, &hole, &tmp)) 1.2659 + return false; 1.2660 + 1.2661 + /* 1.2662 + * Per ECMA 262, 15.4.4.4, step 9, ignore nonexistent 1.2663 + * properties. 1.2664 + */ 1.2665 + if (!hole && !SetArrayElement(cx, narr, length + slot, tmp)) 1.2666 + return false; 1.2667 + } 1.2668 + length += alength; 1.2669 + continue; 1.2670 + } 1.2671 + } 1.2672 + 1.2673 + if (!SetArrayElement(cx, narr, length, v)) 1.2674 + return false; 1.2675 + length++; 1.2676 + } 1.2677 + 1.2678 + return SetLengthProperty(cx, narr, length); 1.2679 +} 1.2680 + 1.2681 +static bool 1.2682 +array_slice(JSContext *cx, unsigned argc, Value *vp) 1.2683 +{ 1.2684 + CallArgs args = CallArgsFromVp(argc, vp); 1.2685 + 1.2686 + RootedObject obj(cx, ToObject(cx, args.thisv())); 1.2687 + if (!obj) 1.2688 + return false; 1.2689 + 1.2690 + uint32_t length; 1.2691 + if (!GetLengthProperty(cx, obj, &length)) 1.2692 + return false; 1.2693 + 1.2694 + uint32_t begin = 0; 1.2695 + uint32_t end = length; 1.2696 + if (args.length() > 0) { 1.2697 + double d; 1.2698 + if (!ToInteger(cx, args[0], &d)) 1.2699 + return false; 1.2700 + if (d < 0) { 1.2701 + d += length; 1.2702 + if (d < 0) 1.2703 + d = 0; 1.2704 + } else if (d > length) { 1.2705 + d = length; 1.2706 + } 1.2707 + begin = (uint32_t)d; 1.2708 + 1.2709 + if (args.hasDefined(1)) { 1.2710 + if (!ToInteger(cx, args[1], &d)) 1.2711 + return false; 1.2712 + if (d < 0) { 1.2713 + d += length; 1.2714 + if (d < 0) 1.2715 + d = 0; 1.2716 + } else if (d > length) { 1.2717 + d = length; 1.2718 + } 1.2719 + end = (uint32_t)d; 1.2720 + } 1.2721 + } 1.2722 + 1.2723 + if (begin > end) 1.2724 + begin = end; 1.2725 + 1.2726 + Rooted<ArrayObject*> narr(cx); 1.2727 + narr = NewDenseAllocatedArray(cx, end - begin); 1.2728 + if (!narr) 1.2729 + return false; 1.2730 + TryReuseArrayType(obj, narr); 1.2731 + 1.2732 + if (obj->is<ArrayObject>() && !ObjectMayHaveExtraIndexedProperties(obj)) { 1.2733 + if (obj->getDenseInitializedLength() > begin) { 1.2734 + uint32_t numSourceElements = obj->getDenseInitializedLength() - begin; 1.2735 + uint32_t initLength = Min(numSourceElements, end - begin); 1.2736 + narr->setDenseInitializedLength(initLength); 1.2737 + narr->initDenseElements(0, &obj->getDenseElement(begin), initLength); 1.2738 + } 1.2739 + args.rval().setObject(*narr); 1.2740 + return true; 1.2741 + } 1.2742 + 1.2743 + if (js::SliceOp op = obj->getOps()->slice) { 1.2744 + // Ensure that we have dense elements, so that DOM can use js::UnsafeDefineElement. 1.2745 + JSObject::EnsureDenseResult result = narr->ensureDenseElements(cx, 0, end - begin); 1.2746 + if (result == JSObject::ED_FAILED) 1.2747 + return false; 1.2748 + 1.2749 + if (result == JSObject::ED_OK) { 1.2750 + if (!op(cx, obj, begin, end, narr)) 1.2751 + return false; 1.2752 + 1.2753 + args.rval().setObject(*narr); 1.2754 + return true; 1.2755 + } 1.2756 + 1.2757 + // Fallthrough 1.2758 + JS_ASSERT(result == JSObject::ED_SPARSE); 1.2759 + } 1.2760 + 1.2761 + 1.2762 + if (!SliceSlowly(cx, obj, obj, begin, end, narr)) 1.2763 + return false; 1.2764 + 1.2765 + args.rval().setObject(*narr); 1.2766 + return true; 1.2767 +} 1.2768 + 1.2769 +JS_FRIEND_API(bool) 1.2770 +js::SliceSlowly(JSContext* cx, HandleObject obj, HandleObject receiver, 1.2771 + uint32_t begin, uint32_t end, HandleObject result) 1.2772 +{ 1.2773 + RootedValue value(cx); 1.2774 + for (uint32_t slot = begin; slot < end; slot++) { 1.2775 + bool hole; 1.2776 + if (!CheckForInterrupt(cx) || 1.2777 + !GetElement(cx, obj, receiver, slot, &hole, &value)) 1.2778 + { 1.2779 + return false; 1.2780 + } 1.2781 + if (!hole && !JSObject::defineElement(cx, result, slot - begin, value)) 1.2782 + return false; 1.2783 + } 1.2784 + return true; 1.2785 +} 1.2786 + 1.2787 +/* ES5 15.4.4.20. */ 1.2788 +static bool 1.2789 +array_filter(JSContext *cx, unsigned argc, Value *vp) 1.2790 +{ 1.2791 + CallArgs args = CallArgsFromVp(argc, vp); 1.2792 + 1.2793 + /* Step 1. */ 1.2794 + RootedObject obj(cx, ToObject(cx, args.thisv())); 1.2795 + if (!obj) 1.2796 + return false; 1.2797 + 1.2798 + /* Step 2-3. */ 1.2799 + uint32_t len; 1.2800 + if (!GetLengthProperty(cx, obj, &len)) 1.2801 + return false; 1.2802 + 1.2803 + /* Step 4. */ 1.2804 + if (args.length() == 0) { 1.2805 + js_ReportMissingArg(cx, args.calleev(), 0); 1.2806 + return false; 1.2807 + } 1.2808 + RootedObject callable(cx, ValueToCallable(cx, args[0], args.length() - 1)); 1.2809 + if (!callable) 1.2810 + return false; 1.2811 + 1.2812 + /* Step 5. */ 1.2813 + RootedValue thisv(cx, args.length() >= 2 ? args[1] : UndefinedValue()); 1.2814 + 1.2815 + /* Step 6. */ 1.2816 + RootedObject arr(cx, NewDenseAllocatedArray(cx, 0)); 1.2817 + if (!arr) 1.2818 + return false; 1.2819 + TypeObject *newtype = GetTypeCallerInitObject(cx, JSProto_Array); 1.2820 + if (!newtype) 1.2821 + return false; 1.2822 + arr->setType(newtype); 1.2823 + 1.2824 + /* Step 7. */ 1.2825 + uint32_t k = 0; 1.2826 + 1.2827 + /* Step 8. */ 1.2828 + uint32_t to = 0; 1.2829 + 1.2830 + /* Step 9. */ 1.2831 + JS_ASSERT(!InParallelSection()); 1.2832 + FastInvokeGuard fig(cx, ObjectValue(*callable)); 1.2833 + InvokeArgs &args2 = fig.args(); 1.2834 + RootedValue kValue(cx); 1.2835 + while (k < len) { 1.2836 + if (!CheckForInterrupt(cx)) 1.2837 + return false; 1.2838 + 1.2839 + /* Step a, b, and c.i. */ 1.2840 + bool kNotPresent; 1.2841 + if (!GetElement(cx, obj, k, &kNotPresent, &kValue)) 1.2842 + return false; 1.2843 + 1.2844 + /* Step c.ii-iii. */ 1.2845 + if (!kNotPresent) { 1.2846 + if (!args2.init(3)) 1.2847 + return false; 1.2848 + args2.setCallee(ObjectValue(*callable)); 1.2849 + args2.setThis(thisv); 1.2850 + args2[0].set(kValue); 1.2851 + args2[1].setNumber(k); 1.2852 + args2[2].setObject(*obj); 1.2853 + if (!fig.invoke(cx)) 1.2854 + return false; 1.2855 + 1.2856 + if (ToBoolean(args2.rval())) { 1.2857 + if (!SetArrayElement(cx, arr, to, kValue)) 1.2858 + return false; 1.2859 + to++; 1.2860 + } 1.2861 + } 1.2862 + 1.2863 + /* Step d. */ 1.2864 + k++; 1.2865 + } 1.2866 + 1.2867 + /* Step 10. */ 1.2868 + args.rval().setObject(*arr); 1.2869 + return true; 1.2870 +} 1.2871 + 1.2872 +static bool 1.2873 +array_isArray(JSContext *cx, unsigned argc, Value *vp) 1.2874 +{ 1.2875 + CallArgs args = CallArgsFromVp(argc, vp); 1.2876 + bool isArray = args.length() > 0 && IsObjectWithClass(args[0], ESClass_Array, cx); 1.2877 + args.rval().setBoolean(isArray); 1.2878 + return true; 1.2879 +} 1.2880 + 1.2881 +static bool 1.2882 +IsArrayConstructor(const Value &v) 1.2883 +{ 1.2884 + // This must only return true if v is *the* Array constructor for the 1.2885 + // current compartment; we rely on the fact that any other Array 1.2886 + // constructor would be represented as a wrapper. 1.2887 + return v.isObject() && 1.2888 + v.toObject().is<JSFunction>() && 1.2889 + v.toObject().as<JSFunction>().isNative() && 1.2890 + v.toObject().as<JSFunction>().native() == js_Array; 1.2891 +} 1.2892 + 1.2893 +static bool 1.2894 +ArrayFromCallArgs(JSContext *cx, RootedTypeObject &type, CallArgs &args) 1.2895 +{ 1.2896 + if (!InitArrayTypes(cx, type, args.array(), args.length())) 1.2897 + return false; 1.2898 + JSObject *obj = (args.length() == 0) 1.2899 + ? NewDenseEmptyArray(cx) 1.2900 + : NewDenseCopiedArray(cx, args.length(), args.array()); 1.2901 + if (!obj) 1.2902 + return false; 1.2903 + obj->setType(type); 1.2904 + args.rval().setObject(*obj); 1.2905 + return true; 1.2906 +} 1.2907 + 1.2908 +static bool 1.2909 +array_of(JSContext *cx, unsigned argc, Value *vp) 1.2910 +{ 1.2911 + CallArgs args = CallArgsFromVp(argc, vp); 1.2912 + 1.2913 + if (IsArrayConstructor(args.thisv()) || !IsConstructor(args.thisv())) { 1.2914 + // IsArrayConstructor(this) will usually be true in practice. This is 1.2915 + // the most common path. 1.2916 + RootedTypeObject type(cx, GetTypeCallerInitObject(cx, JSProto_Array)); 1.2917 + if (!type) 1.2918 + return false; 1.2919 + return ArrayFromCallArgs(cx, type, args); 1.2920 + } 1.2921 + 1.2922 + // Step 4. 1.2923 + RootedObject obj(cx); 1.2924 + { 1.2925 + RootedValue v(cx); 1.2926 + Value argv[1] = {NumberValue(args.length())}; 1.2927 + if (!InvokeConstructor(cx, args.thisv(), 1, argv, v.address())) 1.2928 + return false; 1.2929 + obj = ToObject(cx, v); 1.2930 + if (!obj) 1.2931 + return false; 1.2932 + } 1.2933 + 1.2934 + // Step 8. 1.2935 + for (unsigned k = 0; k < args.length(); k++) { 1.2936 + if (!JSObject::defineElement(cx, obj, k, args[k])) 1.2937 + return false; 1.2938 + } 1.2939 + 1.2940 + // Steps 9-10. 1.2941 + RootedValue v(cx, NumberValue(args.length())); 1.2942 + if (!JSObject::setProperty(cx, obj, obj, cx->names().length, &v, true)) 1.2943 + return false; 1.2944 + 1.2945 + // Step 11. 1.2946 + args.rval().setObject(*obj); 1.2947 + return true; 1.2948 +} 1.2949 + 1.2950 +#define GENERIC JSFUN_GENERIC_NATIVE 1.2951 + 1.2952 +static const JSFunctionSpec array_methods[] = { 1.2953 +#if JS_HAS_TOSOURCE 1.2954 + JS_FN(js_toSource_str, array_toSource, 0,0), 1.2955 +#endif 1.2956 + JS_FN(js_toString_str, array_toString, 0,0), 1.2957 + JS_FN(js_toLocaleString_str,array_toLocaleString,0,0), 1.2958 + 1.2959 + /* Perl-ish methods. */ 1.2960 + JS_FN("join", array_join, 1,JSFUN_GENERIC_NATIVE), 1.2961 + JS_FN("reverse", array_reverse, 0,JSFUN_GENERIC_NATIVE), 1.2962 + JS_FN("sort", array_sort, 1,JSFUN_GENERIC_NATIVE), 1.2963 + JS_FN("push", array_push, 1,JSFUN_GENERIC_NATIVE), 1.2964 + JS_FN("pop", array_pop, 0,JSFUN_GENERIC_NATIVE), 1.2965 + JS_FN("shift", array_shift, 0,JSFUN_GENERIC_NATIVE), 1.2966 + JS_FN("unshift", array_unshift, 1,JSFUN_GENERIC_NATIVE), 1.2967 + JS_FN("splice", array_splice, 2,JSFUN_GENERIC_NATIVE), 1.2968 + 1.2969 + /* Pythonic sequence methods. */ 1.2970 + JS_FN("concat", array_concat, 1,JSFUN_GENERIC_NATIVE), 1.2971 + JS_FN("slice", array_slice, 2,JSFUN_GENERIC_NATIVE), 1.2972 + 1.2973 + JS_SELF_HOSTED_FN("lastIndexOf", "ArrayLastIndexOf", 1,0), 1.2974 + JS_SELF_HOSTED_FN("indexOf", "ArrayIndexOf", 1,0), 1.2975 + JS_SELF_HOSTED_FN("forEach", "ArrayForEach", 1,0), 1.2976 + JS_SELF_HOSTED_FN("map", "ArrayMap", 1,0), 1.2977 + JS_SELF_HOSTED_FN("reduce", "ArrayReduce", 1,0), 1.2978 + JS_SELF_HOSTED_FN("reduceRight", "ArrayReduceRight", 1,0), 1.2979 + JS_FN("filter", array_filter, 1,JSFUN_GENERIC_NATIVE), 1.2980 + JS_SELF_HOSTED_FN("some", "ArraySome", 1,0), 1.2981 + JS_SELF_HOSTED_FN("every", "ArrayEvery", 1,0), 1.2982 + 1.2983 +#ifdef ENABLE_PARALLEL_JS 1.2984 + /* Parallelizable and pure methods. */ 1.2985 + JS_SELF_HOSTED_FN("mapPar", "ArrayMapPar", 2,0), 1.2986 + JS_SELF_HOSTED_FN("reducePar", "ArrayReducePar", 2,0), 1.2987 + JS_SELF_HOSTED_FN("scanPar", "ArrayScanPar", 2,0), 1.2988 + JS_SELF_HOSTED_FN("scatterPar", "ArrayScatterPar", 5,0), 1.2989 + JS_SELF_HOSTED_FN("filterPar", "ArrayFilterPar", 2,0), 1.2990 +#endif 1.2991 + 1.2992 + /* ES6 additions */ 1.2993 + JS_SELF_HOSTED_FN("find", "ArrayFind", 1,0), 1.2994 + JS_SELF_HOSTED_FN("findIndex", "ArrayFindIndex", 1,0), 1.2995 + 1.2996 + JS_SELF_HOSTED_FN("fill", "ArrayFill", 3,0), 1.2997 + 1.2998 + JS_SELF_HOSTED_FN("@@iterator", "ArrayValues", 0,0), 1.2999 + JS_SELF_HOSTED_FN("entries", "ArrayEntries", 0,0), 1.3000 + JS_SELF_HOSTED_FN("keys", "ArrayKeys", 0,0), 1.3001 + JS_FS_END 1.3002 +}; 1.3003 + 1.3004 +static const JSFunctionSpec array_static_methods[] = { 1.3005 + JS_FN("isArray", array_isArray, 1,0), 1.3006 + JS_SELF_HOSTED_FN("lastIndexOf", "ArrayStaticLastIndexOf", 2,0), 1.3007 + JS_SELF_HOSTED_FN("indexOf", "ArrayStaticIndexOf", 2,0), 1.3008 + JS_SELF_HOSTED_FN("forEach", "ArrayStaticForEach", 2,0), 1.3009 + JS_SELF_HOSTED_FN("map", "ArrayStaticMap", 2,0), 1.3010 + JS_SELF_HOSTED_FN("every", "ArrayStaticEvery", 2,0), 1.3011 + JS_SELF_HOSTED_FN("some", "ArrayStaticSome", 2,0), 1.3012 + JS_SELF_HOSTED_FN("reduce", "ArrayStaticReduce", 2,0), 1.3013 + JS_SELF_HOSTED_FN("reduceRight", "ArrayStaticReduceRight", 2,0), 1.3014 + JS_FN("of", array_of, 0,0), 1.3015 + 1.3016 +#ifdef ENABLE_PARALLEL_JS 1.3017 + JS_SELF_HOSTED_FN("build", "ArrayStaticBuild", 2,0), 1.3018 + /* Parallelizable and pure static methods. */ 1.3019 + JS_SELF_HOSTED_FN("buildPar", "ArrayStaticBuildPar", 3,0), 1.3020 +#endif 1.3021 + 1.3022 + JS_FS_END 1.3023 +}; 1.3024 + 1.3025 +/* ES5 15.4.2 */ 1.3026 +bool 1.3027 +js_Array(JSContext *cx, unsigned argc, Value *vp) 1.3028 +{ 1.3029 + CallArgs args = CallArgsFromVp(argc, vp); 1.3030 + RootedTypeObject type(cx, GetTypeCallerInitObject(cx, JSProto_Array)); 1.3031 + if (!type) 1.3032 + return false; 1.3033 + 1.3034 + if (args.length() != 1 || !args[0].isNumber()) 1.3035 + return ArrayFromCallArgs(cx, type, args); 1.3036 + 1.3037 + uint32_t length; 1.3038 + if (args[0].isInt32()) { 1.3039 + int32_t i = args[0].toInt32(); 1.3040 + if (i < 0) { 1.3041 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH); 1.3042 + return false; 1.3043 + } 1.3044 + length = uint32_t(i); 1.3045 + } else { 1.3046 + double d = args[0].toDouble(); 1.3047 + length = ToUint32(d); 1.3048 + if (d != double(length)) { 1.3049 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH); 1.3050 + return false; 1.3051 + } 1.3052 + } 1.3053 + 1.3054 + /* 1.3055 + * Allocate dense elements eagerly for small arrays, to avoid reallocating 1.3056 + * elements when filling the array. 1.3057 + */ 1.3058 + RootedObject obj(cx); 1.3059 + obj = (length <= ArrayObject::EagerAllocationMaxLength) 1.3060 + ? NewDenseAllocatedArray(cx, length) 1.3061 + : NewDenseUnallocatedArray(cx, length); 1.3062 + if (!obj) 1.3063 + return false; 1.3064 + Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>()); 1.3065 + 1.3066 + arr->setType(type); 1.3067 + 1.3068 + /* If the length calculation overflowed, make sure that is marked for the new type. */ 1.3069 + if (arr->length() > INT32_MAX) 1.3070 + arr->setLength(cx, arr->length()); 1.3071 + 1.3072 + args.rval().setObject(*arr); 1.3073 + return true; 1.3074 +} 1.3075 + 1.3076 +JSObject * 1.3077 +js_InitArrayClass(JSContext *cx, HandleObject obj) 1.3078 +{ 1.3079 + JS_ASSERT(obj->isNative()); 1.3080 + 1.3081 + Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>()); 1.3082 + 1.3083 + RootedObject proto(cx, global->getOrCreateObjectPrototype(cx)); 1.3084 + if (!proto) 1.3085 + return nullptr; 1.3086 + 1.3087 + RootedTypeObject type(cx, cx->getNewType(&ArrayObject::class_, proto.get())); 1.3088 + if (!type) 1.3089 + return nullptr; 1.3090 + 1.3091 + JSObject *metadata = nullptr; 1.3092 + if (!NewObjectMetadata(cx, &metadata)) 1.3093 + return nullptr; 1.3094 + 1.3095 + RootedShape shape(cx, EmptyShape::getInitialShape(cx, &ArrayObject::class_, TaggedProto(proto), 1.3096 + proto->getParent(), metadata, 1.3097 + gc::FINALIZE_OBJECT0)); 1.3098 + if (!shape) 1.3099 + return nullptr; 1.3100 + 1.3101 + RootedObject arrayProto(cx, JSObject::createArray(cx, gc::FINALIZE_OBJECT4, gc::TenuredHeap, shape, type, 0)); 1.3102 + if (!arrayProto || !JSObject::setSingletonType(cx, arrayProto) || !AddLengthProperty(cx, arrayProto)) 1.3103 + return nullptr; 1.3104 + 1.3105 + RootedFunction ctor(cx); 1.3106 + ctor = global->createConstructor(cx, js_Array, cx->names().Array, 1); 1.3107 + if (!ctor) 1.3108 + return nullptr; 1.3109 + 1.3110 + /* 1.3111 + * The default 'new' type of Array.prototype is required by type inference 1.3112 + * to have unknown properties, to simplify handling of e.g. heterogenous 1.3113 + * arrays in JSON and script literals and allows setDenseArrayElement to 1.3114 + * be used without updating the indexed type set for such default arrays. 1.3115 + */ 1.3116 + if (!JSObject::setNewTypeUnknown(cx, &ArrayObject::class_, arrayProto)) 1.3117 + return nullptr; 1.3118 + 1.3119 + if (!LinkConstructorAndPrototype(cx, ctor, arrayProto)) 1.3120 + return nullptr; 1.3121 + 1.3122 + if (!DefinePropertiesAndBrand(cx, arrayProto, nullptr, array_methods) || 1.3123 + !DefinePropertiesAndBrand(cx, ctor, nullptr, array_static_methods)) 1.3124 + { 1.3125 + return nullptr; 1.3126 + } 1.3127 + 1.3128 + if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_Array, ctor, arrayProto)) 1.3129 + return nullptr; 1.3130 + 1.3131 + return arrayProto; 1.3132 +} 1.3133 + 1.3134 +/* 1.3135 + * Array allocation functions. 1.3136 + */ 1.3137 + 1.3138 +static inline bool 1.3139 +EnsureNewArrayElements(ExclusiveContext *cx, JSObject *obj, uint32_t length) 1.3140 +{ 1.3141 + /* 1.3142 + * If ensureElements creates dynamically allocated slots, then having 1.3143 + * fixedSlots is a waste. 1.3144 + */ 1.3145 + DebugOnly<uint32_t> cap = obj->getDenseCapacity(); 1.3146 + 1.3147 + if (!obj->ensureElements(cx, length)) 1.3148 + return false; 1.3149 + 1.3150 + JS_ASSERT_IF(cap, !obj->hasDynamicElements()); 1.3151 + 1.3152 + return true; 1.3153 +} 1.3154 + 1.3155 +template<bool allocateCapacity> 1.3156 +static MOZ_ALWAYS_INLINE ArrayObject * 1.3157 +NewArray(ExclusiveContext *cxArg, uint32_t length, 1.3158 + JSObject *protoArg, NewObjectKind newKind = GenericObject) 1.3159 +{ 1.3160 + gc::AllocKind allocKind = GuessArrayGCKind(length); 1.3161 + JS_ASSERT(CanBeFinalizedInBackground(allocKind, &ArrayObject::class_)); 1.3162 + allocKind = GetBackgroundAllocKind(allocKind); 1.3163 + 1.3164 + NewObjectCache::EntryIndex entry = -1; 1.3165 + if (JSContext *cx = cxArg->maybeJSContext()) { 1.3166 + NewObjectCache &cache = cx->runtime()->newObjectCache; 1.3167 + if (newKind == GenericObject && 1.3168 + !cx->compartment()->hasObjectMetadataCallback() && 1.3169 + cache.lookupGlobal(&ArrayObject::class_, cx->global(), allocKind, &entry)) 1.3170 + { 1.3171 + gc::InitialHeap heap = GetInitialHeap(newKind, &ArrayObject::class_); 1.3172 + JSObject *obj = cache.newObjectFromHit<NoGC>(cx, entry, heap); 1.3173 + if (obj) { 1.3174 + /* Fixup the elements pointer and length, which may be incorrect. */ 1.3175 + ArrayObject *arr = &obj->as<ArrayObject>(); 1.3176 + arr->setFixedElements(); 1.3177 + arr->setLength(cx, length); 1.3178 + if (allocateCapacity && !EnsureNewArrayElements(cx, arr, length)) 1.3179 + return nullptr; 1.3180 + return arr; 1.3181 + } else { 1.3182 + RootedObject proto(cxArg, protoArg); 1.3183 + obj = cache.newObjectFromHit<CanGC>(cx, entry, heap); 1.3184 + JS_ASSERT(!obj); 1.3185 + protoArg = proto; 1.3186 + } 1.3187 + } 1.3188 + } 1.3189 + 1.3190 + RootedObject proto(cxArg, protoArg); 1.3191 + if (protoArg) 1.3192 + JS::PoisonPtr(&protoArg); 1.3193 + 1.3194 + if (!proto && !GetBuiltinPrototype(cxArg, JSProto_Array, &proto)) 1.3195 + return nullptr; 1.3196 + 1.3197 + RootedTypeObject type(cxArg, cxArg->getNewType(&ArrayObject::class_, proto.get())); 1.3198 + if (!type) 1.3199 + return nullptr; 1.3200 + 1.3201 + JSObject *metadata = nullptr; 1.3202 + if (!NewObjectMetadata(cxArg, &metadata)) 1.3203 + return nullptr; 1.3204 + 1.3205 + /* 1.3206 + * Get a shape with zero fixed slots, regardless of the size class. 1.3207 + * See JSObject::createArray. 1.3208 + */ 1.3209 + RootedShape shape(cxArg, EmptyShape::getInitialShape(cxArg, &ArrayObject::class_, 1.3210 + TaggedProto(proto), cxArg->global(), 1.3211 + metadata, gc::FINALIZE_OBJECT0)); 1.3212 + if (!shape) 1.3213 + return nullptr; 1.3214 + 1.3215 + Rooted<ArrayObject*> arr(cxArg, JSObject::createArray(cxArg, allocKind, 1.3216 + GetInitialHeap(newKind, &ArrayObject::class_), 1.3217 + shape, type, length)); 1.3218 + if (!arr) 1.3219 + return nullptr; 1.3220 + 1.3221 + if (shape->isEmptyShape()) { 1.3222 + if (!AddLengthProperty(cxArg, arr)) 1.3223 + return nullptr; 1.3224 + shape = arr->lastProperty(); 1.3225 + EmptyShape::insertInitialShape(cxArg, shape, proto); 1.3226 + } 1.3227 + 1.3228 + if (newKind == SingletonObject && !JSObject::setSingletonType(cxArg, arr)) 1.3229 + return nullptr; 1.3230 + 1.3231 + if (entry != -1) { 1.3232 + cxArg->asJSContext()->runtime()->newObjectCache.fillGlobal(entry, &ArrayObject::class_, 1.3233 + cxArg->global(), allocKind, arr); 1.3234 + } 1.3235 + 1.3236 + if (allocateCapacity && !EnsureNewArrayElements(cxArg, arr, length)) 1.3237 + return nullptr; 1.3238 + 1.3239 + probes::CreateObject(cxArg, arr); 1.3240 + return arr; 1.3241 +} 1.3242 + 1.3243 +ArrayObject * JS_FASTCALL 1.3244 +js::NewDenseEmptyArray(JSContext *cx, JSObject *proto /* = nullptr */, 1.3245 + NewObjectKind newKind /* = GenericObject */) 1.3246 +{ 1.3247 + return NewArray<false>(cx, 0, proto, newKind); 1.3248 +} 1.3249 + 1.3250 +ArrayObject * JS_FASTCALL 1.3251 +js::NewDenseAllocatedArray(ExclusiveContext *cx, uint32_t length, JSObject *proto /* = nullptr */, 1.3252 + NewObjectKind newKind /* = GenericObject */) 1.3253 +{ 1.3254 + return NewArray<true>(cx, length, proto, newKind); 1.3255 +} 1.3256 + 1.3257 +ArrayObject * JS_FASTCALL 1.3258 +js::NewDenseUnallocatedArray(ExclusiveContext *cx, uint32_t length, JSObject *proto /* = nullptr */, 1.3259 + NewObjectKind newKind /* = GenericObject */) 1.3260 +{ 1.3261 + return NewArray<false>(cx, length, proto, newKind); 1.3262 +} 1.3263 + 1.3264 +ArrayObject * 1.3265 +js::NewDenseCopiedArray(JSContext *cx, uint32_t length, HandleObject src, uint32_t elementOffset, 1.3266 + JSObject *proto /* = nullptr */) 1.3267 +{ 1.3268 + JS_ASSERT(!src->isIndexed()); 1.3269 + 1.3270 + ArrayObject* arr = NewArray<true>(cx, length, proto); 1.3271 + if (!arr) 1.3272 + return nullptr; 1.3273 + 1.3274 + JS_ASSERT(arr->getDenseCapacity() >= length); 1.3275 + 1.3276 + const Value* vp = src->getDenseElements() + elementOffset; 1.3277 + arr->setDenseInitializedLength(vp ? length : 0); 1.3278 + 1.3279 + if (vp) 1.3280 + arr->initDenseElements(0, vp, length); 1.3281 + 1.3282 + return arr; 1.3283 +} 1.3284 + 1.3285 +// values must point at already-rooted Value objects 1.3286 +ArrayObject * 1.3287 +js::NewDenseCopiedArray(JSContext *cx, uint32_t length, const Value *values, 1.3288 + JSObject *proto /* = nullptr */, NewObjectKind newKind /* = GenericObject */) 1.3289 +{ 1.3290 + ArrayObject* arr = NewArray<true>(cx, length, proto); 1.3291 + if (!arr) 1.3292 + return nullptr; 1.3293 + 1.3294 + JS_ASSERT(arr->getDenseCapacity() >= length); 1.3295 + 1.3296 + arr->setDenseInitializedLength(values ? length : 0); 1.3297 + 1.3298 + if (values) 1.3299 + arr->initDenseElements(0, values, length); 1.3300 + 1.3301 + return arr; 1.3302 +} 1.3303 + 1.3304 +ArrayObject * 1.3305 +js::NewDenseAllocatedArrayWithTemplate(JSContext *cx, uint32_t length, JSObject *templateObject) 1.3306 +{ 1.3307 + gc::AllocKind allocKind = GuessArrayGCKind(length); 1.3308 + JS_ASSERT(CanBeFinalizedInBackground(allocKind, &ArrayObject::class_)); 1.3309 + allocKind = GetBackgroundAllocKind(allocKind); 1.3310 + 1.3311 + RootedTypeObject type(cx, templateObject->type()); 1.3312 + if (!type) 1.3313 + return nullptr; 1.3314 + 1.3315 + RootedShape shape(cx, templateObject->lastProperty()); 1.3316 + if (!shape) 1.3317 + return nullptr; 1.3318 + 1.3319 + gc::InitialHeap heap = GetInitialHeap(GenericObject, &ArrayObject::class_); 1.3320 + Rooted<ArrayObject *> arr(cx, JSObject::createArray(cx, allocKind, heap, shape, type, length)); 1.3321 + if (!arr) 1.3322 + return nullptr; 1.3323 + 1.3324 + if (!EnsureNewArrayElements(cx, arr, length)) 1.3325 + return nullptr; 1.3326 + 1.3327 + probes::CreateObject(cx, arr); 1.3328 + 1.3329 + return arr; 1.3330 +} 1.3331 + 1.3332 +#ifdef DEBUG 1.3333 +bool 1.3334 +js_ArrayInfo(JSContext *cx, unsigned argc, Value *vp) 1.3335 +{ 1.3336 + CallArgs args = CallArgsFromVp(argc, vp); 1.3337 + JSObject *obj; 1.3338 + 1.3339 + for (unsigned i = 0; i < args.length(); i++) { 1.3340 + RootedValue arg(cx, args[i]); 1.3341 + 1.3342 + char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, arg, NullPtr()); 1.3343 + if (!bytes) 1.3344 + return false; 1.3345 + if (arg.isPrimitive() || 1.3346 + !(obj = arg.toObjectOrNull())->is<ArrayObject>()) { 1.3347 + fprintf(stderr, "%s: not array\n", bytes); 1.3348 + js_free(bytes); 1.3349 + continue; 1.3350 + } 1.3351 + fprintf(stderr, "%s: (len %u", bytes, obj->as<ArrayObject>().length()); 1.3352 + fprintf(stderr, ", capacity %u", obj->getDenseCapacity()); 1.3353 + fputs(")\n", stderr); 1.3354 + js_free(bytes); 1.3355 + } 1.3356 + 1.3357 + args.rval().setUndefined(); 1.3358 + return true; 1.3359 +} 1.3360 +#endif