|
|
|
/*===
|
|
|
|
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('A<B>bold</B>and<CODE>coded</CODE>'.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);
|
|
|
|
}
|