CVSROOT: /sources/gnash Module name: gnash Changes by: Sandro Santilli <strk> 07/07/29 12:57:01
Modified files: . : ChangeLog server : array.cpp array.h testsuite/actionscript.all: array.as Log message: Patch #6124 by [EMAIL PROTECTED]: * server/array.{cpp,h}: implement remaining array.sort(..) functionality (flags UNIQUE and RETURNINDEXEDARRAY) all of array.sortOn(...) * testsuite/actionscript.all/array.as: test for Array.sort and Array.sortOn CVSWeb URLs: http://cvs.savannah.gnu.org/viewcvs/gnash/ChangeLog?cvsroot=gnash&r1=1.3858&r2=1.3859 http://cvs.savannah.gnu.org/viewcvs/gnash/server/array.cpp?cvsroot=gnash&r1=1.66&r2=1.67 http://cvs.savannah.gnu.org/viewcvs/gnash/server/array.h?cvsroot=gnash&r1=1.30&r2=1.31 http://cvs.savannah.gnu.org/viewcvs/gnash/testsuite/actionscript.all/array.as?cvsroot=gnash&r1=1.21&r2=1.22 Patches: Index: ChangeLog =================================================================== RCS file: /sources/gnash/gnash/ChangeLog,v retrieving revision 1.3858 retrieving revision 1.3859 diff -u -b -r1.3858 -r1.3859 --- ChangeLog 29 Jul 2007 12:45:24 -0000 1.3858 +++ ChangeLog 29 Jul 2007 12:57:00 -0000 1.3859 @@ -1,5 +1,12 @@ 2007-07-29 <[EMAIL PROTECTED]> + * server/array.{cpp,h}: implement remaining array.sort(..) functionality + (flags UNIQUE and RETURNINDEXEDARRAY) all of array.sortOn(...) + * testsuite/actionscript.all/array.as: test for Array.sort and + Array.sortOn + +2007-07-29 <[EMAIL PROTECTED]> + * server/array.cpp: Fix bug in the array.splice(index) method to prevent an incorrect number of elements being spliced; fix bug in the array.slice(startindex, endindex) method for certain Index: server/array.cpp =================================================================== RCS file: /sources/gnash/gnash/server/array.cpp,v retrieving revision 1.66 retrieving revision 1.67 diff -u -b -r1.66 -r1.67 --- server/array.cpp 29 Jul 2007 12:45:25 -0000 1.66 +++ server/array.cpp 29 Jul 2007 12:57:01 -0000 1.67 @@ -29,6 +29,7 @@ #include "fn_call.h" #include "GnashException.h" #include "action.h" // for call_method +#include "VM.h" // for PROPNAME macro to work #include <string> #include <algorithm> @@ -40,91 +41,239 @@ namespace gnash { +typedef bool (* as_cmp_fn) (const as_value&, const as_value&); + static as_object* getArrayInterface(); static void attachArrayProperties(as_object& proto); static void attachArrayInterface(as_object& proto); -// Default as_value strict weak comparator (string based) -class AsValueLessThen +inline static bool int_lt_or_eq (int a) +{ + return a <= 0; +} + +inline static bool int_gt (int a) +{ + return a > 0; +} + +// simple as_value strict-weak-ordering comparison functions: +inline static bool +as_value_lt (const as_value& a, const as_value& b) { -public: - bool operator() (const as_value& a, const as_value& b) - { return a.to_string().compare(b.to_string()) < 0; - } -}; +} -// Default descending as_value strict weak comparator (string based) -class AsValueLessThenDesc +inline static bool +as_value_gt (const as_value& a, const as_value& b) { -public: - bool operator() (const as_value& a, const as_value& b) - { return a.to_string().compare(b.to_string()) > 0; - } -}; +} -// Case-insensitive as_value strict weak comparator (string) -class AsValueLessThenNoCase +inline static bool +as_value_eq (const as_value& a, const as_value& b) +{ + return a.to_string().compare(b.to_string()) == 0; +} + +inline static int +as_value_StrNoCaseCmp (const as_value& a, const as_value& b) { -public: - bool operator() (const as_value& a, const as_value& b) - { using namespace boost::algorithm; std::string strA = to_upper_copy(a.to_string()); std::string strB = to_upper_copy(b.to_string()); - return strA.compare(strB) < 0; - } -}; + return strA.compare(strB); +} -// Descending Case-insensitive as_value strict weak comparator (string) -class AsValueLessThenDescNoCase +inline static bool +as_value_nocase_lt (const as_value& a, const as_value& b) { -public: - bool operator() (const as_value& a, const as_value& b) - { - using namespace boost::algorithm; + return as_value_StrNoCaseCmp(a, b) < 0; +} - std::string strA = to_upper_copy(a.to_string()); - std::string strB = to_upper_copy(b.to_string()); +inline static bool +as_value_nocase_gt (const as_value& a, const as_value& b) +{ + return as_value_StrNoCaseCmp(a, b) > 0; +} - return strA.compare(strB) > 0; - } -}; +inline static bool +as_value_nocase_eq (const as_value& a, const as_value& b) +{ + return as_value_StrNoCaseCmp(a, b) == 0; +} -// Numeric as_value strict weak comparator -class AsValueLessThenNumeric +inline static bool +as_value_numLT (const as_value& a, const as_value& b) { -public: - bool operator() (const as_value& a, const as_value& b) + if (a.is_undefined()) return false; + if (b.is_undefined()) return true; + if (a.is_null()) return false; + if (b.is_null()) return true; + double aval = a.to_number(); + double bval = b.to_number(); + if (isnan(aval)) return false; + if (isnan(bval)) return true; + return aval < bval; +} + +inline static bool +as_value_numGT (const as_value& a, const as_value& b) +{ + if (b.is_undefined()) return false; + if (a.is_undefined()) return true; + if (b.is_null()) return false; + if (a.is_null()) return true; + double aval = a.to_number(); + double bval = b.to_number(); + if (isnan(bval)) return false; + if (isnan(aval)) return true; + return aval > bval; +} + +inline static bool +as_value_numEQ (const as_value& a, const as_value& b) +{ + if (a.is_undefined() && b.is_undefined()) return true; + if (a.is_null() && b.is_null()) return true; + double aval = a.to_number(); + double bval = b.to_number(); + if (isnan(aval) && isnan(bval)) return true; + return aval == bval; +} + +inline static bool +as_value_num_lt (const as_value& a, const as_value& b) +{ + if (a.is_string() || b.is_string()) + return as_value_lt(a, b); + return as_value_numLT(a, b); +} + +inline static bool +as_value_num_gt (const as_value& a, const as_value& b) +{ + if (a.is_string() || b.is_string()) + return as_value_gt(a, b); + return as_value_numGT(a, b); +} + +inline static bool +as_value_num_eq (const as_value& a, const as_value& b) +{ + if (a.is_string() || b.is_string()) + return as_value_eq(a, b); + return as_value_numEQ(a, b); +} + +inline static bool +as_value_num_nocase_lt (const as_value& a, const as_value& b) +{ + if (a.is_string() || b.is_string()) + return as_value_nocase_lt(a, b); + return as_value_numLT(a, b); +} + +inline static bool +as_value_num_nocase_gt (const as_value& a, const as_value& b) +{ + if (a.is_string() || b.is_string()) + return as_value_nocase_gt(a, b); + return as_value_numGT(a, b); +} + +inline static bool +as_value_num_nocase_eq (const as_value& a, const as_value& b) +{ + if (a.is_string() || b.is_string()) + return as_value_nocase_eq(a, b); + return as_value_numEQ(a, b); +} + +// Return basic as_value comparison function for corresponding sort flag +// Note: +// fUniqueSort and fReturnIndexedArray must first be stripped from the flag +static as_cmp_fn +get_basic_cmp(uint8_t flags) +{ + switch ( flags ) { - return ( a.to_number() < b.to_number() ); + case 0: + return &as_value_lt; + + case as_array_object::fDescending: + return &as_value_gt; + + case as_array_object::fCaseInsensitive: + return &as_value_nocase_lt; + + case as_array_object::fCaseInsensitive | + as_array_object::fDescending: + return &as_value_nocase_gt; + + case as_array_object::fNumeric: + return &as_value_num_lt; + + case as_array_object::fNumeric | as_array_object::fDescending: + return &as_value_num_gt; + + case as_array_object::fCaseInsensitive | + as_array_object::fNumeric: + return &as_value_num_nocase_lt; + + case as_array_object::fCaseInsensitive | + as_array_object::fNumeric | + as_array_object::fDescending: + return &as_value_num_nocase_gt; + + default: + log_error(_("Unhandled sort flags: %d (0x%X)"), flags, flags); + return &as_value_lt; } -}; +} -// Descending Numeric as_value strict weak comparator -class AsValueLessThenDescNumeric +// Return basic as_value equality function for corresponding sort flag +// Note: +// fUniqueSort and fReturnIndexedArray must first be stripped from the flag +static as_cmp_fn +get_basic_eq(uint8_t flags) { -public: - bool operator() (const as_value& a, const as_value& b) + flags &= ~(as_array_object::fDescending); + switch ( flags ) { - return ( a.to_number() > b.to_number() ); - } -}; + case 0: // default string comparison + return &as_value_eq; + + case as_array_object::fCaseInsensitive: + return &as_value_nocase_eq; + + case as_array_object::fNumeric: + return &as_value_num_eq; + + case as_array_object::fCaseInsensitive | + as_array_object::fNumeric: + return &as_value_num_nocase_eq; + default: + return &as_value_eq; + } +} // Custom (ActionScript) comparator -class AsValueFuncComparator +class as_value_custom { public: as_function& _comp; as_object* _object; + bool (*_zeroCmp)(const int); - AsValueFuncComparator(as_function& comparator, boost::intrusive_ptr<as_object> this_ptr) + as_value_custom(as_function& comparator, bool (*zc)(const int), + boost::intrusive_ptr<as_object> this_ptr) : - _comp(comparator) + _comp(comparator), + _zeroCmp(zc) { _object = this_ptr.get(); } @@ -134,18 +283,152 @@ as_value cmp_method(&_comp); as_environment env; as_value ret(0); - int retval; env.push(a); env.push(b); ret = call_method(cmp_method, &env, _object, 2, 1); - retval = (int)ret.to_number(); - if (retval > 0) return true; + return (*_zeroCmp)((int)ret.to_number()); + } +}; + +// Comparator for sorting on a single array property +class as_value_prop +{ +public: + as_cmp_fn _comp; + const std::string& _prop; + + // Note: cmpfn must implement a strict weak ordering + as_value_prop(const std::string& name, + as_cmp_fn cmpfn) + : + _comp(cmpfn), + _prop(name) + { + } + + bool operator() (const as_value& a, const as_value& b) + { + as_value av, bv; + boost::intrusive_ptr<as_object> ao = a.to_object(); + boost::intrusive_ptr<as_object> bo = b.to_object(); + + ao->get_member(_prop, &av); + bo->get_member(_prop, &bv); + return (*_comp)(av, bv); + } +}; + + +// Comparator for sorting on multiple array properties +class as_value_multiprop +{ +public: + const as_cmp_fn* _cmps; + std::deque<std::string>::const_iterator _pBegin, _pEnd, pit; + + // Note: all as_cmp_fns in *cmps must implement strict weak ordering + as_value_multiprop(std::deque<std::string>::const_iterator pBegin, + std::deque<std::string>::const_iterator pEnd, + as_cmp_fn* cmps) + : + _pBegin(pBegin), _pEnd(pEnd) + { + _cmps = cmps; + } + + bool operator() (const as_value& a, const as_value& b) + { + const as_cmp_fn* cmp = _cmps; + + for (pit = _pBegin; pit != _pEnd; ++pit, ++cmp) + { + as_value av, bv; + boost::intrusive_ptr<as_object> ao = a.to_object(); + boost::intrusive_ptr<as_object> bo = b.to_object(); + ao->get_member(*pit, &av); + bo->get_member(*pit, &bv); + + if ( (*cmp)(av, bv) ) return true; + if ( (*cmp)(bv, av) ) return false; + // Note: for loop finishes only if a == b for + // each requested comparison + // (since *cmp(av,bv) == *cmp(bv,av) == false) + } + return false; } }; -// @@ TODO : implement as_array_object's unimplemented functions +class as_value_multiprop_eq : public as_value_multiprop +{ +public: + as_value_multiprop_eq(std::deque<std::string>::const_iterator pBegin, + std::deque<std::string>::const_iterator pEnd, + as_cmp_fn* eq) + : as_value_multiprop(pBegin, pEnd, eq) + { + } + + bool operator() (const as_value& a, const as_value& b) + { + const as_cmp_fn* cmp = _cmps; + + for (pit = _pBegin; pit != _pEnd; ++pit, ++cmp) + { + as_value av, bv; + boost::intrusive_ptr<as_object> ao = a.to_object(); + boost::intrusive_ptr<as_object> bo = b.to_object(); + ao->get_member(*pit, &av); + bo->get_member(*pit, &bv); + + if ( !(*cmp)(av, bv) ) return false; + } + + return true; + } +}; + +// Convenience function to strip fUniqueSort and fReturnIndexedArray from sort +// flag. Presence of flags recorded in douniq and doindex. +static inline uint8_t +flag_preprocess(uint8_t flgs, bool* douniq, bool* doindex) +{ + *douniq = (flgs & as_array_object::fUniqueSort); + *doindex = (flgs & as_array_object::fReturnIndexedArray); + flgs &= ~(as_array_object::fReturnIndexedArray); + flgs &= ~(as_array_object::fUniqueSort); + return flgs; +} + +// Convenience function to process and extract flags from an as_value array +// of flags (as passed to sortOn when sorting on multiple properties) +static void +get_multi_flags(std::deque<as_value>::const_iterator itBegin, + std::deque<as_value>::const_iterator itEnd, + uint8_t* flgs, bool* uniq, bool* index) +{ + std::deque<as_value>::const_iterator it = itBegin; + int i = 0; + + // extract fUniqueSort and fReturnIndexedArray from first flag + if (it != itEnd) + { + uint8_t flag = static_cast<uint8_t>((*it).to_number()); + flag = flag_preprocess(flag, uniq, index); + flgs[i++] = flag; + ++it; + } + + while (it != itEnd) + { + uint8_t flag = static_cast<uint8_t>((*it).to_number()); + flag &= ~(as_array_object::fReturnIndexedArray); + flag &= ~(as_array_object::fUniqueSort); + flgs[i++] = flag; + ++it; + } +} as_array_object::as_array_object() : @@ -172,6 +455,32 @@ { } +std::deque<indexed_as_value> +as_array_object::get_indexed_elements() +{ + std::deque<indexed_as_value> indexed_elements; + int i = 0; + + for (std::deque<as_value>::const_iterator it = elements.begin(); + it != elements.end(); ++it) + { + indexed_elements.push_back(indexed_as_value(*it, i++)); + } + return indexed_elements; +} + +std::deque<as_value>::const_iterator +as_array_object::begin() +{ + return elements.begin(); +} + +std::deque<as_value>::const_iterator +as_array_object::end() +{ + return elements.end(); +} + int as_array_object::index_requested(const std::string& name) { @@ -422,91 +731,17 @@ as_object::set_member_default(name,val); } -std::auto_ptr<as_array_object> -as_array_object::sorted_indexes(uint8_t flags) -{ - assert(flags & as_array_object::fReturnIndexedArray); - log_unimpl("Array.sorted_index"); - return std::auto_ptr<as_array_object>(NULL); -} - -void -as_array_object::sort(uint8_t flags) +as_array_object* +as_array_object::get_indices(std::deque<indexed_as_value> elems) { + as_array_object* intIndexes = new as_array_object(); - // use sorted_index to use this flag - assert( ! (flags & as_array_object::fReturnIndexedArray) ); - - bool do_unique = (flags & as_array_object::fUniqueSort); - - // strip the UniqueSort flag, we'll use the do_unique later - flags &= ~(as_array_object::fUniqueSort); - - switch ( flags ) + for (std::deque<indexed_as_value>::const_iterator it = elems.begin(); + it != elems.end(); ++it) { - case 0: // default sorting - //log_msg(_("Default sorting")); - std::sort(elements.begin(), elements.end(), - AsValueLessThen()); - break; - - case as_array_object::fDescending: - //log_msg(_("Default descending")); - std::sort(elements.begin(), elements.end(), - AsValueLessThenDesc()); - break; - - case as_array_object::fCaseInsensitive: - //log_msg(_("case insensitive")); - std::sort(elements.begin(), elements.end(), - AsValueLessThenNoCase()); - break; - - case as_array_object::fCaseInsensitive | as_array_object::fDescending: - //log_msg(_("case insensitive descending")); - std::sort(elements.begin(), elements.end(), - AsValueLessThenDescNoCase()); - break; - - case as_array_object::fNumeric: - //log_msg(_("numeric")); - std::sort(elements.begin(), elements.end(), - AsValueLessThenNumeric()); - break; - - case as_array_object::fNumeric | as_array_object::fDescending: - //log_msg(_("numeric descending")); - std::sort(elements.begin(), elements.end(), - AsValueLessThenDescNumeric()); - break; - - default: - log_error(_("Unhandled sort flags: %d (0x%X)"), flags, flags); - break; + intIndexes->push(as_value(it->vec_index)); } - - // do the unique step afterwards to simplify code - // (altought it's slower, but we can take care of this later) - // TODO: use the do_unique variable inside the switch cases - // to either use std::sort or std::uniq or similar - if ( do_unique ) - { - log_unimpl(_("array.sort with unique flag")); - } -} - -void -as_array_object::sort(as_function& comparator, boost::intrusive_ptr<as_object> this_ptr, uint8_t flags) -{ - - // use sorted_index to use this flag - assert( ! (flags & as_array_object::fReturnIndexedArray) ); - - // Other flags are simply NOT used - // (or are them ? the descending one could be!) - std::sort(elements.begin(), elements.end(), - AsValueFuncComparator(comparator, this_ptr)); - + return intIndexes; } static as_value @@ -579,47 +814,198 @@ static as_value array_sort(const fn_call& fn) { - boost::intrusive_ptr<as_array_object> array = ensureType<as_array_object>(fn.this_ptr); - + boost::intrusive_ptr<as_array_object> array = + ensureType<as_array_object>(fn.this_ptr); uint8_t flags = 0; if ( fn.nargs == 0 ) { - array->sort(flags); + array->sort(&as_value_lt); + return as_value((boost::intrusive_ptr<as_object>)array); } else if ( fn.nargs == 1 && fn.arg(0).is_number() ) { flags=static_cast<uint8_t>(fn.arg(0).to_number()); - array->sort(flags); } else if ( fn.arg(0).is_as_function() ) { // Get comparison function as_function* as_func = fn.arg(0).to_as_function(); + bool (*icmp)(int); if ( fn.nargs == 2 && fn.arg(1).is_number() ) - { flags=static_cast<uint8_t>(fn.arg(1).to_number()); - } - array->sort(*as_func, fn.this_ptr, flags); + + if (flags & as_array_object::fDescending) icmp = &int_lt_or_eq; + else icmp = &int_gt; + + as_value_custom avc = + as_value_custom(*as_func, icmp, fn.this_ptr); + + if ( (flags & as_array_object::fReturnIndexedArray) ) + return as_value(array->sort_indexed(&avc)); + array->sort(&avc); + return as_value((boost::intrusive_ptr<as_object>)array); + // note: custom AS function sorting apparently ignores the + // UniqueSort flag which is why it is also ignored here } else { IF_VERBOSE_ASCODING_ERRORS( log_aserror(_("Sort called with invalid arguments.")); ) + return as_value(); } - - return as_value(); // returns void + bool do_unique, do_index; + flags = flag_preprocess(flags, &do_unique, &do_index); + as_cmp_fn comp = get_basic_cmp(flags); + + if (do_unique) + { + as_cmp_fn eq = + get_basic_eq(flags); + if (do_index) return array->sort_indexed(comp, eq); + return array->sort(comp, eq); + } + if (do_index) return as_value(array->sort_indexed(comp)); + array->sort(comp); + return as_value((boost::intrusive_ptr<as_object>)array); } static as_value array_sortOn(const fn_call& fn) { - boost::intrusive_ptr<as_array_object> array = ensureType<as_array_object>(fn.this_ptr); - UNUSED(array); + boost::intrusive_ptr<as_array_object> array = + ensureType<as_array_object>(fn.this_ptr); + + // case: sortOn("prop") + if ( fn.nargs == 1 && fn.arg(0).is_string() ) + { + std::string propField = PROPNAME(fn.arg(0).to_string()); + as_value_prop avc = as_value_prop(propField, &as_value_lt); + array->sort(&avc); + return as_value((boost::intrusive_ptr<as_object>)array); + } + + // case: sortOn("prop", Array.FLAG) + bool do_unique = false, do_index = false; + if ( fn.nargs == 2 && fn.arg(0).is_string() ) + { + std::string propField = PROPNAME(fn.arg(0).to_string()); + if ( fn.arg(1).is_number() ) + { + uint8_t flags = + static_cast<uint8_t>(fn.arg(1).to_number()); + flags = flag_preprocess(flags, &do_unique, &do_index); + as_value_prop avc = as_value_prop(propField, + get_basic_cmp(flags)); + if (do_unique) + { + as_value_prop ave = as_value_prop(propField, + get_basic_eq(flags)); + if (do_index) + return array->sort_indexed(&avc, &ave); + return array->sort(&avc, &ave); + } + if (do_index) + return as_value(array->sort_indexed(&avc)); + array->sort(&avc); + return as_value((boost::intrusive_ptr<as_object>)array); + } + } + + // case: sortOn(["prop1", "prop2"] ...) + if (fn.nargs > 0 && fn.arg(0).is_object() ) + { + boost::intrusive_ptr<as_array_object> props = + ensureType<as_array_object>(fn.arg(0).to_object()); + std::deque<std::string> prp; + unsigned int optnum = props->size(); + as_cmp_fn cmp[optnum]; + as_cmp_fn eq[optnum]; - log_unimpl("Array.sortOn()"); + for (std::deque<as_value>::const_iterator it = props->begin(); + it != props->end(); ++it) + { + std::string s = PROPNAME((*it).to_string()); + prp.push_back(s); + } + + // case: sortOn(["prop1", "prop2"]) + if (fn.nargs == 1) + { // assign each cmp function to the standard cmp fn + for (unsigned int i = 0; i < optnum; i++) + cmp[i] = &as_value_lt; + } + // case: sortOn(["prop1", "prop2"], [Array.FLAG1, Array.FLAG2]) + else if ( fn.arg(1).is_object() ) + { + boost::intrusive_ptr<as_array_object> farray = + ensureType<as_array_object>(fn.arg(1).to_object()); + if (farray->size() == optnum) + { + uint8_t flgs[optnum]; + std::deque<as_value>::const_iterator + fBegin = farray->begin(), + fEnd = farray->end(); + + get_multi_flags(fBegin, fEnd, flgs, + &do_unique, &do_index); + + for (unsigned int i = 0; i < optnum; i++) + cmp[i] = get_basic_cmp(flgs[i]); + + if (do_unique) + { + for (unsigned int i = 0; i < optnum; i++) + eq[i] = get_basic_eq(flgs[i]); + } + } + else + { + for (unsigned int i = 0; i < optnum; i++) + cmp[i] = &as_value_lt; + } + } + // case: sortOn(["prop1", "prop2"], Array.FLAG) + else if ( fn.arg(1).is_number() ) + { + uint8_t flags = + static_cast<uint8_t>(fn.arg(1).to_number()); + flag_preprocess(flags, &do_unique, &do_index); + as_cmp_fn c = get_basic_cmp(flags); + + for (unsigned int i = 0; i < optnum; i++) + cmp[i] = c; + + if (do_unique) + { + as_cmp_fn e = get_basic_eq(flags); + for (unsigned int i = 0; i < optnum; i++) + eq[i] = e; + } + } + + as_value_multiprop avc = + as_value_multiprop(prp.begin(), prp.end(), cmp); + + if (do_unique) + { + as_value_multiprop_eq ave = + as_value_multiprop_eq(prp.begin(), prp.end(), eq); + if (do_index) + return array->sort_indexed(&avc, &ave); + return array->sort(&avc, &ave); + } + if (do_index) + return as_value(array->sort_indexed(&avc)); + array->sort(&avc); + return as_value((boost::intrusive_ptr<as_object>)array); + + } + IF_VERBOSE_ASCODING_ERRORS( + log_aserror(_("SortOn called with invalid arguments.")); + ) return as_value(); } Index: server/array.h =================================================================== RCS file: /sources/gnash/gnash/server/array.h,v retrieving revision 1.30 retrieving revision 1.31 diff -u -b -r1.30 -r1.31 --- server/array.h 13 Jul 2007 01:13:46 -0000 1.30 +++ server/array.h 29 Jul 2007 12:57:01 -0000 1.31 @@ -38,6 +38,17 @@ namespace gnash { +struct indexed_as_value : public as_value +{ + int vec_index; + + indexed_as_value(const as_value& val, int index) + : as_value(val) + { + vec_index = index; + } +}; + /// The Array ActionScript object class as_array_object : public as_object { @@ -53,7 +64,10 @@ /// Descending order (b precedes a) fDescending = (1<<1), // 2 - /// Remove consecutive equal elements + /// If two or more elements in the array + /// have identical sort fields, return 0 + /// and don't modify the array. + /// Otherwise proceed to sort the array. fUniqueSort = (1<<2), // 4 /// Don't modify the array, rather return @@ -71,6 +85,12 @@ ~as_array_object(); + std::deque<indexed_as_value> get_indexed_elements(); + + std::deque<as_value>::const_iterator begin(); + + std::deque<as_value>::const_iterator end(); + void push(const as_value& val); void unshift(const as_value& val); @@ -81,6 +101,8 @@ as_value at(unsigned int index); + as_array_object* get_indices(std::deque<indexed_as_value> origElems); + void reverse(); /// @param env @@ -152,17 +174,84 @@ std::auto_ptr<as_array_object> splice(unsigned start, unsigned len, const std::vector<as_value>& replacement); + /// \brief /// Sort the array, using given values comparator - void sort(as_function& comparator, boost::intrusive_ptr<as_object> this_ptr, uint8_t flags=0); + /// + /// @param avc + /// boolean functor or function comparing two as_value& objects + /// + template <class AVCMP> + void sort(AVCMP* avc) + { + std::sort(elements.begin(), elements.end(), *avc); + } + + /// \brief + /// Attempt to sort the array using given values comparator, avc. + /// If two or more elements in the array are equal, as determined + /// by the equality comparator ave, then the array is not sorted + /// and 0 is returned. Otherwise the array is sorted and returned. + /// + /// @param avc + /// boolean functor or function comparing two as_value& objects + /// used to determine sort-order + /// + /// @param ave + /// boolean functor or function comparing two as_value& objects + /// used to determine equality + /// + template <class AVCMP, class AVEQ> + as_value sort(AVCMP* avc, AVEQ* ave) + { + std::deque<as_value> nelem = std::deque<as_value>(elements); - void sort(uint8_t flags=0); + std::sort(nelem.begin(), nelem.end(), *avc); + if (adjacent_find(nelem.begin(), nelem.end(), *ave) != nelem.end() ) + return as_value(0); + elements = nelem; + return as_value(this); + } + + /// \brief /// Return a new array containing sorted index of this array - // - /// NOTE: assert(flags & Array::fReturnIndexedArray) - std::auto_ptr<as_array_object> sorted_indexes(uint8_t flags); + /// + /// @param avc + /// boolean functor or function comparing two as_value& objects + /// + template <class AVCMP> + as_array_object* sort_indexed(AVCMP* avc) + { + std::deque<indexed_as_value> ielem = get_indexed_elements(); + std::sort(ielem.begin(), ielem.end(), *avc); + return get_indices(ielem); + } + + /// \brief + /// Return a new array containing sorted index of this array. + /// If two or more elements in the array are equal, as determined + /// by the equality comparator ave, then 0 is returned instead. + /// + /// @param avc + /// boolean functor or function comparing two as_value& objects + /// used to determine sort-order + /// + /// @param ave + /// boolean functor or function comparing two as_value& objects + /// used to determine equality + /// + template <class AVCMP, class AVEQ> + as_value sort_indexed(AVCMP* avc, AVEQ* ave) + { + std::deque<indexed_as_value> ielem = get_indexed_elements(); + + std::sort(ielem.begin(), ielem.end(), *avc); - std::auto_ptr<as_array_object> sorted_indexes(as_function& comparator, uint8_t flags); + if (adjacent_find(ielem.begin(), ielem.end(), *ave) != ielem.end() ) + return as_value(0); + + return get_indices(ielem); + } /// Overridden to provide 'length' member // Index: testsuite/actionscript.all/array.as =================================================================== RCS file: /sources/gnash/gnash/testsuite/actionscript.all/array.as,v retrieving revision 1.21 retrieving revision 1.22 diff -u -b -r1.21 -r1.22 --- testsuite/actionscript.all/array.as 29 Jul 2007 12:45:25 -0000 1.21 +++ testsuite/actionscript.all/array.as 29 Jul 2007 12:57:01 -0000 1.22 @@ -5,7 +5,7 @@ // Updated with sort functions, and to use check() macro // by Mike Carlson Feb. 14th, 2006 -rcsid="$Id: array.as,v 1.21 2007/07/29 12:45:25 strk Exp $"; +rcsid="$Id: array.as,v 1.22 2007/07/29 12:57:01 strk Exp $"; #include "check.as" @@ -348,3 +348,430 @@ xcheck(c.hasOwnProperty('2')); #endif +//------------------------------- +// Test sort +//------------------------------- + +function cmp_fn(x,y) +{ + if (x.length < y.length) { return -1; } + if (x.length > y.length) { return 1; } + return 0; +} + +function tolen(x) +{ + var i; + str = "["; + for (i = 0; i < x.length; i++) + { + str += String(x[i].length); + if (i != x.length - 1) str += ", "; + } + str += "]"; + return str; +} + +a = ["ed", "emacs", "", "vi", "nano", "Jedit"]; +b = [8, 1, -2, 5, -7, -9, 3, 0]; +c = [7.2, 2.0, -0.5, 3/0, 0.0, 8.35, 0.001, -3.7]; +d = []; +e = ["singleton"]; + +trace(" -- Basic Sort Tests -- "); + +r = a.sort( Array.NUMERIC ); +check_equals( r.toString(), ",Jedit,ed,emacs,nano,vi" ); +check_equals( a.toString(), ",Jedit,ed,emacs,nano,vi" ); +a.sort( Array.NUMERIC | Array.CASEINSENSITIVE ); +check_equals( a.toString(), ",ed,emacs,Jedit,nano,vi" ); +a.sort(); +check_equals( a.toString(), ",Jedit,ed,emacs,nano,vi" ); +a.sort( Array.CASEINSENSITIVE ); +check_equals( a.toString(), ",ed,emacs,Jedit,nano,vi" ); +a.sort( Array.UNIQUESORT ); +check_equals( a.toString(), ",Jedit,ed,emacs,nano,vi" ); +r = a.sort( Array.DESCENDING ); +check_equals( r.toString(), "vi,nano,emacs,ed,Jedit," ); +check_equals( a.toString(), "vi,nano,emacs,ed,Jedit," ); + +r = b.sort(); +check_equals( r.toString(), "-2,-7,-9,0,1,3,5,8" ); +check_equals( b.toString(), "-2,-7,-9,0,1,3,5,8" ); +b.sort( Array.NUMERIC ); +check_equals( b.toString(), "-9,-7,-2,0,1,3,5,8" ); +b.sort( Array.UNIQUESORT ); +check_equals( b.toString(), "-2,-7,-9,0,1,3,5,8" ); +b.sort( Array.DESCENDING ); +check_equals( b.toString(), "8,5,3,1,0,-9,-7,-2" ); +r = b.sort( Array.DESCENDING | Array.NUMERIC ); +check_equals( r.toString(), "8,5,3,1,0,-2,-7,-9" ); +check_equals( b.toString(), "8,5,3,1,0,-2,-7,-9" ); + +r = c.sort(); +check_equals( r.toString(), "-0.5,-3.7,0,0.001,2,7.2,8.35,Infinity" ); +check_equals( c.toString(), "-0.5,-3.7,0,0.001,2,7.2,8.35,Infinity" ); +c.sort( Array.CASEINSENSITIVE ); +check_equals( c.toString(), "-0.5,-3.7,0,0.001,2,7.2,8.35,Infinity" ); +c.sort( Array.NUMERIC ); +check_equals( c.toString(), "-3.7,-0.5,0,0.001,2,7.2,8.35,Infinity" ); +r = c.sort( Array.UNIQUESORT ); +check_equals( c.toString(), "-0.5,-3.7,0,0.001,2,7.2,8.35,Infinity" ); +r = c.sort( Array.DESCENDING | Array.NUMERIC ); +check_equals( c.toString(), "Infinity,8.35,7.2,2,0.001,0,-0.5,-3.7" ); + +r = d.sort(); +check_equals( r.toString(), "" ); +check_equals( d.toString(), "" ); +d.sort( Array.UNIQUESORT ); +check_equals( d.toString(), "" ); +d.sort( Array.DESCENDING | Array.NUMERIC ); +check_equals( d.toString(), "" ); + +r = e.sort(); +check_equals( r.toString(), "singleton" ); +check_equals( e.toString(), "singleton" ); +e.sort( Array.UNIQUESORT ); +check_equals( e.toString(), "singleton" ); +e.sort( Array.DESCENDING | Array.CASEINSENSITIVE ); +check_equals( e.toString(), "singleton" ); + +trace(" -- Return Indexed Array Tests -- "); + +r = a.sort( Array.RETURNINDEXEDARRAY ); +check_equals( r.toString(), "5,4,3,2,1,0" ); +check_equals( a.toString(), "vi,nano,emacs,ed,Jedit," ); +r = a.sort( Array.RETURNINDEXEDARRAY | Array.DESCENDING | Array.CASEINSENSITIVE ); +check_equals( r.toString(), "0,1,4,2,3,5" ); +check_equals( a.toString(), "vi,nano,emacs,ed,Jedit," ); +r = b.sort( Array.RETURNINDEXEDARRAY ); +check_equals( r.toString(), "5,6,7,4,3,2,1,0" ); +r = b.sort( Array.RETURNINDEXEDARRAY | Array.NUMERIC ); +check_equals( r.toString(), "7,6,5,4,3,2,1,0" ); +r = b.sort( Array.RETURNINDEXEDARRAY | Array.DESCENDING | Array.CASEINSENSITIVE ); +check_equals( r.toString(), "0,1,2,3,4,7,6,5" ); +r = c.sort( Array.RETURNINDEXEDARRAY ); +check_equals( r.toString(), "6,7,5,4,3,2,1,0" ); +r = c.sort( Array.RETURNINDEXEDARRAY | Array.NUMERIC ); +check_equals( r.toString(), "7,6,5,4,3,2,1,0" ); +r = c.sort( Array.RETURNINDEXEDARRAY | Array.DESCENDING | Array.CASEINSENSITIVE ); +check_equals( r.toString(), "0,1,2,3,4,5,7,6" ); +r = d.sort( Array.RETURNINDEXEDARRAY | Array.DESCENDING ); +check_equals( r.toString(), "" ); +check_equals( d.toString(), "" ); +r = d.sort( Array.NUMERIC | Array.RETURNINDEXEDARRAY ); +check_equals( r.toString(), "" ); +check_equals( d.toString(), "" ); +r = e.sort( Array.CASEINSENSITIVE | Array.RETURNINDEXEDARRAY ); +check_equals( r.toString(), "0" ); +check_equals( e.toString(), "singleton" ); +r = e.sort( Array.NUMERIC | Array.RETURNINDEXEDARRAY | Array.DESCENDING ); +check_equals( r.toString(), "0" ); + +trace(" -- Custom AS function tests -- "); +r = a.sort( cmp_fn, Array.UNIQUE ); +check_equals( r.toString(), ",vi,ed,nano,emacs,Jedit" ); +check_equals( a.toString(), ",vi,ed,nano,emacs,Jedit" ); +r = a.sort( something_undefined ); +check_equals(typeof(r), 'undefined'); +r = a.sort( cmp_fn, Array.DESCENDING ); +check_equals( tolen(r), "[5, 5, 4, 2, 2, 0]" ); +check_equals( tolen(a), "[5, 5, 4, 2, 2, 0]" ); +a.sort( cmp_fn, Array.CASEINSENSITIVE | Array.NUMERIC ); +check_equals( tolen(a), "[0, 2, 2, 4, 5, 5]" ); +r = a.sort( cmp_fn, Array.RETURNINDEXEDARRAY ); +check_equals( r.toString(), "0,1,2,3,4,5" ); +r = a.sort( cmp_fn, Array.RETURNINDEXEDARRAY | Array.DESCENDING ); +check_equals( r.toString(), "5,4,3,2,1,0" ); +r = d.sort( cmp_fn ); +check_equals( r.toString(), "" ); +check_equals( d.toString(), "" ); +r = d.sort( cmp_fn, Array.UNIQUESORT | Array.CASEINSENSITIVE ); +check_equals( r.toString(), "" ); +check_equals( d.toString(), "" ); +r = e.sort( cmp_fn, Array.UNIQUESORT | Array.CASEINSENSITIVE ); +check_equals( r.toString(), "singleton" ); +check_equals( e.toString(), "singleton" ); + +a.push("ED"); +b.push(3.0); +c.push(9/0); + +trace(" -- UNIQUESORT tests -- "); + +r = a.sort( Array.UNIQUESORT ); +check_equals( r.toString(), ",ED,Jedit,ed,emacs,nano,vi" ); +check_equals( a.toString(), ",ED,Jedit,ed,emacs,nano,vi" ); +r = a.sort( Array.UNIQUESORT | Array.CASEINSENSITIVE ); +check_equals( r.toString(), "0" ); +check_equals( a.toString(), ",ED,Jedit,ed,emacs,nano,vi" ); +r = a.sort( Array.UNIQUESORT | Array.CASEINSENSITIVE | Array.DESCENDING ); +check_equals( r.toString(), "0" ); +check_equals( a.toString(), ",ED,Jedit,ed,emacs,nano,vi" ); +r = a.sort( Array.UNIQUESORT | Array.RETURNINDEXEDARRAY ); +check_equals( r.toString(), "0,1,2,3,4,5,6" ); +r = a.sort( Array.UNIQUESORT | Array.CASEINSENSITIVE | Array.DESCENDING | Array.RETURNINDEXEDARRAY ); +check_equals( r.toString(), "0" ); + +r = b.sort( Array.UNIQUESORT ); +check_equals( r.toString(), "0" ); +check_equals( b.toString(), "8,5,3,1,0,-2,-7,-9,3" ); +r = b.sort( Array.UNIQUESORT | Array.NUMERIC ); +check_equals( r.toString(), "0" ); +r = b.sort( Array.UNIQUESORT | Array.NUMERIC | Array.DESCENDING ); +check_equals( r.toString(), "0" ); +r = b.sort( Array.UNIQUESORT | Array.RETURNINDEXEDARRAY ); +check_equals( r.toString(), "0" ); +r = b.sort( Array.UNIQUESORT | Array.NUMERIC | Array.DESCENDING | Array.RETURNINDEXEDARRAY ); +check_equals( r.toString(), "0" ); + +r = c.sort( Array.UNIQUESORT ); +check_equals( r.toString(), "0" ); +check_equals( c.toString(), "Infinity,8.35,7.2,2,0.001,0,-0.5,-3.7,Infinity" ); +r = c.sort( Array.UNIQUESORT | Array.NUMERIC ); +check_equals( r.toString(), "0" ); +r = c.sort( Array.UNIQUESORT | Array.NUMERIC | Array.DESCENDING ); +check_equals( r.toString(), "0" ); +r = c.sort( Array.UNIQUESORT | Array.RETURNINDEXEDARRAY ); +check_equals( r.toString(), "0" ); +r = c.sort( Array.UNIQUESORT | Array.NUMERIC | Array.DESCENDING | Array.RETURNINDEXEDARRAY ); +check_equals( r.toString(), "0" ); +r = a.sort( cmp_fn, Array.UNIQUE | Array.CASEINSENSITIVE ); +check_equals( tolen(r), "[0, 2, 2, 2, 4, 5, 5]" ); +check_equals( tolen(a), "[0, 2, 2, 2, 4, 5, 5]" ); +r = a.sort( cmp_fn, Array.UNIQUE | Array.CASEINSENSITIVE | Array.RETURNINDEXEDARRAY ); +check_equals( r.toString(), "0,1,2,3,4,5,6" ); +r = a.sort( cmp_fn, Array.UNIQUE | Array.CASEINSENSITIVE | Array.RETURNINDEXEDARRAY | Array.DESCENDING ); +check_equals( r.toString(), "6,5,4,3,2,1,0" ); + +trace(" -- Array with null value -- "); +c.push(null); + +r = c.sort(); +check_equals( r.toString(), "-0.5,-3.7,0,0.001,2,7.2,8.35,Infinity,Infinity,null" ); +check_equals( c.toString(), "-0.5,-3.7,0,0.001,2,7.2,8.35,Infinity,Infinity,null" ); +c.sort( Array.NUMERIC ); +check_equals( c.toString(), "-3.7,-0.5,0,0.001,2,7.2,8.35,Infinity,Infinity,null" ); +c.sort( Array.DESCENDING | Array.NUMERIC ); +check_equals( c.toString(), "null,Infinity,Infinity,8.35,7.2,2,0.001,0,-0.5,-3.7" ); +r = c.sort( Array.RETURNINDEXEDARRAY | Array.NUMERIC ); +check_equals( r.toString(), "9,8,7,6,5,4,3,1,2,0" ); +r = c.sort( Array.RETURNINDEXEDARRAY | Array.DESCENDING | Array.CASEINSENSITIVE ); +check_equals( r.toString(), "0,1,2,3,4,5,6,7,9,8" ); +r = c.sort( Array.UNIQUESORT ); +check_equals( r.toString(), "0" ); +r = c.sort( Array.UNIQUESORT | Array.NUMERIC ); +check_equals( r.toString(), "0" ); +r = c.sort( Array.UNIQUESORT | Array.RETURNINDEXEDARRAY ); +check_equals( r.toString(), "0" ); +r = c.sort( Array.UNIQUESORT | Array.NUMERIC | Array.DESCENDING | Array.RETURNINDEXEDARRAY ); +check_equals( r.toString(), "0" ); + +trace(" -- Array with 2 null values -- "); +c = [7.2, 2.0, null, -0.5, 3/0, 0.0, null, 8.35, 0.001, -3.7]; +c.sort( Array.NUMERIC ); +check_equals( c.toString(), "-3.7,-0.5,0,0.001,2,7.2,8.35,Infinity,null,null" ); +c.sort( Array.DESCENDING | Array.NUMERIC ); +check_equals( c.toString(), "null,null,Infinity,8.35,7.2,2,0.001,0,-0.5,-3.7" ); +r = c.sort( Array.RETURNINDEXEDARRAY | Array.NUMERIC ); +check_equals( r.toString(), "9,8,7,6,5,4,3,2,0,1" ); +check_equals( c.toString(), "null,null,Infinity,8.35,7.2,2,0.001,0,-0.5,-3.7" ); +r = c.sort( Array.UNIQUESORT ); +check_equals( r.toString(), "0" ); +r = c.sort( Array.UNIQUESORT | Array.NUMERIC ); +check_equals( r.toString(), "0" ); + +trace(" -- Array with 2 undefined values -- "); +c = [7.2, 2.0, undefined, -0.5, 3/0, 0.0, undefined, 8.35, 0.001, -3.7]; +r = c.sort( Array.UNIQUESORT ); +check_equals( r.toString(), "0" ); +r = c.sort( Array.UNIQUESORT | Array.NUMERIC ); +check_equals( r.toString(), "0" ); + +trace(" -- Array with 2 NaN values -- "); +c = [7.2, 2.0, NaN, -0.5, 3/0, 0.0, NaN, 8.35, 0.001, -3.7]; +r = c.sort( Array.UNIQUESORT ); +check_equals( r.toString(), "0" ); +r = c.sort( Array.UNIQUESORT | Array.NUMERIC ); +check_equals( r.toString(), "0" ); + +//------------------------------- +// Test sortOn +//------------------------------- + +a = []; +a.push({Name: "Zuse Z3", Year: 1941, Electronic: false}); +a.push({Name: "Colossus", Year: 1943, Electronic: true}); +a.push({Name: "ENIAC", Year: 1944, Electronic: true}); + +function tostr(x) +{ + var i; + str = ""; + for(i = 0; i < x.length; i++) + { + y = a[i]; + str += (y.Name + "," + y.Year + "," + y.Electronic ); + if (i != x.length - 1) str += " | "; + } + return str; +} + +trace("sortOn a single property "); +r = a.sortOn( "Name" ); +check_equals( tostr(r), "Colossus,1943,true | ENIAC,1944,true | Zuse Z3,1941,false" ); +check_equals( tostr(a), "Colossus,1943,true | ENIAC,1944,true | Zuse Z3,1941,false" ); + +r = a.sortOn( "Year" ); +check_equals( tostr(r), "Zuse Z3,1941,false | Colossus,1943,true | ENIAC,1944,true" ); +check_equals( tostr(a), "Zuse Z3,1941,false | Colossus,1943,true | ENIAC,1944,true" ); + +a.sortOn( "Electronic" ); +check_equals( tostr(a), "Zuse Z3,1941,false | Colossus,1943,true | ENIAC,1944,true" ); + +a.sortOn("Year", Array.NUMERIC ); +check_equals( tostr(a), "Zuse Z3,1941,false | Colossus,1943,true | ENIAC,1944,true" ); + +a.sortOn("Year", Array.NUMERIC | Array.DESCENDING ); +check_equals ( tostr(a), "ENIAC,1944,true | Colossus,1943,true | Zuse Z3,1941,false" ); + +r = a.sortOn("Year", Array.UNIQUESORT | Array.NUMERIC ); +check_equals ( tostr(r), "Zuse Z3,1941,false | Colossus,1943,true | ENIAC,1944,true" ); +check_equals ( tostr(a), "Zuse Z3,1941,false | Colossus,1943,true | ENIAC,1944,true" ); + +r = a.sortOn("Year", Array.RETURNINDEXEDARRAY | Array.NUMERIC ); +check_equals( r.toString(), "0,1,2" ); +check_equals ( tostr(a), "Zuse Z3,1941,false | Colossus,1943,true | ENIAC,1944,true" ); + +r = a.sortOn("Name", Array.UNIQUESORT ); +check_equals( tostr(r), "Colossus,1943,true | ENIAC,1944,true | Zuse Z3,1941,false" ); +check_equals( tostr(a), "Colossus,1943,true | ENIAC,1944,true | Zuse Z3,1941,false" ); + +r = a.sortOn("Name", Array.UNIQUESORT | Array.DESCENDING ); +check_equals( tostr(r), "Zuse Z3,1941,false | ENIAC,1944,true | Colossus,1943,true" ); + +r = a.sortOn("Name", Array.UNIQUESORT | Array.RETURNINDEXEDARRAY ); +check_equals( r.toString(), "2,1,0" ); + +r = a.sortOn("Electronic", Array.UNIQUESORT | Array.RETURNINDEXEDARRAY ); +check_equals( r.toString(), "0" ); +check_equals( tostr(a), "Zuse Z3,1941,false | ENIAC,1944,true | Colossus,1943,true"); + +trace("sortOn multiple properties"); +a.push({Name: "Atanasoff-Berry", Year: 1941, Electronic: true, Mass: 320}); + +r = a.sortOn( ["Name", "Year"] ); +check_equals( tostr(r), "Atanasoff-Berry,1941,true | Colossus,1943,true | ENIAC,1944,true | Zuse Z3,1941,false" ); +check_equals( tostr(a), "Atanasoff-Berry,1941,true | Colossus,1943,true | ENIAC,1944,true | Zuse Z3,1941,false" ); + +a.sortOn( ["Electronic", "Year"] ); +check_equals( tostr(a), "Zuse Z3,1941,false | Atanasoff-Berry,1941,true | Colossus,1943,true | ENIAC,1944,true" ); + +a.sortOn( ["Electronic", "Year"], [Array.DESCENDING, Array.NUMERIC] ); +check_equals( tostr(a), "Atanasoff-Berry,1941,true | Colossus,1943,true | ENIAC,1944,true | Zuse Z3,1941,false" ); + +r = a.sortOn( ["Name", "Year"], [Array.UNIQUESORT, Array.NUMERIC] ); +check_equals( tostr(r), "Atanasoff-Berry,1941,true | Colossus,1943,true | ENIAC,1944,true | Zuse Z3,1941,false" ); + +r = a.sortOn( ["Electronic", "Name"], [Array.UNIQUESORT, Array.NUMERIC] ); +check_equals( tostr(r), "Zuse Z3,1941,false | Atanasoff-Berry,1941,true | Colossus,1943,true | ENIAC,1944,true" ); + + +trace("sortOn missing properties" ); +r = a.sortOn(["Megaflops"] ); +check_equals( tostr(r), "Zuse Z3,1941,false | Atanasoff-Berry,1941,true | Colossus,1943,true | ENIAC,1944,true" ); +check_equals( tostr(a), "Zuse Z3,1941,false | Atanasoff-Berry,1941,true | Colossus,1943,true | ENIAC,1944,true" ); + +a.sortOn(["Binary", "Turing complete"] ); +check_equals( tostr(a), "Zuse Z3,1941,false | Atanasoff-Berry,1941,true | Colossus,1943,true | ENIAC,1944,true" ); + +a.sortOn(["Inventor", "Cost"], [Array.DESCENDING, 0] ); +check_equals( tostr(a), "Zuse Z3,1941,false | Atanasoff-Berry,1941,true | Colossus,1943,true | ENIAC,1944,true" ); + +r = a.sortOn(["Name", "Year", "Cost"], [Array.DESCENDING, Array.NUMERIC, 0] ); +check_equals( tostr(a), "Zuse Z3,1941,false | ENIAC,1944,true | Colossus,1943,true | Atanasoff-Berry,1941,true" ); + +r = a.sortOn(["Name", "Cost", "Year"], [0, 0, Array.NUMERIC] ); +check_equals( tostr(a), "Atanasoff-Berry,1941,true | Colossus,1943,true | ENIAC,1944,true | Zuse Z3,1941,false" ); + +r = a.sortOn(["Electronic", "Year", "Cost"], [Array.UNIQUESORT, Array.NUMERIC, Array.NUMERIC] ); +check_equals( tostr(r), "Zuse Z3,1941,false | Atanasoff-Berry,1941,true | Colossus,1943,true | ENIAC,1944,true" ); +check_equals( tostr(a), "Zuse Z3,1941,false | Atanasoff-Berry,1941,true | Colossus,1943,true | ENIAC,1944,true" ); + +r = a.sortOn(["Electronic", "Cost" ], [Array.UNIQUESORT, Array.NUMERIC] ); +check_equals( r.toString(), "0" ); + +trace("sortOn with mismatching array lengths"); +r = a.sortOn( ["Name", "Year"], [0] ); +check_equals( tostr(r), "Atanasoff-Berry,1941,true | Colossus,1943,true | ENIAC,1944,true | Zuse Z3,1941,false" ); +check_equals( tostr(a), "Atanasoff-Berry,1941,true | Colossus,1943,true | ENIAC,1944,true | Zuse Z3,1941,false" ); + +r = a.sortOn( ["Name", "Year"], [Array.DESCENDING] ); +check_equals( tostr(r), "Atanasoff-Berry,1941,true | Colossus,1943,true | ENIAC,1944,true | Zuse Z3,1941,false" ); +check_equals( tostr(a), "Atanasoff-Berry,1941,true | Colossus,1943,true | ENIAC,1944,true | Zuse Z3,1941,false" ); + +a.sortOn(["Name", "Electronic"], [Array.DESCENDING] ); +check_equals( tostr(a), "Atanasoff-Berry,1941,true | Colossus,1943,true | ENIAC,1944,true | Zuse Z3,1941,false" ); + +r = a.sortOn(["Name", "Year"], [Array.RETURNINDEXEDARRAY] ); +check_equals( tostr(r), "Atanasoff-Berry,1941,true | Colossus,1943,true | ENIAC,1944,true | Zuse Z3,1941,false" ); + +trace("sortOn, undocumented invocation"); +r = a.sortOn( ["Name", "Year"], Array.DESCENDING ); +check_equals( tostr(r), "Zuse Z3,1941,false | ENIAC,1944,true | Colossus,1943,true | Atanasoff-Berry,1941,true" ); +check_equals( tostr(a), "Zuse Z3,1941,false | ENIAC,1944,true | Colossus,1943,true | Atanasoff-Berry,1941,true" ); + +a.sortOn( ["Year", "Name"], Array.NUMERIC ); +check_equals( tostr(a), "Atanasoff-Berry,1941,true | Zuse Z3,1941,false | Colossus,1943,true | ENIAC,1944,true" ); + +a.sortOn( ["Electronic", "Year", "Name"], Array.NUMERIC | Array.DESCENDING ); +check_equals( tostr(a), "ENIAC,1944,true | Colossus,1943,true | Atanasoff-Berry,1941,true | Zuse Z3,1941,false" ); + +#if OUTPUT_VERSION > 6 +trace("sortOn partially missing properties"); + +r = a.sortOn(["Name", "Electronic"], [Array.DESCENDING] ); +check_equals( tostr(r), "Atanasoff-Berry,1941,true | Colossus,1943,true | ENIAC,1944,true | Zuse Z3,1941,false" ); +check_equals( tostr(a), "Atanasoff-Berry,1941,true | Colossus,1943,true | ENIAC,1944,true | Zuse Z3,1941,false" ); + +r = a.sortOn(["Name", "Year"], [Array.RETURNINDEXEDARRAY]); +check_equals( tostr(r), "Atanasoff-Berry,1941,true | Colossus,1943,true | ENIAC,1944,true | Zuse Z3,1941,false" ); +check_equals( tostr(a), "Atanasoff-Berry,1941,true | Colossus,1943,true | ENIAC,1944,true | Zuse Z3,1941,false" ); + +trace("sortOn partially missing properties"); +a.push({Name: "Harvard Mark I", Year: 1944, Mass: 4500}); + +a.sortOn(["Electronic", "Year"], Array.DESCENDING | Array.IGNORECASE ); +check_equals( tostr(a), "Harvard Mark I,1944,undefined | ENIAC,1944,true | Colossus,1943,true | Atanasoff-Berry,1941,true | Zuse Z3,1941,false" ); + +a.sortOn( ["Electronic", "Name"], [Array.NUMERIC, Array.DESCENDING] ); +check_equals( tostr(a), "Zuse Z3,1941,false | ENIAC,1944,true | Colossus,1943,true | Atanasoff-Berry,1941,true | Harvard Mark I,1944,undefined" ); + +r = a.sortOn( ["Electronic", "Name"], [Array.UNIQUESORT, Array.NUMERIC] ); +check_equals( tostr(r), "Zuse Z3,1941,false | Atanasoff-Berry,1941,true | Colossus,1943,true | ENIAC,1944,true | Harvard Mark I,1944,undefined" ); +check_equals( tostr(a), "Zuse Z3,1941,false | Atanasoff-Berry,1941,true | Colossus,1943,true | ENIAC,1944,true | Harvard Mark I,1944,undefined" ); + +a.sortOn( ["Mass", "Name"], [0, 0] ); +check_equals( tostr(a), "Atanasoff-Berry,1941,true | Harvard Mark I,1944,undefined | Colossus,1943,true | ENIAC,1944,true | Zuse Z3,1941,false" ); + +a.sortOn( ["Mass", "Year", "Name"], [Array.NUMERIC | Array.DESCENDING, Array.NUMERIC | Array.DESCENDING | 0] ); +check_equals( tostr(a), "Atanasoff-Berry,1941,true | Harvard Mark I,1944,undefined | Zuse Z3,1941,false | Colossus,1943,true | ENIAC,1944,true" ); + +a.sortOn( ["Mass", "Name"], [Array.UNIQUE, Array.DESCENDING] ); +check_equals( tostr(a), "Atanasoff-Berry,1941,true | Harvard Mark I,1944,undefined | Zuse Z3,1941,false | ENIAC,1944,true | Colossus,1943,true" ); + +a.sortOn( ["Electronic", "Mass", "Name"], [0, Array.NUMERIC | Array.DESCENDING, 0] ); +check_equals( tostr(a), "Zuse Z3,1941,false | Colossus,1943,true | ENIAC,1944,true | Atanasoff-Berry,1941,true | Harvard Mark I,1944,undefined" ); + +r = a.sortOn( ["Electronic", "Mass", "Year", "Name"], [Array.RETURNINDEXEDARRAY, Array.NUMERIC, Array.NUMERIC, Array.DESCENDING] ); +check_equals( r.toString(), "0,3,1,2,4"); +#endif // OUTPUT_VERSION > 6 + +#if OUTPUT_VERSION < 7 +trace("sortOn property name case-mismatch"); +a.sortOn( ["name"] ); +check_equals( tostr(a), "Atanasoff-Berry,1941,true | Colossus,1943,true | ENIAC,1944,true | Zuse Z3,1941,false" ); +a.sortOn( ["year", "name"], Array.NUMERIC ); +check_equals( tostr(a), "Atanasoff-Berry,1941,true | Zuse Z3,1941,false | Colossus,1943,true | ENIAC,1944,true" ); +#endif // OUTPUT_VERSION < 7 + _______________________________________________ Gnash-commit mailing list Gnash-commit@gnu.org http://lists.gnu.org/mailman/listinfo/gnash-commit