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
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.
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 ***** */
42 #include "BuiltinNatives.h"
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)
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
;
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().
96 3. Let E be this object.
97 4. If E is not an Array object, go to step 16.
99 6. Call the [[Get]] method of E with argument "length".
100 7. If k equals Result(6) go to step 19.
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).
110 16. Call ToString(n).
111 17. Call the [[Put]] method of A with arguments Result(16) and E.
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).
116 22. Call the [[Put]] method of A with arguments "length" and n.
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
);
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
);
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
);
164 array_concat(toplevel
, out
, b
);
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
);
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();
194 d
->setLengthProperty(0);
195 return undefinedAtom
;
199 Atom outAtom
= d
->getUintProperty (len
-1);
200 d
->delUintProperty (len
- 1);
201 d
->setLengthProperty(len
- 1);
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())
218 // generic object version
219 if (!AvmCore::isObject(thisAtom
))
222 ScriptObject
*d
= AvmCore::atomToScriptObject(thisAtom
);
223 uint32_t j
= d
->getLengthProperty();
230 Atom frontAtom
= d
->getUintProperty(i
);
231 Atom backAtom
= d
->getUintProperty(j
);
233 d
->setUintProperty(i
++, backAtom
);
234 d
->setUintProperty(j
--, frontAtom
);
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
);
249 if (a
&& a
->try_shift(result
))
254 if (!AvmCore::isObject(thisAtom
))
255 return undefinedAtom
;
258 ScriptObject
*d
= AvmCore::atomToScriptObject(thisAtom
);
259 uint32_t len
= d
->getLengthProperty();
263 // set len to 0 (ecmascript spec)
264 d
->setLengthProperty(0);
265 outAtom
= undefinedAtom
;
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);
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
))
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
);
303 ArrayObject
*out
= toplevel
->arrayClass()->newArray(b
-a
);
306 for (uint32_t i
=a
; i
<b
; i
++) {
307 out
->setUintProperty (outIndex
++, d
->getUintProperty (i
));
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
324 /*************************************************************
325 * Forward declarations required by public methods
326 *************************************************************/
328 /** Options from script */
333 ,kReturnIndexedArray
= 8
337 /** Array.sortOn() will pass an array of field names */
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
); }
355 /*************************************************************
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
);
368 /*************************************************************
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
];
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;
404 /*************************************************************
405 * Private Member Variables
406 *************************************************************/
413 CompareFuncPtr cmpFunc
;
414 CompareFuncPtr altCmpFunc
;
415 Atom cmpActionScript
;
422 HeapAtomList
* fieldatoms
;
425 ArraySort::ArraySort(
430 CompareFuncPtr cmpFunc
,
431 CompareFuncPtr altCmpFunc
,
432 Atom cmpActionScript
,
438 toplevel(f
->toplevel()),
441 altCmpFunc(altCmpFunc
),
442 cmpActionScript(cmpActionScript
),
445 numFields(numFields
),
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
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
--)
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
);
505 uint32_t temp
= index
[i
];
508 if (!d
->hasUintProperty(i
)) {
510 index
[j
] = index
[newlen
];
511 index
[newlen
] = 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
;
527 this->cmpFunc
= ArraySort::StringCompareFunc
;
530 if (opt
& ArraySort::kDescending
) {
531 this->altCmpFunc
= this->cmpFunc
;
532 this->cmpFunc
= ArraySort::DescendingCompareFunc
;
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
--)
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
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
))) {
572 uint32_t temp
= index
[i
];
575 if (!d
->hasUintProperty(i
)) {
577 index
[j
] = index
[newlen
];
578 index
[newlen
] = temp
;
586 iFirstAbsent
= newlen
;
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
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);
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();
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
628 HeapAtomList
* temp
= atoms
;
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
651 ArraySort::~ArraySort()
653 mmfx_delete_array(index
);
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
);
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
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.
681 struct StackFrame
{ uint32_t lo
, hi
; };
685 // leave without doing anything if the array is empty (lo > hi) or only one element (lo == hi)
689 // code below branches to this label instead of recursively calling qsort()
692 size
= (hi
- lo
) + 1; // number of elements in the partition
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,
707 if (compare(lo
, lo
+ 1) > 0) {
709 if (compare(lo
+ 1, lo
+ 2) > 0) {
710 swap(lo
+ 1, lo
+ 2);
711 if (compare(lo
, lo
+ 1) > 0) {
716 if (compare(lo
+ 1, lo
+ 2) > 0) {
717 swap(lo
+ 1, lo
+ 2);
718 if (compare(lo
, lo
+ 1) > 0) {
723 } else if (size
== 2) {
724 if (compare(lo
, lo
+ 1) > 0)
727 // size is one, zero or negative, so there isn't any sorting to be done
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);
739 uint32_t right
= hi
+ 1;
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.
751 } while ((left
<= hi
) && (compare(left
, lo
) <= 0));
755 } while ((right
> lo
) && (compare(right
, lo
) >= 0));
763 // move the pivot after the lower partition
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
)
783 stk
[stkptr
].hi
= right
- 1;
797 stk
[stkptr
].lo
= left
;
802 if ((lo
+ 1) < right
)
805 goto recurse
; /* do small recursion */
810 // we reached the bottom of the well, pop the nested stack frame
818 // we've returned to the top, so we are done!
823 * compare(j, k) as string's
825 int ArraySort::StringCompare(uint32_t j
, uint32_t k
) const
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
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
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
);
892 if (diff
== diff
) { // same as !isNaN
893 return (diff
< 0) ? -1 : ((diff
> 0) ? 1 : 0);
894 } else if (!MathUtils::isNaN(y
)) {
896 } else if (!MathUtils::isNaN(x
)) {
904 * compare(j, k) as numbers
906 int ArraySort::NumericCompareCorrect(uint32_t j
, uint32_t k
) const
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
);
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;
929 double x
= AvmCore::number(atmj
);
930 double y
= AvmCore::number(atmk
);
933 if (diff
== diff
) { // same as !isNaN
934 return (diff
< 0) ? -1 : ((diff
> 0) ? 1 : 0);
935 } else if (!MathUtils::isNaN(y
)) {
937 } else if (!MathUtils::isNaN(x
)) {
944 ScriptObject
* ArraySort::toFieldObject(Atom atom
) const
946 if (atomKind(atom
) != kObjectType
)
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). */
953 toplevel
->throwTypeError(
954 (atom
== undefinedAtom
) ? kConvertUndefinedToObjectError
:
955 kConvertNullToObjectError
);
959 return AvmCore::atomToScriptObject(atom
);
963 * FieldCompare is for Array.sortOn()
965 inline int ArraySort::FieldCompare(uint32_t lhs
, uint32_t rhs
) const
974 ScriptObject
* obj_j
= toFieldObject(j
);
975 ScriptObject
* obj_k
= toFieldObject(k
);
977 if (!(obj_j
&& obj_k
))
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
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
) {
1023 } else if (has_x
&& !has_y
) {
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
)) {
1038 } else if (!MathUtils::isNaN(lhs
)) {
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
);
1062 if (opt
& kDescending
)
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:
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
;
1097 if (args
->getLength() >= 1)
1100 Atom arg0
= args
->getUintProperty(0);
1101 if (AvmCore::isObject (arg0
))
1103 // make sure the sort function is callable
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
);
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
);
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
;
1141 compare
= ArraySort::StringCompareFunc
;
1145 if (opt
& ArraySort::kDescending
) {
1146 altCompare
= compare
;
1147 compare
= ArraySort::DescendingCompareFunc
;
1151 ArraySort
sort(result
, toplevel
->arrayClass(), d
, opt
, compare
, altCompare
, cmp
);
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;
1182 if (AvmCore::istype(namesAtom
, STRING_TYPE
))
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
));
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
));
1221 options
= AvmCore::integer(optionsAtom
);
1222 for (uint32_t i
= 0; i
< nFields
; i
++)
1224 fn
[i
].options
= options
;
1229 // ~ArraySort() will "delete [] fn;"
1231 ArraySort
sort(result
, toplevel
->arrayClass(), d
, options
, ArraySort::FieldCompareFunc
, NULL
, undefinedAtom
, nFields
, fn
);
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())
1250 if (!AvmCore::isObject(thisAtom
))
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
);
1272 if (a
&& (out
= a
->try_splice(insertPoint
, insertCount
, deleteCount
, args
, 2)) != NULL
)
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
);
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.
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
);
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
);
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
);
1339 /*static*/ int ArrayClass::generic_indexOf(Toplevel
* toplevel
, Atom thisAtom
, Atom searchElement
, int startIndex
)
1341 if (!AvmCore::isObject(thisAtom
))
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
)
1360 /*static*/ int ArrayClass::generic_lastIndexOf(Toplevel
* toplevel
, Atom thisAtom
, Atom searchElement
, int startIndex
)
1362 if (!AvmCore::isObject(thisAtom
))
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
))
1373 for (int i
= start
; i
>= 0; i
--)
1375 Atom atom
= d
->getUintProperty(i
);
1376 if (core
->stricteq (atom
, searchElement
) == trueAtom
)
1383 /*static*/ bool ArrayClass::generic_every(Toplevel
* toplevel
, Atom thisAtom
, ScriptObject
*callback
, Atom thisObject
)
1385 if (!AvmCore::isObject(thisAtom
) || !callback
)
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
1406 Atom result
= callback
->call(3, args
);
1407 if (result
!= trueAtom
)
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
)
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
1440 core
->uintToAtom(i
), // index
1443 Atom result
= callback
->call(3, args
);
1444 if (result
== trueAtom
)
1445 r
->push(&element
, 1);
1451 /*static*/ void ArrayClass::generic_forEach(Toplevel
* toplevel
, Atom thisAtom
, ScriptObject
*callback
, Atom thisObject
)
1453 if (!AvmCore::isObject(thisAtom
) || !callback
)
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
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
)
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
1498 d
->getUintProperty(i
), // element
1499 core
->uintToAtom(i
), // index
1502 Atom result
= callback
->call(3, args
);
1503 if (result
== trueAtom
)
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
)
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
1527 d
->getUintProperty(i
), // element
1528 core
->uintToAtom(i
), // index
1531 Atom result
= callback
->call(3, args
);
1532 r
->push(&result
, 1);
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();