/*=== basic object 3 ["foo"," bar"," quux"] object 1 ["foo - undefined - bar"] object 1 ["foo - undefined - bar"] object 6 ["f","o","o","b","a","r"] object 4 97 837 4660 65244 object 6 ["f","o","o","b","a","r"] object 6 ["f","o","o","b","a","r"] object 7 ["","","","","","",""] object 3 ["foo","bar","quux"] object 0 [] object 1 [""] object 0 [] object 1 [""] object 10 ["foo",";"," ","bar",";","","quux",";"," ","baz"] object 7 ["foo",";",null," bar",null,","," baz"] object 2 ["a","b"] object 2 ["","b"] object 13 ["A",null,"B","bold","/","B","and",null,"CODE","coded","/","CODE",""] object 2 ["foo",""] object 1 ["foo"] object 2 ["foo",""] object 3 1 291 1 4660 1 65244 object 3 1 291 1 4660 1 65244 ===*/ print('basic'); function basicTest() { var t; function p(x) { print(typeof x, x.length, JSON.stringify(x)); } // Basic test p('foo, bar, quux'.split(',')); // Undefined separator results in an array containing just 'this' as a // string; it must not be coerced to 'undefined' (and then used as a // separator string). p(new String('foo - undefined - bar').split()); p(new String('foo - undefined - bar').split(undefined)); // Empty separator splits a string into individual characters p('foobar'.split('')); // Unicode characters cause different processing internally t = 'a\u0345\u1234\ufedc'.split(''); print(typeof t, t.length, t[0].charCodeAt(0), t[1].charCodeAt(0), t[2].charCodeAt(0), t[3].charCodeAt(0)); // Empty RegExp, also splits into individual characters p('foobar'.split(/(?:)/)); // Only first RegExp match matters at a given position; this is // equivalent to an empty regexp p('foobar'.split(/(?:)|./)); // first alt matches p('foobar'.split(/.|(?:)/)); // Simple two character separator p('foo::bar::quux'.split('::')); // If 'this' coerces to empty string, result depends on whether separator // can match the empty string p(''.split('')); // can match -> no elems p(''.split('a')); // cannot match -> [''] p(''.split(/a*/)); // can match -> no elems p(''.split(/a+/)); // cannot match -> [''] // RegExp captures are made part of the result array p('foo; bar;quux; baz'.split(/(;)(\s?)/)); p('foo; bar, baz'.split(/(;)|(,)/)); // unmatched -> undefined -> JSON null // Specific example from spec: evaluates to ['a','b'] p('ab'.split(/a*?/)); // Specific example from spec: evaluates to ['', 'b'] p('ab'.split(/a*/)); // Specific example from spec: note that 'undefined' values join as empty p('Aboldandcoded'.split(/<(\/)?([^<>]+)>/)); // Trailer is added as an empty string if last match coincides with // the end of the string. p('foobar'.split('bar')); // Trailer may hit the limit p('foobar'.split('bar', 1)); // trailer dropped p('foobar'.split('bar', 2)); // trailer fits // Some non-BMP tests; important because both byte and char offsets // are used internally. t = '\u0123\u1234\ufedc'.split(''); print(typeof t, t.length, t[0].length, t[0].charCodeAt(0), t[1].length, t[1].charCodeAt(0), t[2].length, t[2].charCodeAt(0)); t = '\u0123\u1234\ufedc'.split(/(?:)/); print(typeof t, t.length, t[0].length, t[0].charCodeAt(0), t[1].length, t[1].charCodeAt(0), t[2].length, t[2].charCodeAt(0)); // XXX: add more non-BMP tests } try { basicTest(); } catch (e) { print(e); } /*=== limit object 1 ["foo"] object 0 [] object 3 ["foo","bar","quux"] object 3 ["foo","bar","quux"] object 2 ["foo","bar"] object 1 ["foo"] object 0 [] object 0 [] object 1 ["foo"] object 2 ["foo","bar"] object 4 ["foo","bar","quux","baz"] object 1 ["foo"] object 2 ["foo","bar"] object 2 ["foo","bar"] object 3 ["foo","bar","quux"] object 9 ["foo:","1","2","3",":bar:","3","2","1",":quux"] object 9 ["foo:","1","2","3",":bar:","3","2","1",":quux"] object 8 ["foo:","1","2","3",":bar:","3","2","1"] object 7 ["foo:","1","2","3",":bar:","3","2"] object 6 ["foo:","1","2","3",":bar:","3"] object 5 ["foo:","1","2","3",":bar:"] object 4 ["foo:","1","2","3"] object 3 ["foo:","1","2"] object 2 ["foo:","1"] object 1 ["foo:"] object 0 [] ===*/ /* Limit tests. Limit may affect the result at any point; in particular, * RegExp captures may need be limited from the middle. * * The default limit of 2^32 - 1 is achievable with captures, but requires * a huge amount of memory. It is not tested now. */ print('limit'); function limitTest() { var i; function p(x) { print(typeof x, x.length, JSON.stringify(x)); } // zero limit p('foo'.split(undefined)); // undefined limit -> 2**32-1 p('foo'.split(undefined, 0)); // normally returns array with string as is, limit 0 -> return [] // limit, normal case p('foo,bar,quux'.split(',')); p('foo,bar,quux'.split(',', 3)); p('foo,bar,quux'.split(',', 2)); p('foo,bar,quux'.split(',', 1)); p('foo,bar,quux'.split(',', 0)); // huge positive limit wraps through ToUint32() p('foo,bar,quux,baz'.split(',', 4294967296)); // = 0 p('foo,bar,quux,baz'.split(',', 4294967297)); // = 1 p('foo,bar,quux,baz'.split(',', 4294967298)); // = 2 // negative limit goes through ToUint32() and becomes a positive one // -1 -> 4294967295 // -4294967295 -> 1 // -4294967294 -> 2 p('foo,bar,quux,baz'.split(',', -1)); p('foo,bar,quux,baz'.split(',', -4294967295)); p('foo,bar,quux,baz'.split(',', -4294967294)); // ToUint32() coerces fractions towards zero p('foo,bar,quux,baz'.split(',', 2.4)); // limit 2 p('foo,bar,quux,baz'.split(',', 2.4 - (256*256*256*256))); // limit 3 (!) // limit, happens in the middle of a capture p('foo:123:bar:321:quux'.split(/(\d)(\d)(\d)/)); for (i = 9; i >= 0; i--) { p('foo:123:bar:321:quux'.split(/(\d)(\d)(\d)/, i)); } } try { limitTest(); } catch (e) { print(e); } /*=== regexp before object string 12345 object 3 ["foo","bar","quux"] after object string 12345 before object string 12345 object 5 ["foo","z","bar","Z","quux"] after object string 12345 before object string 12345 object 1 ["foo\nbar\nquux"] after object string 12345 before object string 12345 object 3 ["foo\n","bar\n","quux"] after object string 12345 before object string 12345 object 3 ["foo","bar","quux"] after object string 12345 ===*/ /* A RegExp separator is used through the internal [[Match]] method. This * executes the regexp pattern, taking ignoreCase and multiline flags into * account but ignoring the global flag. The RegExp instance properties * (such as lastIndex) are neither read or written. */ print('regexp'); function regExpTest() { var re; function test(this_val, sep_val, limit_val) { var t; print('before', typeof sep_val, typeof sep_val.lastIndex, sep_val.lastIndex); try { t = String.prototype.split.call(this_val, sep_val, limit_val); print(typeof t, t.length, JSON.stringify(t)); } catch (e) { print(e.name); } print('after', typeof sep_val, typeof sep_val.lastIndex, sep_val.lastIndex); } // ignoreCase matcher re = /z/i; re.lastIndex = '12345'; test('foozbarZquux', re); // ignoreCase + captures; captures have original casing re = /(z)/i; re.lastIndex = '12345'; test('foozbarZquux', re); // multiline matcher; use multiline '^' to detect re = /^/; // single line match, does not match start of line re.lastIndex = '12345'; test('foo\nbar\nquux', re); re = /^/m; // matches start of line, empty match, newlines will be part of match re.lastIndex = '12345'; test('foo\nbar\nquux', re); // global matcher, no effect re = /z/gi; re.lastIndex = '12345'; test('foozbarZquux', re); } try { regExpTest(); } catch (e) { print(e); } /*=== coercion TypeError TypeError TypeError object 1 ["true"] object 1 ["false"] object 1 ["123"] object 1 ["quux"] object 3 ["1","2","3"] object 2 ["[object","Object]"] object 3 ["foo","bar","quux"] object 1 ["fooundefinedbarundefinedquux"] object 3 ["foo","bar","quux"] object 3 ["foo","bar","quux"] object 3 ["foo","bar","quux"] object 3 ["foo","bar","quux"] object 3 ["foo","bar","quux"] object 3 ["foo","bar","quux"] object 3 ["foo","bar","quux"] toString() this valueOf() limit toString() sep object 2 ["foo","bar"] ===*/ print('coercion'); function coercionTest() { function test(this_val, sep_val, limit_val, arg_count) { var t; try { if (arg_count === 0) { t = String.prototype.split.call(); } else if (arg_count === 1) { t = String.prototype.split.call(this_val); } else if (arg_count === 2) { t = String.prototype.split.call(this_val, sep_val); } else { t = String.prototype.split.call(this_val, sep_val, limit_val); } print(typeof t, t.length, JSON.stringify(t)); } catch (e) { print(e.name); } } // this coercion check test(undefined, undefined, undefined, 0); test(undefined, ',', 3); test(null, ',', 3); test(true, ',', 3); test(false, ',', 3); test(123, ',', 3); test('quux', ',', 3); test([1,2,3], ',', 3); test({ foo: 1, bar: 2 }, ' ', 3); // limit coercion (most limit checks already done above) test('foo,bar,quux,baz', ',', '3.9000e0'); // separator coercion test('fooundefinedbarundefinedquux', undefined); // undefined separator -> return input as only array elem (E5.1 Section 15.4.14, step 10) test('foonullbarnullquux', null); test('footruebartruequux', true); test('foofalsebarfalsequux', false); test('foo123bar123quux', 123); test('fooXbarXquux', 'X'); test('foo1,2bar1,2quux', [1,2]); test('foo[object Object]bar[object Object]quux', { foo: 1, bar: 2 }); // coercion order: 'this', limit, separator test({ toString: function() { print('toString() this'); return 'foo,bar,quux,baz'; }, valueOf: function() { print('valueOf() this'); return 'Foo,Bar,Quux,Baz'; } }, { toString: function() { print('toString() sep'); return ','; }, valueOf: function() { print('valueOf() sep'); return ';'; } }, { toString: function() { print('toString() limit'); return 3; }, valueOf: function() { print('valueOf() limit'); return 2; } }); } try { coercionTest(); } catch (e) { print(e); }