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 crate::avm2::Error;
|
||||||
use enumset::{EnumSet, EnumSetType};
|
use enumset::{EnumSet, EnumSetType};
|
||||||
use gc_arena::{GcCell, MutationContext};
|
use gc_arena::{GcCell, MutationContext};
|
||||||
use std::cmp::min;
|
use std::cmp::{min, Ordering};
|
||||||
use std::mem::swap;
|
use std::mem::swap;
|
||||||
|
|
||||||
/// Implements `Array`'s instance initializer.
|
/// Implements `Array`'s instance initializer.
|
||||||
|
@ -786,6 +786,222 @@ enum SortOptions {
|
||||||
Numeric,
|
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.
|
/// Construct `Array`'s class.
|
||||||
pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> {
|
pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> {
|
||||||
let class = Class::new(
|
let class = Class::new(
|
||||||
|
@ -896,6 +1112,11 @@ pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>
|
||||||
Method::from_builtin(splice),
|
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(
|
class.write(mc).define_class_trait(Trait::from_const(
|
||||||
QName::new(Namespace::public_namespace(), "CASEINSENSITIVE"),
|
QName::new(Namespace::public_namespace(), "CASEINSENSITIVE"),
|
||||||
Multiname::from(QName::new(Namespace::public_namespace(), "uint")),
|
Multiname::from(QName::new(Namespace::public_namespace(), "uint")),
|
||||||
|
|
|
@ -379,6 +379,7 @@ swf_tests! {
|
||||||
(as3_array_unshift, "avm2/array_unshift", 1),
|
(as3_array_unshift, "avm2/array_unshift", 1),
|
||||||
(as3_array_slice, "avm2/array_slice", 1),
|
(as3_array_slice, "avm2/array_slice", 1),
|
||||||
(as3_array_splice, "avm2/array_splice", 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.
|
// 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