mirror of https://github.com/svaarala/duktape.git
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
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);
|
|
}
|
|
|
|
|