Merge remote-tracking branch 'redux/master' into sh4-pool
[tamarin-stm.git] / core / ArrayClass.cpp
blob0d65060414e79b4df51d11220608ab016c43d8ab
1 /* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
2 /* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is [Open Source Virtual Machine.].
18 * The Initial Developer of the Original Code is
19 * Adobe System Incorporated.
20 * Portions created by the Initial Developer are Copyright (C) 2004-2006
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
24 * Adobe AS3 Team
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
41 #include "avmplus.h"
42 #include "BuiltinNatives.h"
44 #ifdef _MAC
45 #ifndef __GNUC__
46 // inline_max_total_size() defaults to 10000.
47 // This module includes so many inline functions that we
48 // exceed this limit and we start getting compile warnings,
49 // so bump up the limit for this file.
50 #pragma inline_max_total_size(100000)
51 #endif
52 #endif
54 using namespace MMgc;
56 namespace avmplus
58 // todo while close, the implementations here do not exactly match the
59 // ECMA spec, and probably in ways that the SpiderMonkey test suite won't
60 // catch... for instance, it will go haywire with array indices over
61 // 2^32 because we're not applying the ToInteger algorithm correctly.
62 // Indices have to handled as doubles until the last minute, then...
63 // the full 2^32 range is needed but there is behavior involving
64 // negative numbers too.
66 ArrayClass::ArrayClass(VTable* cvtable)
67 : ClassClosure(cvtable)
69 AvmCore* core = this->core();
70 Toplevel* toplevel = this->toplevel();
72 AvmAssert(traits()->getSizeOfInstance() == sizeof(ArrayClass));
74 VTable* ivtable = this->ivtable();
75 ScriptObject* objectPrototype = toplevel->objectClass->prototypePtr();
76 setPrototypePtr(ArrayObject::create(core->GetGC(), ivtable, objectPrototype, 0));
78 // According to ECMAscript spec (ECMA-262.pdf)
79 // generic support: concat, join, pop, push, reverse, shift, slice, sort, splice, unshift
80 // NOT generic: toString, toLocaleString
81 // unknown: sortOn (our own extension)
84 static ArrayObject* toArray(Atom instance)
86 return AvmCore::isObject(instance) ? AvmCore::atomToScriptObject(instance)->toArrayObject() : NULL;
89 /**
90 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ] ] ] )
91 When the concat method is called with zero or more arguments item1, item2, etc., it returns an array containing
92 the array elements of the object followed by the array elements of each argument in order.
93 The following steps are taken:
94 1. Let A be a new array created as if by the expression new Array().
95 2. Let n be 0.
96 3. Let E be this object.
97 4. If E is not an Array object, go to step 16.
98 5. Let k be 0.
99 6. Call the [[Get]] method of E with argument "length".
100 7. If k equals Result(6) go to step 19.
101 8. Call ToString(k).
102 9. If E has a property named by Result(8), go to step 10,
103 but if E has no property named by Result(8), go to step 13.
104 10. Call ToString(n).
105 11. Call the [[Get]] method of E with argument Result(8).
106 12. Call the [[Put]] method of A with arguments Result(10) and Result(11).
107 13. Increase n by 1.
108 14. Increase k by 1.
109 15. Go to step 7.
110 16. Call ToString(n).
111 17. Call the [[Put]] method of A with arguments Result(16) and E.
112 18. Increase n by 1.
113 19. Get the next argument in the argument list; if there are no more arguments, go to step 22.
114 20. Let E be Result(19).
115 21. Go to step 4.
116 22. Call the [[Put]] method of A with arguments "length" and n.
117 23. Return A.
118 The length property of the concat method is 1.
119 NOTE The concat function is intentionally generic; it does not require that its this value be an Array object. Therefore it can be
120 transferred to other kinds of objects for use as a method. Whether the concat function can be applied successfully to a host
121 object is implementation-dependent.
124 /*static*/ void ArrayClass::array_concat(Toplevel* /*toplevel*/, ArrayObject* a, ArrayObject* b)
126 if (!a->try_concat(b))
128 for (uint32_t j = 0, n = b->getLengthProperty(); j < n; ++j)
130 Atom ba = b->getUintProperty(j);
131 a->push(&ba, 1);
137 /*static*/ ArrayObject* ArrayClass::generic_concat(Toplevel* toplevel, Atom thisAtom, ArrayObject* args)
139 ScriptObject* d = AvmCore::isObject(thisAtom) ? AvmCore::atomToScriptObject(thisAtom) : NULL;
140 uint32_t len = d ? d->getLengthProperty() : 0;
142 uint32_t newLength = len;
143 uint32_t argc = args->getLength();
144 for (uint32_t i = 0; i< argc; i++)
146 Atom atom = args->getUintProperty(i);
147 ArrayObject* b = toArray(atom);
148 newLength += b ? b->getLengthProperty() : 1;
151 ArrayObject* out = toplevel->arrayClass()->newArray(newLength);
152 ArrayObject* a = toArray(thisAtom);
153 if (a)
155 array_concat(toplevel, out, a);
158 for (uint32_t i = 0; i < argc; i++)
160 Atom atom = args->getUintProperty(i);
161 ArrayObject* b = toArray(atom);
162 if (b)
164 array_concat(toplevel, out, b);
166 else
168 out->push(&atom, 1);
172 return out;
176 * Array.prototype.pop()
177 * TRANSFERABLE: Needs to support generic objects as well as Array objects
179 /*static*/ Atom ArrayClass::generic_pop(Toplevel* /*toplevel*/, Atom thisAtom)
181 ArrayObject* a = toArray(thisAtom);
183 if (a)
184 return a->pop();
186 if (!AvmCore::isObject(thisAtom))
187 return undefinedAtom;
189 // Different than Rhino (because of delete) but matches 262.pdf
190 ScriptObject *d = AvmCore::atomToScriptObject(thisAtom);
191 uint32_t len = d->getLengthProperty();
192 if (!len)
194 d->setLengthProperty(0);
195 return undefinedAtom;
197 else
199 Atom outAtom = d->getUintProperty (len-1);
200 d->delUintProperty (len - 1);
201 d->setLengthProperty(len - 1);
202 return outAtom;
207 * Array.prototype.reverse()
208 * TRANSFERABLE: Needs to support generic objects as well as Array objects
210 /*static*/ Atom ArrayClass::generic_reverse(Toplevel* /*toplevel*/, Atom thisAtom)
212 ArrayObject* a = toArray(thisAtom);
213 if (a && a->try_reverse())
215 return thisAtom;
218 // generic object version
219 if (!AvmCore::isObject(thisAtom))
220 return thisAtom;
222 ScriptObject *d = AvmCore::atomToScriptObject(thisAtom);
223 uint32_t j = d->getLengthProperty();
225 uint32_t i = 0;
226 if (j)
227 j--;
229 while (i < j) {
230 Atom frontAtom = d->getUintProperty(i);
231 Atom backAtom = d->getUintProperty(j);
233 d->setUintProperty(i++, backAtom);
234 d->setUintProperty(j--, frontAtom);
237 return thisAtom;
241 * Array.prototype.shift()
242 * TRANSFERABLE: Needs to support generic objects as well as Array objects
244 /*static*/ Atom ArrayClass::generic_shift(Toplevel* /*toplevel*/, Atom thisAtom)
246 ArrayObject* a = toArray(thisAtom);
248 Atom result;
249 if (a && a->try_shift(result))
251 return result;
254 if (!AvmCore::isObject(thisAtom))
255 return undefinedAtom;
257 Atom outAtom;
258 ScriptObject *d = AvmCore::atomToScriptObject(thisAtom);
259 uint32_t len = d->getLengthProperty();
261 if (len == 0)
263 // set len to 0 (ecmascript spec)
264 d->setLengthProperty(0);
265 outAtom = undefinedAtom;
267 else
269 // Get the 0th element to return
270 outAtom = d->getUintProperty(0);
272 // Move all of the elements down
273 for (uint32_t i=0; i<len-1; i++) {
274 d->setUintProperty(i, d->getUintProperty(i+1));
277 d->delUintProperty (len - 1);
278 d->setLengthProperty(len - 1);
281 return outAtom;
285 * Array.prototype.slice()
286 * TRANSFERABLE: Needs to support generic objects as well as Array objects
288 /*static*/ ArrayObject* ArrayClass::generic_slice(Toplevel* toplevel, Atom thisAtom, double A, double B)
290 if (!AvmCore::isObject(thisAtom))
291 return 0;
293 ScriptObject *d = AvmCore::atomToScriptObject(thisAtom);
294 uint32_t len = d->getLengthProperty();
296 // if a param is passed then the first one is A
297 // if no params are passed then A = 0
298 uint32_t a = NativeObjectHelpers::ClampIndex(A, len);
299 uint32_t b = NativeObjectHelpers::ClampIndex(B, len);
300 if (b < a)
301 b = a;
303 ArrayObject *out = toplevel->arrayClass()->newArray(b-a);
305 uint32_t outIndex=0;
306 for (uint32_t i=a; i<b; i++) {
307 out->setUintProperty (outIndex++, d->getUintProperty (i));
310 return out;
313 static inline bool defined(Atom atom) { return (atom != undefinedAtom); }
315 typedef HeapList<AtomList> HeapAtomList;
318 * ArraySort implements actionscript Array.sort().
319 * It's also the base class SortWithParameters, which handles all other permutations of Array
321 class ArraySort
323 public:
324 /*************************************************************
325 * Forward declarations required by public methods
326 *************************************************************/
328 /** Options from script */
329 enum {
330 kCaseInsensitive = 1
331 ,kDescending = 2
332 ,kUniqueSort = 4
333 ,kReturnIndexedArray = 8
334 ,kNumeric = 16
337 /** Array.sortOn() will pass an array of field names */
338 struct FieldName
340 DRCWB(Stringp) name;
341 int options;
344 typedef int (*CompareFuncPtr) (const ArraySort *s, uint32_t i, uint32_t j);
346 static int StringCompareFunc(const ArraySort *s, uint32_t j, uint32_t k) { return s->StringCompare(j, k); }
347 static int CaseInsensitiveStringCompareFunc(const ArraySort *s, uint32_t j, uint32_t k) { return s->CaseInsensitiveStringCompare(j, k); }
348 static int ScriptCompareFunc(const ArraySort *s, uint32_t j, uint32_t k) { return s->ScriptCompare(j, k); }
349 static int NumericCompareFuncCompatible(const ArraySort *s, uint32_t j, uint32_t k) { return s->NumericCompareCompatible(j, k); }
350 static int NumericCompareFuncCorrect(const ArraySort *s, uint32_t j, uint32_t k) { return s->NumericCompareCorrect(j, k); }
351 static int DescendingCompareFunc(const ArraySort *s, uint32_t j, uint32_t k) { return s->altCmpFunc(s, k, j); }
352 static int FieldCompareFunc(const ArraySort *s, uint32_t j, uint32_t k) { return s->FieldCompare(j, k); }
354 public:
355 /*************************************************************
356 * Public Functions
357 *************************************************************/
359 ArraySort(Atom &result, ArrayClass *f, ScriptObject *d, int options, CompareFuncPtr cmpFunc,
360 CompareFuncPtr altCmpFunc, Atom cmpActionScript, uint32_t numFields = 0, FieldName *fields = NULL);
362 ~ArraySort();
364 // do the sort
365 Atom sort();
367 private:
368 /*************************************************************
369 * Private Functions
370 *************************************************************/
372 // non-recursive quicksort implementation
373 void qsort(uint32_t lo, uint32_t hi);
375 // qsort() is abstracted from the array by swap() and compare()
376 // compare(), in turn, is abstracted from the array via get()
378 // cmpFunc is conditional, for instance :
379 // cmpFunc = DefaultCompareFunc; // Array.sort()
380 // cmpFunc = ScriptCompareFunc; // Array.sort(compareFunction)
381 int compare(uint32_t lhs, uint32_t rhs) const { return cmpFunc(this, lhs, rhs); }
382 Atom get(uint32_t i) const { return atoms->list.get(index[i]); }
383 void swap(uint32_t j, uint32_t k)
385 uint32_t temp = index[j];
386 index[j] = index[k];
387 index[k] = temp;
390 inline int StringCompare(uint32_t j, uint32_t k) const;
391 inline int CaseInsensitiveStringCompare(uint32_t j, uint32_t k) const;
392 inline int ScriptCompare(uint32_t j, uint32_t k) const;
393 inline int NumericCompareCompatible(uint32_t j, uint32_t k) const;
394 inline int NumericCompareCorrect(uint32_t j, uint32_t k) const;
395 inline int FieldCompare(uint32_t j, uint32_t k) const;
398 * null check + pointer cast. only used in contexts where we know we
399 * have a ScriptObject* already (verified, etc)
401 ScriptObject* toFieldObject(Atom atom) const;
403 private:
404 /*************************************************************
405 * Private Member Variables
406 *************************************************************/
408 ScriptObject *d;
409 AvmCore* core;
410 Toplevel* toplevel;
411 int options;
413 CompareFuncPtr cmpFunc;
414 CompareFuncPtr altCmpFunc;
415 Atom cmpActionScript;
417 uint32_t *index;
418 HeapAtomList* atoms;
420 uint32_t numFields;
421 FieldName *fields;
422 HeapAtomList* fieldatoms;
425 ArraySort::ArraySort(
426 Atom &result,
427 ArrayClass *f,
428 ScriptObject *d,
429 int options,
430 CompareFuncPtr cmpFunc,
431 CompareFuncPtr altCmpFunc,
432 Atom cmpActionScript,
433 uint32_t numFields,
434 FieldName *fields
436 d(d),
437 core(f->core()),
438 toplevel(f->toplevel()),
439 options(options),
440 cmpFunc(cmpFunc),
441 altCmpFunc(altCmpFunc),
442 cmpActionScript(cmpActionScript),
443 index(NULL),
444 atoms(NULL),
445 numFields(numFields),
446 fields(fields),
447 fieldatoms(NULL)
449 uint32_t len = d->getLengthProperty();
450 uint32_t iFirstUndefined = len;
451 uint32_t iFirstAbsent = len;
453 // new class[n] compiles into code which tries to allocate n * sizeof(class).
454 // unfortunately, the generated assembly does not protect against this product overflowing int.
455 // So I limit the length -- for larger values, I expect new will fail anyways.
456 if ((len > 0) && (len < (0x10000000)))
459 index = mmfx_new_array(uint32_t, len);
460 atoms = new (core->GetGC()) HeapAtomList(core->GetGC(), len);
463 if (!index || !atoms)
465 // return the unsorted array.
467 // todo : grandma : Should I be raising an exception? Sort too big?
468 // could also fallback to an unindexed sort, but with that many elements, it won't finish for hours
469 result = d->atom();
470 return;
473 uint32_t i, j;
474 uint32_t newlen = len;
476 // One field value - pre-get our field values so we can just do a regular sort
477 if (cmpFunc == ArraySort::FieldCompareFunc && numFields == 1)
479 fieldatoms = new (core->GetGC()) HeapAtomList(core->GetGC(), len);
481 // note, loop needs to go until i = -1, but i is unsigned. 0xffffffff is a valid index, so check (i+1) != 0.
482 for (i = (len - 1), j = len; (i+1) != 0; i--)
484 index[i] = i;
485 Atom a = d->getUintProperty(i);
486 fieldatoms->list.set(i, a);
488 if (AvmCore::isObject (a))
490 ScriptObject* obj = AvmCore::atomToScriptObject (a);
492 Stringp name = fields[0].name;
494 // NOTE we want to sort the public members of the caller's version
495 Multiname mname(core->findPublicNamespace(), name);
497 // An undefined prop just becomes undefined in our sort
498 Atom x = toplevel->getproperty(obj->atom(), &mname, obj->vtable);
499 atoms->list.set(i, x);
501 else
503 j--;
505 uint32_t temp = index[i];
506 index[i] = index[j];
508 if (!d->hasUintProperty(i)) {
509 newlen--;
510 index[j] = index[newlen];
511 index[newlen] = temp;
512 } else {
513 index[j] = temp;
518 int opt = fields[0].options;
520 if (opt & ArraySort::kNumeric) {
521 this->cmpFunc = core->currentBugCompatibility()->bugzilla524122 ?
522 ArraySort::NumericCompareFuncCorrect :
523 ArraySort::NumericCompareFuncCompatible;
524 } else if (opt & ArraySort::kCaseInsensitive) {
525 this->cmpFunc = ArraySort::CaseInsensitiveStringCompareFunc;
526 } else {
527 this->cmpFunc = ArraySort::StringCompareFunc;
530 if (opt & ArraySort::kDescending) {
531 this->altCmpFunc = this->cmpFunc;
532 this->cmpFunc = ArraySort::DescendingCompareFunc;
535 else
537 bool isNumericCompare = (cmpFunc == ArraySort::NumericCompareFuncCompatible) ||
538 (altCmpFunc == ArraySort::NumericCompareFuncCompatible) ||
539 (cmpFunc == ArraySort::NumericCompareFuncCorrect) ||
540 (altCmpFunc == ArraySort::NumericCompareFuncCorrect);
541 // note, loop needs to go until i = -1, but i is unsigned. 0xffffffff is a valid index, so check (i+1) != 0.
542 for (i = (len - 1), j = len; (i+1) != 0; i--)
544 index[i] = i;
545 atoms->list.set(i, d->getUintProperty(i));
547 // We want to throw if this is an Array.NUMERIC sort and any items are not numbers,
548 // and not strings that can be converted into numbers
549 if(isNumericCompare && !core->isNumber(atoms->list.get(i)))
551 double val = AvmCore::number(atoms->list.get(i));
552 if(MathUtils::isNaN(val))
553 // throw exception (not a Number)
554 toplevel->throwTypeError(kCheckTypeFailedError, core->atomToErrorString(atoms->list.get(i)), core->toErrorString(core->traits.number_itraits));
558 // getAtomProperty() returns undefined when that's the value of the property.
559 // It also returns undefined when the object does not have the property.
561 // SortCompare() from ECMA 262 distinguishes these two cases. The end
562 // result is undefined comes after everything else, and missing properties
563 // come after that.
565 // To simplify our compare, partitition the array into { defined | undefined | missing }
566 // Note that every missing element shrinks the length -- we'll set this new
567 // length at the end of this routine when we are done.
569 if (!defined(atoms->list.get(i))) {
570 j--;
572 uint32_t temp = index[i];
573 index[i] = index[j];
575 if (!d->hasUintProperty(i)) {
576 newlen--;
577 index[j] = index[newlen];
578 index[newlen] = temp;
579 } else {
580 index[j] = temp;
586 iFirstAbsent = newlen;
587 iFirstUndefined = j;
589 // The portion of the array containing defined values is now [0, iFirstUndefined).
590 // The portion of the array containing values undefined is now [iFirstUndefined, iFirstAbsent).
591 // The portion of the array containing absent values is now [iFirstAbsent, len).
593 // now sort the remaining defined() elements
594 qsort(0, j-1);
596 if (options & kUniqueSort)
598 // todo : kUniqueSort could throw an exception.
599 // todo : kUniqueSort could abort the sort once equal members are found
600 for (uint32_t i = 0; i < (len - 1); i++)
602 if (compare(i, (i+1)) == 0)
604 result = core->uintToAtom(0);
605 return;
610 if (options & kReturnIndexedArray)
612 // return the index array without modifying the original array
613 ArrayObject *obj = toplevel->arrayClass()->newArray(len);
615 for (uint32_t i = 0; i < len; i++)
617 obj->setUintProperty(i, core->uintToAtom(index[i]));
620 result = obj->atom();
622 else
624 // If we need to use our fieldatoms as results, temporarily swap them with
625 // our atoms array so the below code works on the right data. Fieldatoms contain
626 // our original objects while atoms contain our objects[field] values for faster
627 // sorting.
628 HeapAtomList* temp = atoms;
629 if (fieldatoms)
631 atoms = fieldatoms;
634 for (i = 0; i < iFirstAbsent; i++) {
635 d->setUintProperty(i, get(i));
638 for (i = iFirstAbsent; i < len; i++) {
639 d->delUintProperty(i);
642 //a->setLength(len); ES3: don't shrink array on sort. Seems silly
643 result = d->atom();
644 atoms = temp;
648 return;
651 ArraySort::~ArraySort()
653 mmfx_delete_array(index);
654 delete atoms;
655 delete fieldatoms;
656 if(fields) {
657 int numFields = (int)GC::Size(fields)/sizeof(FieldName);
658 for(int i=0; i<numFields; i++) {
659 fields[i].name = NULL;
661 core->GetGC()->Free(fields);
662 fields = NULL;
667 * QuickSort a portion of the ArrayObject.
669 void ArraySort::qsort(uint32_t lo, uint32_t hi)
671 // This is an iterative implementation of the recursive quick sort.
672 // Recursive implementations are basically storing nested (lo,hi) pairs
673 // in the stack frame, so we can avoid the recursion by storing them
674 // in an array.
676 // Once partitioned, we sub-partition the smaller half first. This means
677 // the greatest stack depth happens with equal partitions, all the way down,
678 // which would be 1 + log2(size), which could never exceed 33.
680 uint32_t size;
681 struct StackFrame { uint32_t lo, hi; };
682 StackFrame stk[33];
683 int stkptr = 0;
685 // leave without doing anything if the array is empty (lo > hi) or only one element (lo == hi)
686 if (lo >= hi)
687 return;
689 // code below branches to this label instead of recursively calling qsort()
690 recurse:
692 size = (hi - lo) + 1; // number of elements in the partition
694 if (size < 4) {
696 // It is standard to use another sort for smaller partitions,
697 // for instance c library source uses insertion sort for 8 or less.
699 // However, as our swap() is essentially free, the relative cost of
700 // compare() is high, and with profiling, I found quicksort()-ing
701 // down to four had better performance.
703 // Although verbose, handling the remaining cases explicitly is faster,
704 // so I do so here.
706 if (size == 3) {
707 if (compare(lo, lo + 1) > 0) {
708 swap(lo, lo + 1);
709 if (compare(lo + 1, lo + 2) > 0) {
710 swap(lo + 1, lo + 2);
711 if (compare(lo, lo + 1) > 0) {
712 swap(lo, lo + 1);
715 } else {
716 if (compare(lo + 1, lo + 2) > 0) {
717 swap(lo + 1, lo + 2);
718 if (compare(lo, lo + 1) > 0) {
719 swap(lo, lo + 1);
723 } else if (size == 2) {
724 if (compare(lo, lo + 1) > 0)
725 swap(lo, lo + 1);
726 } else {
727 // size is one, zero or negative, so there isn't any sorting to be done
729 } else {
730 // qsort()-ing a near or already sorted list goes much better if
731 // you use the midpoint as the pivot, but the algorithm is simpler
732 // if the pivot is at the start of the list, so move the middle
733 // element to the front!
734 uint32_t pivot = lo + (size / 2);
735 swap(pivot, lo);
738 uint32_t left = lo;
739 uint32_t right = hi + 1;
741 for (;;) {
742 // Move the left right until it's at an element greater than the pivot.
743 // Move the right left until it's at an element less than the pivot.
744 // If left and right cross, we can terminate, otherwise swap and continue.
746 // As each pass of the outer loop increments left at least once,
747 // and decrements right at least once, this loop has to terminate.
749 do {
750 left++;
751 } while ((left <= hi) && (compare(left, lo) <= 0));
753 do {
754 right--;
755 } while ((right > lo) && (compare(right, lo) >= 0));
757 if (right < left)
758 break;
760 swap(left, right);
763 // move the pivot after the lower partition
764 swap(lo, right);
766 // The array is now in three partions:
767 // 1. left partition : i in [lo, right), elements less than or equal to pivot
768 // 2. center partition : i in [right, left], elements equal to pivot
769 // 3. right partition : i in (left, hi], elements greater than pivot
770 // NOTE : [ means the range includes the lower bounds, ( means it excludes it, with the same for ] and ).
772 // Many quick sorts recurse into the left partition, and then the right.
773 // The worst case of this can lead to a stack depth of size -- for instance,
774 // the left is empty, the center is just the pivot, and the right is everything else.
776 // If you recurse into the smaller partition first, then the worst case is an
777 // equal partitioning, which leads to a depth of log2(size).
778 if ((right - 1 - lo) >= (hi - left))
780 if ((lo + 1) < right)
782 stk[stkptr].lo = lo;
783 stk[stkptr].hi = right - 1;
784 ++stkptr;
787 if (left < hi)
789 lo = left;
790 goto recurse;
793 else
795 if (left < hi)
797 stk[stkptr].lo = left;
798 stk[stkptr].hi = hi;
799 ++stkptr;
802 if ((lo + 1) < right)
804 hi = right - 1;
805 goto recurse; /* do small recursion */
810 // we reached the bottom of the well, pop the nested stack frame
811 if (--stkptr >= 0)
813 lo = stk[stkptr].lo;
814 hi = stk[stkptr].hi;
815 goto recurse;
818 // we've returned to the top, so we are done!
819 return;
823 * compare(j, k) as string's
825 int ArraySort::StringCompare(uint32_t j, uint32_t k) const
827 Atom x = get(j);
828 Atom y = get(k);
830 Stringp str_lhs = core->string(x);
831 Stringp str_rhs = core->string(y);
833 return str_rhs->Compare(*str_lhs);
837 * compare(j, k) as case insensitive string's
839 int ArraySort::CaseInsensitiveStringCompare(uint32_t j, uint32_t k) const
841 Atom x = get(j);
842 Atom y = get(k);
844 Stringp str_lhs = core->string(x)->toLowerCase();
845 Stringp str_rhs = core->string(y)->toLowerCase();
847 return str_rhs->Compare(*str_lhs);
851 * compare(j, k) using an actionscript function
853 int ArraySort::ScriptCompare(uint32_t j, uint32_t k) const
855 // todo must figure out the kosher way to invoke
856 // callbacks like the sort comparator.
858 // todo what is thisAtom supposed to be for the
859 // comparator? Passing in the array for now.
861 ScriptObject *o = (ScriptObject*) AvmCore::atomToScriptObject(cmpActionScript);
863 Atom args[3] = { d->atom(), get(j), get(k) };
864 double result = AvmCore::toInteger(o->call(2, args));
865 // cn: don't use AvmCore::integer on result of call. The returned
866 // value could be bigger than 2^32 and toInt32 will return the
867 // ((value % 2^32) - 2^31), which could change the intended sign of the number.
869 //return AvmCore::integer(o->call(a->atom(), args, 2));
870 return (result > 0 ? 1 : (result < 0 ? -1 : 0));
874 * compare(j, k) as numbers
876 int ArraySort::NumericCompareCompatible(uint32_t j, uint32_t k) const
878 Atom atmj = get(j);
879 Atom atmk = get(k);
880 // Integer checks makes an int array sort about 3x faster.
881 // A double array sort is 5% slower because of this overhead
882 if (atomIsBothIntptr(atmj, atmk))
884 // This is incorrect, see bugzilla 524122. NumericCompareCorrect, below, fixes the bug.
885 return ((int)atmj - (int)atmk);
888 double x = AvmCore::number(atmj);
889 double y = AvmCore::number(atmk);
890 double diff = x - y;
892 if (diff == diff) { // same as !isNaN
893 return (diff < 0) ? -1 : ((diff > 0) ? 1 : 0);
894 } else if (!MathUtils::isNaN(y)) {
895 return 1;
896 } else if (!MathUtils::isNaN(x)) {
897 return -1;
898 } else {
899 return 0;
904 * compare(j, k) as numbers
906 int ArraySort::NumericCompareCorrect(uint32_t j, uint32_t k) const
908 Atom atmj = get(j);
909 Atom atmk = get(k);
910 // Integer checks makes an int array sort about 3x faster.
911 // A double array sort is 5% slower because of this overhead
912 if (atomIsBothIntptr(atmj, atmk))
914 // Must convert to native values. Just subtracting the atoms may lead to
915 // overflows which result in the incorrect sign being returned. See
916 // NumericCompareCompatible, above.
917 intptr_t tmp = atomGetIntptr(atmj) - atomGetIntptr(atmk);
918 #ifdef AVMPLUS_64BIT
919 // On 64-bit systems atoms carry more than 32 significant bits of integer
920 // data, so we need to be careful.
921 if (tmp < 0) return -1;
922 if (tmp == 0) return 0;
923 return 1;
924 #else
925 return int(tmp);
926 #endif
929 double x = AvmCore::number(atmj);
930 double y = AvmCore::number(atmk);
931 double diff = x - y;
933 if (diff == diff) { // same as !isNaN
934 return (diff < 0) ? -1 : ((diff > 0) ? 1 : 0);
935 } else if (!MathUtils::isNaN(y)) {
936 return 1;
937 } else if (!MathUtils::isNaN(x)) {
938 return -1;
939 } else {
940 return 0;
944 ScriptObject* ArraySort::toFieldObject(Atom atom) const
946 if (atomKind(atom) != kObjectType)
948 #if 0
949 /* cn: ifdefed out, not sure what the intent was here, but calling code in FieldCompare
950 * does null checking, apparently expecting this function to return null when the item
951 * isn't an object (and thus can't have custom properties added to it). */
952 // TypeError in ECMA
953 toplevel->throwTypeError(
954 (atom == undefinedAtom) ? kConvertUndefinedToObjectError :
955 kConvertNullToObjectError);
956 #endif
957 return NULL;
959 return AvmCore::atomToScriptObject(atom);
963 * FieldCompare is for Array.sortOn()
965 inline int ArraySort::FieldCompare(uint32_t lhs, uint32_t rhs) const
967 Atom j, k;
968 int opt = options;
969 int result = 0;
971 j = get(lhs);
972 k = get(rhs);
974 ScriptObject* obj_j = toFieldObject(j);
975 ScriptObject* obj_k = toFieldObject(k);
977 if (!(obj_j && obj_k))
979 if (obj_k) {
980 result = 1;
981 } else if (obj_j) {
982 result = -1;
983 } else {
984 result = 0;
986 return (opt & kDescending) ? -result : result;
989 for (uint32_t i = 0; i < numFields; i++)
991 Stringp name = fields[i].name;
992 // NOTE compare the names of the caller's version
993 Multiname mname(core->findPublicNamespace(), name);
995 opt = fields[i].options; // override the group defaults with the current field
997 Atom x = toplevel->getproperty(obj_j->atom(), &mname, obj_j->vtable);
998 Atom y = toplevel->getproperty(obj_k->atom(), &mname, obj_k->vtable);
1000 bool def_x = defined(x);
1001 bool def_y = defined(y);
1003 if (!(def_x && def_y))
1005 // ECMA 262 : Section 15.4.4.11 lists the rules.
1006 // There is a difference between the object has a property
1007 // with value undefined, and it does not have the property,
1008 // for which getAtomProperty() returns undefined.
1010 // def_x implies has_x
1011 // def_y implies has_y
1013 if (def_y) {
1014 result = 1;
1015 } else if (def_x) {
1016 result = -1;
1017 } else {
1018 bool has_x = (toplevel->getBinding(obj_j->vtable->traits, &mname) != BIND_NONE) || obj_j->hasStringProperty(name);
1019 bool has_y = (toplevel->getBinding(obj_k->vtable->traits, &mname) != BIND_NONE) || obj_k->hasStringProperty(name);
1021 if (!has_x && has_y) {
1022 result = 1;
1023 } else if (has_x && !has_y) {
1024 result = -1;
1025 } else {
1026 result = 0;
1029 } else if (opt & kNumeric) {
1030 double lhs = AvmCore::number(x);
1031 double rhs = AvmCore::number(y);
1032 double diff = lhs - rhs;
1034 if (diff == diff) { // same as !isNaN
1035 result = (diff < 0) ? -1 : ((diff > 0) ? 1 : 0);
1036 } else if (!MathUtils::isNaN(rhs)) {
1037 result = 1;
1038 } else if (!MathUtils::isNaN(lhs)) {
1039 result = -1;
1040 } else {
1041 result = 0;
1044 else
1046 Stringp str_lhs = core->string(x);
1047 Stringp str_rhs = core->string(y);
1049 if (opt & kCaseInsensitive)
1051 str_lhs = str_lhs->toLowerCase();
1052 str_rhs = str_rhs->toLowerCase();
1055 result = str_rhs->Compare(*str_lhs);
1058 if (result != 0)
1059 break;
1062 if (opt & kDescending)
1063 return -result;
1064 else
1065 return result;
1069 * Array.prototype.sort()
1070 * TRANSFERABLE: Needs to support generic objects as well as Array objects
1073 // thisAtom is object to process
1074 // 1st arg of args is a function or a number
1075 // 2nd arg of args is a number
1077 // valid AS3 syntax:
1078 // sort()
1079 // sort(function object)
1080 // sort(number flags)
1081 // sort(function object, number flags)
1083 // This takes a args object because there is no way to distinguigh between sort()
1084 // and sort(undefined, 0) if we take default parameters.
1085 /*static*/ Atom ArrayClass::generic_sort(Toplevel* toplevel, Atom thisAtom, ArrayObject *args)
1087 AvmCore* core = toplevel->core();
1088 if (!AvmCore::isObject(thisAtom))
1089 return undefinedAtom;
1091 ScriptObject *d = AvmCore::atomToScriptObject(thisAtom);
1092 ArraySort::CompareFuncPtr compare = NULL;
1093 ArraySort::CompareFuncPtr altCompare = NULL;
1095 Atom cmp = undefinedAtom;
1096 int opt = 0;
1097 if (args->getLength() >= 1)
1099 // function ptr
1100 Atom arg0 = args->getUintProperty(0);
1101 if (AvmCore::isObject (arg0))
1103 // make sure the sort function is callable
1104 cmp = arg0;
1105 toplevel->coerce(cmp, core->traits.function_itraits);
1106 compare = ArraySort::ScriptCompareFunc;
1107 if (args->getLength() >= 2)
1109 Atom arg1 = args->getUintProperty(1);
1110 if (core->isNumber(arg1))
1112 opt = AvmCore::integer(arg1);
1114 else
1116 // throw exception (not a Number)
1117 toplevel->throwTypeError(kCheckTypeFailedError, core->atomToErrorString(arg1), core->toErrorString(core->traits.number_itraits));
1121 else if (core->isNumber(arg0))
1123 opt = AvmCore::integer(arg0);
1125 else
1127 // throw exception (not a function)
1128 toplevel->throwTypeError(kCheckTypeFailedError, core->atomToErrorString(arg0), core->toErrorString(core->traits.function_itraits));
1132 if (cmp == undefinedAtom)
1134 if (opt & ArraySort::kNumeric) {
1135 compare = core->currentBugCompatibility()->bugzilla524122 ?
1136 ArraySort::NumericCompareFuncCorrect :
1137 ArraySort::NumericCompareFuncCompatible;
1138 } else if (opt & ArraySort::kCaseInsensitive) {
1139 compare = ArraySort::CaseInsensitiveStringCompareFunc;
1140 } else {
1141 compare = ArraySort::StringCompareFunc;
1145 if (opt & ArraySort::kDescending) {
1146 altCompare = compare;
1147 compare = ArraySort::DescendingCompareFunc;
1150 Atom result;
1151 ArraySort sort(result, toplevel->arrayClass(), d, opt, compare, altCompare, cmp);
1153 return result;
1158 * Array.prototype.sortOn()
1159 * TRANSFERABLE: Needs to support generic objects as well as Array objects
1161 /*static*/ Atom ArrayClass::generic_sortOn(Toplevel* toplevel, Atom thisAtom, Atom namesAtom, Atom optionsAtom)
1163 AvmCore* core = toplevel->core();
1164 if (!AvmCore::isObject(thisAtom))
1165 return undefinedAtom;
1166 ScriptObject *d = AvmCore::atomToScriptObject(thisAtom);
1168 // Possible combinations:
1169 // Array.sortOn(String)
1170 // Array.sortOn(String, options)
1171 // Array.sortOn(Array of String)
1172 // Array.sortOn(Array of String, options)
1173 // Array.sortOn(Array of String, Array of options)
1175 // What about options which must be global, such as kReturnIndexedArray?
1176 // Perhaps it is the union of all field's options?
1178 ArraySort::FieldName *fn = NULL;
1179 uint32_t nFields = 0;
1180 int options = 0;
1182 if (AvmCore::istype(namesAtom, STRING_TYPE))
1184 nFields = 1;
1186 options = AvmCore::integer(optionsAtom);
1188 fn = (ArraySort::FieldName*) core->GetGC()->Alloc(sizeof(ArraySort::FieldName), GC::kZero|GC::kContainsPointers);
1189 fn[0].name = core->internString(namesAtom);
1190 fn[0].options = options;
1192 else if (AvmCore::istype(namesAtom, toplevel->arrayClass()->ivtable()->traits /* array itraits */))
1194 ArrayObject *obj = (ArrayObject *)AvmCore::atomToScriptObject(namesAtom);
1196 nFields = obj->getLength();
1197 fn = (ArraySort::FieldName*) core->GetGC()->Calloc(nFields, sizeof(ArraySort::FieldName), GC::kZero|GC::kContainsPointers);
1199 for (uint32_t i = 0; i < nFields; i++)
1201 fn[i].name = core->intern(obj->getUintProperty(i));
1202 fn[i].options = 0;
1205 if (AvmCore::istype(optionsAtom, toplevel->arrayClass()->ivtable()->traits /* array itraits */))
1207 ArrayObject *obj = (ArrayObject *)AvmCore::atomToScriptObject(optionsAtom);
1208 uint32_t nOptions = obj->getLength();
1209 if (nOptions == nFields)
1211 // The first options are used for uniqueSort and returnIndexedArray option
1212 options = AvmCore::integer(obj->getUintProperty(0));
1213 for (uint32_t i = 0; i < nFields; i++)
1215 fn[i].options = AvmCore::integer(obj->getUintProperty (i));
1219 else
1221 options = AvmCore::integer(optionsAtom);
1222 for (uint32_t i = 0; i < nFields; i++)
1224 fn[i].options = options;
1229 // ~ArraySort() will "delete [] fn;"
1230 Atom result;
1231 ArraySort sort(result, toplevel->arrayClass(), d, options, ArraySort::FieldCompareFunc, NULL, undefinedAtom, nFields, fn);
1232 return result;
1236 * Array.prototype.splice()
1237 * TRANSFERABLE: Needs to support generic objects as well as Array objects
1240 // Spidermonkey behavior that we are mimicking...
1241 // splice() - no arguments - return undefined
1242 // splice(org arg) coerce the input to a number (otherwise it's zero) normal behavior
1243 // splice (two args) - coerce both args to numbers (otherwise they are zero)
1244 /*static*/ ArrayObject* ArrayClass::generic_splice(Toplevel* toplevel, Atom thisAtom, ArrayObject* args)
1246 // This will return null but this case should never get hit (see Array.as)
1247 if (!args->getLength())
1248 return 0;
1250 if (!AvmCore::isObject(thisAtom))
1251 return 0;
1253 ScriptObject *d = AvmCore::atomToScriptObject(thisAtom);
1255 uint32_t len = d->getLengthProperty();
1257 uint32_t insertPoint = NativeObjectHelpers::ClampIndex(AvmCore::toInteger(args->getUintProperty(0)),len);
1259 double d_deleteCount = args->getLength() > 1 ? AvmCore::toInteger(args->getUintProperty(1)) : (len - insertPoint);
1260 uint32_t deleteCount = (d_deleteCount < 0) ? 0 : AvmCore::integer_d(d_deleteCount);
1261 if (deleteCount > (len - insertPoint)) {
1262 deleteCount = len - insertPoint;
1264 uint32_t end = insertPoint + deleteCount;
1266 uint32_t insertCount = (args->getLength() > 2) ? (args->getLength() - 2) : 0;
1267 long l_shiftAmount = (long)insertCount - (long) deleteCount; // long because result could be negative
1268 uint32_t shiftAmount;
1270 ArrayObject* a = toArray(thisAtom);
1271 ArrayObject* out;
1272 if (a && (out = a->try_splice(insertPoint, insertCount, deleteCount, args, 2)) != NULL)
1274 return out;
1276 // Copy out the elements we are going to remove
1277 out = toplevel->arrayClass()->newArray(deleteCount);
1278 for (uint32_t i=0; i< deleteCount; i++) {
1279 out->setUintProperty(i, d->getUintProperty(i+insertPoint));
1282 // delete items by shifting elements past end (of delete) by l_shiftAmount
1283 if (l_shiftAmount < 0) {
1284 // Shift the remaining elements down
1285 shiftAmount = (uint32_t)(-l_shiftAmount);
1287 for (uint32_t i=end; i<len; i++) {
1288 d->setUintProperty(i-shiftAmount, d->getUintProperty(i));
1291 // delete top elements here to match ECMAscript spec (generic object support)
1292 for (uint32_t i=len-shiftAmount; i<len; i++) {
1293 d->delUintProperty (i);
1295 } else {
1296 // Shift the remaining elements up.
1297 shiftAmount = (uint32_t)l_shiftAmount;
1299 for (uint32_t i=len; i > end; ) { // Note: i is unsigned, can't check if --i >=0.
1300 --i;
1301 d->setUintProperty(i+shiftAmount, d->getUintProperty(i));
1305 // Add the items to insert
1306 for (uint32_t i=0; i<insertCount; i++) {
1307 d->setUintProperty(insertPoint+i, args->getUintProperty(i + 2));
1310 // shrink array if shiftAmount is negative
1311 d->setLengthProperty(len+l_shiftAmount);
1313 return out;
1316 ArrayObject* ArrayClass::newarray(Atom* argv, int argc)
1318 return ArrayObject::create(core()->GetGC(), ivtable(), prototypePtr(), argv, argc);
1321 ArrayObject* ArrayClass::newArray(uint32_t capacity)
1323 return ArrayObject::create(core()->GetGC(), ivtable(), prototypePtr(), capacity);
1326 #ifdef VMCFG_AOT
1327 template <typename ADT>
1328 ArrayObject* ArrayClass::newArray(MethodEnv *env, ADT argDesc, va_list ap)
1330 uint32_t argc = argDescArgCount(argDesc);
1331 AvmAssert(argc >= 0);
1332 return ArrayObject::create<ADT>(core()->GetGC(), ivtable(), prototypePtr(), env, argDesc, argc, ap);
1335 template ArrayObject* ArrayClass::newArray(MethodEnv *env, uint32_t argDesc, va_list ap);
1336 template ArrayObject* ArrayClass::newArray(MethodEnv *env, char* argDesc, va_list ap);
1337 #endif
1339 /*static*/ int ArrayClass::generic_indexOf(Toplevel* toplevel, Atom thisAtom, Atom searchElement, int startIndex)
1341 if (!AvmCore::isObject(thisAtom))
1342 return -1;
1344 AvmCore* core = toplevel->core();
1345 ScriptObject *d = AvmCore::atomToScriptObject(thisAtom);
1346 uint32_t len = d->getLengthProperty();
1348 uint32_t start = NativeObjectHelpers::ClampIndexInt(startIndex, len);
1350 for (uint32_t i = start; i < len; i++)
1352 Atom atom = d->getUintProperty(i);
1353 if (core->stricteq(atom, searchElement) == trueAtom)
1354 return i;
1357 return -1;
1360 /*static*/ int ArrayClass::generic_lastIndexOf(Toplevel* toplevel, Atom thisAtom, Atom searchElement, int startIndex)
1362 if (!AvmCore::isObject(thisAtom))
1363 return -1;
1365 AvmCore* core = toplevel->core();
1366 ScriptObject *d = AvmCore::atomToScriptObject(thisAtom);
1367 uint32_t len = d->getLengthProperty();
1369 int start = NativeObjectHelpers::ClampIndexInt(startIndex, len);
1370 if (start == int(len))
1371 start--;
1373 for (int i = start; i >= 0; i--)
1375 Atom atom = d->getUintProperty(i);
1376 if (core->stricteq (atom, searchElement) == trueAtom)
1377 return i;
1380 return -1;
1383 /*static*/ bool ArrayClass::generic_every(Toplevel* toplevel, Atom thisAtom, ScriptObject *callback, Atom thisObject)
1385 if (!AvmCore::isObject(thisAtom) || !callback)
1386 return true;
1388 if (callback->isMethodClosure() && !AvmCore::isNull(thisObject))
1390 toplevel->throwTypeError(kArrayFilterNonNullObjectError);
1393 ScriptObject *d = AvmCore::atomToScriptObject(thisAtom);
1394 uint32_t len = d->getLengthProperty();
1396 AvmCore* core = toplevel->core();
1397 for (uint32_t i = 0; i < len; i++)
1399 // If thisObject is null, the call function will substitute the global object.
1400 // args are modified in place by callee
1401 Atom args[4] = { thisObject,
1402 d->getUintProperty(i), // element
1403 core->uintToAtom(i), // index
1404 thisAtom
1406 Atom result = callback->call(3, args);
1407 if (result != trueAtom)
1408 return false;
1411 return true;
1414 /*static*/ ArrayObject* ArrayClass::generic_filter(Toplevel* toplevel, Atom thisAtom, ScriptObject *callback, Atom thisObject)
1416 ArrayObject *r = toplevel->arrayClass()->newArray();
1418 if (!AvmCore::isObject(thisAtom) || !callback)
1419 return r;
1421 if (callback->isMethodClosure() && !AvmCore::isNull(thisObject))
1423 toplevel->throwTypeError(kArrayFilterNonNullObjectError);
1426 ScriptObject *d = AvmCore::atomToScriptObject(thisAtom);
1427 uint32_t len = d->getLengthProperty();
1429 AvmCore* core = toplevel->core();
1430 for (uint32_t i = 0; i < len; i++)
1432 // The Array and/or the args may be modified by the caller,
1433 // so get a local reference to the element.
1434 Atom element = d->getUintProperty(i);
1435 // If thisObject is null, the call function will substitute the global object
1436 // args are modified in place by callee
1437 Atom args[4] = {
1438 thisObject,
1439 element,
1440 core->uintToAtom(i), // index
1441 thisAtom
1443 Atom result = callback->call(3, args);
1444 if (result == trueAtom)
1445 r->push(&element, 1);
1448 return r;
1451 /*static*/ void ArrayClass::generic_forEach(Toplevel* toplevel, Atom thisAtom, ScriptObject *callback, Atom thisObject)
1453 if (!AvmCore::isObject(thisAtom) || !callback)
1454 return;
1456 if (callback->isMethodClosure() && !AvmCore::isNull(thisObject))
1458 toplevel->throwTypeError(kArrayFilterNonNullObjectError);
1461 ScriptObject *d = AvmCore::atomToScriptObject(thisAtom);
1462 uint32_t len = d->getLengthProperty();
1464 AvmCore* core = toplevel->core();
1465 for (uint32_t i = 0; i < len; i++)
1467 // If thisObject is null, the call function will substitute the global object
1468 // args are modified in place by callee
1469 Atom args[4] = { thisObject,
1470 d->getUintProperty(i), // element
1471 core->uintToAtom(i), // index
1472 thisAtom
1474 callback->call(3, args);
1478 /*static*/ bool ArrayClass::generic_some(Toplevel* toplevel, Atom thisAtom, ScriptObject *callback, Atom thisObject)
1480 if (!AvmCore::isObject(thisAtom) || !callback)
1481 return false;
1483 if (callback->isMethodClosure() && !AvmCore::isNull(thisObject))
1485 toplevel->throwTypeError(kArrayFilterNonNullObjectError);
1488 ScriptObject *d = AvmCore::atomToScriptObject(thisAtom);
1489 uint32_t len = d->getLengthProperty();
1491 AvmCore* core = toplevel->core();
1492 for (uint32_t i = 0; i < len; i++)
1494 // If thisObject is null, the call function will substitute the global object
1495 // args are modified in place by callee
1496 Atom args[4] = {
1497 thisObject,
1498 d->getUintProperty(i), // element
1499 core->uintToAtom(i), // index
1500 thisAtom
1502 Atom result = callback->call(3, args);
1503 if (result == trueAtom)
1504 return true;
1507 return false;
1510 /*static*/ ArrayObject* ArrayClass::generic_map(Toplevel* toplevel, Atom thisAtom, ScriptObject *callback, Atom thisObject)
1512 ArrayObject *r = toplevel->arrayClass()->newArray();
1514 if (!AvmCore::isObject(thisAtom) || !callback)
1515 return r;
1517 ScriptObject *d = AvmCore::atomToScriptObject(thisAtom);
1518 uint32_t len = d->getLengthProperty();
1520 AvmCore* core = toplevel->core();
1521 for (uint32_t i = 0; i < len; i++)
1523 // If thisObject is null, the call function will substitute the global object
1524 // args are modified in place by callee
1525 Atom args[4] = {
1526 thisObject,
1527 d->getUintProperty(i), // element
1528 core->uintToAtom(i), // index
1529 thisAtom
1531 Atom result = callback->call(3, args);
1532 r->push(&result, 1);
1535 return r;
1538 /* static */ uint32_t ArrayClass::generic_unshift(Toplevel* /*toplevel*/, Atom thisAtom, ArrayObject* args)
1540 ArrayObject* a = toArray(thisAtom);
1541 if (!a || !a->try_unshift(args))
1543 for (uint32_t i = args->getLength() ; i > 0; i--)
1545 Atom atom = args->getUintProperty(i - 1);
1546 a->unshift(&atom, 1);
1549 return a->getLengthProperty();