avm2: Implement `Array.sort`
This commit is contained in:
parent
bb19699739
commit
0aa2c50118
|
@ -12,7 +12,7 @@ use crate::avm2::value::Value;
|
|||
use crate::avm2::Error;
|
||||
use enumset::{EnumSet, EnumSetType};
|
||||
use gc_arena::{GcCell, MutationContext};
|
||||
use std::cmp::min;
|
||||
use std::cmp::{min, Ordering};
|
||||
use std::mem::swap;
|
||||
|
||||
/// Implements `Array`'s instance initializer.
|
||||
|
@ -786,6 +786,222 @@ enum SortOptions {
|
|||
Numeric,
|
||||
}
|
||||
|
||||
/// Identity closure shim which exists purely to decorate closure types with
|
||||
/// the HRTB necessary to accept an activation.
|
||||
fn constrain<'a, 'gc, 'ctxt, F>(f: F) -> F
|
||||
where
|
||||
F: FnMut(&mut Activation<'a, 'gc, 'ctxt>, Value<'gc>, Value<'gc>) -> Result<Ordering, Error>,
|
||||
{
|
||||
f
|
||||
}
|
||||
|
||||
/// 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 will reverse the sort order if `Descending` sort is requested.
|
||||
///
|
||||
/// This function will return `false` in the event that the `UniqueSort`
|
||||
/// constraint has been violated (`sort_func` returned `Ordering::Equal`). In
|
||||
/// this case, you should cancel the in-place sorting operation and return 0 to
|
||||
/// the caller. In the event that this function yields a runtime error, the
|
||||
/// 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>>)],
|
||||
options: EnumSet<SortOptions>,
|
||||
mut sort_func: C,
|
||||
) -> Result<bool, Error>
|
||||
where
|
||||
C: FnMut(&mut Activation<'a, 'gc, 'ctxt>, Value<'gc>, Value<'gc>) -> Result<Ordering, Error>,
|
||||
{
|
||||
let mut unique_sort_satisfied = true;
|
||||
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);
|
||||
|
||||
if matches!(unresolved_a, Value::Undefined) && matches!(unresolved_b, Value::Undefined) {
|
||||
unique_sort_satisfied = false;
|
||||
return Ordering::Equal;
|
||||
} else if matches!(unresolved_a, Value::Undefined) {
|
||||
return Ordering::Greater;
|
||||
} else if matches!(unresolved_b, Value::Undefined) {
|
||||
return Ordering::Less;
|
||||
}
|
||||
|
||||
match sort_func(
|
||||
activation,
|
||||
a.clone().unwrap_or(Value::Undefined),
|
||||
b.clone().unwrap_or(Value::Undefined),
|
||||
) {
|
||||
Ok(Ordering::Equal) => {
|
||||
unique_sort_satisfied = false;
|
||||
Ordering::Equal
|
||||
}
|
||||
Ok(v) if options.contains(SortOptions::Descending) => v.reverse(),
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
error_signal = Err(e);
|
||||
Ordering::Less
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
error_signal?;
|
||||
|
||||
Ok(!options.contains(SortOptions::UniqueSort) || unique_sort_satisfied)
|
||||
}
|
||||
|
||||
fn compare_string_case_sensitive<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
a: Value<'gc>,
|
||||
b: Value<'gc>,
|
||||
) -> Result<Ordering, Error> {
|
||||
let string_a = a.coerce_to_string(activation)?;
|
||||
let string_b = b.coerce_to_string(activation)?;
|
||||
|
||||
Ok(string_a.cmp(&string_b))
|
||||
}
|
||||
|
||||
fn compare_string_case_insensitive<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
a: Value<'gc>,
|
||||
b: Value<'gc>,
|
||||
) -> Result<Ordering, Error> {
|
||||
let string_a = a.coerce_to_string(activation)?.to_lowercase();
|
||||
let string_b = b.coerce_to_string(activation)?.to_lowercase();
|
||||
|
||||
Ok(string_a.cmp(&string_b))
|
||||
}
|
||||
|
||||
fn compare_numeric<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
a: Value<'gc>,
|
||||
b: Value<'gc>,
|
||||
) -> Result<Ordering, Error> {
|
||||
let num_a = a.coerce_to_number(activation)?;
|
||||
let num_b = b.coerce_to_number(activation)?;
|
||||
|
||||
if num_a.is_nan() && num_b.is_nan() {
|
||||
Ok(Ordering::Equal)
|
||||
} else if num_a.is_nan() {
|
||||
Ok(Ordering::Greater)
|
||||
} else if num_b.is_nan() {
|
||||
Ok(Ordering::Less)
|
||||
} else {
|
||||
Ok(num_a.partial_cmp(&num_b).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
/// Impl `Array.sort`
|
||||
pub fn sort<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
this: Option<Object<'gc>>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
if let Some(this) = this {
|
||||
let (compare_fnc, options) = if args.len() > 1 {
|
||||
(
|
||||
Some(
|
||||
args.get(0)
|
||||
.cloned()
|
||||
.unwrap_or(Value::Undefined)
|
||||
.coerce_to_object(activation)?,
|
||||
),
|
||||
args.get(1)
|
||||
.cloned()
|
||||
.unwrap_or_else(|| 0.into())
|
||||
.coerce_to_enumset(activation)?,
|
||||
)
|
||||
} else {
|
||||
(
|
||||
None,
|
||||
args.get(0)
|
||||
.cloned()
|
||||
.unwrap_or_else(|| 0.into())
|
||||
.coerce_to_enumset(activation)?,
|
||||
)
|
||||
};
|
||||
|
||||
let mut values = if let Some(array) = this.as_array_storage() {
|
||||
array
|
||||
.iter()
|
||||
.enumerate()
|
||||
.collect::<Vec<(usize, Option<Value<'gc>>)>>()
|
||||
} else {
|
||||
return Ok(0.into());
|
||||
};
|
||||
|
||||
let unique_satisified = if let Some(v) = compare_fnc {
|
||||
sort_inner(
|
||||
activation,
|
||||
&mut values,
|
||||
options,
|
||||
constrain(|activation, a, b| {
|
||||
let order = v
|
||||
.call(None, &[a, b], activation, None)?
|
||||
.coerce_to_number(activation)?;
|
||||
|
||||
if order > 0.0 {
|
||||
Ok(Ordering::Greater)
|
||||
} else if order < 0.0 {
|
||||
Ok(Ordering::Less)
|
||||
} else {
|
||||
Ok(Ordering::Equal)
|
||||
}
|
||||
}),
|
||||
)?
|
||||
} else if options.contains(SortOptions::Numeric) {
|
||||
sort_inner(activation, &mut values, options, compare_numeric)?
|
||||
} else if options.contains(SortOptions::CaseInsensitive) {
|
||||
sort_inner(
|
||||
activation,
|
||||
&mut values,
|
||||
options,
|
||||
compare_string_case_insensitive,
|
||||
)?
|
||||
} else {
|
||||
sort_inner(
|
||||
activation,
|
||||
&mut values,
|
||||
options,
|
||||
compare_string_case_sensitive,
|
||||
)?
|
||||
};
|
||||
|
||||
if unique_satisified {
|
||||
if options.contains(SortOptions::ReturnIndexedArray) {
|
||||
return build_array(
|
||||
activation,
|
||||
ArrayStorage::from_storage(
|
||||
values
|
||||
.iter()
|
||||
.map(|(i, _v)| Some(i.clone().into()))
|
||||
.collect(),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
let mut new_array =
|
||||
ArrayStorage::from_storage(values.iter().map(|(_i, v)| v.clone()).collect());
|
||||
|
||||
if let Some(mut old_array) =
|
||||
this.as_array_storage_mut(activation.context.gc_context)
|
||||
{
|
||||
swap(&mut *old_array, &mut new_array);
|
||||
}
|
||||
|
||||
return Ok(this.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(0.into())
|
||||
}
|
||||
|
||||
/// Construct `Array`'s class.
|
||||
pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> {
|
||||
let class = Class::new(
|
||||
|
@ -896,6 +1112,11 @@ pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>
|
|||
Method::from_builtin(splice),
|
||||
));
|
||||
|
||||
class.write(mc).define_instance_trait(Trait::from_method(
|
||||
QName::new(Namespace::public_namespace(), "sort"),
|
||||
Method::from_builtin(sort),
|
||||
));
|
||||
|
||||
class.write(mc).define_class_trait(Trait::from_const(
|
||||
QName::new(Namespace::public_namespace(), "CASEINSENSITIVE"),
|
||||
Multiname::from(QName::new(Namespace::public_namespace(), "uint")),
|
||||
|
|
|
@ -379,6 +379,7 @@ swf_tests! {
|
|||
(as3_array_unshift, "avm2/array_unshift", 1),
|
||||
(as3_array_slice, "avm2/array_slice", 1),
|
||||
(as3_array_splice, "avm2/array_splice", 1),
|
||||
(as3_array_sort, "avm2/array_sort", 1),
|
||||
}
|
||||
|
||||
// TODO: These tests have some inaccuracies currently, so we use approx_eq to test that numeric values are close enough.
|
||||
|
|
|
@ -0,0 +1,196 @@
|
|||
package {
|
||||
public class Test {
|
||||
}
|
||||
}
|
||||
|
||||
function assert_array(a) {
|
||||
for (var i = 0; i < a.length; i += 1) {
|
||||
trace(a[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function length_based_comparison(a, b) {
|
||||
if ("length" in a) {
|
||||
if ("length" in b) {
|
||||
return a.length - b.length;
|
||||
} else {
|
||||
return a.length - b;
|
||||
}
|
||||
} else {
|
||||
if ("length" in b) {
|
||||
return a - b.length;
|
||||
} else {
|
||||
return a - b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function sub_comparison(a, b) {
|
||||
return a - b;
|
||||
}
|
||||
|
||||
function lbc(a, b) {
|
||||
trace(a);
|
||||
trace(b);
|
||||
var x = length_based_comparison(a, b);
|
||||
trace(x);
|
||||
return x;
|
||||
}
|
||||
|
||||
function sc(a, b) {
|
||||
trace(a);
|
||||
trace(b);
|
||||
var x = sub_comparison(a, b);
|
||||
trace(x);
|
||||
return x;
|
||||
}
|
||||
|
||||
function fresh_array() {
|
||||
trace("//var a = new Array(5,3,1,\"Abc\",\"2\",\"aba\",false,null,\"zzz\")");
|
||||
var a = new Array(5,3,1,"Abc","2","aba",false,null,"zzz");
|
||||
|
||||
trace("//a[11] = \"not a hole\";");
|
||||
a[11] = "not a hole";
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
function fresh_array_b() {
|
||||
trace("//var b = new Array(5,3,\"2\",false,true,NaN)");
|
||||
var b = new Array(5,3,"2",false,true,NaN);
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
function check_holes(a) {
|
||||
trace("//Array.prototype[10] = \"hole10\";");
|
||||
Array.prototype[10] = "hole10";
|
||||
|
||||
trace("//Array.prototype[11] = \"hole11\";");
|
||||
Array.prototype[11] = "hole11";
|
||||
|
||||
trace("//Array.prototype[12] = \"hole12\";");
|
||||
Array.prototype[12] = "hole12";
|
||||
|
||||
trace("//(contents of previous array)");
|
||||
assert_array(a);
|
||||
|
||||
trace("//(cleaning up our holes...)");
|
||||
delete Array.prototype[10];
|
||||
delete Array.prototype[11];
|
||||
delete Array.prototype[12];
|
||||
|
||||
trace("//Array.prototype[9] = undefined;");
|
||||
Array.prototype[9] = undefined;
|
||||
|
||||
trace("//Array.prototype[10] = \"hole in slot 10\";");
|
||||
Array.prototype[10] = "hole in slot 10";
|
||||
}
|
||||
|
||||
var a = fresh_array();
|
||||
|
||||
trace("//Array.prototype[9] = undefined;");
|
||||
Array.prototype[9] = undefined;
|
||||
|
||||
trace("//Array.prototype[10] = \"hole in slot 10\";");
|
||||
Array.prototype[10] = "hole in slot 10";
|
||||
|
||||
trace("//a.sort(Array.UNIQUESORT) === 0");
|
||||
trace(a.sort(Array.UNIQUESORT) === 0);
|
||||
|
||||
a = fresh_array();
|
||||
|
||||
trace("//(contents of a.sort(Array.RETURNINDEXEDARRAY))");
|
||||
assert_array(a.sort(Array.RETURNINDEXEDARRAY));
|
||||
|
||||
trace("//(contents of a.sort())");
|
||||
assert_array(a.sort());
|
||||
check_holes(a);
|
||||
|
||||
a = fresh_array();
|
||||
|
||||
trace("//(contents of a.sort(Array.CASEINSENSITIVE | Array.RETURNINDEXEDARRAY))");
|
||||
assert_array(a.sort(Array.CASEINSENSITIVE | Array.RETURNINDEXEDARRAY));
|
||||
|
||||
trace("//(contents of a.sort(Array.CASEINSENSITIVE))");
|
||||
assert_array(a.sort(Array.CASEINSENSITIVE));
|
||||
check_holes(a);
|
||||
|
||||
a = fresh_array();
|
||||
|
||||
trace("//(contents of a.sort(Array.DESCENDING | Array.RETURNINDEXEDARRAY))");
|
||||
assert_array(a.sort(Array.DESCENDING | Array.RETURNINDEXEDARRAY));
|
||||
|
||||
trace("//(contents of a.sort(Array.DESCENDING))");
|
||||
assert_array(a.sort(Array.DESCENDING));
|
||||
check_holes(a);
|
||||
|
||||
a = fresh_array();
|
||||
|
||||
trace("//(contents of a.sort(Array.CASEINSENSITIVE | Array.DESCENDING | Array.RETURNINDEXEDARRAY))");
|
||||
assert_array(a.sort(Array.CASEINSENSITIVE | Array.DESCENDING | Array.RETURNINDEXEDARRAY));
|
||||
|
||||
trace("//(contents of a.sort(Array.CASEINSENSITIVE | Array.DESCENDING))");
|
||||
assert_array(a.sort(Array.CASEINSENSITIVE | Array.DESCENDING));
|
||||
check_holes(a);
|
||||
|
||||
a = fresh_array();
|
||||
|
||||
trace("//var b = new Array(5,3,2,1,\"2\",false,true,NaN)");
|
||||
var b = new Array(5,3,2,1,"2",false,true,NaN);
|
||||
|
||||
trace("//b.sort(Array.NUMERIC | Array.UNIQUESORT) === 0");
|
||||
trace(b.sort(Array.NUMERIC | Array.UNIQUESORT) === 0);
|
||||
|
||||
b = fresh_array_b();
|
||||
|
||||
trace("//(contents of b.sort(Array.NUMERIC | Array.RETURNINDEXEDARRAY))");
|
||||
assert_array(b.sort(Array.NUMERIC | Array.RETURNINDEXEDARRAY));
|
||||
|
||||
trace("//(contents of b.sort(Array.NUMERIC))");
|
||||
assert_array(b.sort(Array.NUMERIC));
|
||||
check_holes(b);
|
||||
|
||||
b = fresh_array_b();
|
||||
|
||||
trace("//(contents of b.sort(Array.NUMERIC | 1))");
|
||||
assert_array(b.sort(Array.NUMERIC | 1));
|
||||
|
||||
b = fresh_array_b();
|
||||
|
||||
trace("//(contents of b.sort(Array.NUMERIC | Array.DESCENDING | Array.RETURNINDEXEDARRAY))");
|
||||
assert_array(b.sort(Array.NUMERIC | Array.DESCENDING | Array.RETURNINDEXEDARRAY));
|
||||
|
||||
trace("//(contents of b.sort(16 | Array.DESCENDING))");
|
||||
assert_array(b.sort(16 | Array.DESCENDING));
|
||||
check_holes(b);
|
||||
|
||||
trace("//var a = new Array(7,2,1,\"3\",\"4\")");
|
||||
var a = new Array(7,2,1,"3","4");
|
||||
|
||||
trace("//(contents of a.sort(sub_comparison))");
|
||||
assert_array(a.sort(sub_comparison));
|
||||
|
||||
trace("//(contents of a.sort(sub_comparison, 2))");
|
||||
assert_array(a.sort(sub_comparison, 2));
|
||||
|
||||
trace("//(contents of a.sort(sub_comparison, Array.RETURNINDEXEDARRAY))");
|
||||
assert_array(a.sort(sub_comparison, Array.RETURNINDEXEDARRAY));
|
||||
|
||||
trace("//(contents of a.sort(sub_comparison, Array.DESCENDING | 8))");
|
||||
assert_array(a.sort(sub_comparison, Array.DESCENDING | 8));
|
||||
|
||||
trace("//a.sort(sub_comparison, Array.UNIQUESORT) === 0");
|
||||
trace(a.sort(sub_comparison, Array.UNIQUESORT) === 0);
|
||||
|
||||
trace("//var c = new Array(3,\"abc\")");
|
||||
var c = new Array(3,"abc");
|
||||
|
||||
trace("//c.sort(sub_comparison, Array.UNIQUESORT) === 0");
|
||||
trace(c.sort(sub_comparison, Array.UNIQUESORT) === 0);
|
||||
|
||||
trace("//var d = new Array(3,\"4\")");
|
||||
var d = new Array(3,"4");
|
||||
|
||||
trace("//(contents of d.sort(sub_comparison, 4))");
|
||||
assert_array(d.sort(sub_comparison, 4));
|
|
@ -0,0 +1,297 @@
|
|||
//var a = new Array(5,3,1,"Abc","2","aba",false,null,"zzz")
|
||||
//a[11] = "not a hole";
|
||||
//Array.prototype[9] = undefined;
|
||||
//Array.prototype[10] = "hole in slot 10";
|
||||
//a.sort(Array.UNIQUESORT) === 0
|
||||
false
|
||||
//var a = new Array(5,3,1,"Abc","2","aba",false,null,"zzz")
|
||||
//a[11] = "not a hole";
|
||||
//(contents of a.sort(Array.RETURNINDEXEDARRAY))
|
||||
2
|
||||
4
|
||||
1
|
||||
0
|
||||
3
|
||||
5
|
||||
6
|
||||
10
|
||||
11
|
||||
7
|
||||
8
|
||||
9
|
||||
//(contents of a.sort())
|
||||
1
|
||||
2
|
||||
3
|
||||
5
|
||||
Abc
|
||||
aba
|
||||
false
|
||||
hole in slot 10
|
||||
not a hole
|
||||
null
|
||||
zzz
|
||||
undefined
|
||||
//Array.prototype[10] = "hole10";
|
||||
//Array.prototype[11] = "hole11";
|
||||
//Array.prototype[12] = "hole12";
|
||||
//(contents of previous array)
|
||||
1
|
||||
2
|
||||
3
|
||||
5
|
||||
Abc
|
||||
aba
|
||||
false
|
||||
hole in slot 10
|
||||
not a hole
|
||||
null
|
||||
zzz
|
||||
hole11
|
||||
//(cleaning up our holes...)
|
||||
//Array.prototype[9] = undefined;
|
||||
//Array.prototype[10] = "hole in slot 10";
|
||||
//var a = new Array(5,3,1,"Abc","2","aba",false,null,"zzz")
|
||||
//a[11] = "not a hole";
|
||||
//(contents of a.sort(Array.CASEINSENSITIVE | Array.RETURNINDEXEDARRAY))
|
||||
2
|
||||
4
|
||||
1
|
||||
0
|
||||
5
|
||||
3
|
||||
6
|
||||
10
|
||||
11
|
||||
7
|
||||
8
|
||||
9
|
||||
//(contents of a.sort(Array.CASEINSENSITIVE))
|
||||
1
|
||||
2
|
||||
3
|
||||
5
|
||||
aba
|
||||
Abc
|
||||
false
|
||||
hole in slot 10
|
||||
not a hole
|
||||
null
|
||||
zzz
|
||||
undefined
|
||||
//Array.prototype[10] = "hole10";
|
||||
//Array.prototype[11] = "hole11";
|
||||
//Array.prototype[12] = "hole12";
|
||||
//(contents of previous array)
|
||||
1
|
||||
2
|
||||
3
|
||||
5
|
||||
aba
|
||||
Abc
|
||||
false
|
||||
hole in slot 10
|
||||
not a hole
|
||||
null
|
||||
zzz
|
||||
hole11
|
||||
//(cleaning up our holes...)
|
||||
//Array.prototype[9] = undefined;
|
||||
//Array.prototype[10] = "hole in slot 10";
|
||||
//var a = new Array(5,3,1,"Abc","2","aba",false,null,"zzz")
|
||||
//a[11] = "not a hole";
|
||||
//(contents of a.sort(Array.DESCENDING | Array.RETURNINDEXEDARRAY))
|
||||
8
|
||||
7
|
||||
11
|
||||
10
|
||||
6
|
||||
5
|
||||
3
|
||||
0
|
||||
1
|
||||
4
|
||||
2
|
||||
9
|
||||
//(contents of a.sort(Array.DESCENDING))
|
||||
zzz
|
||||
null
|
||||
not a hole
|
||||
hole in slot 10
|
||||
false
|
||||
aba
|
||||
Abc
|
||||
5
|
||||
3
|
||||
2
|
||||
1
|
||||
undefined
|
||||
//Array.prototype[10] = "hole10";
|
||||
//Array.prototype[11] = "hole11";
|
||||
//Array.prototype[12] = "hole12";
|
||||
//(contents of previous array)
|
||||
zzz
|
||||
null
|
||||
not a hole
|
||||
hole in slot 10
|
||||
false
|
||||
aba
|
||||
Abc
|
||||
5
|
||||
3
|
||||
2
|
||||
1
|
||||
hole11
|
||||
//(cleaning up our holes...)
|
||||
//Array.prototype[9] = undefined;
|
||||
//Array.prototype[10] = "hole in slot 10";
|
||||
//var a = new Array(5,3,1,"Abc","2","aba",false,null,"zzz")
|
||||
//a[11] = "not a hole";
|
||||
//(contents of a.sort(Array.CASEINSENSITIVE | Array.DESCENDING | Array.RETURNINDEXEDARRAY))
|
||||
8
|
||||
7
|
||||
11
|
||||
10
|
||||
6
|
||||
3
|
||||
5
|
||||
0
|
||||
1
|
||||
4
|
||||
2
|
||||
9
|
||||
//(contents of a.sort(Array.CASEINSENSITIVE | Array.DESCENDING))
|
||||
zzz
|
||||
null
|
||||
not a hole
|
||||
hole in slot 10
|
||||
false
|
||||
Abc
|
||||
aba
|
||||
5
|
||||
3
|
||||
2
|
||||
1
|
||||
undefined
|
||||
//Array.prototype[10] = "hole10";
|
||||
//Array.prototype[11] = "hole11";
|
||||
//Array.prototype[12] = "hole12";
|
||||
//(contents of previous array)
|
||||
zzz
|
||||
null
|
||||
not a hole
|
||||
hole in slot 10
|
||||
false
|
||||
Abc
|
||||
aba
|
||||
5
|
||||
3
|
||||
2
|
||||
1
|
||||
hole11
|
||||
//(cleaning up our holes...)
|
||||
//Array.prototype[9] = undefined;
|
||||
//Array.prototype[10] = "hole in slot 10";
|
||||
//var a = new Array(5,3,1,"Abc","2","aba",false,null,"zzz")
|
||||
//a[11] = "not a hole";
|
||||
//var b = new Array(5,3,2,1,"2",false,true,NaN)
|
||||
//b.sort(Array.NUMERIC | Array.UNIQUESORT) === 0
|
||||
true
|
||||
//var b = new Array(5,3,"2",false,true,NaN)
|
||||
//(contents of b.sort(Array.NUMERIC | Array.RETURNINDEXEDARRAY))
|
||||
3
|
||||
4
|
||||
2
|
||||
1
|
||||
0
|
||||
5
|
||||
//(contents of b.sort(Array.NUMERIC))
|
||||
false
|
||||
true
|
||||
2
|
||||
3
|
||||
5
|
||||
NaN
|
||||
//Array.prototype[10] = "hole10";
|
||||
//Array.prototype[11] = "hole11";
|
||||
//Array.prototype[12] = "hole12";
|
||||
//(contents of previous array)
|
||||
false
|
||||
true
|
||||
2
|
||||
3
|
||||
5
|
||||
NaN
|
||||
//(cleaning up our holes...)
|
||||
//Array.prototype[9] = undefined;
|
||||
//Array.prototype[10] = "hole in slot 10";
|
||||
//var b = new Array(5,3,"2",false,true,NaN)
|
||||
//(contents of b.sort(Array.NUMERIC | 1))
|
||||
false
|
||||
true
|
||||
2
|
||||
3
|
||||
5
|
||||
NaN
|
||||
//var b = new Array(5,3,"2",false,true,NaN)
|
||||
//(contents of b.sort(Array.NUMERIC | Array.DESCENDING | Array.RETURNINDEXEDARRAY))
|
||||
5
|
||||
0
|
||||
1
|
||||
2
|
||||
4
|
||||
3
|
||||
//(contents of b.sort(16 | Array.DESCENDING))
|
||||
NaN
|
||||
5
|
||||
3
|
||||
2
|
||||
true
|
||||
false
|
||||
//Array.prototype[10] = "hole10";
|
||||
//Array.prototype[11] = "hole11";
|
||||
//Array.prototype[12] = "hole12";
|
||||
//(contents of previous array)
|
||||
NaN
|
||||
5
|
||||
3
|
||||
2
|
||||
true
|
||||
false
|
||||
//(cleaning up our holes...)
|
||||
//Array.prototype[9] = undefined;
|
||||
//Array.prototype[10] = "hole in slot 10";
|
||||
//var a = new Array(7,2,1,"3","4")
|
||||
//(contents of a.sort(sub_comparison))
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
7
|
||||
//(contents of a.sort(sub_comparison, 2))
|
||||
7
|
||||
4
|
||||
3
|
||||
2
|
||||
1
|
||||
//(contents of a.sort(sub_comparison, Array.RETURNINDEXEDARRAY))
|
||||
4
|
||||
3
|
||||
2
|
||||
1
|
||||
0
|
||||
//(contents of a.sort(sub_comparison, Array.DESCENDING | 8))
|
||||
0
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
//a.sort(sub_comparison, Array.UNIQUESORT) === 0
|
||||
false
|
||||
//var c = new Array(3,"abc")
|
||||
//c.sort(sub_comparison, Array.UNIQUESORT) === 0
|
||||
true
|
||||
//var d = new Array(3,"4")
|
||||
//(contents of d.sort(sub_comparison, 4))
|
||||
3
|
||||
4
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue