avm2: Implement `Array.sortOn`
This commit is contained in:
parent
0aa2c50118
commit
3b7922d222
|
@ -797,9 +797,9 @@ where
|
|||
|
||||
/// Sort array storage.
|
||||
///
|
||||
/// This function expects it's values to have been pre-enumerated. They will be
|
||||
/// sorted in-place. It is the caller's responsibility to place the resulting
|
||||
/// half of the sorted array wherever.
|
||||
/// This function expects it's values to have been pre-enumerated and
|
||||
/// pre-resolved. They will be sorted in-place. It is the caller's
|
||||
/// responsibility to place the resulting half of the sorted array wherever.
|
||||
///
|
||||
/// This function will reverse the sort order if `Descending` sort is requested.
|
||||
///
|
||||
|
@ -810,7 +810,7 @@ where
|
|||
/// contents of the `values` array will be sorted in a random order.
|
||||
fn sort_inner<'a, 'gc, 'ctxt, C>(
|
||||
activation: &mut Activation<'a, 'gc, 'ctxt>,
|
||||
values: &mut [(usize, Option<Value<'gc>>)],
|
||||
values: &mut [(usize, Value<'gc>)],
|
||||
options: EnumSet<SortOptions>,
|
||||
mut sort_func: C,
|
||||
) -> Result<bool, Error>
|
||||
|
@ -821,8 +821,8 @@ where
|
|||
let mut error_signal = Ok(());
|
||||
|
||||
values.sort_unstable_by(|(_a_index, a), (_b_index, b)| {
|
||||
let unresolved_a = a.clone().unwrap_or(Value::Undefined);
|
||||
let unresolved_b = b.clone().unwrap_or(Value::Undefined);
|
||||
let unresolved_a = a.clone();
|
||||
let unresolved_b = b.clone();
|
||||
|
||||
if matches!(unresolved_a, Value::Undefined) && matches!(unresolved_b, Value::Undefined) {
|
||||
unique_sort_satisfied = false;
|
||||
|
@ -833,11 +833,7 @@ where
|
|||
return Ordering::Less;
|
||||
}
|
||||
|
||||
match sort_func(
|
||||
activation,
|
||||
a.clone().unwrap_or(Value::Undefined),
|
||||
b.clone().unwrap_or(Value::Undefined),
|
||||
) {
|
||||
match sort_func(activation, a.clone(), b.clone()) {
|
||||
Ok(Ordering::Equal) => {
|
||||
unique_sort_satisfied = false;
|
||||
Ordering::Equal
|
||||
|
@ -897,6 +893,86 @@ fn compare_numeric<'gc>(
|
|||
}
|
||||
}
|
||||
|
||||
/// Take a sorted set of values and produce the result requested by the caller.
|
||||
fn sort_postprocess<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
this: Object<'gc>,
|
||||
options: EnumSet<SortOptions>,
|
||||
unique_satisfied: bool,
|
||||
values: Vec<(usize, Value<'gc>)>,
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
if unique_satisfied {
|
||||
if options.contains(SortOptions::ReturnIndexedArray) {
|
||||
return build_array(
|
||||
activation,
|
||||
ArrayStorage::from_storage(
|
||||
values
|
||||
.iter()
|
||||
.map(|(i, _v)| Some(i.clone().into()))
|
||||
.collect(),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
if let Some(mut old_array) = this.as_array_storage_mut(activation.context.gc_context) {
|
||||
let mut new_vec = Vec::new();
|
||||
|
||||
for (src, v) in values.iter() {
|
||||
if old_array.get(*src).is_none() && !matches!(v, Value::Undefined) {
|
||||
new_vec.push(Some(v.clone()));
|
||||
} else {
|
||||
new_vec.push(old_array.get(*src).clone());
|
||||
}
|
||||
}
|
||||
|
||||
let mut new_array = ArrayStorage::from_storage(new_vec);
|
||||
|
||||
swap(&mut *old_array, &mut new_array);
|
||||
}
|
||||
|
||||
return Ok(this.into());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(0.into())
|
||||
}
|
||||
|
||||
/// Given a value, extract it's array values.
|
||||
///
|
||||
/// If the value is not an array, this function yields `None`.
|
||||
fn extract_array_values<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
value: Value<'gc>,
|
||||
) -> Result<Option<Vec<Value<'gc>>>, Error> {
|
||||
let object = value.coerce_to_object(activation).ok();
|
||||
let holey_vec = if let Some(object) = object {
|
||||
if let Some(field_array) = object.as_array_storage() {
|
||||
let mut array = Vec::new();
|
||||
|
||||
for v in field_array.iter() {
|
||||
array.push(v);
|
||||
}
|
||||
|
||||
array
|
||||
} else {
|
||||
return Ok(None);
|
||||
}
|
||||
} else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let mut unholey_vec = Vec::new();
|
||||
for (i, v) in holey_vec.iter().enumerate() {
|
||||
unholey_vec.push(resolve_array_hole(
|
||||
activation,
|
||||
object.unwrap(),
|
||||
i,
|
||||
v.clone(),
|
||||
)?);
|
||||
}
|
||||
|
||||
Ok(Some(unholey_vec))
|
||||
}
|
||||
|
||||
/// Impl `Array.sort`
|
||||
pub fn sort<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
|
@ -927,16 +1003,17 @@ pub fn sort<'gc>(
|
|||
)
|
||||
};
|
||||
|
||||
let mut values = if let Some(array) = this.as_array_storage() {
|
||||
array
|
||||
let mut values = if let Some(values) = extract_array_values(activation, this.into())? {
|
||||
values
|
||||
.iter()
|
||||
.enumerate()
|
||||
.collect::<Vec<(usize, Option<Value<'gc>>)>>()
|
||||
.map(|(i, v)| (i, v.clone()))
|
||||
.collect::<Vec<(usize, Value<'gc>)>>()
|
||||
} else {
|
||||
return Ok(0.into());
|
||||
};
|
||||
|
||||
let unique_satisified = if let Some(v) = compare_fnc {
|
||||
let unique_satisfied = if let Some(v) = compare_fnc {
|
||||
sort_inner(
|
||||
activation,
|
||||
&mut values,
|
||||
|
@ -973,29 +1050,145 @@ pub fn sort<'gc>(
|
|||
)?
|
||||
};
|
||||
|
||||
if unique_satisified {
|
||||
if options.contains(SortOptions::ReturnIndexedArray) {
|
||||
return build_array(
|
||||
return sort_postprocess(activation, this, options, unique_satisfied, values);
|
||||
}
|
||||
|
||||
Ok(0.into())
|
||||
}
|
||||
|
||||
/// Given a value, extract it's array values.
|
||||
///
|
||||
/// If the value is not an array, it will be returned as if it was present in a
|
||||
/// one-element array containing itself. This is intended for use with parsing
|
||||
/// parameters which are optionally arrays.
|
||||
fn extract_maybe_array_values<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
value: Value<'gc>,
|
||||
) -> Result<Vec<Value<'gc>>, Error> {
|
||||
Ok(extract_array_values(activation, value.clone())?.unwrap_or_else(|| vec![value]))
|
||||
}
|
||||
|
||||
/// Given a value, extract it's array values and coerce them to strings.
|
||||
///
|
||||
/// If the value is not an array, it will be returned as if it was present in a
|
||||
/// one-element array containing itself. This is intended for use with parsing
|
||||
/// parameters which are optionally arrays. The returned value will still be
|
||||
/// coerced into a string in this case.
|
||||
fn extract_maybe_array_strings<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
value: Value<'gc>,
|
||||
) -> Result<Vec<AvmString<'gc>>, Error> {
|
||||
let mut out = Vec::new();
|
||||
|
||||
for value in extract_maybe_array_values(activation, value)? {
|
||||
out.push(value.coerce_to_string(activation)?);
|
||||
}
|
||||
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
/// Given a value, extract it's array values and coerce them to enumsets.
|
||||
///
|
||||
/// If the value is not an array, it will be returned as if it was present in a
|
||||
/// one-element array containing itself. This is intended for use with parsing
|
||||
/// parameters which are optionally arrays. The returned value will still be
|
||||
/// coerced into a string in this case.
|
||||
fn extract_maybe_array_enumsets<'gc, E>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
value: Value<'gc>,
|
||||
) -> Result<Vec<EnumSet<E>>, Error>
|
||||
where
|
||||
E: EnumSetType,
|
||||
{
|
||||
let mut out = Vec::new();
|
||||
|
||||
for value in extract_maybe_array_values(activation, value)? {
|
||||
out.push(value.coerce_to_enumset(activation)?);
|
||||
}
|
||||
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
/// Impl `Array.sortOn`
|
||||
pub fn sort_on<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
this: Option<Object<'gc>>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
if let Some(this) = this {
|
||||
if let Some(field_names_value) = args.get(0).cloned() {
|
||||
let field_names = extract_maybe_array_strings(activation, field_names_value)?;
|
||||
let mut options = extract_maybe_array_enumsets(
|
||||
activation,
|
||||
ArrayStorage::from_storage(
|
||||
args.get(1).cloned().unwrap_or_else(|| 0.into()),
|
||||
)?;
|
||||
|
||||
let first_option = options
|
||||
.get(0)
|
||||
.cloned()
|
||||
.unwrap_or_else(EnumSet::empty)
|
||||
.intersection(SortOptions::UniqueSort | SortOptions::ReturnIndexedArray);
|
||||
let mut values = if let Some(values) = extract_array_values(activation, this.into())? {
|
||||
values
|
||||
.iter()
|
||||
.map(|(i, _v)| Some(i.clone().into()))
|
||||
.collect(),
|
||||
),
|
||||
);
|
||||
.enumerate()
|
||||
.map(|(i, v)| (i, v.clone()))
|
||||
.collect::<Vec<(usize, Value<'gc>)>>()
|
||||
} else {
|
||||
let mut new_array =
|
||||
ArrayStorage::from_storage(values.iter().map(|(_i, v)| v.clone()).collect());
|
||||
return Ok(0.into());
|
||||
};
|
||||
|
||||
if let Some(mut old_array) =
|
||||
this.as_array_storage_mut(activation.context.gc_context)
|
||||
{
|
||||
swap(&mut *old_array, &mut new_array);
|
||||
if options.len() < field_names.len() {
|
||||
options.resize(
|
||||
field_names.len(),
|
||||
options.last().cloned().unwrap_or_else(EnumSet::empty),
|
||||
);
|
||||
}
|
||||
|
||||
return Ok(this.into());
|
||||
let unique_satisfied = sort_inner(
|
||||
activation,
|
||||
&mut values,
|
||||
first_option,
|
||||
constrain(|activation, a, b| {
|
||||
for (field_name, options) in field_names.iter().zip(options.iter()) {
|
||||
let mut a_object = a.coerce_to_object(activation)?;
|
||||
let a_field = a_object.get_property(
|
||||
a_object,
|
||||
&QName::new(Namespace::public_namespace(), *field_name),
|
||||
activation,
|
||||
)?;
|
||||
|
||||
let mut b_object = b.coerce_to_object(activation)?;
|
||||
let b_field = b_object.get_property(
|
||||
b_object,
|
||||
&QName::new(Namespace::public_namespace(), *field_name),
|
||||
activation,
|
||||
)?;
|
||||
|
||||
let ord = if options.contains(SortOptions::Numeric) {
|
||||
compare_numeric(activation, a_field, b_field)?
|
||||
} else if options.contains(SortOptions::CaseInsensitive) {
|
||||
compare_string_case_insensitive(activation, a_field, b_field)?
|
||||
} else {
|
||||
compare_string_case_sensitive(activation, a_field, b_field)?
|
||||
};
|
||||
|
||||
if matches!(ord, Ordering::Equal) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if options.contains(SortOptions::Descending) {
|
||||
return Ok(ord.reverse());
|
||||
} else {
|
||||
return Ok(ord);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Ordering::Equal)
|
||||
}),
|
||||
)?;
|
||||
|
||||
return sort_postprocess(activation, this, first_option, unique_satisfied, values);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1117,6 +1310,11 @@ pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>
|
|||
Method::from_builtin(sort),
|
||||
));
|
||||
|
||||
class.write(mc).define_instance_trait(Trait::from_method(
|
||||
QName::new(Namespace::public_namespace(), "sortOn"),
|
||||
Method::from_builtin(sort_on),
|
||||
));
|
||||
|
||||
class.write(mc).define_class_trait(Trait::from_const(
|
||||
QName::new(Namespace::public_namespace(), "CASEINSENSITIVE"),
|
||||
Multiname::from(QName::new(Namespace::public_namespace(), "uint")),
|
||||
|
|
|
@ -380,6 +380,7 @@ swf_tests! {
|
|||
(as3_array_slice, "avm2/array_slice", 1),
|
||||
(as3_array_splice, "avm2/array_splice", 1),
|
||||
(as3_array_sort, "avm2/array_sort", 1),
|
||||
(as3_array_sorton, "avm2/array_sorton", 1),
|
||||
}
|
||||
|
||||
// TODO: These tests have some inaccuracies currently, so we use approx_eq to test that numeric values are close enough.
|
||||
|
|
|
@ -0,0 +1,218 @@
|
|||
package {
|
||||
public class Test {
|
||||
}
|
||||
}
|
||||
|
||||
function assert_array(a) {
|
||||
for (var i = 0; i < a.length; i += 1) {
|
||||
trace(a[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function assert_array_props(a) {
|
||||
for (var i = 0; i < a.length; i += 1) {
|
||||
if (a[i] !== undefined && a[i] !== null) {
|
||||
trace(a[i].numprop);
|
||||
trace(a[i].strprop);
|
||||
} else {
|
||||
trace("//(undefined value omitted)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function fresh_array_a() {
|
||||
trace("//var a = new Array(item1, item2, item3)");
|
||||
var a = new Array(item1, item2, item3);
|
||||
|
||||
trace("//a[4] = item5;");
|
||||
a[4] = item5;
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
function test_holes(a) {
|
||||
var item4 = {"numprop": 9, "strprop": "boo", "numprop2": 4};
|
||||
|
||||
trace("//Array.prototype[2] = \"hole10\";");
|
||||
Array.prototype[2] = "hole10";
|
||||
|
||||
trace("//Array.prototype[3] = \"hole11\";");
|
||||
Array.prototype[3] = "hole11";
|
||||
|
||||
trace("//Array.prototype[4] = \"hole12\";");
|
||||
Array.prototype[4] = "hole12";
|
||||
|
||||
trace("//(properties of contents of a)");
|
||||
assert_array_props(a);
|
||||
|
||||
trace("//(cleaning up our holes...)");
|
||||
|
||||
delete Array.prototype[2];
|
||||
delete Array.prototype[4];
|
||||
|
||||
trace("//Array.prototype[3] = item4;");
|
||||
Array.prototype[3] = item4;
|
||||
}
|
||||
|
||||
trace("//var item1 = {\"numprop\": 3, \"strprop\": \"Abc\", \"numprop2\": 3}");
|
||||
var item1 = {"numprop": 5, "strprop": "Abc", "numprop2": 3};
|
||||
|
||||
trace("//var item2 = {\"numprop\": 3, \"strprop\": \"Azc\", \"numprop2\": 2}");
|
||||
var item2 = {"numprop": 3, "strprop": "Azc", "numprop2": 2};
|
||||
|
||||
trace("//var item3 = {\"numprop\": 3, \"strprop\": \"aXc\", \"numprop2\": 1}");
|
||||
var item3 = {"numprop": 7, "strprop": "aXc", "numprop2": 1};
|
||||
|
||||
trace("//var item4 = {\"numprop\": 3, \"strprop\": \"boo\", \"numprop2\": 4}");
|
||||
var item4 = {"numprop": 9, "strprop": "boo", "numprop2": 4};
|
||||
|
||||
trace("//var item5 = {\"numprop\": 5, \"strprop\": \"bool\", \"numprop2\": \"5\"}");
|
||||
var item5 = {"numprop": 11, "strprop": "bool", "numprop2": "5"};
|
||||
|
||||
var a = fresh_array_a();
|
||||
|
||||
trace("//Array.prototype[3] = item4;");
|
||||
Array.prototype[3] = item4;
|
||||
|
||||
trace("//a.sortOn(\"numprop\", Array.UNIQUESORT) === 0");
|
||||
trace(a.sortOn("numprop", Array.UNIQUESORT) === 0);
|
||||
|
||||
a = fresh_array_a();
|
||||
|
||||
trace("//(contents of a.sortOn([\"numprop\", \"strprop\"], Array.RETURNINDEXEDARRAY))");
|
||||
assert_array(a.sortOn(["numprop", "strprop"], Array.RETURNINDEXEDARRAY));
|
||||
|
||||
trace("//(properties of contents of a.sortOn([\"numprop\", \"strprop\"]))");
|
||||
assert_array_props(a.sortOn(["numprop", "strprop"]));
|
||||
test_holes(a);
|
||||
|
||||
a = fresh_array_a();
|
||||
|
||||
trace("//(contents of a.sortOn([\"numprop\", \"strprop\"], Array.CASEINSENSITIVE | Array.RETURNINDEXEDARRAY))");
|
||||
assert_array(a.sortOn(["numprop", "strprop"], Array.CASEINSENSITIVE | Array.RETURNINDEXEDARRAY));
|
||||
|
||||
trace("//(properties of contents of a.sortOn([\"numprop\", \"strprop\"], Array.CASEINSENSITIVE))");
|
||||
assert_array_props(a.sortOn(["numprop", "strprop"], Array.CASEINSENSITIVE));
|
||||
test_holes(a);
|
||||
|
||||
a = fresh_array_a();
|
||||
|
||||
trace("//(contents of a.sortOn([\"numprop\", \"strprop\"], Array.DESCENDING | Array.RETURNINDEXEDARRAY))");
|
||||
assert_array(a.sortOn(["numprop", "strprop"], Array.DESCENDING | Array.RETURNINDEXEDARRAY));
|
||||
|
||||
trace("//(properties of contents of a.sortOn([\"numprop\", \"strprop\"], Array.DESCENDING))");
|
||||
assert_array_props(a.sortOn(["numprop", "strprop"], Array.DESCENDING));
|
||||
test_holes(a);
|
||||
|
||||
a = fresh_array_a();
|
||||
|
||||
trace("//(contents of a.sortOn([\"numprop\", \"strprop\"], Array.CASEINSENSITIVE | Array.DESCENDING | Array.RETURNINDEXEDARRAY))");
|
||||
assert_array(a.sortOn(["numprop", "strprop"], Array.CASEINSENSITIVE | Array.DESCENDING | Array.RETURNINDEXEDARRAY));
|
||||
|
||||
trace("//(properties of contents of a.sortOn([\"numprop\", \"strprop\"], Array.CASEINSENSITIVE | Array.DESCENDING))");
|
||||
assert_array_props(a.sortOn(["numprop", "strprop"], Array.CASEINSENSITIVE | Array.DESCENDING));
|
||||
test_holes(a);
|
||||
|
||||
a = fresh_array_a();
|
||||
|
||||
trace("//(contents of a.sortOn([\"numprop\", \"strprop\"], Array.NUMERIC | Array.RETURNINDEXEDARRAY))");
|
||||
assert_array(a.sortOn(["numprop", "strprop"], Array.NUMERIC | Array.RETURNINDEXEDARRAY));
|
||||
|
||||
trace("//(properties of contents of a.sortOn([\"numprop\", \"strprop\"], Array.NUMERIC))");
|
||||
assert_array_props(a.sortOn(["numprop", "strprop"], Array.NUMERIC));
|
||||
test_holes(a);
|
||||
|
||||
a = fresh_array_a();
|
||||
|
||||
trace("//(contents of a.sortOn([\"numprop\", \"strprop\"], Array.DESCENDING | Array.NUMERIC | Array.RETURNINDEXEDARRAY))");
|
||||
assert_array(a.sortOn(["numprop", "strprop"], Array.DESCENDING | Array.NUMERIC | Array.RETURNINDEXEDARRAY));
|
||||
|
||||
trace("//(properties of contents of a.sortOn([\"numprop\", \"strprop\"], Array.DESCENDING | Array.NUMERIC))");
|
||||
assert_array_props(a.sortOn(["numprop", "strprop"], Array.DESCENDING | Array.NUMERIC));
|
||||
test_holes(a);
|
||||
|
||||
a = fresh_array_a();
|
||||
|
||||
trace("//(contents of a.sortOn([\"numprop\", \"strprop\"], [Array.RETURNINDEXEDARRAY, 0]))");
|
||||
assert_array(a.sortOn(["numprop", "strprop"], [Array.RETURNINDEXEDARRAY, 0]));
|
||||
|
||||
trace("//(properties of contents of a.sortOn([\"numprop\", \"strprop\"], [0, 0]))");
|
||||
assert_array_props(a.sortOn(["numprop", "strprop"], [0, 0]));
|
||||
test_holes(a);
|
||||
|
||||
a = fresh_array_a();
|
||||
|
||||
trace("//(contents of a.sortOn([\"numprop\", \"strprop\"], [Array.RETURNINDEXEDARRAY, Array.DESCENDING]))");
|
||||
assert_array(a.sortOn(["numprop", "strprop"], [Array.RETURNINDEXEDARRAY, Array.DESCENDING]));
|
||||
|
||||
trace("//(properties of contents of a.sortOn([\"numprop\", \"strprop\"], [0, Array.DESCENDING]))");
|
||||
assert_array_props(a.sortOn(["numprop", "strprop"], [0, Array.DESCENDING]));
|
||||
test_holes(a);
|
||||
|
||||
a = fresh_array_a();
|
||||
|
||||
trace("//(contents of a.sortOn([\"numprop\", \"strprop\"], [Array.RETURNINDEXEDARRAY | Array.DESCENDING, 0]))");
|
||||
assert_array(a.sortOn(["numprop", "strprop"], [Array.RETURNINDEXEDARRAY | Array.DESCENDING, 0]));
|
||||
|
||||
trace("//(properties of contents of a.sortOn([\"numprop\", \"strprop\"], [Array.DESCENDING, 0]))");
|
||||
assert_array_props(a.sortOn(["numprop", "strprop"], [Array.DESCENDING, 0]));
|
||||
test_holes(a);
|
||||
|
||||
a = fresh_array_a();
|
||||
|
||||
trace("//(contents of a.sortOn([\"numprop\", \"strprop\"], [Array.RETURNINDEXEDARRAY, Array.CASEINSENSITIVE]))");
|
||||
assert_array(a.sortOn(["numprop", "strprop"], [Array.RETURNINDEXEDARRAY, Array.CASEINSENSITIVE]));
|
||||
|
||||
trace("//(properties of contents of a.sortOn([\"numprop\", \"strprop\"], [0, Array.CASEINSENSITIVE]))");
|
||||
assert_array_props(a.sortOn(["numprop", "strprop"], [0, Array.CASEINSENSITIVE]));
|
||||
test_holes(a);
|
||||
|
||||
a = fresh_array_a();
|
||||
|
||||
trace("//(contents of a.sortOn([\"numprop\", \"strprop\"], [Array.RETURNINDEXEDARRAY | Array.CASEINSENSITIVE, 0]))");
|
||||
assert_array(a.sortOn(["numprop", "strprop"], [Array.RETURNINDEXEDARRAY | Array.CASEINSENSITIVE, 0]));
|
||||
|
||||
trace("//(properties of contents of a.sortOn([\"numprop\", \"strprop\"], [Array.CASEINSENSITIVE, 0]))");
|
||||
assert_array_props(a.sortOn(["numprop", "strprop"], [Array.CASEINSENSITIVE, 0]));
|
||||
test_holes(a);
|
||||
|
||||
a = fresh_array_a();
|
||||
|
||||
trace("//(contents of a.sortOn([\"numprop\", \"strprop\"], [Array.RETURNINDEXEDARRAY, Array.CASEINSENSITIVE | Array.DESCENDING]))");
|
||||
assert_array(a.sortOn(["numprop", "strprop"], [Array.RETURNINDEXEDARRAY, Array.CASEINSENSITIVE | Array.DESCENDING]));
|
||||
|
||||
trace("//(properties of contents of a.sortOn([\"numprop\", \"strprop\"], [0, Array.CASEINSENSITIVE | Array.DESCENDING]))");
|
||||
assert_array_props(a.sortOn(["numprop", "strprop"], [0, Array.CASEINSENSITIVE | Array.DESCENDING]));
|
||||
test_holes(a);
|
||||
|
||||
a = fresh_array_a();
|
||||
|
||||
trace("//(contents of a.sortOn([\"numprop\", \"strprop\"], [Array.RETURNINDEXEDARRAY | Array.CASEINSENSITIVE | Array.DESCENDING, 0]))");
|
||||
assert_array(a.sortOn(["numprop", "strprop"], [Array.RETURNINDEXEDARRAY | Array.CASEINSENSITIVE | Array.DESCENDING, 0]));
|
||||
|
||||
trace("//(properties of contents of a.sortOn([\"numprop\", \"strprop\"], [Array.CASEINSENSITIVE | Array.DESCENDING, 0]))");
|
||||
assert_array_props(a.sortOn(["numprop", "strprop"], [Array.CASEINSENSITIVE | Array.DESCENDING, 0]));
|
||||
test_holes(a);
|
||||
|
||||
a = fresh_array_a();
|
||||
|
||||
trace("//(contents of a.sortOn([\"numprop\", \"strprop\"], [Array.RETURNINDEXEDARRAY | Array.DESCENDING, Array.CASEINSENSITIVE]))");
|
||||
assert_array(a.sortOn(["numprop", "strprop"], [Array.RETURNINDEXEDARRAY | Array.DESCENDING, Array.CASEINSENSITIVE]));
|
||||
|
||||
trace("//(properties of contents of a.sortOn([\"numprop\", \"strprop\"], [Array.DESCENDING, Array.CASEINSENSITIVE]))");
|
||||
assert_array_props(a.sortOn(["numprop", "strprop"], [Array.DESCENDING, Array.CASEINSENSITIVE]));
|
||||
test_holes(a);
|
||||
|
||||
a = fresh_array_a();
|
||||
|
||||
trace("//(contents of a.sortOn([\"numprop\", \"strprop\"], [Array.RETURNINDEXEDARRAY | Array.CASEINSENSITIVE, Array.DESCENDING]))");
|
||||
assert_array(a.sortOn(["numprop", "strprop"], [Array.RETURNINDEXEDARRAY | Array.CASEINSENSITIVE, Array.DESCENDING]));
|
||||
|
||||
trace("//(properties of contents of a.sortOn([\"numprop\", \"strprop\"], [Array.CASEINSENSITIVE, Array.DESCENDING]))");
|
||||
assert_array_props(a.sortOn(["numprop", "strprop"], [Array.CASEINSENSITIVE, Array.DESCENDING]));
|
||||
test_holes(a);
|
||||
|
||||
a = fresh_array_a();
|
||||
|
||||
trace("//a.sortOn([\"strprop\", \"numprop\"], [Array.NUMERIC, Array.UNIQUESORT]) === 0");
|
||||
trace(a.sortOn(["strprop", "numprop"], [Array.NUMERIC, Array.UNIQUESORT]) === 0);
|
|
@ -0,0 +1,539 @@
|
|||
//var item1 = {"numprop": 3, "strprop": "Abc", "numprop2": 3}
|
||||
//var item2 = {"numprop": 3, "strprop": "Azc", "numprop2": 2}
|
||||
//var item3 = {"numprop": 3, "strprop": "aXc", "numprop2": 1}
|
||||
//var item4 = {"numprop": 3, "strprop": "boo", "numprop2": 4}
|
||||
//var item5 = {"numprop": 5, "strprop": "bool", "numprop2": "5"}
|
||||
//var a = new Array(item1, item2, item3)
|
||||
//a[4] = item5;
|
||||
//Array.prototype[3] = item4;
|
||||
//a.sortOn("numprop", Array.UNIQUESORT) === 0
|
||||
false
|
||||
//var a = new Array(item1, item2, item3)
|
||||
//a[4] = item5;
|
||||
//(contents of a.sortOn(["numprop", "strprop"], Array.RETURNINDEXEDARRAY))
|
||||
4
|
||||
1
|
||||
0
|
||||
2
|
||||
3
|
||||
//(properties of contents of a.sortOn(["numprop", "strprop"]))
|
||||
11
|
||||
bool
|
||||
3
|
||||
Azc
|
||||
5
|
||||
Abc
|
||||
7
|
||||
aXc
|
||||
9
|
||||
boo
|
||||
//Array.prototype[2] = "hole10";
|
||||
//Array.prototype[3] = "hole11";
|
||||
//Array.prototype[4] = "hole12";
|
||||
//(properties of contents of a)
|
||||
11
|
||||
bool
|
||||
3
|
||||
Azc
|
||||
5
|
||||
Abc
|
||||
7
|
||||
aXc
|
||||
9
|
||||
boo
|
||||
//(cleaning up our holes...)
|
||||
//Array.prototype[3] = item4;
|
||||
//var a = new Array(item1, item2, item3)
|
||||
//a[4] = item5;
|
||||
//(contents of a.sortOn(["numprop", "strprop"], Array.CASEINSENSITIVE | Array.RETURNINDEXEDARRAY))
|
||||
4
|
||||
1
|
||||
0
|
||||
2
|
||||
3
|
||||
//(properties of contents of a.sortOn(["numprop", "strprop"], Array.CASEINSENSITIVE))
|
||||
11
|
||||
bool
|
||||
3
|
||||
Azc
|
||||
5
|
||||
Abc
|
||||
7
|
||||
aXc
|
||||
9
|
||||
boo
|
||||
//Array.prototype[2] = "hole10";
|
||||
//Array.prototype[3] = "hole11";
|
||||
//Array.prototype[4] = "hole12";
|
||||
//(properties of contents of a)
|
||||
11
|
||||
bool
|
||||
3
|
||||
Azc
|
||||
5
|
||||
Abc
|
||||
7
|
||||
aXc
|
||||
9
|
||||
boo
|
||||
//(cleaning up our holes...)
|
||||
//Array.prototype[3] = item4;
|
||||
//var a = new Array(item1, item2, item3)
|
||||
//a[4] = item5;
|
||||
//(contents of a.sortOn(["numprop", "strprop"], Array.DESCENDING | Array.RETURNINDEXEDARRAY))
|
||||
3
|
||||
2
|
||||
0
|
||||
1
|
||||
4
|
||||
//(properties of contents of a.sortOn(["numprop", "strprop"], Array.DESCENDING))
|
||||
9
|
||||
boo
|
||||
7
|
||||
aXc
|
||||
5
|
||||
Abc
|
||||
3
|
||||
Azc
|
||||
11
|
||||
bool
|
||||
//Array.prototype[2] = "hole10";
|
||||
//Array.prototype[3] = "hole11";
|
||||
//Array.prototype[4] = "hole12";
|
||||
//(properties of contents of a)
|
||||
9
|
||||
boo
|
||||
7
|
||||
aXc
|
||||
5
|
||||
Abc
|
||||
3
|
||||
Azc
|
||||
11
|
||||
bool
|
||||
//(cleaning up our holes...)
|
||||
//Array.prototype[3] = item4;
|
||||
//var a = new Array(item1, item2, item3)
|
||||
//a[4] = item5;
|
||||
//(contents of a.sortOn(["numprop", "strprop"], Array.CASEINSENSITIVE | Array.DESCENDING | Array.RETURNINDEXEDARRAY))
|
||||
3
|
||||
2
|
||||
0
|
||||
1
|
||||
4
|
||||
//(properties of contents of a.sortOn(["numprop", "strprop"], Array.CASEINSENSITIVE | Array.DESCENDING))
|
||||
9
|
||||
boo
|
||||
7
|
||||
aXc
|
||||
5
|
||||
Abc
|
||||
3
|
||||
Azc
|
||||
11
|
||||
bool
|
||||
//Array.prototype[2] = "hole10";
|
||||
//Array.prototype[3] = "hole11";
|
||||
//Array.prototype[4] = "hole12";
|
||||
//(properties of contents of a)
|
||||
9
|
||||
boo
|
||||
7
|
||||
aXc
|
||||
5
|
||||
Abc
|
||||
3
|
||||
Azc
|
||||
11
|
||||
bool
|
||||
//(cleaning up our holes...)
|
||||
//Array.prototype[3] = item4;
|
||||
//var a = new Array(item1, item2, item3)
|
||||
//a[4] = item5;
|
||||
//(contents of a.sortOn(["numprop", "strprop"], Array.NUMERIC | Array.RETURNINDEXEDARRAY))
|
||||
1
|
||||
0
|
||||
2
|
||||
3
|
||||
4
|
||||
//(properties of contents of a.sortOn(["numprop", "strprop"], Array.NUMERIC))
|
||||
3
|
||||
Azc
|
||||
5
|
||||
Abc
|
||||
7
|
||||
aXc
|
||||
9
|
||||
boo
|
||||
11
|
||||
bool
|
||||
//Array.prototype[2] = "hole10";
|
||||
//Array.prototype[3] = "hole11";
|
||||
//Array.prototype[4] = "hole12";
|
||||
//(properties of contents of a)
|
||||
3
|
||||
Azc
|
||||
5
|
||||
Abc
|
||||
7
|
||||
aXc
|
||||
9
|
||||
boo
|
||||
11
|
||||
bool
|
||||
//(cleaning up our holes...)
|
||||
//Array.prototype[3] = item4;
|
||||
//var a = new Array(item1, item2, item3)
|
||||
//a[4] = item5;
|
||||
//(contents of a.sortOn(["numprop", "strprop"], Array.DESCENDING | Array.NUMERIC | Array.RETURNINDEXEDARRAY))
|
||||
4
|
||||
3
|
||||
2
|
||||
0
|
||||
1
|
||||
//(properties of contents of a.sortOn(["numprop", "strprop"], Array.DESCENDING | Array.NUMERIC))
|
||||
11
|
||||
bool
|
||||
9
|
||||
boo
|
||||
7
|
||||
aXc
|
||||
5
|
||||
Abc
|
||||
3
|
||||
Azc
|
||||
//Array.prototype[2] = "hole10";
|
||||
//Array.prototype[3] = "hole11";
|
||||
//Array.prototype[4] = "hole12";
|
||||
//(properties of contents of a)
|
||||
11
|
||||
bool
|
||||
9
|
||||
boo
|
||||
7
|
||||
aXc
|
||||
5
|
||||
Abc
|
||||
3
|
||||
Azc
|
||||
//(cleaning up our holes...)
|
||||
//Array.prototype[3] = item4;
|
||||
//var a = new Array(item1, item2, item3)
|
||||
//a[4] = item5;
|
||||
//(contents of a.sortOn(["numprop", "strprop"], [Array.RETURNINDEXEDARRAY, 0]))
|
||||
4
|
||||
1
|
||||
0
|
||||
2
|
||||
3
|
||||
//(properties of contents of a.sortOn(["numprop", "strprop"], [0, 0]))
|
||||
11
|
||||
bool
|
||||
3
|
||||
Azc
|
||||
5
|
||||
Abc
|
||||
7
|
||||
aXc
|
||||
9
|
||||
boo
|
||||
//Array.prototype[2] = "hole10";
|
||||
//Array.prototype[3] = "hole11";
|
||||
//Array.prototype[4] = "hole12";
|
||||
//(properties of contents of a)
|
||||
11
|
||||
bool
|
||||
3
|
||||
Azc
|
||||
5
|
||||
Abc
|
||||
7
|
||||
aXc
|
||||
9
|
||||
boo
|
||||
//(cleaning up our holes...)
|
||||
//Array.prototype[3] = item4;
|
||||
//var a = new Array(item1, item2, item3)
|
||||
//a[4] = item5;
|
||||
//(contents of a.sortOn(["numprop", "strprop"], [Array.RETURNINDEXEDARRAY, Array.DESCENDING]))
|
||||
4
|
||||
1
|
||||
0
|
||||
2
|
||||
3
|
||||
//(properties of contents of a.sortOn(["numprop", "strprop"], [0, Array.DESCENDING]))
|
||||
11
|
||||
bool
|
||||
3
|
||||
Azc
|
||||
5
|
||||
Abc
|
||||
7
|
||||
aXc
|
||||
9
|
||||
boo
|
||||
//Array.prototype[2] = "hole10";
|
||||
//Array.prototype[3] = "hole11";
|
||||
//Array.prototype[4] = "hole12";
|
||||
//(properties of contents of a)
|
||||
11
|
||||
bool
|
||||
3
|
||||
Azc
|
||||
5
|
||||
Abc
|
||||
7
|
||||
aXc
|
||||
9
|
||||
boo
|
||||
//(cleaning up our holes...)
|
||||
//Array.prototype[3] = item4;
|
||||
//var a = new Array(item1, item2, item3)
|
||||
//a[4] = item5;
|
||||
//(contents of a.sortOn(["numprop", "strprop"], [Array.RETURNINDEXEDARRAY | Array.DESCENDING, 0]))
|
||||
3
|
||||
2
|
||||
0
|
||||
1
|
||||
4
|
||||
//(properties of contents of a.sortOn(["numprop", "strprop"], [Array.DESCENDING, 0]))
|
||||
9
|
||||
boo
|
||||
7
|
||||
aXc
|
||||
5
|
||||
Abc
|
||||
3
|
||||
Azc
|
||||
11
|
||||
bool
|
||||
//Array.prototype[2] = "hole10";
|
||||
//Array.prototype[3] = "hole11";
|
||||
//Array.prototype[4] = "hole12";
|
||||
//(properties of contents of a)
|
||||
9
|
||||
boo
|
||||
7
|
||||
aXc
|
||||
5
|
||||
Abc
|
||||
3
|
||||
Azc
|
||||
11
|
||||
bool
|
||||
//(cleaning up our holes...)
|
||||
//Array.prototype[3] = item4;
|
||||
//var a = new Array(item1, item2, item3)
|
||||
//a[4] = item5;
|
||||
//(contents of a.sortOn(["numprop", "strprop"], [Array.RETURNINDEXEDARRAY, Array.CASEINSENSITIVE]))
|
||||
4
|
||||
1
|
||||
0
|
||||
2
|
||||
3
|
||||
//(properties of contents of a.sortOn(["numprop", "strprop"], [0, Array.CASEINSENSITIVE]))
|
||||
11
|
||||
bool
|
||||
3
|
||||
Azc
|
||||
5
|
||||
Abc
|
||||
7
|
||||
aXc
|
||||
9
|
||||
boo
|
||||
//Array.prototype[2] = "hole10";
|
||||
//Array.prototype[3] = "hole11";
|
||||
//Array.prototype[4] = "hole12";
|
||||
//(properties of contents of a)
|
||||
11
|
||||
bool
|
||||
3
|
||||
Azc
|
||||
5
|
||||
Abc
|
||||
7
|
||||
aXc
|
||||
9
|
||||
boo
|
||||
//(cleaning up our holes...)
|
||||
//Array.prototype[3] = item4;
|
||||
//var a = new Array(item1, item2, item3)
|
||||
//a[4] = item5;
|
||||
//(contents of a.sortOn(["numprop", "strprop"], [Array.RETURNINDEXEDARRAY | Array.CASEINSENSITIVE, 0]))
|
||||
4
|
||||
1
|
||||
0
|
||||
2
|
||||
3
|
||||
//(properties of contents of a.sortOn(["numprop", "strprop"], [Array.CASEINSENSITIVE, 0]))
|
||||
11
|
||||
bool
|
||||
3
|
||||
Azc
|
||||
5
|
||||
Abc
|
||||
7
|
||||
aXc
|
||||
9
|
||||
boo
|
||||
//Array.prototype[2] = "hole10";
|
||||
//Array.prototype[3] = "hole11";
|
||||
//Array.prototype[4] = "hole12";
|
||||
//(properties of contents of a)
|
||||
11
|
||||
bool
|
||||
3
|
||||
Azc
|
||||
5
|
||||
Abc
|
||||
7
|
||||
aXc
|
||||
9
|
||||
boo
|
||||
//(cleaning up our holes...)
|
||||
//Array.prototype[3] = item4;
|
||||
//var a = new Array(item1, item2, item3)
|
||||
//a[4] = item5;
|
||||
//(contents of a.sortOn(["numprop", "strprop"], [Array.RETURNINDEXEDARRAY, Array.CASEINSENSITIVE | Array.DESCENDING]))
|
||||
4
|
||||
1
|
||||
0
|
||||
2
|
||||
3
|
||||
//(properties of contents of a.sortOn(["numprop", "strprop"], [0, Array.CASEINSENSITIVE | Array.DESCENDING]))
|
||||
11
|
||||
bool
|
||||
3
|
||||
Azc
|
||||
5
|
||||
Abc
|
||||
7
|
||||
aXc
|
||||
9
|
||||
boo
|
||||
//Array.prototype[2] = "hole10";
|
||||
//Array.prototype[3] = "hole11";
|
||||
//Array.prototype[4] = "hole12";
|
||||
//(properties of contents of a)
|
||||
11
|
||||
bool
|
||||
3
|
||||
Azc
|
||||
5
|
||||
Abc
|
||||
7
|
||||
aXc
|
||||
9
|
||||
boo
|
||||
//(cleaning up our holes...)
|
||||
//Array.prototype[3] = item4;
|
||||
//var a = new Array(item1, item2, item3)
|
||||
//a[4] = item5;
|
||||
//(contents of a.sortOn(["numprop", "strprop"], [Array.RETURNINDEXEDARRAY | Array.CASEINSENSITIVE | Array.DESCENDING, 0]))
|
||||
3
|
||||
2
|
||||
0
|
||||
1
|
||||
4
|
||||
//(properties of contents of a.sortOn(["numprop", "strprop"], [Array.CASEINSENSITIVE | Array.DESCENDING, 0]))
|
||||
9
|
||||
boo
|
||||
7
|
||||
aXc
|
||||
5
|
||||
Abc
|
||||
3
|
||||
Azc
|
||||
11
|
||||
bool
|
||||
//Array.prototype[2] = "hole10";
|
||||
//Array.prototype[3] = "hole11";
|
||||
//Array.prototype[4] = "hole12";
|
||||
//(properties of contents of a)
|
||||
9
|
||||
boo
|
||||
7
|
||||
aXc
|
||||
5
|
||||
Abc
|
||||
3
|
||||
Azc
|
||||
11
|
||||
bool
|
||||
//(cleaning up our holes...)
|
||||
//Array.prototype[3] = item4;
|
||||
//var a = new Array(item1, item2, item3)
|
||||
//a[4] = item5;
|
||||
//(contents of a.sortOn(["numprop", "strprop"], [Array.RETURNINDEXEDARRAY | Array.DESCENDING, Array.CASEINSENSITIVE]))
|
||||
3
|
||||
2
|
||||
0
|
||||
1
|
||||
4
|
||||
//(properties of contents of a.sortOn(["numprop", "strprop"], [Array.DESCENDING, Array.CASEINSENSITIVE]))
|
||||
9
|
||||
boo
|
||||
7
|
||||
aXc
|
||||
5
|
||||
Abc
|
||||
3
|
||||
Azc
|
||||
11
|
||||
bool
|
||||
//Array.prototype[2] = "hole10";
|
||||
//Array.prototype[3] = "hole11";
|
||||
//Array.prototype[4] = "hole12";
|
||||
//(properties of contents of a)
|
||||
9
|
||||
boo
|
||||
7
|
||||
aXc
|
||||
5
|
||||
Abc
|
||||
3
|
||||
Azc
|
||||
11
|
||||
bool
|
||||
//(cleaning up our holes...)
|
||||
//Array.prototype[3] = item4;
|
||||
//var a = new Array(item1, item2, item3)
|
||||
//a[4] = item5;
|
||||
//(contents of a.sortOn(["numprop", "strprop"], [Array.RETURNINDEXEDARRAY | Array.CASEINSENSITIVE, Array.DESCENDING]))
|
||||
4
|
||||
1
|
||||
0
|
||||
2
|
||||
3
|
||||
//(properties of contents of a.sortOn(["numprop", "strprop"], [Array.CASEINSENSITIVE, Array.DESCENDING]))
|
||||
11
|
||||
bool
|
||||
3
|
||||
Azc
|
||||
5
|
||||
Abc
|
||||
7
|
||||
aXc
|
||||
9
|
||||
boo
|
||||
//Array.prototype[2] = "hole10";
|
||||
//Array.prototype[3] = "hole11";
|
||||
//Array.prototype[4] = "hole12";
|
||||
//(properties of contents of a)
|
||||
11
|
||||
bool
|
||||
3
|
||||
Azc
|
||||
5
|
||||
Abc
|
||||
7
|
||||
aXc
|
||||
9
|
||||
boo
|
||||
//(cleaning up our holes...)
|
||||
//Array.prototype[3] = item4;
|
||||
//var a = new Array(item1, item2, item3)
|
||||
//a[4] = item5;
|
||||
//a.sortOn(["strprop", "numprop"], [Array.NUMERIC, Array.UNIQUESORT]) === 0
|
||||
false
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue