function test(this_value, args) { var t; try { t = Array.prototype.indexOf.apply(this_value, args); print(typeof t, t); } catch (e) { print(e.name); } } /*=== basic number 0 number 1 number 2 number -1 number 50 number 100 number -1 number -1 number 3 number 4 number 5 number -1 number 0 number 0 number 0 number 0 number 0 number 0 number 3 number 3 number 3 number -1 number -1 number 0 number 0 number 3 number 3 number 3 number -1 number -1 number -1 number -1 number -1 number 0 number 0 number 2 ===*/ print('basic'); function basicTest() { var obj; var i; // dense array test([1,2,3,1,2,3], [1]); test([1,2,3,1,2,3], [2]); test([1,2,3,1,2,3], [3]); test([1,2,3,1,2,3], [4]); // sparse array obj = [1]; obj[100] = 3; obj[50] = 2; test(obj, [2]); // non-array object obj = { '0': 'foo', '50': 'bar', '100': 'quux', '200': 'baz', length: 101 }; test(obj, ['quux']); test(obj, ['baz']); // not found, because length limits // zero length test([], ['ignored']); // fromIndex test([1,2,3,1,2,3], [1, 3]); test([1,2,3,1,2,3], [2, 3]); test([1,2,3,1,2,3], [3, 3]); test([1,2,3,1,2,3], [4, 3]); // fromIndex not given, same as 0; if given as undefined, coerces to 0 too test([1,2,3,1,2,3], [1]); test([1,2,3,1,2,3], [1, undefined]); // fromIndex range var indexlist = [ Number.NEGATIVE_INFINITY, -100, -7, -6, -5, -4, -3, -2, -1, -0, +0, 1, 2, 3, 4, 5, 6, 7, Number.POSITIVE_INFINITY, Number.NaN ]; for (i = 0; i < indexlist.length; i++) { test([1,2,3,1,2,3], [1, indexlist[i]]); } // comparison is with strict equals, negative and positive zero are equal test([-0,-0,+0,+0], [+0]); // matches at 0 test(['',0,false], [false]); // matches at 2, "falsy" values don't match } try { basicTest(); } catch (e) { print(e); } /*=== coercion TypeError TypeError number -1 number -1 number -1 number -1 number 0 number -1 number 3 number -1 number 0 number -1 number -1 number -1 fromIndex valueOf number 2 4 0 getter 1 getter number 1 0 getter 1 getter 2 getter 3 getter number -1 0 getter 1 getter number 1 0 getter 1 getter 2 getter 3 getter number -1 ===*/ print('coercion'); function coercionTest() { var obj; var idxobj; // this coercion test(undefined, [1]); test(null, [1]); test(true, [1]); test(false, [1]); test(123, [1]); test('quux', [1]); test([1,2], [1]); test({ foo: 1, bar: 2 }, [1]); // length coercion test([1,1,1,1,1,1], [1, '3.9']); test([1,1,1,1,1,1], [1, 256*256*256*256 + 3.9]); test([1,1,1,1,1,1], [1, -256*256*256*256 + 3.9]); // zero array length check happens before fromIndex coercion test([], [1, { toString: function() { print('fromIndex toString'); return 4; }, valueOf: function() { print('fromIndex valueOf'); return 3; }, }]); test({ '0': 'foo', length: 0 }, [1, { toString: function() { print('fromIndex toString'); return 4; }, valueOf: function() { print('fromIndex valueOf'); return 3; }, }]); // ToUint32(length) == 0 suffices for a fast exit without fromIndex coercion test({ '0': 'foo', length: 256*256*256*256 }, [1, { toString: function() { print('fromIndex toString'); return 4; }, valueOf: function() { print('fromIndex valueOf'); return 3; }, }]); // fromIndex coercion idxobj = { toString: function() { print('fromIndex toString'); return '1.9'; }, valueOf: function() { print('fromIndex valueOf'); return '0.29e1'; }, // = 2.9 -> 2 } test([1,1,1,1,1], [1, idxobj]); // element [[Get]] side effects idxobj = { toString: function() { print('fromIndex toString'); return '0.9'; }, valueOf: function() { print('fromIndex valueOf'); return '0.9'; }, } obj = []; Object.defineProperties(obj, { '0': { get: function() { print('0 getter'); return 'foo'; }, set: function() { print('0 setter'); }, enumerable: true, configurable: true }, '1': { get: function() { print('1 getter'); return 'bar'; }, set: function() { print('1 setter'); }, enumerable: true, configurable: true }, '2': { get: function() { print('2 getter'); return 'quux'; }, set: function() { print('2 setter'); }, enumerable: true, configurable: true }, '3': { get: function() { print('3 getter'); return 'baz'; }, set: function() { print('3 setter'); }, enumerable: true, configurable: true }, }); print(obj.length); test(obj, ['bar']); test(obj, ['nonexistent']); // same for a non-array obj = {}; Object.defineProperties(obj, { '0': { get: function() { print('0 getter'); return 'foo'; }, set: function() { print('0 setter'); }, enumerable: true, configurable: true }, '1': { get: function() { print('1 getter'); return 'bar'; }, set: function() { print('1 setter'); }, enumerable: true, configurable: true }, '2': { get: function() { print('2 getter'); return 'quux'; }, set: function() { print('2 setter'); }, enumerable: true, configurable: true }, '3': { get: function() { print('3 getter'); return 'baz'; }, set: function() { print('3 setter'); }, enumerable: true, configurable: true }, }); obj.length = 4; test(obj, ['bar']); test(obj, ['nonexistent']); } try { coercionTest(); } catch (e) { print(e); }