You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

417 lines
11 KiB

function test(this_value, args) {
try {
var t = Array.prototype.reduce.apply(this_value, args);
print(typeof t, t);
} catch (e) {
print(e.name);
}
}
function adder(prev, curr, index, obj) {
print('adder', typeof prev + ':' + prev, typeof curr + ':' + curr, typeof index, index, typeof obj);
return prev + curr;
}
/*===
basic
TypeError
number 100
number 1
adder number:100 number:1 number 0 object
number 101
adder number:1 number:2 number 1 object
number 3
adder number:100 number:1 number 0 object
adder number:101 number:2 number 1 object
number 103
adder number:1 number:2 number 1 object
adder number:3 number:3 number 2 object
adder number:6 number:4 number 3 object
adder number:10 number:5 number 4 object
number 15
adder number:100 number:1 number 0 object
adder number:101 number:2 number 1 object
adder number:103 number:3 number 2 object
adder number:106 number:4 number 3 object
adder number:110 number:5 number 4 object
number 115
adder number:1 number:2 number 1 object
adder number:3 number:3 number 2 object
adder number:6 number:4 number 3 object
adder number:10 number:5 number 4 object
adder number:15 number:6 number 5 object
adder number:21 number:7 number 6 object
adder number:28 number:8 number 7 object
adder number:36 number:9 number 8 object
adder number:45 number:10 number 9 object
number 55
adder number:1 number:2 number 1 object
adder number:3 number:3 number 2 object
adder number:6 number:4 number 50 object
adder number:10 number:5 number 100 object
number 15
adder number:1 number:2 number 1 object
adder number:3 number:3 number 2 object
adder number:6 number:4 number 3 object
adder number:10 number:5 number 4 object
number 15
1 true
2 true
3 true
4 true
undefined undefined
adder number:1 number:2 number 1 object
adder number:3 number:3 number 2 object
number 6
adder undefined:undefined number:1 number 0 object
adder number:NaN number:2 number 1 object
adder number:NaN number:3 number 2 object
number NaN
===*/
print('basic');
function basicTest() {
var obj;
// basic combinations of different input length and presence of an
// initial value
test([], [adder])
test([], [adder, 100])
test([1], [adder])
test([1], [adder, 100])
test([1,2], [adder])
test([1,2], [adder, 100])
test([1,2,3,4,5], [adder])
test([1,2,3,4,5], [adder, 100])
// dense array
test([1,2,3,4,5,6,7,8,9,10], [adder]);
// sparse array
obj = [1,2,3];
obj[100] = 5;
obj[50] = 4;
test(obj, [adder]);
// non-array
obj = {
'0': 1, '1': 2, '2': 3, '3': 4, '4': 5, length: 5
};
test(obj, [adder]);
// check callback object argument
obj = {
'0': 1, '1': 2, '2': 3, '3': 4, '4': 5, length: 5
}
test(obj, [ function (prev, curr, index, o) {
print(index, obj === o);
} ]);
// undefined initialValue is different from an initialValue not
// given at all
test([1,2,3], [adder]);
test([1,2,3], [adder, undefined]);
}
try {
basicTest();
} catch (e) {
print(e);
}
/*===
inherited
adder string:foo string:bar number 1 object
adder string:foobar string:quux number 2 object
string foobarquux
adder string:foo string:bar number 1 object
adder string:foobar string:quux number 2 object
adder string:foobarquux string:baz number 3 object
string foobarquuxbaz
===*/
/* Since [[HasProperty]] and [[Get]] are used, inherited properties
* must "show through" properly, for both arrays and objects.
*/
print('inherited');
function inheritedTest() {
var obj;
var proto;
obj = [];
obj[0] = 'foo';
Array.prototype[1] = 'bar';
obj[2] = 'quux';
test(obj, [ adder ]);
delete Array.prototype[1];
proto = Object.create(Object.prototype);
proto[0] = 'foo';
proto[1] = 'bar';
proto[3] = 'baz';
proto.length = 5;
obj = Object.create(proto);
obj[2] = 'quux'; // no 'length' in obj own properties
test(obj, [ adder ]);
}
try {
inheritedTest();
} catch (e) {
print(e);
}
/*===
mutation
callback foo bar 1 foo,bar,quux
callback foobar quux 2 foo,bar,quux,baz
string foobarquux
callback foo bar 1 foo,bar,quux
string foobar
callback foo bar 1 [object Object]
callback foobar quux 2 [object Object]
string foobarquux
callback foo bar 1 [object Object]
callback foobar quux 2 [object Object]
string foobarquux
callback foo bar 1 [object Object]
callback foobar quux 2 [object Object]
callback foobarquux baz 3 [object Object]
string foobarquuxbaz
callback foo bar 1 [object Object]
callback foobar baz 3 [object Object]
string foobarbaz
===*/
print('mutation');
function mutationTest() {
var obj;
// Mutation: adding elements to end of array after iteration starts
// -> new elements not included
obj = [ 'foo', 'bar', 'quux' ];
test(obj, [function(prev, curr, index, obj) {
print('callback', prev, curr, index, obj);
obj['3'] = 'baz'; // not included
return prev + curr;
}]);
// Mutation: deleting an element inside the initial length range
// -> deleted element not included in iteration
obj = [ 'foo', 'bar', 'quux' ];
test(obj, [function(prev, curr, index, obj) {
print('callback', prev, curr, index, obj);
delete obj['2']; // not included
return prev + curr;
}]);
// Mutation: if 'length' is updated during processing, the updated
// length is not respected.
obj = { '0': 'foo', '1': 'bar', '2': 'quux', length: 3 };
test(obj, [function(prev, curr, index, obj) {
print('callback', prev, curr, index, obj);
obj['3'] = 'baz';
obj.length = 4; // not respected
return prev + curr;
}]);
obj = { '0': 'foo', '1': 'bar', '2': 'quux', length: 3 };
test(obj, [function(prev, curr, index, obj) {
print('callback', prev, curr, index, obj);
obj.length = 0; // not respected
return prev + curr;
}]);
// Mutation: non-existent elements can be added and will be processed
// as long as they are within the initial length range.
obj = { '0': 'foo', '1': 'bar', length: 4 };
test(obj, [function(prev, curr, index, obj) {
print('callback', prev, curr, index, obj);
obj['2'] = 'quux';
obj['3'] = 'baz';
return prev + curr;
}]);
// Mutation: deleted elements are skipped.
obj = { '0': 'foo', '1': 'bar', '2': 'quux', '3': 'baz', length: 4 };
test(obj, [function(prev, curr, index, obj) {
print('callback', prev, curr, index, obj);
delete obj['2'];
return prev + curr;
}]);
}
try {
mutationTest();
} catch (e) {
print(e);
}
/*===
coercion
TypeError
TypeError
TypeError
TypeError
TypeError
adder string:f string:o number 1 object
adder string:fo string:o number 2 object
string foo
adder number:1 number:2 number 1 object
adder number:3 number:3 number 2 object
number 6
number 123
TypeError
adder string:foo string:bar number 1 object
adder string:foobar string:quux number 2 object
string foobarquux
adder string:foo string:bar number 1 object
adder string:foobar string:quux number 2 object
string foobarquux
adder string:foo string:bar number 1 object
adder string:foobar string:quux number 2 object
adder string:foobarquux string:baz number 3 object
string foobarquuxbaz
adder string:foo string:bar number 1 object
adder string:foobar string:quux number 2 object
string foobarquux
length valueOf
adder string:foo string:bar number 1 object
adder string:foobar string:quux number 2 object
string foobarquux
TypeError
TypeError
TypeError
TypeError
TypeError
TypeError
TypeError
TypeError
TypeError
length valueOf
TypeError
length getter
0 getter
callback 0
initialValue toString()
1 getter
callback 1
2 getter
callback 2
string initial1st2nd3rd
===*/
print('coercion');
function coercionTest() {
var obj;
// this
test(undefined, [ adder ]);
test(null, [ adder ]);
test(true, [ adder ]);
test(false, [ adder ]);
test(123, [ adder ]);
test('foo', [ adder ]);
test([1,2,3], [ adder ]);
test({ foo: 1, bar: 2 }, [ adder, 123 ]); // object is empty, initial value -> returned as is
test({ foo: 1, bar: 2 }, [ adder ]); // object is empty, no initial value -> TypeError
// length
test({ '0': 'foo', '1': 'bar', '2': 'quux', '3': 'baz', length: 3.9 }, [ adder ]);
test({ '0': 'foo', '1': 'bar', '2': 'quux', '3': 'baz', length: 256*256*256*256 + 3.9 }, [ adder ]); // coerces to 3
test({ '0': 'foo', '1': 'bar', '2': 'quux', '3': 'baz', length: -256*256*256*256 + 3.9 }, [ adder ]); // coerces to 4
test({ '0': 'foo', '1': 'bar', '2': 'quux', '3': 'baz', length: '3.9' }, [ adder ]);
test({ '0': 'foo', '1': 'bar', '2': 'quux', '3': 'baz', length: {
toString: function() { print('length toString'); return 4; },
valueOf: function() { print('length valueOf'); return 3; },
} }, [ adder ]);
// length is not coerced if this coercion fails
test(null, [ {
toString: function() { print('length toString'); return 4; },
valueOf: function() { print('length valueOf'); return 3; },
} ]);
// callback must be callable, otherwise TypeError (occurs after length coercion)
test([1,2,3], [ undefined ]);
test([1,2,3], [ null ]);
test([1,2,3], [ true ]);
test([1,2,3], [ false ]);
test([1,2,3], [ 123 ]);
test([1,2,3], [ 'foo' ]);
test([1,2,3], [ [1,2] ]);
test([1,2,3], [ { foo: 1, bar: 2 } ]);
test({ '0': 'foo', '1': 'bar', '2': 'quux', '3': 'baz', length: {
toString: function() { print('length toString'); return 4; },
valueOf: function() { print('length valueOf'); return 3; },
} }, [ true ]);
// coercion order test
obj = Object.create(Object.prototype, {
'0': {
get: function() { print('0 getter'); return '1st'; },
set: function() { print('0 setter'); throw new Error('setter called') },
enumerable: true,
configurable: true
},
'1': {
get: function() { print('1 getter'); return '2nd'; },
set: function() { print('1 setter'); throw new Error('setter called') },
enumerable: true,
configurable: true
},
'2': {
get: function() { print('2 getter'); return '3rd'; },
set: function() { print('2 setter'); throw new Error('setter called') },
enumerable: true,
configurable: true
},
'length': {
get: function() { print('length getter'); return '4.9'; }, // coerces to 4
set: function() { print('length setter'); throw new Error('setter called') },
enumerable: true,
configurable: true
},
});
test(obj, [
function callback(prev, curr, index, obj) {
print('callback', index); // avoid side effects here
return String(prev) + String(curr);
},
{
// initial value is not coerced by the algorithm, but by the callback code
toString: function() { print('initialValue toString()'); return 'initial'; },
valueOf: function() { print('initialValue valueOf()'); return 'initial-notused'; }
}
]);
}
try {
coercionTest();
} catch (e) {
print(e);
}