/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sts=4 et sw=4 tw=99: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#include"jsarray.h"#include"mozilla/ArrayUtils.h"#include"mozilla/CheckedInt.h"#include"mozilla/DebugOnly.h"#include"mozilla/FloatingPoint.h"#include"mozilla/MathAlgorithms.h"#include<algorithm>#include"jsapi.h"#include"jsatom.h"#include"jscntxt.h"#include"jsfriendapi.h"#include"jsfun.h"#include"jsiter.h"#include"jsnum.h"#include"jsobj.h"#include"jstypes.h"#include"jsutil.h"#include"ds/Sort.h"#include"gc/Heap.h"#include"js/Class.h"#include"js/Conversions.h"#include"vm/ArgumentsObject.h"#include"vm/Interpreter.h"#include"vm/Shape.h"#include"vm/StringBuffer.h"#include"vm/TypedArrayCommon.h"#include"jsatominlines.h"#include"vm/ArgumentsObject-inl.h"#include"vm/ArrayObject-inl.h"#include"vm/Interpreter-inl.h"#include"vm/NativeObject-inl.h"#include"vm/Runtime-inl.h"usingnamespacejs;usingnamespacejs::gc;usingmozilla::Abs;usingmozilla::ArrayLength;usingmozilla::CeilingLog2;usingmozilla::CheckedInt;usingmozilla::DebugOnly;usingmozilla::IsNaN;usingmozilla::UniquePtr;usingJS::AutoCheckCannotGC;usingJS::ToUint32;booljs::GetLengthProperty(JSContext*cx,HandleObjectobj,uint32_t*lengthp){if(obj->is<ArrayObject>()){*lengthp=obj->as<ArrayObject>().length();returntrue;}if(obj->is<ArgumentsObject>()){ArgumentsObject&argsobj=obj->as<ArgumentsObject>();if(!argsobj.hasOverriddenLength()){*lengthp=argsobj.initialLength();returntrue;}}RootedValuevalue(cx);if(!GetProperty(cx,obj,obj,cx->names().length,&value))returnfalse;if(value.isInt32()){*lengthp=uint32_t(value.toInt32());// uint32_t cast does ToUint32returntrue;}returnToUint32(cx,value,lengthp);}/* * Determine if the id represents an array index. * * An id is an array index according to ECMA by (15.4): * * "Array objects give special treatment to a certain class of property names. * A property name P (in the form of a string value) is an array index if and * only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal * to 2^32-1." * * This means the largest allowed index is actually 2^32-2 (4294967294). * * In our implementation, it would be sufficient to check for id.isInt32() * except that by using signed 31-bit integers we miss the top half of the * valid range. This function checks the string representation itself; note * that calling a standard conversion routine might allow strings such as * "08" or "4.0" as array indices, which they are not. * */template<typenameCharT>staticboolStringIsArrayIndex(constCharT*s,uint32_tlength,uint32_t*indexp){constCharT*end=s+length;if(length==0||length>(sizeof("4294967294")-1)||!JS7_ISDEC(*s))returnfalse;uint32_tc=0,previous=0;uint32_tindex=JS7_UNDEC(*s++);/* Don't allow leading zeros. */if(index==0&&s!=end)returnfalse;for(;s<end;s++){if(!JS7_ISDEC(*s))returnfalse;previous=index;c=JS7_UNDEC(*s);index=10*index+c;}/* Make sure we didn't overflow. */if(previous<(MAX_ARRAY_INDEX/10)||(previous==(MAX_ARRAY_INDEX/10)&&c<=(MAX_ARRAY_INDEX%10))){MOZ_ASSERT(index<=MAX_ARRAY_INDEX);*indexp=index;returntrue;}returnfalse;}JS_FRIEND_API(bool)js::StringIsArrayIndex(JSLinearString*str,uint32_t*indexp){AutoCheckCannotGCnogc;returnstr->hasLatin1Chars()?::StringIsArrayIndex(str->latin1Chars(nogc),str->length(),indexp):::StringIsArrayIndex(str->twoByteChars(nogc),str->length(),indexp);}staticboolToId(JSContext*cx,doubleindex,MutableHandleIdid){if(index==uint32_t(index))returnIndexToId(cx,uint32_t(index),id);Valuetmp=DoubleValue(index);returnValueToId<CanGC>(cx,HandleValue::fromMarkedLocation(&tmp),id);}staticboolToId(JSContext*cx,uint32_tindex,MutableHandleIdid){returnIndexToId(cx,index,id);}/* * If the property at the given index exists, get its value into location * pointed by vp and set *hole to false. Otherwise set *hole to true and *vp * to JSVAL_VOID. This function assumes that the location pointed by vp is * properly rooted and can be used as GC-protected storage for temporaries. */template<typenameIndexType>staticinlineboolDoGetElement(JSContext*cx,HandleObjectobj,HandleObjectreceiver,IndexTypeindex,bool*hole,MutableHandleValuevp){RootedIdid(cx);if(!ToId(cx,index,&id))returnfalse;boolfound;if(!HasProperty(cx,obj,id,&found))returnfalse;if(found){if(!GetProperty(cx,obj,receiver,id,vp))returnfalse;}else{vp.setUndefined();}*hole=!found;returntrue;}template<typenameIndexType>staticvoidAssertGreaterThanZero(IndexTypeindex){MOZ_ASSERT(index>=0);MOZ_ASSERT(index==floor(index));}template<>voidAssertGreaterThanZero(uint32_tindex){}template<typenameIndexType>staticboolGetElement(JSContext*cx,HandleObjectobj,HandleObjectreceiver,IndexTypeindex,bool*hole,MutableHandleValuevp){AssertGreaterThanZero(index);if(obj->isNative()&&index<obj->as<NativeObject>().getDenseInitializedLength()){vp.set(obj->as<NativeObject>().getDenseElement(uint32_t(index)));if(!vp.isMagic(JS_ELEMENTS_HOLE)){*hole=false;returntrue;}}if(obj->is<ArgumentsObject>()){if(obj->as<ArgumentsObject>().maybeGetElement(uint32_t(index),vp)){*hole=false;returntrue;}}returnDoGetElement(cx,obj,receiver,index,hole,vp);}template<typenameIndexType>staticinlineboolGetElement(JSContext*cx,HandleObjectobj,IndexTypeindex,bool*hole,MutableHandleValuevp){returnGetElement(cx,obj,obj,index,hole,vp);}voidElementAdder::append(JSContext*cx,HandleValuev){MOZ_ASSERT(index_<length_);if(resObj_)resObj_->as<NativeObject>().setDenseElementWithType(cx,index_++,v);elsevp_[index_++]=v;}voidElementAdder::appendHole(){MOZ_ASSERT(getBehavior_==ElementAdder::CheckHasElemPreserveHoles);MOZ_ASSERT(index_<length_);if(resObj_){MOZ_ASSERT(resObj_->as<NativeObject>().getDenseElement(index_).isMagic(JS_ELEMENTS_HOLE));index_++;}else{vp_[index_++].setMagic(JS_ELEMENTS_HOLE);}}booljs::GetElementsWithAdder(JSContext*cx,HandleObjectobj,HandleObjectreceiver,uint32_tbegin,uint32_tend,ElementAdder*adder){MOZ_ASSERT(begin<=end);RootedValueval(cx);for(uint32_ti=begin;i<end;i++){if(adder->getBehavior()==ElementAdder::CheckHasElemPreserveHoles){boolhole;if(!GetElement(cx,obj,receiver,i,&hole,&val))returnfalse;if(hole){adder->appendHole();continue;}}else{MOZ_ASSERT(adder->getBehavior()==ElementAdder::GetElement);if(!GetElement(cx,obj,receiver,i,&val))returnfalse;}adder->append(cx,val);}returntrue;}booljs::GetElements(JSContext*cx,HandleObjectaobj,uint32_tlength,Value*vp){if(aobj->is<ArrayObject>()&&length<=aobj->as<ArrayObject>().getDenseInitializedLength()&&!ObjectMayHaveExtraIndexedProperties(aobj)){/* No other indexed properties so hole = undefined */constValue*srcbeg=aobj->as<ArrayObject>().getDenseElements();constValue*srcend=srcbeg+length;constValue*src=srcbeg;for(Value*dst=vp;src<srcend;++dst,++src)*dst=src->isMagic(JS_ELEMENTS_HOLE)?UndefinedValue():*src;returntrue;}if(aobj->is<ArgumentsObject>()){ArgumentsObject&argsobj=aobj->as<ArgumentsObject>();if(!argsobj.hasOverriddenLength()){if(argsobj.maybeGetElements(0,length,vp))returntrue;}}if(js::GetElementsOpop=aobj->getOps()->getElements){ElementAdderadder(cx,vp,length,ElementAdder::GetElement);returnop(cx,aobj,0,length,&adder);}for(uint32_ti=0;i<length;i++){if(!GetElement(cx,aobj,aobj,i,MutableHandleValue::fromMarkedLocation(&vp[i])))returnfalse;}returntrue;}/* * Set the value of the property at the given index to v assuming v is rooted. */staticboolSetArrayElement(JSContext*cx,HandleObjectobj,doubleindex,HandleValuev){MOZ_ASSERT(index>=0);if(obj->is<ArrayObject>()&&!obj->isIndexed()){Rooted<ArrayObject*>arr(cx,&obj->as<ArrayObject>());/* Predicted/prefetched code should favor the remains-dense case. */NativeObject::EnsureDenseResultresult=NativeObject::ED_SPARSE;do{if(index>uint32_t(-1))break;uint32_tidx=uint32_t(index);if(idx>=arr->length()&&!arr->lengthIsWritable()){JS_ReportErrorFlagsAndNumber(cx,JSREPORT_ERROR,GetErrorMessage,nullptr,JSMSG_CANT_REDEFINE_ARRAY_LENGTH);returnfalse;}result=arr->ensureDenseElements(cx,idx,1);if(result!=NativeObject::ED_OK)break;if(idx>=arr->length())arr->setLengthInt32(idx+1);arr->setDenseElementWithType(cx,idx,v);returntrue;}while(false);if(result==NativeObject::ED_FAILED)returnfalse;MOZ_ASSERT(result==NativeObject::ED_SPARSE);}RootedIdid(cx);if(!ToId(cx,index,&id))returnfalse;returnSetProperty(cx,obj,id,v);}/* * Attempt to delete the element |index| from |obj| as if by * |obj.[[Delete]](index)|. * * If an error occurs while attempting to delete the element (that is, the call * to [[Delete]] threw), return false. * * Otherwise call result.succeed() or result.fail() to indicate whether the * deletion attempt succeeded (that is, whether the call to [[Delete]] returned * true or false). (Deletes generally fail only when the property is * non-configurable, but proxies may implement different semantics.) */staticboolDeleteArrayElement(JSContext*cx,HandleObjectobj,doubleindex,ObjectOpResult&result){MOZ_ASSERT(index>=0);MOZ_ASSERT(floor(index)==index);if(obj->is<ArrayObject>()&&!obj->isIndexed()){ArrayObject*aobj=&obj->as<ArrayObject>();if(index<=UINT32_MAX){uint32_tidx=uint32_t(index);if(idx<aobj->getDenseInitializedLength()){if(!aobj->maybeCopyElementsForWrite(cx))returnfalse;if(idx+1==aobj->getDenseInitializedLength()){aobj->setDenseInitializedLength(idx);}else{aobj->markDenseElementsNotPacked(cx);aobj->setDenseElement(idx,MagicValue(JS_ELEMENTS_HOLE));}if(!SuppressDeletedElement(cx,obj,idx))returnfalse;}}returnresult.succeed();}RootedIdid(cx);if(!ToId(cx,index,&id))returnfalse;returnDeleteProperty(cx,obj,id,result);}/* ES6 draft rev 32 (2 Febr 2015) 7.3.7 */staticboolDeletePropertyOrThrow(JSContext*cx,HandleObjectobj,doubleindex){ObjectOpResultsuccess;if(!DeleteArrayElement(cx,obj,index,success))returnfalse;if(!success){RootedIdid(cx);RootedValueindexv(cx,NumberValue(index));if(!ValueToId<CanGC>(cx,indexv,&id))returnfalse;returnsuccess.reportError(cx,obj,id);}returntrue;}booljs::SetLengthProperty(JSContext*cx,HandleObjectobj,doublelength){RootedValuev(cx,NumberValue(length));returnSetProperty(cx,obj,cx->names().length,v);}staticboolarray_length_getter(JSContext*cx,HandleObjectobj,HandleIdid,MutableHandleValuevp){vp.setNumber(obj->as<ArrayObject>().length());returntrue;}staticboolarray_length_setter(JSContext*cx,HandleObjectobj,HandleIdid,MutableHandleValuevp,ObjectOpResult&result){if(!obj->is<ArrayObject>()){// This array .length property was found on the prototype// chain. Ideally the setter should not have been called, but since// we're here, do an impression of SetPropertyByDefining.constClass*clasp=obj->getClass();returnDefineProperty(cx,obj,cx->names().length,vp,clasp->getProperty,clasp->setProperty,JSPROP_ENUMERATE,result);}Rooted<ArrayObject*>arr(cx,&obj->as<ArrayObject>());MOZ_ASSERT(arr->lengthIsWritable(),"setter shouldn't be called if property is non-writable");returnArraySetLength(cx,arr,id,JSPROP_PERMANENT,vp,result);}structReverseIndexComparator{booloperator()(constuint32_t&a,constuint32_t&b,bool*lessOrEqualp){MOZ_ASSERT(a!=b,"how'd we get duplicate indexes?");*lessOrEqualp=b<=a;returntrue;}};booljs::CanonicalizeArrayLengthValue(JSContext*cx,HandleValuev,uint32_t*newLen){doubled;if(!ToUint32(cx,v,newLen))returnfalse;if(!ToNumber(cx,v,&d))returnfalse;if(d==*newLen)returntrue;JS_ReportErrorNumber(cx,GetErrorMessage,nullptr,JSMSG_BAD_ARRAY_LENGTH);returnfalse;}/* ES6 draft rev 34 (2015 Feb 20) 9.4.2.4 ArraySetLength */booljs::ArraySetLength(JSContext*cx,Handle<ArrayObject*>arr,HandleIdid,unsignedattrs,HandleValuevalue,ObjectOpResult&result){MOZ_ASSERT(id==NameToId(cx->names().length));if(!arr->maybeCopyElementsForWrite(cx))returnfalse;// Step 1.uint32_tnewLen;if(attrs&JSPROP_IGNORE_VALUE){// The spec has us calling OrdinaryDefineOwnProperty if// Desc.[[Value]] is absent, but our implementation is so different that// this is impossible. Instead, set newLen to the current length and// proceed to step 9.newLen=arr->length();}else{// Step 2 is irrelevant in our implementation.// Steps 3-7.MOZ_ASSERT_IF(attrs&JSPROP_IGNORE_VALUE,value.isUndefined());if(!CanonicalizeArrayLengthValue(cx,value,&newLen))returnfalse;// Step 8 is irrelevant in our implementation.}// Steps 9-11.boollengthIsWritable=arr->lengthIsWritable();#ifdef DEBUG{RootedShapelengthShape(cx,arr->lookupPure(id));MOZ_ASSERT(lengthShape);MOZ_ASSERT(lengthShape->writable()==lengthIsWritable);}#endifuint32_toldLen=arr->length();// Part of steps 1.a, 12.a, and 16: Fail if we're being asked to change// enumerability or configurability, or otherwise break the object// invariants. (ES6 checks these by calling OrdinaryDefineOwnProperty, but// in SM, the array length property is hardly ordinary.)if((attrs&(JSPROP_PERMANENT|JSPROP_IGNORE_PERMANENT))==0||(attrs&(JSPROP_ENUMERATE|JSPROP_IGNORE_ENUMERATE))==JSPROP_ENUMERATE||(attrs&(JSPROP_GETTER|JSPROP_SETTER))!=0||(!lengthIsWritable&&(attrs&(JSPROP_READONLY|JSPROP_IGNORE_READONLY))==0)){returnresult.fail(JSMSG_CANT_REDEFINE_PROP);}// Steps 12-13 for arrays with non-writable length.if(!lengthIsWritable){if(newLen==oldLen)returnresult.succeed();returnresult.fail(JSMSG_CANT_REDEFINE_ARRAY_LENGTH);}// Step 19.boolsucceeded=true;do{// The initialized length and capacity of an array only need updating// when non-hole elements are added or removed, which doesn't happen// when array length stays the same or increases.if(newLen>=oldLen)break;// Attempt to propagate dense-element optimization tricks, if possible,// and avoid the generic (and accordingly slow) deletion code below.// We can only do this if there are only densely-indexed elements.// Once there's a sparse indexed element, there's no good way to know,// save by enumerating all the properties to find it. But we *have* to// know in case that sparse indexed element is non-configurable, as// that element must prevent any deletions below it. Bug 586842 should// fix this inefficiency by moving indexed storage to be entirely// separate from non-indexed storage.if(!arr->isIndexed()){if(!arr->maybeCopyElementsForWrite(cx))returnfalse;uint32_toldCapacity=arr->getDenseCapacity();uint32_toldInitializedLength=arr->getDenseInitializedLength();MOZ_ASSERT(oldCapacity>=oldInitializedLength);if(oldInitializedLength>newLen)arr->setDenseInitializedLength(newLen);if(oldCapacity>newLen)arr->shrinkElements(cx,newLen);// We've done the work of deleting any dense elements needing// deletion, and there are no sparse elements. Thus we can skip// straight to defining the length.break;}// Step 15.//// Attempt to delete all elements above the new length, from greatest// to least. If any of these deletions fails, we're supposed to define// the length to one greater than the index that couldn't be deleted,// *with the property attributes specified*. This might convert the// length to be not the value specified, yet non-writable. (You may be// forgiven for thinking these are interesting semantics.) Example://// var arr =// Object.defineProperty([0, 1, 2, 3], 1, { writable: false });// Object.defineProperty(arr, "length",// { value: 0, writable: false });//// will convert |arr| to an array of non-writable length two, then// throw a TypeError.//// We implement this behavior, in the relevant lops below, by setting// |succeeded| to false. Then we exit the loop, define the length// appropriately, and only then throw a TypeError, if necessary.uint32_tgap=oldLen-newLen;constuint32_tRemoveElementsFastLimit=1<<24;if(gap<RemoveElementsFastLimit){// If we're removing a relatively small number of elements, just do// it exactly by the spec.while(newLen<oldLen){// Step 15a.oldLen--;// Steps 15b-d.ObjectOpResultdeleteSucceeded;if(!DeleteElement(cx,arr,oldLen,deleteSucceeded))returnfalse;if(!deleteSucceeded){newLen=oldLen+1;succeeded=false;break;}}}else{// If we're removing a large number of elements from an array// that's probably sparse, try a different tack. Get all the own// property names, sift out the indexes in the deletion range into// a vector, sort the vector greatest to least, then delete the// indexes greatest to least using that vector. See bug 322135.//// This heuristic's kind of a huge guess -- "large number of// elements" and "probably sparse" are completely unprincipled// predictions. In the long run, bug 586842 will support the right// fix: store sparse elements in a sorted data structure that// permits fast in-reverse-order traversal and concurrent removals.Vector<uint32_t>indexes(cx);{AutoIdVectorprops(cx);if(!GetPropertyKeys(cx,arr,JSITER_OWNONLY|JSITER_HIDDEN,&props))returnfalse;for(size_ti=0;i<props.length();i++){if(!CheckForInterrupt(cx))returnfalse;uint32_tindex;if(!IdIsIndex(props[i],&index))continue;if(index>=newLen&&index<oldLen){if(!indexes.append(index))returnfalse;}}}uint32_tcount=indexes.length();{// We should use radix sort to be O(n), but this is uncommon// enough that we'll punt til someone complains.Vector<uint32_t>scratch(cx);if(!scratch.resize(count))returnfalse;MOZ_ALWAYS_TRUE(MergeSort(indexes.begin(),count,scratch.begin(),ReverseIndexComparator()));}uint32_tindex=UINT32_MAX;for(uint32_ti=0;i<count;i++){MOZ_ASSERT(indexes[i]<index,"indexes should never repeat");index=indexes[i];// Steps 15b-d.ObjectOpResultdeleteSucceeded;if(!DeleteElement(cx,arr,index,deleteSucceeded))returnfalse;if(!deleteSucceeded){newLen=index+1;succeeded=false;break;}}}}while(false);// Update array length. Technically we should have been doing this// throughout the loop, in step 19.d.iii.arr->setLength(cx,newLen);// Step 20.if(attrs&JSPROP_READONLY){// Yes, we totally drop a non-stub getter/setter from a defineProperty// API call on the floor here. Given that getter/setter will go away in// the long run, with accessors replacing them both internally and at the// API level, just run with this.RootedShapelengthShape(cx,arr->lookup(cx,id));if(!NativeObject::changeProperty(cx,arr,lengthShape,lengthShape->attributes()|JSPROP_READONLY,array_length_getter,array_length_setter)){returnfalse;}}// All operations past here until the |!succeeded| code must be infallible,// so that all element fields remain properly synchronized.// Trim the initialized length, if needed, to preserve the <= length// invariant. (Capacity was already reduced during element deletion, if// necessary.)ObjectElements*header=arr->getElementsHeader();header->initializedLength=Min(header->initializedLength,newLen);if(attrs&JSPROP_READONLY){header->setNonwritableArrayLength();// When an array's length becomes non-writable, writes to indexes// greater than or equal to the length don't change the array. We// handle this with a check for non-writable length in most places.// But in JIT code every check counts -- so we piggyback the check on// the already-required range check for |index < capacity| by making// capacity of arrays with non-writable length never exceed the length.if(arr->getDenseCapacity()>newLen){arr->shrinkElements(cx,newLen);arr->getElementsHeader()->capacity=newLen;}}if(!succeeded)returnresult.fail(JSMSG_CANT_TRUNCATE_ARRAY);returnresult.succeed();}booljs::WouldDefinePastNonwritableLength(HandleNativeObjectobj,uint32_tindex){if(!obj->is<ArrayObject>())returnfalse;ArrayObject*arr=&obj->as<ArrayObject>();return!arr->lengthIsWritable()&&index>=arr->length();}staticboolarray_addProperty(JSContext*cx,HandleObjectobj,HandleIdid,HandleValuev){Rooted<ArrayObject*>arr(cx,&obj->as<ArrayObject>());uint32_tindex;if(!IdIsIndex(id,&index))returntrue;uint32_tlength=arr->length();if(index>=length){MOZ_ASSERT(arr->lengthIsWritable(),"how'd this element get added if length is non-writable?");arr->setLength(cx,index+1);}returntrue;}booljs::ObjectMayHaveExtraIndexedProperties(JSObject*obj){/* * Whether obj may have indexed properties anywhere besides its dense * elements. This includes other indexed properties in its shape hierarchy, * and indexed properties or elements along its prototype chain. */MOZ_ASSERT(obj->isNative());if(obj->isIndexed())returntrue;/* * Walk up the prototype chain and see if this indexed element already * exists. If we hit the end of the prototype chain, it's safe to set the * element on the original object. */while((obj=obj->getProto())!=nullptr){/* * If the prototype is a non-native object (possibly a dense array), or * a native object (possibly a slow array) that has indexed properties, * return true. */if(!obj->isNative())returntrue;if(obj->isIndexed())returntrue;if(obj->as<NativeObject>().getDenseInitializedLength()>0)returntrue;if(IsAnyTypedArray(obj))returntrue;}returnfalse;}staticboolAddLengthProperty(ExclusiveContext*cx,HandleArrayObjectobj){/* * Add the 'length' property for a newly created array, * and update the elements to be an empty array owned by the object. * The shared emptyObjectElements singleton cannot be used for slow arrays, * as accesses to 'length' will use the elements header. */RootedIdlengthId(cx,NameToId(cx->names().length));MOZ_ASSERT(!obj->lookup(cx,lengthId));returnNativeObject::addProperty(cx,obj,lengthId,array_length_getter,array_length_setter,SHAPE_INVALID_SLOT,JSPROP_PERMANENT|JSPROP_SHARED|JSPROP_SHADOWABLE,0,/* allowDictionary = */false);}#if JS_HAS_TOSOURCEstaticboolarray_toSource(JSContext*cx,unsignedargc,Value*vp){JS_CHECK_RECURSION(cx,returnfalse);CallArgsargs=CallArgsFromVp(argc,vp);if(!args.thisv().isObject()){ReportIncompatible(cx,args);returnfalse;}Rooted<JSObject*>obj(cx,&args.thisv().toObject());RootedValueelt(cx);AutoCycleDetectordetector(cx,obj);if(!detector.init())returnfalse;StringBuffersb(cx);if(detector.foundCycle()){if(!sb.append("[]"))returnfalse;gotomake_string;}if(!sb.append('['))returnfalse;uint32_tlength;if(!GetLengthProperty(cx,obj,&length))returnfalse;for(uint32_tindex=0;index<length;index++){boolhole;if(!CheckForInterrupt(cx)||!GetElement(cx,obj,index,&hole,&elt)){returnfalse;}/* Get element's character string. */JSString*str;if(hole){str=cx->runtime()->emptyString;}else{str=ValueToSource(cx,elt);if(!str)returnfalse;}/* Append element to buffer. */if(!sb.append(str))returnfalse;if(index+1!=length){if(!sb.append(", "))returnfalse;}elseif(hole){if(!sb.append(','))returnfalse;}}/* Finalize the buffer. */if(!sb.append(']'))returnfalse;make_string:JSString*str=sb.finishString();if(!str)returnfalse;args.rval().setString(str);returntrue;}#endifstructEmptySeparatorOp{booloperator()(JSContext*,StringBuffer&sb){returntrue;}};template<typenameCharT>structCharSeparatorOp{constCharTsep;explicitCharSeparatorOp(CharTsep):sep(sep){}booloperator()(JSContext*,StringBuffer&sb){returnsb.append(sep);}};structStringSeparatorOp{HandleLinearStringsep;explicitStringSeparatorOp(HandleLinearStringsep):sep(sep){}booloperator()(JSContext*cx,StringBuffer&sb){returnsb.append(sep);}};template<boolLocale,typenameSeparatorOp>staticboolArrayJoinKernel(JSContext*cx,SeparatorOpsepOp,HandleObjectobj,uint32_tlength,StringBuffer&sb){uint32_ti=0;if(!Locale&&obj->is<ArrayObject>()&&!ObjectMayHaveExtraIndexedProperties(obj)){// This loop handles all elements up to initializedLength. If// length > initLength we rely on the second loop to add the// other elements.uint32_tinitLength=obj->as<ArrayObject>().getDenseInitializedLength();while(i<initLength){if(!CheckForInterrupt(cx))returnfalse;constValue&elem=obj->as<ArrayObject>().getDenseElement(i);if(elem.isString()){if(!sb.append(elem.toString()))returnfalse;}elseif(elem.isNumber()){if(!NumberValueToStringBuffer(cx,elem,sb))returnfalse;}elseif(elem.isBoolean()){if(!BooleanToStringBuffer(elem.toBoolean(),sb))returnfalse;}elseif(elem.isObject()||elem.isSymbol()){/* * Object stringifying could modify the initialized length or make * the array sparse. Delegate it to a separate loop to keep this * one tight. * * Symbol stringifying is a TypeError, so into the slow path * with those as well. */break;}else{MOZ_ASSERT(elem.isMagic(JS_ELEMENTS_HOLE)||elem.isNullOrUndefined());}if(++i!=length&&!sepOp(cx,sb))returnfalse;}}if(i!=length){RootedValuev(cx);while(i<length){if(!CheckForInterrupt(cx))returnfalse;boolhole;if(!GetElement(cx,obj,i,&hole,&v))returnfalse;if(!hole&&!v.isNullOrUndefined()){if(Locale){JSObject*robj=ToObject(cx,v);if(!robj)returnfalse;RootedIdid(cx,NameToId(cx->names().toLocaleString));if(!robj->callMethod(cx,id,0,nullptr,&v))returnfalse;}if(!ValueToStringBuffer(cx,v,sb))returnfalse;}if(++i!=length&&!sepOp(cx,sb))returnfalse;}}returntrue;}template<boolLocale>JSString*js::ArrayJoin(JSContext*cx,HandleObjectobj,HandleLinearStringsepstr,uint32_tlength){// This method is shared by Array.prototype.join and// Array.prototype.toLocaleString. The steps in ES5 are nearly the same, so// the annotations in this function apply to both toLocaleString and join.// Steps 1 to 6, should be done by the caller.// Step 6 is implicit in the loops below.// An optimized version of a special case of steps 7-11: when length==1 and// the 0th element is a string, ToString() of that element is a no-op and// so it can be immediately returned as the result.if(length==1&&!Locale&&obj->is<ArrayObject>()&&obj->as<ArrayObject>().getDenseInitializedLength()==1){constValue&elem0=obj->as<ArrayObject>().getDenseElement(0);if(elem0.isString()){returnelem0.toString();}}StringBuffersb(cx);if(sepstr->hasTwoByteChars()&&!sb.ensureTwoByteChars())returnnullptr;// The separator will be added |length - 1| times, reserve space for that// so that we don't have to unnecessarily grow the buffer.size_tseplen=sepstr->length();CheckedInt<uint32_t>res=CheckedInt<uint32_t>(seplen)*(length-1);if(length>0&&!res.isValid()){ReportAllocationOverflow(cx);returnnullptr;}if(length>0&&!sb.reserve(res.value()))returnnullptr;// Various optimized versions of steps 7-10.if(seplen==0){EmptySeparatorOpop;if(!ArrayJoinKernel<Locale>(cx,op,obj,length,sb))returnnullptr;}elseif(seplen==1){char16_tc=sepstr->latin1OrTwoByteChar(0);if(c<=JSString::MAX_LATIN1_CHAR){CharSeparatorOp<Latin1Char>op(c);if(!ArrayJoinKernel<Locale>(cx,op,obj,length,sb))returnnullptr;}else{CharSeparatorOp<char16_t>op(c);if(!ArrayJoinKernel<Locale>(cx,op,obj,length,sb))returnnullptr;}}else{StringSeparatorOpop(sepstr);if(!ArrayJoinKernel<Locale>(cx,op,obj,length,sb))returnnullptr;}// Step 11JSString*str=sb.finishString();if(!str)returnnullptr;returnstr;}template<boolLocale>boolArrayJoin(JSContext*cx,CallArgs&args){// Step 1RootedObjectobj(cx,ToObject(cx,args.thisv()));if(!obj)returnfalse;AutoCycleDetectordetector(cx,obj);if(!detector.init())returnfalse;if(detector.foundCycle()){args.rval().setString(cx->names().empty);returntrue;}// Steps 2 and 3uint32_tlength;if(!GetLengthProperty(cx,obj,&length))returnfalse;// Steps 4 and 5RootedLinearStringsepstr(cx);if(!Locale&&args.hasDefined(0)){JSString*s=ToString<CanGC>(cx,args[0]);if(!s)returnfalse;sepstr=s->ensureLinear(cx);if(!sepstr)returnfalse;}else{sepstr=cx->names().comma;}// Step 6 to 11JSString*res=js::ArrayJoin<Locale>(cx,obj,sepstr,length);if(!res)returnfalse;args.rval().setString(res);returntrue;}/* ES5 15.4.4.2. NB: The algorithm here differs from the one in ES3. */staticboolarray_toString(JSContext*cx,unsignedargc,Value*vp){JS_CHECK_RECURSION(cx,returnfalse);CallArgsargs=CallArgsFromVp(argc,vp);RootedObjectobj(cx,ToObject(cx,args.thisv()));if(!obj)returnfalse;RootedValuejoin(cx,args.calleev());if(!GetProperty(cx,obj,obj,cx->names().join,&join))returnfalse;if(!IsCallable(join)){JSString*str=JS_BasicObjectToString(cx,obj);if(!str)returnfalse;args.rval().setString(str);returntrue;}InvokeArgsargs2(cx);if(!args2.init(0))returnfalse;args2.setCallee(join);args2.setThis(ObjectValue(*obj));/* Do the call. */if(!Invoke(cx,args2))returnfalse;args.rval().set(args2.rval());returntrue;}/* ES5 15.4.4.3 */staticboolarray_toLocaleString(JSContext*cx,unsignedargc,Value*vp){JS_CHECK_RECURSION(cx,returnfalse);CallArgsargs=CallArgsFromVp(argc,vp);returnArrayJoin<true>(cx,args);}/* ES5 15.4.4.5 */booljs::array_join(JSContext*cx,unsignedargc,Value*vp){JS_CHECK_RECURSION(cx,returnfalse);CallArgsargs=CallArgsFromVp(argc,vp);returnArrayJoin<false>(cx,args);}staticinlineboolInitArrayTypes(JSContext*cx,ObjectGroup*group,JSObject*obj,constValue*vector,unsignedcount){if(!group->unknownProperties()){AutoEnterAnalysisenter(cx);HeapTypeSet*types=group->getProperty(cx,obj,JSID_VOID);if(!types)returnfalse;for(unsignedi=0;i<count;i++){if(vector[i].isMagic(JS_ELEMENTS_HOLE))continue;types->addType(cx,TypeSet::GetValueType(vector[i]));}}returntrue;}enumShouldUpdateTypes{UpdateTypes=true,DontUpdateTypes=false};/* vector must point to rooted memory. */staticboolInitArrayElements(JSContext*cx,HandleObjectobj,uint32_tstart,uint32_tcount,constValue*vector,ShouldUpdateTypesupdateTypes){MOZ_ASSERT(count<=MAX_ARRAY_INDEX);if(count==0)returntrue;ObjectGroup*group=obj->getGroup(cx);if(!group)returnfalse;if(updateTypes&&!InitArrayTypes(cx,group,obj,vector,count))returnfalse;/* * Optimize for dense arrays so long as adding the given set of elements * wouldn't otherwise make the array slow or exceed a non-writable array * length. */do{if(!obj->is<ArrayObject>())break;if(ObjectMayHaveExtraIndexedProperties(obj))break;HandleArrayObjectarr=obj.as<ArrayObject>();if(arr->shouldConvertDoubleElements())break;if(!arr->lengthIsWritable()&&start+count>arr->length())break;NativeObject::EnsureDenseResultresult=arr->ensureDenseElements(cx,start,count);if(result!=NativeObject::ED_OK){if(result==NativeObject::ED_FAILED)returnfalse;MOZ_ASSERT(result==NativeObject::ED_SPARSE);break;}uint32_tnewlen=start+count;if(newlen>arr->length())arr->setLengthInt32(newlen);MOZ_ASSERT(count<UINT32_MAX/sizeof(Value));arr->copyDenseElements(start,vector,count);MOZ_ASSERT_IF(count!=0,!arr->getDenseElement(newlen-1).isMagic(JS_ELEMENTS_HOLE));returntrue;}while(false);constValue*end=vector+count;while(vector<end&&start<=MAX_ARRAY_INDEX){if(!CheckForInterrupt(cx)||!SetArrayElement(cx,obj,start++,HandleValue::fromMarkedLocation(vector++))){returnfalse;}}if(vector==end)returntrue;MOZ_ASSERT(start==MAX_ARRAY_INDEX+1);RootedValuevalue(cx);RootedIdid(cx);RootedValueindexv(cx);doubleindex=MAX_ARRAY_INDEX+1;do{value=*vector++;indexv=DoubleValue(index);if(!ValueToId<CanGC>(cx,indexv,&id))returnfalse;if(!SetProperty(cx,obj,id,value))returnfalse;index+=1;}while(vector!=end);returntrue;}staticboolarray_reverse(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);RootedObjectobj(cx,ToObject(cx,args.thisv()));if(!obj)returnfalse;uint32_tlen;if(!GetLengthProperty(cx,obj,&len))returnfalse;do{if(!obj->is<ArrayObject>())break;if(ObjectMayHaveExtraIndexedProperties(obj))break;HandleArrayObjectarr=obj.as<ArrayObject>();/* An empty array or an array with no elements is already reversed. */if(len==0||arr->getDenseCapacity()==0){args.rval().setObject(*obj);returntrue;}/* * It's actually surprisingly complicated to reverse an array due to the * orthogonality of array length and array capacity while handling * leading and trailing holes correctly. Reversing seems less likely to * be a common operation than other array mass-mutation methods, so for * now just take a probably-small memory hit (in the absence of too many * holes in the array at its start) and ensure that the capacity is * sufficient to hold all the elements in the array if it were full. */NativeObject::EnsureDenseResultresult=arr->ensureDenseElements(cx,len,0);if(result!=NativeObject::ED_OK){if(result==NativeObject::ED_FAILED)returnfalse;MOZ_ASSERT(result==NativeObject::ED_SPARSE);break;}/* Fill out the array's initialized length to its proper length. */arr->ensureDenseInitializedLength(cx,len,0);RootedValueoriglo(cx),orighi(cx);uint32_tlo=0,hi=len-1;for(;lo<hi;lo++,hi--){origlo=arr->getDenseElement(lo);orighi=arr->getDenseElement(hi);arr->setDenseElement(lo,orighi);if(orighi.isMagic(JS_ELEMENTS_HOLE)&&!SuppressDeletedProperty(cx,arr,INT_TO_JSID(lo))){returnfalse;}arr->setDenseElement(hi,origlo);if(origlo.isMagic(JS_ELEMENTS_HOLE)&&!SuppressDeletedProperty(cx,arr,INT_TO_JSID(hi))){returnfalse;}}/* * Per ECMA-262, don't update the length of the array, even if the new * array has trailing holes (and thus the original array began with * holes). */args.rval().setObject(*arr);returntrue;}while(false);RootedValuelowval(cx),hival(cx);for(uint32_ti=0,half=len/2;i<half;i++){boolhole,hole2;if(!CheckForInterrupt(cx)||!GetElement(cx,obj,i,&hole,&lowval)||!GetElement(cx,obj,len-i-1,&hole2,&hival)){returnfalse;}if(!hole&&!hole2){if(!SetArrayElement(cx,obj,i,hival))returnfalse;if(!SetArrayElement(cx,obj,len-i-1,lowval))returnfalse;}elseif(hole&&!hole2){if(!SetArrayElement(cx,obj,i,hival))returnfalse;if(!DeletePropertyOrThrow(cx,obj,len-i-1))returnfalse;}elseif(!hole&&hole2){if(!DeletePropertyOrThrow(cx,obj,i))returnfalse;if(!SetArrayElement(cx,obj,len-i-1,lowval))returnfalse;}else{// No action required.}}args.rval().setObject(*obj);returntrue;}staticinlineboolCompareStringValues(JSContext*cx,constValue&a,constValue&b,bool*lessOrEqualp){if(!CheckForInterrupt(cx))returnfalse;JSString*astr=a.toString();JSString*bstr=b.toString();int32_tresult;if(!CompareStrings(cx,astr,bstr,&result))returnfalse;*lessOrEqualp=(result<=0);returntrue;}staticconstuint64_tpowersOf10[]={1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000,1000000000000ULL};staticinlineunsignedNumDigitsBase10(uint32_tn){/* * This is just floor_log10(n) + 1 * Algorithm taken from * http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 */uint32_tlog2=CeilingLog2(n);uint32_tt=log2*1233>>12;returnt-(n<powersOf10[t])+1;}staticinlineboolCompareLexicographicInt32(constValue&a,constValue&b,bool*lessOrEqualp){int32_taint=a.toInt32();int32_tbint=b.toInt32();/* * If both numbers are equal ... trivial * If only one of both is negative --> arithmetic comparison as char code * of '-' is always less than any other digit * If both numbers are negative convert them to positive and continue * handling ... */if(aint==bint){*lessOrEqualp=true;}elseif((aint<0)&&(bint>=0)){*lessOrEqualp=true;}elseif((aint>=0)&&(bint<0)){*lessOrEqualp=false;}else{uint32_tauint=Abs(aint);uint32_tbuint=Abs(bint);/* * ... get number of digits of both integers. * If they have the same number of digits --> arithmetic comparison. * If digits_a > digits_b: a < b*10e(digits_a - digits_b). * If digits_b > digits_a: a*10e(digits_b - digits_a) <= b. */unsigneddigitsa=NumDigitsBase10(auint);unsigneddigitsb=NumDigitsBase10(buint);if(digitsa==digitsb){*lessOrEqualp=(auint<=buint);}elseif(digitsa>digitsb){MOZ_ASSERT((digitsa-digitsb)<ArrayLength(powersOf10));*lessOrEqualp=(uint64_t(auint)<uint64_t(buint)*powersOf10[digitsa-digitsb]);}else{/* if (digitsb > digitsa) */MOZ_ASSERT((digitsb-digitsa)<ArrayLength(powersOf10));*lessOrEqualp=(uint64_t(auint)*powersOf10[digitsb-digitsa]<=uint64_t(buint));}}returntrue;}template<typenameChar1,typenameChar2>staticinlineboolCompareSubStringValues(JSContext*cx,constChar1*s1,size_tlen1,constChar2*s2,size_tlen2,bool*lessOrEqualp){if(!CheckForInterrupt(cx))returnfalse;if(!s1||!s2)returnfalse;int32_tresult=CompareChars(s1,len1,s2,len2);*lessOrEqualp=(result<=0);returntrue;}namespace{structSortComparatorStrings{JSContext*constcx;explicitSortComparatorStrings(JSContext*cx):cx(cx){}booloperator()(constValue&a,constValue&b,bool*lessOrEqualp){returnCompareStringValues(cx,a,b,lessOrEqualp);}};structSortComparatorLexicographicInt32{booloperator()(constValue&a,constValue&b,bool*lessOrEqualp){returnCompareLexicographicInt32(a,b,lessOrEqualp);}};structStringifiedElement{size_tcharsBegin;size_tcharsEnd;size_telementIndex;};structSortComparatorStringifiedElements{JSContext*constcx;constStringBuffer&sb;SortComparatorStringifiedElements(JSContext*cx,constStringBuffer&sb):cx(cx),sb(sb){}booloperator()(constStringifiedElement&a,constStringifiedElement&b,bool*lessOrEqualp){size_tlenA=a.charsEnd-a.charsBegin;size_tlenB=b.charsEnd-b.charsBegin;if(sb.isUnderlyingBufferLatin1()){returnCompareSubStringValues(cx,sb.rawLatin1Begin()+a.charsBegin,lenA,sb.rawLatin1Begin()+b.charsBegin,lenB,lessOrEqualp);}returnCompareSubStringValues(cx,sb.rawTwoByteBegin()+a.charsBegin,lenA,sb.rawTwoByteBegin()+b.charsBegin,lenB,lessOrEqualp);}};structSortComparatorFunction{JSContext*constcx;constValue&fval;FastInvokeGuard&fig;SortComparatorFunction(JSContext*cx,constValue&fval,FastInvokeGuard&fig):cx(cx),fval(fval),fig(fig){}booloperator()(constValue&a,constValue&b,bool*lessOrEqualp);};boolSortComparatorFunction::operator()(constValue&a,constValue&b,bool*lessOrEqualp){/* * array_sort deals with holes and undefs on its own and they should not * come here. */MOZ_ASSERT(!a.isMagic()&&!a.isUndefined());MOZ_ASSERT(!a.isMagic()&&!b.isUndefined());if(!CheckForInterrupt(cx))returnfalse;InvokeArgs&args=fig.args();if(!args.init(2))returnfalse;args.setCallee(fval);args.setThis(UndefinedValue());args[0].set(a);args[1].set(b);if(!fig.invoke(cx))returnfalse;doublecmp;if(!ToNumber(cx,args.rval(),&cmp))returnfalse;/* * XXX eport some kind of error here if cmp is NaN? ECMA talks about * 'consistent compare functions' that don't return NaN, but is silent * about what the result should be. So we currently ignore it. */*lessOrEqualp=(IsNaN(cmp)||cmp<=0);returntrue;}structNumericElement{doubledv;size_telementIndex;};staticboolComparatorNumericLeftMinusRight(constNumericElement&a,constNumericElement&b,bool*lessOrEqualp){*lessOrEqualp=(a.dv<=b.dv);returntrue;}staticboolComparatorNumericRightMinusLeft(constNumericElement&a,constNumericElement&b,bool*lessOrEqualp){*lessOrEqualp=(b.dv<=a.dv);returntrue;}typedefbool(*ComparatorNumeric)(constNumericElement&a,constNumericElement&b,bool*lessOrEqualp);staticconstComparatorNumericSortComparatorNumerics[]={nullptr,nullptr,ComparatorNumericLeftMinusRight,ComparatorNumericRightMinusLeft};staticboolComparatorInt32LeftMinusRight(constValue&a,constValue&b,bool*lessOrEqualp){*lessOrEqualp=(a.toInt32()<=b.toInt32());returntrue;}staticboolComparatorInt32RightMinusLeft(constValue&a,constValue&b,bool*lessOrEqualp){*lessOrEqualp=(b.toInt32()<=a.toInt32());returntrue;}typedefbool(*ComparatorInt32)(constValue&a,constValue&b,bool*lessOrEqualp);staticconstComparatorInt32SortComparatorInt32s[]={nullptr,nullptr,ComparatorInt32LeftMinusRight,ComparatorInt32RightMinusLeft};// Note: Values for this enum must match up with SortComparatorNumerics// and SortComparatorInt32s.enumComparatorMatchResult{Match_Failure=0,Match_None,Match_LeftMinusRight,Match_RightMinusLeft};}/* namespace anonymous *//* * Specialize behavior for comparator functions with particular common bytecode * patterns: namely, |return x - y| and |return y - x|. */staticComparatorMatchResultMatchNumericComparator(JSContext*cx,constValue&v){if(!v.isObject())returnMatch_None;JSObject&obj=v.toObject();if(!obj.is<JSFunction>())returnMatch_None;JSFunction*fun=&obj.as<JSFunction>();if(!fun->isInterpreted())returnMatch_None;JSScript*script=fun->getOrCreateScript(cx);if(!script)returnMatch_Failure;jsbytecode*pc=script->code();uint16_targ0,arg1;if(JSOp(*pc)!=JSOP_GETARG)returnMatch_None;arg0=GET_ARGNO(pc);pc+=JSOP_GETARG_LENGTH;if(JSOp(*pc)!=JSOP_GETARG)returnMatch_None;arg1=GET_ARGNO(pc);pc+=JSOP_GETARG_LENGTH;if(JSOp(*pc)!=JSOP_SUB)returnMatch_None;pc+=JSOP_SUB_LENGTH;if(JSOp(*pc)!=JSOP_RETURN)returnMatch_None;if(arg0==0&&arg1==1)returnMatch_LeftMinusRight;if(arg0==1&&arg1==0)returnMatch_RightMinusLeft;returnMatch_None;}template<typenameK,typenameC>staticinlineboolMergeSortByKey(Kkeys,size_tlen,Kscratch,Ccomparator,AutoValueVector*vec){MOZ_ASSERT(vec->length()>=len);/* Sort keys. */if(!MergeSort(keys,len,scratch,comparator))returnfalse;/* * Reorder vec by keys in-place, going element by element. When an out-of- * place element is encountered, move that element to its proper position, * displacing whatever element was at *that* point to its proper position, * and so on until an element must be moved to the current position. * * At each outer iteration all elements up to |i| are sorted. If * necessary each inner iteration moves some number of unsorted elements * (including |i|) directly to sorted position. Thus on completion |*vec| * is sorted, and out-of-position elements have moved once. Complexity is * Θ(len) + O(len) == O(2*len), with each element visited at most twice. */for(size_ti=0;i<len;i++){size_tj=keys[i].elementIndex;if(i==j)continue;// fixed pointMOZ_ASSERT(j>i,"Everything less than |i| should be in the right place!");Valuetv=(*vec)[j];do{size_tk=keys[j].elementIndex;keys[j].elementIndex=j;(*vec)[j].set((*vec)[k]);j=k;}while(j!=i);// We could assert the loop invariant that |i == keys[i].elementIndex|// here if we synced |keys[i].elementIndex|. But doing so would render// the assertion vacuous, so don't bother, even in debug builds.(*vec)[i].set(tv);}returntrue;}/* * Sort Values as strings. * * To minimize #conversions, SortLexicographically() first converts all Values * to strings at once, then sorts the elements by these cached strings. */staticboolSortLexicographically(JSContext*cx,AutoValueVector*vec,size_tlen){MOZ_ASSERT(vec->length()>=len);StringBuffersb(cx);Vector<StringifiedElement,0,TempAllocPolicy>strElements(cx);/* MergeSort uses the upper half as scratch space. */if(!strElements.reserve(2*len))returnfalse;/* Convert Values to strings. */size_tcursor=0;for(size_ti=0;i<len;i++){if(!CheckForInterrupt(cx))returnfalse;if(!ValueToStringBuffer(cx,(*vec)[i],sb))returnfalse;StringifiedElementel={cursor,sb.length(),i};strElements.infallibleAppend(el);cursor=sb.length();}/* Resize strElements so we can perform MergeSort. */JS_ALWAYS_TRUE(strElements.resize(2*len));/* Sort Values in vec alphabetically. */returnMergeSortByKey(strElements.begin(),len,strElements.begin()+len,SortComparatorStringifiedElements(cx,sb),vec);}/* * Sort Values as numbers. * * To minimize #conversions, SortNumerically first converts all Values to * numerics at once, then sorts the elements by these cached numerics. */staticboolSortNumerically(JSContext*cx,AutoValueVector*vec,size_tlen,ComparatorMatchResultcomp){MOZ_ASSERT(vec->length()>=len);Vector<NumericElement,0,TempAllocPolicy>numElements(cx);/* MergeSort uses the upper half as scratch space. */if(!numElements.reserve(2*len))returnfalse;/* Convert Values to numerics. */for(size_ti=0;i<len;i++){if(!CheckForInterrupt(cx))returnfalse;doubledv;if(!ToNumber(cx,(*vec)[i],&dv))returnfalse;NumericElementel={dv,i};numElements.infallibleAppend(el);}/* Resize strElements so we can perform MergeSort. */JS_ALWAYS_TRUE(numElements.resize(2*len));/* Sort Values in vec numerically. */returnMergeSortByKey(numElements.begin(),len,numElements.begin()+len,SortComparatorNumerics[comp],vec);}booljs::array_sort(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);RootedValuefvalRoot(cx);Value&fval=fvalRoot.get();if(args.hasDefined(0)){if(args[0].isPrimitive()){JS_ReportErrorNumber(cx,GetErrorMessage,nullptr,JSMSG_BAD_SORT_ARG);returnfalse;}fval=args[0];/* non-default compare function */}else{fval.setNull();}RootedObjectobj(cx,ToObject(cx,args.thisv()));if(!obj)returnfalse;uint32_tlen;if(!GetLengthProperty(cx,obj,&len))returnfalse;if(len<2){/* [] and [a] remain unchanged when sorted. */args.rval().setObject(*obj);returntrue;}/* * We need a temporary array of 2 * len Value to hold the array elements * and the scratch space for merge sort. Check that its size does not * overflow size_t, which would allow for indexing beyond the end of the * malloc'd vector. */#if JS_BITS_PER_WORD == 32if(size_t(len)>size_t(-1)/(2*sizeof(Value))){ReportAllocationOverflow(cx);returnfalse;}#endif/* * Initialize vec as a root. We will clear elements of vec one by * one while increasing the rooted amount of vec when we know that the * property at the corresponding index exists and its value must be rooted. * * In this way when sorting a huge mostly sparse array we will not * access the tail of vec corresponding to properties that do not * exist, allowing OS to avoiding committing RAM. See bug 330812. */size_tn,undefs;{AutoValueVectorvec(cx);if(!vec.reserve(2*size_t(len)))returnfalse;/* * By ECMA 262, 15.4.4.11, a property that does not exist (which we * call a "hole") is always greater than an existing property with * value undefined and that is always greater than any other property. * Thus to sort holes and undefs we simply count them, sort the rest * of elements, append undefs after them and then make holes after * undefs. */undefs=0;boolallStrings=true;boolallInts=true;RootedValuev(cx);for(uint32_ti=0;i<len;i++){if(!CheckForInterrupt(cx))returnfalse;/* Clear vec[newlen] before including it in the rooted set. */boolhole;if(!GetElement(cx,obj,i,&hole,&v))returnfalse;if(hole)continue;if(v.isUndefined()){++undefs;continue;}vec.infallibleAppend(v);allStrings=allStrings&&v.isString();allInts=allInts&&v.isInt32();}/* * If the array only contains holes, we're done. But if it contains * undefs, those must be sorted to the front of the array. */n=vec.length();if(n==0&&undefs==0){args.rval().setObject(*obj);returntrue;}/* Here len == n + undefs + number_of_holes. */if(fval.isNull()){/* * Sort using the default comparator converting all elements to * strings. */if(allStrings){JS_ALWAYS_TRUE(vec.resize(n*2));if(!MergeSort(vec.begin(),n,vec.begin()+n,SortComparatorStrings(cx)))returnfalse;}elseif(allInts){JS_ALWAYS_TRUE(vec.resize(n*2));if(!MergeSort(vec.begin(),n,vec.begin()+n,SortComparatorLexicographicInt32())){returnfalse;}}else{if(!SortLexicographically(cx,&vec,n))returnfalse;}}else{ComparatorMatchResultcomp=MatchNumericComparator(cx,fval);if(comp==Match_Failure)returnfalse;if(comp!=Match_None){if(allInts){JS_ALWAYS_TRUE(vec.resize(n*2));if(!MergeSort(vec.begin(),n,vec.begin()+n,SortComparatorInt32s[comp]))returnfalse;}else{if(!SortNumerically(cx,&vec,n,comp))returnfalse;}}else{FastInvokeGuardfig(cx,fval);JS_ALWAYS_TRUE(vec.resize(n*2));if(!MergeSort(vec.begin(),n,vec.begin()+n,SortComparatorFunction(cx,fval,fig))){returnfalse;}}}if(!InitArrayElements(cx,obj,0,uint32_t(n),vec.begin(),DontUpdateTypes))returnfalse;}/* Set undefs that sorted after the rest of elements. */while(undefs!=0){--undefs;if(!CheckForInterrupt(cx)||!SetArrayElement(cx,obj,n++,UndefinedHandleValue))returnfalse;}/* Re-create any holes that sorted to the end of the array. */while(len>n){if(!CheckForInterrupt(cx)||!DeletePropertyOrThrow(cx,obj,--len))returnfalse;}args.rval().setObject(*obj);returntrue;}booljs::NewbornArrayPush(JSContext*cx,HandleObjectobj,constValue&v){Rooted<ArrayObject*>arr(cx,&obj->as<ArrayObject>());MOZ_ASSERT(!v.isMagic());MOZ_ASSERT(arr->lengthIsWritable());uint32_tlength=arr->length();MOZ_ASSERT(length<=arr->getDenseCapacity());if(!arr->ensureElements(cx,length+1))returnfalse;arr->setDenseInitializedLength(length+1);arr->setLengthInt32(length+1);arr->initDenseElementWithType(cx,length,v);returntrue;}/* ES5 15.4.4.7 */booljs::array_push(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);/* Step 1. */RootedObjectobj(cx,ToObject(cx,args.thisv()));if(!obj)returnfalse;/* Steps 2-3. */uint32_tlength;if(!GetLengthProperty(cx,obj,&length))returnfalse;/* Fast path for native objects with dense elements. */do{if(!obj->isNative()||IsAnyTypedArray(obj.get()))break;if(obj->is<ArrayObject>()&&!obj->as<ArrayObject>().lengthIsWritable())break;if(ObjectMayHaveExtraIndexedProperties(obj))break;uint32_targCount=args.length();NativeObject::EnsureDenseResultresult=obj->as<NativeObject>().ensureDenseElements(cx,length,argCount);if(result==NativeObject::ED_FAILED)returnfalse;if(result==NativeObject::ED_OK){for(uint32_ti=0,index=length;i<argCount;index++,i++)obj->as<NativeObject>().setDenseElementWithType(cx,index,args[i]);uint32_tnewlength=length+argCount;args.rval().setNumber(newlength);if(obj->is<ArrayObject>()){obj->as<ArrayObject>().setLengthInt32(newlength);returntrue;}returnSetLengthProperty(cx,obj,newlength);}MOZ_ASSERT(result==NativeObject::ED_SPARSE);}while(false);/* Steps 4-5. */if(!InitArrayElements(cx,obj,length,args.length(),args.array(),UpdateTypes))returnfalse;/* Steps 6-7. */doublenewlength=length+double(args.length());args.rval().setNumber(newlength);returnSetLengthProperty(cx,obj,newlength);}/* ES6 20130308 draft 15.4.4.6. */booljs::array_pop(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);/* Step 1. */RootedObjectobj(cx,ToObject(cx,args.thisv()));if(!obj)returnfalse;/* Steps 2-3. */uint32_tindex;if(!GetLengthProperty(cx,obj,&index))returnfalse;/* Steps 4-5. */if(index==0){/* Step 4b. */args.rval().setUndefined();}else{/* Step 5a. */index--;/* Step 5b, 5e. */boolhole;if(!GetElement(cx,obj,index,&hole,args.rval()))returnfalse;/* Step 5c. */if(!hole&&!DeletePropertyOrThrow(cx,obj,index))returnfalse;}/* Steps 4a, 5d. */returnSetLengthProperty(cx,obj,index);}voidjs::ArrayShiftMoveElements(ArrayObject*obj){MOZ_ASSERT(obj->lengthIsWritable());/* * At this point the length and initialized length have already been * decremented and the result fetched, so just shift the array elements * themselves. */uint32_tinitlen=obj->getDenseInitializedLength();obj->moveDenseElementsNoPreBarrier(0,1,initlen);}/* ES5 15.4.4.9 */booljs::array_shift(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);/* Step 1. */RootedObjectobj(cx,ToObject(cx,args.thisv()));if(!obj)returnfalse;/* Steps 2-3. */uint32_tlen;if(!GetLengthProperty(cx,obj,&len))returnfalse;/* Step 4. */if(len==0){/* Step 4a. */if(!SetLengthProperty(cx,obj,0))returnfalse;/* Step 4b. */args.rval().setUndefined();returntrue;}uint32_tnewlen=len-1;/* Fast paths. */if(obj->is<ArrayObject>()){ArrayObject*aobj=&obj->as<ArrayObject>();if(aobj->getDenseInitializedLength()>0&&newlen<aobj->getDenseCapacity()&&!ObjectMayHaveExtraIndexedProperties(aobj)){args.rval().set(aobj->getDenseElement(0));if(args.rval().isMagic(JS_ELEMENTS_HOLE))args.rval().setUndefined();if(!aobj->maybeCopyElementsForWrite(cx))returnfalse;aobj->moveDenseElements(0,1,aobj->getDenseInitializedLength()-1);aobj->setDenseInitializedLength(aobj->getDenseInitializedLength()-1);if(!SetLengthProperty(cx,obj,newlen))returnfalse;returnSuppressDeletedProperty(cx,obj,INT_TO_JSID(newlen));}}/* Steps 5, 10. */boolhole;if(!GetElement(cx,obj,uint32_t(0),&hole,args.rval()))returnfalse;/* Steps 6-7. */RootedValuevalue(cx);for(uint32_ti=0;i<newlen;i++){if(!CheckForInterrupt(cx))returnfalse;if(!GetElement(cx,obj,i+1,&hole,&value))returnfalse;if(hole){if(!DeletePropertyOrThrow(cx,obj,i))returnfalse;}else{if(!SetArrayElement(cx,obj,i,value))returnfalse;}}/* Step 8. */if(!DeletePropertyOrThrow(cx,obj,newlen))returnfalse;/* Step 9. */returnSetLengthProperty(cx,obj,newlen);}booljs::array_unshift(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);RootedObjectobj(cx,ToObject(cx,args.thisv()));if(!obj)returnfalse;uint32_tlength;if(!GetLengthProperty(cx,obj,&length))returnfalse;doublenewlen=length;if(args.length()>0){/* Slide up the array to make room for all args at the bottom. */if(length>0){booloptimized=false;do{if(!obj->is<ArrayObject>())break;if(ObjectMayHaveExtraIndexedProperties(obj))break;ArrayObject*aobj=&obj->as<ArrayObject>();if(!aobj->lengthIsWritable())break;NativeObject::EnsureDenseResultresult=aobj->ensureDenseElements(cx,length,args.length());if(result!=NativeObject::ED_OK){if(result==NativeObject::ED_FAILED)returnfalse;MOZ_ASSERT(result==NativeObject::ED_SPARSE);break;}aobj->moveDenseElements(args.length(),0,length);for(uint32_ti=0;i<args.length();i++)aobj->setDenseElement(i,MagicValue(JS_ELEMENTS_HOLE));optimized=true;}while(false);if(!optimized){doublelast=length;doubleupperIndex=last+args.length();RootedValuevalue(cx);do{--last,--upperIndex;boolhole;if(!CheckForInterrupt(cx))returnfalse;if(!GetElement(cx,obj,last,&hole,&value))returnfalse;if(hole){if(!DeletePropertyOrThrow(cx,obj,upperIndex))returnfalse;}else{if(!SetArrayElement(cx,obj,upperIndex,value))returnfalse;}}while(last!=0);}}/* Copy from args to the bottom of the array. */if(!InitArrayElements(cx,obj,0,args.length(),args.array(),UpdateTypes))returnfalse;newlen+=args.length();}if(!SetLengthProperty(cx,obj,newlen))returnfalse;/* Follow Perl by returning the new array length. */args.rval().setNumber(newlen);returntrue;}staticinlinevoidTryReuseArrayGroup(JSObject*obj,ArrayObject*narr){/* * Try to change the group of a newly created array narr to the same group * as obj. This can only be performed if the original object is an array * and has the same prototype. */MOZ_ASSERT(ObjectGroup::hasDefaultNewGroup(narr->getProto(),&ArrayObject::class_,narr->group()));if(obj->is<ArrayObject>()&&!obj->isSingleton()&&!obj->group()->hasUnanalyzedPreliminaryObjects()&&obj->getProto()==narr->getProto()){narr->setGroup(obj->group());}}/* * Returns true if this is a dense array whose |count| properties starting from * |startingIndex| may be accessed (get, set, delete) directly through its * contiguous vector of elements without fear of getters, setters, etc. along * the prototype chain, or of enumerators requiring notification of * modifications. */staticinlineboolCanOptimizeForDenseStorage(HandleObjectarr,uint32_tstartingIndex,uint32_tcount,JSContext*cx){/* If the desired properties overflow dense storage, we can't optimize. */if(UINT32_MAX-startingIndex<count)returnfalse;/* There's no optimizing possible if it's not an array. */if(!arr->is<ArrayObject>())returnfalse;/* * Don't optimize if the array might be in the midst of iteration. We * rely on this to be able to safely move dense array elements around with * just a memmove (see JSObject::moveDenseArrayElements), without worrying * about updating any in-progress enumerators for properties implicitly * deleted if a hole is moved from one location to another location not yet * visited. See bug 690622. * * Another potential wrinkle: what if the enumeration is happening on an * object which merely has |arr| on its prototype chain? It turns out this * case can't happen, because any dense array used as the prototype of * another object is first slowified, for type inference's sake. */ObjectGroup*arrGroup=arr->getGroup(cx);if(MOZ_UNLIKELY(!arrGroup||arrGroup->hasAllFlags(OBJECT_FLAG_ITERATED)))returnfalse;/* * Now watch out for getters and setters along the prototype chain or in * other indexed properties on the object. (Note that non-writable length * is subsumed by the initializedLength comparison.) */return!ObjectMayHaveExtraIndexedProperties(arr)&&startingIndex+count<=arr->as<ArrayObject>().getDenseInitializedLength();}/* ES5 15.4.4.12. */booljs::array_splice(JSContext*cx,unsignedargc,Value*vp){returnarray_splice_impl(cx,argc,vp,true);}booljs::array_splice_impl(JSContext*cx,unsignedargc,Value*vp,boolreturnValueIsUsed){CallArgsargs=CallArgsFromVp(argc,vp);/* Step 1. */RootedObjectobj(cx,ToObject(cx,args.thisv()));if(!obj)returnfalse;/* Steps 3-4. */uint32_tlen;if(!GetLengthProperty(cx,obj,&len))returnfalse;/* Step 5. */doublerelativeStart;if(!ToInteger(cx,args.get(0),&relativeStart))returnfalse;/* Step 6. */uint32_tactualStart;if(relativeStart<0)actualStart=Max(len+relativeStart,0.0);elseactualStart=Min(relativeStart,double(len));/* Step 7. */uint32_tactualDeleteCount;if(args.length()!=1){doubledeleteCountDouble;RootedValuecnt(cx,args.length()>=2?args[1]:Int32Value(0));if(!ToInteger(cx,cnt,&deleteCountDouble))returnfalse;actualDeleteCount=Min(Max(deleteCountDouble,0.0),double(len-actualStart));}else{/* * Non-standard: if start was specified but deleteCount was omitted, * delete to the end of the array. See bug 668024 for discussion. */actualDeleteCount=len-actualStart;}MOZ_ASSERT(len-actualStart>=actualDeleteCount);/* Steps 2, 8-9. */Rooted<ArrayObject*>arr(cx);if(CanOptimizeForDenseStorage(obj,actualStart,actualDeleteCount,cx)){if(returnValueIsUsed){arr=NewDenseCopiedArray(cx,actualDeleteCount,obj.as<ArrayObject>(),actualStart);if(!arr)returnfalse;TryReuseArrayGroup(obj,arr);}}else{arr=NewDenseFullyAllocatedArray(cx,actualDeleteCount);if(!arr)returnfalse;TryReuseArrayGroup(obj,arr);RootedValuefromValue(cx);for(uint32_tk=0;k<actualDeleteCount;k++){boolhole;if(!CheckForInterrupt(cx)||!GetElement(cx,obj,actualStart+k,&hole,&fromValue)||(!hole&&!DefineElement(cx,arr,k,fromValue))){returnfalse;}}}/* Step 11. */uint32_titemCount=(args.length()>=2)?(args.length()-2):0;if(itemCount<actualDeleteCount){/* Step 12: the array is being shrunk. */uint32_tsourceIndex=actualStart+actualDeleteCount;uint32_ttargetIndex=actualStart+itemCount;uint32_tfinalLength=len-actualDeleteCount+itemCount;if(CanOptimizeForDenseStorage(obj,0,len,cx)){ArrayObject*aobj=&obj->as<ArrayObject>();if(!aobj->maybeCopyElementsForWrite(cx))returnfalse;/* Steps 12(a)-(b). */aobj->moveDenseElements(targetIndex,sourceIndex,len-sourceIndex);/* * Update the initialized length. Do so before shrinking so that we * can apply the write barrier to the old slots. */aobj->setDenseInitializedLength(finalLength);/* Steps 12(c)-(d). */aobj->shrinkElements(cx,finalLength);}else{/* * This is all very slow if the length is very large. We don't yet * have the ability to iterate in sorted order, so we just do the * pessimistic thing and let CheckForInterrupt handle the * fallout. *//* Steps 12(a)-(b). */RootedValuefromValue(cx);for(uint32_tfrom=sourceIndex,to=targetIndex;from<len;from++,to++){if(!CheckForInterrupt(cx))returnfalse;boolhole;if(!GetElement(cx,obj,from,&hole,&fromValue))returnfalse;if(hole){if(!DeletePropertyOrThrow(cx,obj,to))returnfalse;}else{if(!SetArrayElement(cx,obj,to,fromValue))returnfalse;}}/* Steps 12(c)-(d). */for(uint32_tk=len;k>finalLength;k--){if(!DeletePropertyOrThrow(cx,obj,k-1))returnfalse;}}}elseif(itemCount>actualDeleteCount){/* Step 13. *//* * Optimize only if the array is already dense and we can extend it to * its new length. It would be wrong to extend the elements here for a * number of reasons. * * First, this could cause us to fall into the fast-path below. This * would cause elements to be moved into places past the non-writable * length. And when the dense initialized length is updated, that'll * cause the |in| operator to think that those elements actually exist, * even though, properly, setting them must fail. * * Second, extending the elements here will trigger assertions inside * ensureDenseElements that the elements aren't being extended past the * length of a non-writable array. This is because extending elements * will extend capacity -- which might extend them past a non-writable * length, violating the |capacity <= length| invariant for such * arrays. And that would make the various JITted fast-path method * implementations of [].push, [].unshift, and so on wrong. * * If the array length is non-writable, this method *will* throw. For * simplicity, have the slow-path code do it. (Also note that the slow * path may validly *not* throw -- if all the elements being moved are * holes.) */if(obj->is<ArrayObject>()){Rooted<ArrayObject*>arr(cx,&obj->as<ArrayObject>());if(arr->lengthIsWritable()){NativeObject::EnsureDenseResultres=arr->ensureDenseElements(cx,arr->length(),itemCount-actualDeleteCount);if(res==NativeObject::ED_FAILED)returnfalse;}}if(CanOptimizeForDenseStorage(obj,len,itemCount-actualDeleteCount,cx)){ArrayObject*aobj=&obj->as<ArrayObject>();if(!aobj->maybeCopyElementsForWrite(cx))returnfalse;aobj->moveDenseElements(actualStart+itemCount,actualStart+actualDeleteCount,len-(actualStart+actualDeleteCount));aobj->setDenseInitializedLength(len+itemCount-actualDeleteCount);}else{RootedValuefromValue(cx);for(doublek=len-actualDeleteCount;k>actualStart;k--){if(!CheckForInterrupt(cx))returnfalse;doublefrom=k+actualDeleteCount-1;doubleto=k+itemCount-1;boolhole;if(!GetElement(cx,obj,from,&hole,&fromValue))returnfalse;if(hole){if(!DeletePropertyOrThrow(cx,obj,to))returnfalse;}else{if(!SetArrayElement(cx,obj,to,fromValue))returnfalse;}}}}/* Step 10. */Value*items=args.array()+2;/* Steps 14-15. */for(uint32_tk=actualStart,i=0;i<itemCount;i++,k++){if(!SetArrayElement(cx,obj,k,HandleValue::fromMarkedLocation(&items[i])))returnfalse;}/* Step 16. */doublefinalLength=double(len)-actualDeleteCount+itemCount;if(!SetLengthProperty(cx,obj,finalLength))returnfalse;/* Step 17. */if(returnValueIsUsed)args.rval().setObject(*arr);returntrue;}booljs::array_concat_dense(JSContext*cx,Handle<ArrayObject*>arr1,Handle<ArrayObject*>arr2,Handle<ArrayObject*>result){uint32_tinitlen1=arr1->getDenseInitializedLength();MOZ_ASSERT(initlen1==arr1->length());uint32_tinitlen2=arr2->getDenseInitializedLength();MOZ_ASSERT(initlen2==arr2->length());/* No overflow here due to nelements limit. */uint32_tlen=initlen1+initlen2;if(!result->ensureElements(cx,len))returnfalse;MOZ_ASSERT(!result->getDenseInitializedLength());result->setDenseInitializedLength(len);result->initDenseElements(0,arr1->getDenseElements(),initlen1);result->initDenseElements(initlen1,arr2->getDenseElements(),initlen2);result->setLengthInt32(len);returntrue;}/* * Python-esque sequence operations. */booljs::array_concat(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);/* Treat our |this| object as the first argument; see ECMA 15.4.4.4. */Value*p=args.array()-1;/* Create a new Array object and root it using *vp. */RootedObjectaobj(cx,ToObject(cx,args.thisv()));if(!aobj)returnfalse;Rooted<ArrayObject*>narr(cx);uint32_tlength;if(aobj->is<ArrayObject>()&&!aobj->isIndexed()){length=aobj->as<ArrayObject>().length();uint32_tinitlen=aobj->as<ArrayObject>().getDenseInitializedLength();narr=NewDenseCopiedArray(cx,initlen,aobj.as<ArrayObject>(),0);if(!narr)returnfalse;TryReuseArrayGroup(aobj,narr);narr->setLength(cx,length);args.rval().setObject(*narr);if(argc==0)returntrue;argc--;p++;}else{narr=NewDenseEmptyArray(cx);if(!narr)returnfalse;args.rval().setObject(*narr);length=0;}/* Loop over [0, argc] to concat args into narr, expanding all Arrays. */for(unsignedi=0;i<=argc;i++){if(!CheckForInterrupt(cx))returnfalse;HandleValuev=HandleValue::fromMarkedLocation(&p[i]);if(v.isObject()){RootedObjectobj(cx,&v.toObject());// This should be IsConcatSpreadableif(IsArray(obj,cx)){uint32_talength;if(!GetLengthProperty(cx,obj,&alength))returnfalse;RootedValuetmp(cx);for(uint32_tslot=0;slot<alength;slot++){boolhole;if(!CheckForInterrupt(cx)||!GetElement(cx,obj,slot,&hole,&tmp))returnfalse;/* * Per ECMA 262, 15.4.4.4, step 9, ignore nonexistent * properties. */if(!hole&&!SetArrayElement(cx,narr,length+slot,tmp))returnfalse;}length+=alength;continue;}}if(!SetArrayElement(cx,narr,length,v))returnfalse;length++;}returnSetLengthProperty(cx,narr,length);}structSortComparatorIndexes{booloperator()(uint32_ta,uint32_tb,bool*lessOrEqualp){*lessOrEqualp=(a<=b);returntrue;}};// Returns all indexed properties in the range [begin, end) found on |obj| or// its proto chain. This function does not handle proxies, objects with// resolve/lookupProperty hooks or indexed getters, as those can introduce// new properties. In those cases, *success is set to |false|.staticboolGetIndexedPropertiesInRange(JSContext*cx,HandleObjectobj,uint32_tbegin,uint32_tend,Vector<uint32_t>&indexes,bool*success){*success=false;// First, look for proxies or class hooks that can introduce extra// properties.JSObject*pobj=obj;do{if(!pobj->isNative()||pobj->getClass()->resolve||pobj->getOps()->lookupProperty)returntrue;}while((pobj=pobj->getProto()));// Collect indexed property names.pobj=obj;do{// Append dense elements.NativeObject*nativeObj=&pobj->as<NativeObject>();uint32_tinitLen=nativeObj->getDenseInitializedLength();for(uint32_ti=begin;i<initLen&&i<end;i++){if(nativeObj->getDenseElement(i).isMagic(JS_ELEMENTS_HOLE))continue;if(!indexes.append(i))returnfalse;}// Append typed array elements.if(IsAnyTypedArray(pobj)){uint32_tlen=AnyTypedArrayLength(pobj);for(uint32_ti=begin;i<len&&i<end;i++){if(!indexes.append(i))returnfalse;}}// Append sparse elements.if(pobj->isIndexed()){Shape::Range<NoGC>r(pobj->as<NativeObject>().lastProperty());for(;!r.empty();r.popFront()){Shape&shape=r.front();jsidid=shape.propid();if(!JSID_IS_INT(id))continue;uint32_ti=uint32_t(JSID_TO_INT(id));if(!(begin<=i&&i<end))continue;// Watch out for getters, they can add new properties.if(!shape.hasDefaultGetter())returntrue;if(!indexes.append(i))returnfalse;}}}while((pobj=pobj->getProto()));// Sort the indexes.Vector<uint32_t>tmp(cx);size_tn=indexes.length();if(!tmp.resize(n))returnfalse;if(!MergeSort(indexes.begin(),n,tmp.begin(),SortComparatorIndexes()))returnfalse;// Remove duplicates.if(!indexes.empty()){uint32_tlast=0;for(size_ti=1,len=indexes.length();i<len;i++){uint32_telem=indexes[i];if(indexes[last]!=elem){last++;indexes[last]=elem;}}if(!indexes.resize(last+1))returnfalse;}*success=true;returntrue;}staticboolSliceSlowly(JSContext*cx,HandleObjectobj,HandleObjectreceiver,uint32_tbegin,uint32_tend,HandleObjectresult){RootedValuevalue(cx);for(uint32_tslot=begin;slot<end;slot++){boolhole;if(!CheckForInterrupt(cx)||!GetElement(cx,obj,receiver,slot,&hole,&value)){returnfalse;}if(!hole&&!DefineElement(cx,result,slot-begin,value))returnfalse;}returntrue;}staticboolSliceSparse(JSContext*cx,HandleObjectobj,uint32_tbegin,uint32_tend,HandleObjectresult){MOZ_ASSERT(begin<=end);Vector<uint32_t>indexes(cx);boolsuccess;if(!GetIndexedPropertiesInRange(cx,obj,begin,end,indexes,&success))returnfalse;if(!success)returnSliceSlowly(cx,obj,obj,begin,end,result);RootedValuevalue(cx);for(size_ti=0,len=indexes.length();i<len;i++){uint32_tindex=indexes[i];MOZ_ASSERT(begin<=index&&index<end);boolhole;if(!GetElement(cx,obj,obj,index,&hole,&value))returnfalse;if(!hole&&!DefineElement(cx,result,index-begin,value))returnfalse;}returntrue;}booljs::array_slice(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);RootedObjectobj(cx,ToObject(cx,args.thisv()));if(!obj)returnfalse;uint32_tlength;if(!GetLengthProperty(cx,obj,&length))returnfalse;uint32_tbegin=0;uint32_tend=length;if(args.length()>0){doubled;if(!ToInteger(cx,args[0],&d))returnfalse;if(d<0){d+=length;if(d<0)d=0;}elseif(d>length){d=length;}begin=(uint32_t)d;if(args.hasDefined(1)){if(!ToInteger(cx,args[1],&d))returnfalse;if(d<0){d+=length;if(d<0)d=0;}elseif(d>length){d=length;}end=(uint32_t)d;}}if(begin>end)begin=end;Rooted<ArrayObject*>narr(cx);if(obj->is<ArrayObject>()&&!ObjectMayHaveExtraIndexedProperties(obj)){narr=NewDenseFullyAllocatedArray(cx,end-begin);if(!narr)returnfalse;TryReuseArrayGroup(obj,narr);ArrayObject*aobj=&obj->as<ArrayObject>();if(aobj->getDenseInitializedLength()>begin){uint32_tnumSourceElements=aobj->getDenseInitializedLength()-begin;uint32_tinitLength=Min(numSourceElements,end-begin);narr->setDenseInitializedLength(initLength);narr->initDenseElements(0,&aobj->getDenseElement(begin),initLength);}args.rval().setObject(*narr);returntrue;}narr=NewDensePartlyAllocatedArray(cx,end-begin);if(!narr)returnfalse;TryReuseArrayGroup(obj,narr);if(js::GetElementsOpop=obj->getOps()->getElements){// Ensure that we have dense elements, so that ElementAdder::append can// use setDenseElementWithType.NativeObject::EnsureDenseResultresult=narr->ensureDenseElements(cx,0,end-begin);if(result==NativeObject::ED_FAILED)returnfalse;if(result==NativeObject::ED_OK){ElementAdderadder(cx,narr,end-begin,ElementAdder::CheckHasElemPreserveHoles);if(!op(cx,obj,begin,end,&adder))returnfalse;args.rval().setObject(*narr);returntrue;}// FallthroughMOZ_ASSERT(result==NativeObject::ED_SPARSE);}if(obj->isNative()&&obj->isIndexed()&&end-begin>1000){if(!SliceSparse(cx,obj,begin,end,narr))returnfalse;}else{if(!SliceSlowly(cx,obj,obj,begin,end,narr))returnfalse;}args.rval().setObject(*narr);returntrue;}/* ES5 15.4.4.20. */staticboolarray_filter(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);/* Step 1. */RootedObjectobj(cx,ToObject(cx,args.thisv()));if(!obj)returnfalse;/* Step 2-3. */uint32_tlen;if(!GetLengthProperty(cx,obj,&len))returnfalse;/* Step 4. */if(args.length()==0){ReportMissingArg(cx,args.calleev(),0);returnfalse;}RootedObjectcallable(cx,ValueToCallable(cx,args[0],args.length()-1));if(!callable)returnfalse;/* Step 5. */RootedValuethisv(cx,args.length()>=2?args[1]:UndefinedValue());/* Step 6. */RootedObjectarr(cx,NewDenseFullyAllocatedArray(cx,0));if(!arr)returnfalse;ObjectGroup*newGroup=ObjectGroup::callingAllocationSiteGroup(cx,JSProto_Array);if(!newGroup)returnfalse;arr->setGroup(newGroup);/* Step 7. */uint32_tk=0;/* Step 8. */uint32_tto=0;/* Step 9. */FastInvokeGuardfig(cx,ObjectValue(*callable));InvokeArgs&args2=fig.args();RootedValuekValue(cx);while(k<len){if(!CheckForInterrupt(cx))returnfalse;/* Step a, b, and c.i. */boolkNotPresent;if(!GetElement(cx,obj,k,&kNotPresent,&kValue))returnfalse;/* Step c.ii-iii. */if(!kNotPresent){if(!args2.init(3))returnfalse;args2.setCallee(ObjectValue(*callable));args2.setThis(thisv);args2[0].set(kValue);args2[1].setNumber(k);args2[2].setObject(*obj);if(!fig.invoke(cx))returnfalse;if(ToBoolean(args2.rval())){if(!SetArrayElement(cx,arr,to,kValue))returnfalse;to++;}}/* Step d. */k++;}/* Step 10. */args.rval().setObject(*arr);returntrue;}staticboolarray_isArray(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);boolisArray=false;if(args.get(0).isObject()){RootedObjectobj(cx,&args[0].toObject());isArray=IsArray(obj,cx);}args.rval().setBoolean(isArray);returntrue;}staticboolIsArrayConstructor(constValue&v){// This must only return true if v is *the* Array constructor for the// current compartment; we rely on the fact that any other Array// constructor would be represented as a wrapper.returnv.isObject()&&v.toObject().is<JSFunction>()&&v.toObject().as<JSFunction>().isNative()&&v.toObject().as<JSFunction>().native()==ArrayConstructor;}staticboolArrayFromCallArgs(JSContext*cx,HandleObjectGroupgroup,CallArgs&args){if(!InitArrayTypes(cx,group,nullptr,args.array(),args.length()))returnfalse;JSObject*obj=(args.length()==0)?NewDenseEmptyArray(cx):NewDenseCopiedArray(cx,args.length(),args.array());if(!obj)returnfalse;obj->setGroup(group);args.rval().setObject(*obj);returntrue;}staticboolarray_of(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);if(IsArrayConstructor(args.thisv())||!IsConstructor(args.thisv())){// IsArrayConstructor(this) will usually be true in practice. This is// the most common path.RootedObjectGroupgroup(cx,ObjectGroup::callingAllocationSiteGroup(cx,JSProto_Array));if(!group)returnfalse;returnArrayFromCallArgs(cx,group,args);}// Step 4.RootedObjectobj(cx);{RootedValuev(cx);Valueargv[1]={NumberValue(args.length())};if(!InvokeConstructor(cx,args.thisv(),1,argv,&v))returnfalse;obj=ToObject(cx,v);if(!obj)returnfalse;}// Step 8.for(unsignedk=0;k<args.length();k++){if(!DefineElement(cx,obj,k,args[k]))returnfalse;}// Steps 9-10.if(!SetLengthProperty(cx,obj,args.length()))returnfalse;// Step 11.args.rval().setObject(*obj);returntrue;}#define GENERIC JSFUN_GENERIC_NATIVEstaticconstJSFunctionSpecarray_methods[]={#if JS_HAS_TOSOURCEJS_FN(js_toSource_str,array_toSource,0,0),#endifJS_FN(js_toString_str,array_toString,0,0),JS_FN(js_toLocaleString_str,array_toLocaleString,0,0),/* Perl-ish methods. */JS_FN("join",array_join,1,JSFUN_GENERIC_NATIVE),JS_FN("reverse",array_reverse,0,JSFUN_GENERIC_NATIVE),JS_FN("sort",array_sort,1,JSFUN_GENERIC_NATIVE),JS_FN("push",array_push,1,JSFUN_GENERIC_NATIVE),JS_FN("pop",array_pop,0,JSFUN_GENERIC_NATIVE),JS_FN("shift",array_shift,0,JSFUN_GENERIC_NATIVE),JS_FN("unshift",array_unshift,1,JSFUN_GENERIC_NATIVE),JS_FN("splice",array_splice,2,JSFUN_GENERIC_NATIVE),/* Pythonic sequence methods. */JS_FN("concat",array_concat,1,JSFUN_GENERIC_NATIVE),JS_FN("slice",array_slice,2,JSFUN_GENERIC_NATIVE),JS_SELF_HOSTED_FN("lastIndexOf","ArrayLastIndexOf",1,0),JS_SELF_HOSTED_FN("indexOf","ArrayIndexOf",1,0),JS_SELF_HOSTED_FN("forEach","ArrayForEach",1,0),JS_SELF_HOSTED_FN("map","ArrayMap",1,0),JS_SELF_HOSTED_FN("reduce","ArrayReduce",1,0),JS_SELF_HOSTED_FN("reduceRight","ArrayReduceRight",1,0),JS_FN("filter",array_filter,1,JSFUN_GENERIC_NATIVE),JS_SELF_HOSTED_FN("some","ArraySome",1,0),JS_SELF_HOSTED_FN("every","ArrayEvery",1,0),/* ES6 additions */JS_SELF_HOSTED_FN("find","ArrayFind",1,0),JS_SELF_HOSTED_FN("findIndex","ArrayFindIndex",1,0),JS_SELF_HOSTED_FN("copyWithin","ArrayCopyWithin",3,0),JS_SELF_HOSTED_FN("fill","ArrayFill",3,0),JS_SELF_HOSTED_SYM_FN(iterator,"ArrayValues",0,0),JS_SELF_HOSTED_FN("entries","ArrayEntries",0,0),JS_SELF_HOSTED_FN("keys","ArrayKeys",0,0),/* ES7 additions */#ifdef NIGHTLY_BUILDJS_SELF_HOSTED_FN("includes","ArrayIncludes",2,0),#endifJS_FS_END};staticconstJSFunctionSpecarray_static_methods[]={JS_FN("isArray",array_isArray,1,0),JS_SELF_HOSTED_FN("lastIndexOf","ArrayStaticLastIndexOf",2,0),JS_SELF_HOSTED_FN("indexOf","ArrayStaticIndexOf",2,0),JS_SELF_HOSTED_FN("forEach","ArrayStaticForEach",2,0),JS_SELF_HOSTED_FN("map","ArrayStaticMap",2,0),JS_SELF_HOSTED_FN("every","ArrayStaticEvery",2,0),JS_SELF_HOSTED_FN("some","ArrayStaticSome",2,0),JS_SELF_HOSTED_FN("reduce","ArrayStaticReduce",2,0),JS_SELF_HOSTED_FN("reduceRight","ArrayStaticReduceRight",2,0),JS_SELF_HOSTED_FN("from","ArrayFrom",3,0),JS_FN("of",array_of,0,0),JS_FS_END};/* ES5 15.4.2 */booljs::ArrayConstructor(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);RootedObjectGroupgroup(cx,ObjectGroup::callingAllocationSiteGroup(cx,JSProto_Array));if(!group)returnfalse;if(args.length()!=1||!args[0].isNumber())returnArrayFromCallArgs(cx,group,args);uint32_tlength;if(args[0].isInt32()){int32_ti=args[0].toInt32();if(i<0){JS_ReportErrorNumber(cx,GetErrorMessage,nullptr,JSMSG_BAD_ARRAY_LENGTH);returnfalse;}length=uint32_t(i);}else{doubled=args[0].toDouble();length=ToUint32(d);if(d!=double(length)){JS_ReportErrorNumber(cx,GetErrorMessage,nullptr,JSMSG_BAD_ARRAY_LENGTH);returnfalse;}}/* * Allocate up to |EagerAllocationMaxLength| dense elements eagerly, to * avoid reallocating elements when filling the array. */AllocatingBehaviourallocating=(length<=ArrayObject::EagerAllocationMaxLength)?NewArray_FullyAllocating:NewArray_PartlyAllocating;RootedObjectobj(cx,NewDenseArray(cx,length,group,allocating));if(!obj)returnfalse;args.rval().setObject(*obj);returntrue;}ArrayObject*js::ArrayConstructorOneArg(JSContext*cx,HandleObjectGroupgroup,int32_tlengthInt){if(lengthInt<0){JS_ReportErrorNumber(cx,GetErrorMessage,nullptr,JSMSG_BAD_ARRAY_LENGTH);returnnullptr;}uint32_tlength=uint32_t(lengthInt);AllocatingBehaviourallocating=(length<=ArrayObject::EagerAllocationMaxLength)?NewArray_FullyAllocating:NewArray_PartlyAllocating;returnNewDenseArray(cx,length,group,allocating);}staticJSObject*CreateArrayPrototype(JSContext*cx,JSProtoKeykey){MOZ_ASSERT(key==JSProto_Array);RootedObjectproto(cx,cx->global()->getOrCreateObjectPrototype(cx));if(!proto)returnnullptr;RootedObjectGroupgroup(cx,ObjectGroup::defaultNewGroup(cx,&ArrayObject::class_,TaggedProto(proto)));if(!group)returnnullptr;RootedShapeshape(cx,EmptyShape::getInitialShape(cx,&ArrayObject::class_,TaggedProto(proto),gc::AllocKind::OBJECT0));if(!shape)returnnullptr;RootedArrayObjectarrayProto(cx,ArrayObject::createArray(cx,gc::AllocKind::OBJECT4,gc::TenuredHeap,shape,group,0));if(!arrayProto||!JSObject::setSingleton(cx,arrayProto)||!AddLengthProperty(cx,arrayProto)){returnnullptr;}/* * The default 'new' group of Array.prototype is required by type inference * to have unknown properties, to simplify handling of e.g. heterogenous * arrays in JSON and script literals and allows setDenseArrayElement to * be used without updating the indexed type set for such default arrays. */if(!JSObject::setNewGroupUnknown(cx,&ArrayObject::class_,arrayProto))returnnullptr;returnarrayProto;}constClassArrayObject::class_={"Array",JSCLASS_HAS_CACHED_PROTO(JSProto_Array),array_addProperty,nullptr,/* delProperty */nullptr,/* getProperty */nullptr,/* setProperty */nullptr,/* enumerate */nullptr,/* resolve */nullptr,/* mayResolve */nullptr,/* convert */nullptr,/* finalize */nullptr,/* call */nullptr,/* hasInstance */nullptr,/* construct */nullptr,/* trace */{GenericCreateConstructor<ArrayConstructor,1,gc::AllocKind::FUNCTION>,CreateArrayPrototype,array_static_methods,nullptr,array_methods}};/* * Array allocation functions. */staticinlineboolEnsureNewArrayElements(ExclusiveContext*cx,ArrayObject*obj,uint32_tlength){/* * If ensureElements creates dynamically allocated slots, then having * fixedSlots is a waste. */DebugOnly<uint32_t>cap=obj->getDenseCapacity();if(!obj->ensureElements(cx,length))returnfalse;MOZ_ASSERT_IF(cap,!obj->hasDynamicElements());returntrue;}staticboolNewArrayIsCachable(ExclusiveContext*cxArg,NewObjectKindnewKind){returncxArg->isJSContext()&&newKind==GenericObject;}template<uint32_tmaxLength>staticMOZ_ALWAYS_INLINEArrayObject*NewArray(ExclusiveContext*cxArg,uint32_tlength,HandleObjectprotoArg,NewObjectKindnewKind=GenericObject){gc::AllocKindallocKind=GuessArrayGCKind(length);MOZ_ASSERT(CanBeFinalizedInBackground(allocKind,&ArrayObject::class_));allocKind=GetBackgroundAllocKind(allocKind);boolisCachable=NewArrayIsCachable(cxArg,newKind);if(isCachable){JSContext*cx=cxArg->asJSContext();JSRuntime*rt=cx->runtime();NewObjectCache&cache=rt->newObjectCache;NewObjectCache::EntryIndexentry=-1;if(cache.lookupGlobal(&ArrayObject::class_,cx->global(),allocKind,&entry)){gc::InitialHeapheap=GetInitialHeap(newKind,&ArrayObject::class_);JSObject*obj=cache.newObjectFromHit(cx,entry,heap);if(obj){/* Fixup the elements pointer and length, which may be incorrect. */ArrayObject*arr=&obj->as<ArrayObject>();arr->setFixedElements();arr->setLength(cx,length);if(maxLength>0&&!EnsureNewArrayElements(cx,arr,std::min(maxLength,length))){returnnullptr;}returnarr;}}}RootedObjectproto(cxArg,protoArg);if(!proto&&!GetBuiltinPrototype(cxArg,JSProto_Array,&proto))returnnullptr;RootedObjectGroupgroup(cxArg,ObjectGroup::defaultNewGroup(cxArg,&ArrayObject::class_,TaggedProto(proto)));if(!group)returnnullptr;/* * Get a shape with zero fixed slots, regardless of the size class. * See JSObject::createArray. */RootedShapeshape(cxArg,EmptyShape::getInitialShape(cxArg,&ArrayObject::class_,TaggedProto(proto),gc::AllocKind::OBJECT0));if(!shape)returnnullptr;RootedArrayObjectarr(cxArg,ArrayObject::createArray(cxArg,allocKind,GetInitialHeap(newKind,&ArrayObject::class_),shape,group,length));if(!arr)returnnullptr;if(shape->isEmptyShape()){if(!AddLengthProperty(cxArg,arr))returnnullptr;shape=arr->lastProperty();EmptyShape::insertInitialShape(cxArg,shape,proto);}if(newKind==SingletonObject&&!JSObject::setSingleton(cxArg,arr))returnnullptr;if(isCachable){NewObjectCache&cache=cxArg->asJSContext()->runtime()->newObjectCache;NewObjectCache::EntryIndexentry=-1;cache.lookupGlobal(&ArrayObject::class_,cxArg->global(),allocKind,&entry);cache.fillGlobal(entry,&ArrayObject::class_,cxArg->global(),allocKind,arr);}if(maxLength>0&&!EnsureNewArrayElements(cxArg,arr,std::min(maxLength,length)))returnnullptr;probes::CreateObject(cxArg,arr);returnarr;}ArrayObject*JS_FASTCALLjs::NewDenseEmptyArray(JSContext*cx,HandleObjectproto/* = NullPtr() */,NewObjectKindnewKind/* = GenericObject */){returnNewArray<0>(cx,0,proto,newKind);}ArrayObject*JS_FASTCALLjs::NewDenseFullyAllocatedArray(ExclusiveContext*cx,uint32_tlength,HandleObjectproto/* = NullPtr() */,NewObjectKindnewKind/* = GenericObject */){returnNewArray<NativeObject::NELEMENTS_LIMIT>(cx,length,proto,newKind);}ArrayObject*JS_FASTCALLjs::NewDensePartlyAllocatedArray(ExclusiveContext*cx,uint32_tlength,HandleObjectproto/* = NullPtr() */,NewObjectKindnewKind/* = GenericObject */){returnNewArray<ArrayObject::EagerAllocationMaxLength>(cx,length,proto,newKind);}ArrayObject*JS_FASTCALLjs::NewDenseUnallocatedArray(ExclusiveContext*cx,uint32_tlength,HandleObjectproto/* = NullPtr() */,NewObjectKindnewKind/* = GenericObject */){returnNewArray<0>(cx,length,proto,newKind);}ArrayObject*js::NewDenseArray(ExclusiveContext*cx,uint32_tlength,HandleObjectGroupgroup,AllocatingBehaviourallocating,boolconvertDoubleElements){NewObjectKindnewKind=!group?SingletonObject:GenericObject;if(group&&group->shouldPreTenure())newKind=TenuredObject;ArrayObject*arr;if(allocating==NewArray_Unallocating){arr=NewDenseUnallocatedArray(cx,length,NullPtr(),newKind);}elseif(allocating==NewArray_PartlyAllocating){arr=NewDensePartlyAllocatedArray(cx,length,NullPtr(),newKind);}else{MOZ_ASSERT(allocating==NewArray_FullyAllocating);arr=NewDenseFullyAllocatedArray(cx,length,NullPtr(),newKind);}if(!arr)returnnullptr;if(group)arr->setGroup(group);if(convertDoubleElements)arr->setShouldConvertDoubleElements();// If the length calculation overflowed, make sure that is marked for the// new group.if(arr->length()>INT32_MAX)arr->setLength(cx,arr->length());returnarr;}ArrayObject*js::NewDenseCopiedArray(JSContext*cx,uint32_tlength,HandleArrayObjectsrc,uint32_telementOffset,HandleObjectproto/* = NullPtr() */){MOZ_ASSERT(!src->isIndexed());ArrayObject*arr=NewArray<NativeObject::NELEMENTS_LIMIT>(cx,length,proto);if(!arr)returnnullptr;MOZ_ASSERT(arr->getDenseCapacity()>=length);constValue*vp=src->getDenseElements()+elementOffset;arr->setDenseInitializedLength(vp?length:0);if(vp)arr->initDenseElements(0,vp,length);returnarr;}// values must point at already-rooted Value objectsArrayObject*js::NewDenseCopiedArray(JSContext*cx,uint32_tlength,constValue*values,HandleObjectproto/* = NullPtr() */,NewObjectKindnewKind/* = GenericObject */){ArrayObject*arr=NewArray<NativeObject::NELEMENTS_LIMIT>(cx,length,proto);if(!arr)returnnullptr;MOZ_ASSERT(arr->getDenseCapacity()>=length);arr->setDenseInitializedLength(values?length:0);if(values)arr->initDenseElements(0,values,length);returnarr;}ArrayObject*js::NewDenseFullyAllocatedArrayWithTemplate(JSContext*cx,uint32_tlength,JSObject*templateObject){gc::AllocKindallocKind=GuessArrayGCKind(length);MOZ_ASSERT(CanBeFinalizedInBackground(allocKind,&ArrayObject::class_));allocKind=GetBackgroundAllocKind(allocKind);RootedObjectGroupgroup(cx,templateObject->group());RootedShapeshape(cx,templateObject->as<ArrayObject>().lastProperty());gc::InitialHeapheap=GetInitialHeap(GenericObject,&ArrayObject::class_);Rooted<ArrayObject*>arr(cx,ArrayObject::createArray(cx,allocKind,heap,shape,group,length));if(!arr)returnnullptr;if(!EnsureNewArrayElements(cx,arr,length))returnnullptr;probes::CreateObject(cx,arr);returnarr;}JSObject*js::NewDenseCopyOnWriteArray(JSContext*cx,HandleArrayObjecttemplateObject,gc::InitialHeapheap){MOZ_ASSERT(!gc::IsInsideNursery(templateObject));ArrayObject*arr=ArrayObject::createCopyOnWriteArray(cx,heap,templateObject);if(!arr)returnnullptr;probes::CreateObject(cx,arr);returnarr;}#ifdef DEBUGbooljs::ArrayInfo(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);JSObject*obj;for(unsignedi=0;i<args.length();i++){RootedValuearg(cx,args[i]);UniquePtr<char[],JS::FreePolicy>bytes=DecompileValueGenerator(cx,JSDVG_SEARCH_STACK,arg,NullPtr());if(!bytes)returnfalse;if(arg.isPrimitive()||!(obj=arg.toObjectOrNull())->is<ArrayObject>()){fprintf(stderr,"%s: not array\n",bytes.get());continue;}fprintf(stderr,"%s: (len %u",bytes.get(),obj->as<ArrayObject>().length());fprintf(stderr,", capacity %u",obj->as<ArrayObject>().getDenseCapacity());fputs(")\n",stderr);}args.rval().setUndefined();returntrue;}#endif