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.
 
 
 
 
 
 

343 lines
11 KiB

/*===
basic
object 1 foo 0 foobarfoo
object 1 foo 0 foobarfoo
object true
object 1 foo 0 foobarfoo
object true
object 2 foo foo
object true
object 1 0 foobarfoo
object 1 0 foobarfoo
object 10 0 0 0 0 0 0 0 0 0 0
object 1 49 64974 1
object 1 49 64974 1
object 7 0 0 0 0 0 0 0
===*/
print('basic');
function basicTest() {
var s = new String('foobarfoo');
var m;
// 'foo' coerces to /foo/, non-global regexp; result is the same as if
// /foo/.exec() had been called
m = s.match('foo');
print(typeof m, m.length, m[0], m.index, m.input);
// a string argument creates a RegExp, and plain string match cannot be done
m = s.match('f..');
print(typeof m, m.length, m[0], m.index, m.input);
// no match
m = s.match('quux');
print(typeof m, m === null);
// explicit non-global regexps, for comparison
m = s.match(/foo/);
print(typeof m, m.length, m[0], m.index, m.input);
m = s.match(/quux/);
print(typeof m, m === null);
// global regexp: return list of matches
m = s.match(/foo/g);
print(typeof m, m.length, m[0], m[1]);
// global regexp, no match; null instead of empty array
m = s.match(/quux/g);
print(typeof m, m === null);
// empty string or empty regexp match (internally the same, because a
// string is used to create a regexp matching an empty string) -> match
// once at the beginning
m = s.match('');
print(typeof m, m.length, m[0], m.index, m.input);
m = s.match(/(?:)/);
print(typeof m, m.length, m[0], m.index, m.input);
// global regexp matching an empty string -> match once between each
// character, once before the first char, and once after the last char
m = s.match(/(?:)/g);
print(typeof m, m.length, m[0].length, m[1].length, m[2].length, m[3].length,
m[4].length, m[5].length, m[6].length, m[7].length, m[8].length, m[9].length);
// Some non-BMP tests; important because implementation uses both char
// and byte offsets. These tests are similar to the ones above.
s = new String('\u1234\u0031\ufdce\u1234\u0031\ufdce');
m = s.match('\u0031\ufdce');
print(typeof m, m.length, m[0].charCodeAt(0), m[0].charCodeAt(1), m.index);
m = s.match('\u0031.');
print(typeof m, m.length, m[0].charCodeAt(0), m[0].charCodeAt(1), m.index);
m = s.match(/(?:)/g);
print(typeof m, m.length, m[0].length, m[1].length, m[2].length, m[3].length,
m[4].length, m[5].length, m[6].length);
}
try {
basicTest();
} catch (e) {
print(e);
}
/*===
regexp
before [object RegExp], source=zoo, global=false, ignoreCase=false, multiline=false, lastIndex=1 (string)
[object Null]
after [object RegExp], source=zoo, global=false, ignoreCase=false, multiline=false, lastIndex=0 (number)
valueOf() lastIndex
before [object RegExp], source=foo, global=false, ignoreCase=false, multiline=false, lastIndex=1 (object)
valueOf() lastIndex
[object Array] 1 foo input=foobarfoobar index=0
valueOf() lastIndex
after [object RegExp], source=foo, global=false, ignoreCase=false, multiline=false, lastIndex=1 (object)
before [object RegExp], source=zoo, global=true, ignoreCase=false, multiline=false, lastIndex=1 (string)
[object Null]
after [object RegExp], source=zoo, global=true, ignoreCase=false, multiline=false, lastIndex=0 (number)
before [object RegExp], source=foo, global=true, ignoreCase=false, multiline=false, lastIndex=0 (number)
[object Array] 1 foo
after [object RegExp], source=foo, global=true, ignoreCase=false, multiline=false, lastIndex=0 (number)
before [object RegExp], source=foo, global=true, ignoreCase=false, multiline=false, lastIndex=0 (number)
[object Array] 2 foo foo
after [object RegExp], source=foo, global=true, ignoreCase=false, multiline=false, lastIndex=0 (number)
before [object RegExp], source=foo, global=true, ignoreCase=true, multiline=false, lastIndex=0 (number)
[object Array] 2 Foo fOO
after [object RegExp], source=foo, global=true, ignoreCase=true, multiline=false, lastIndex=0 (number)
before [object RegExp], source=foo, global=true, ignoreCase=true, multiline=false, lastIndex=1 (string)
[object Array] 3 Foo fOO FOO
after [object RegExp], source=foo, global=true, ignoreCase=true, multiline=false, lastIndex=0 (number)
===*/
/* For non-global RegExp instances, match() logic is the same as normal
* RegExp exec(): E5.1 Section 15.5.4.10, step 7.a calls exec() directly,
* returning the exec() return value from match().
*
* Global RegExp instances get an entirely different handling in match(),
* though exec() is used as a component. In essence, match() finds all
* matches, and creates a result array containing the matching strings
* (match[0]) for each match. 'lastIndex' of the RegExp is updated in
* the process. If no matches are found, null is returned instead of an
* empty array. See E5.1 Section 15.5.4.10, step 8.
*
* match() algorithm initializes lastIndex to zero (without ever reading or
* coercing it). The exec() calls in match() step 8.f.i will update
* lastIndex for both matching and non-matching case. For the non-matching
* case, lastIndex is written to zero (exec() step 9.a). For the matching
* case, lastIndex is written to end of match (exec() step 11.a). The
* match() algorithm has a special case for an empty match, which will
* advance the scan index and also update lastIndex; step 8.f.iii.2. Note
* that since the global case match() loop only exits by a non-matching
* exec() call, lastIndex should *always* be left to zero: a non-matching
* exec() always leaves a zero in lastIndex (exec() step 9.a).
*
* In summary:
*
* 1) non-global match() essentially wraps a single regexp exec() call;
* lastIndex is written to zero if regexp does not match, and is not
* not touched if it matches. It is always ToInteger() coerced.
* 2) global match() resets lastIndex to zero (step 8.a) before it is
* read or coerced, so no lastIndex side effects should occur.
* 3) global match() always leaves lastIndex as zero in the end.
*
* Note that 'lastIndex' could be an accessor property in principle and
* this would expose the intermediate values used during match(). However,
* 'lastIndex' is always initialized as a data property and is always
* non-configurable so it cannot be changed into an accessor.
*
* Note that the expected match logic described above does not seem to match
* V8 and Rhino (e.g. V8 seems to never update lastIndex). Perhaps the
* reasoning above is wrong?
*/
print('regexp');
function getRegExpDump(re) {
return Object.prototype.toString.call(re) +
', source=' + re.source +
', global=' + re.global +
', ignoreCase=' + re.ignoreCase +
', multiline=' + re.multiline +
', lastIndex=' + re.lastIndex + ' (' + typeof re.lastIndex + ')';
}
function regExpTest() {
var re;
function test(this_val, regexp) {
var t;
var tmp = [];
print('before', getRegExpDump(regexp));
try {
t = String.prototype.match.call(this_val, regexp);
tmp.push(Object.prototype.toString.call(t));
if (t !== null) {
tmp.push(t.length);
for (i = 0; i < t.length; i++) {
tmp.push(t[i]);
}
if (typeof t.input !== 'undefined') {
tmp.push('input=' + t.input);
}
if (typeof t.index !== 'undefined') {
tmp.push('index=' + t.index);
}
}
print(tmp.join(' '));
} catch (e) {
print(e.name, e);
}
print('after', getRegExpDump(regexp));
}
// Non-global match, match not found -> null
//
// RegExp.prototype.exec() will be called; it will read 'lastIndex'
// and coerce it with ToInteger() but then ignore the result when
// global flag is false (E5.1 Section 15.10.6.2, steps 4, 5, 7).
// If the match fails, lastIndex will be written to zero (step 9.a).
re = /zoo/;
re.lastIndex = '1';
test('foobarfoobar', re);
// Non-global match, match found.
//
// In the matching case for a non-global regexp, exec() *won't* write
// lastIndex at all. Also check that lastIndex ToInteger() coercion
// occurs even when it is unused.
re = /foo/;
re.lastIndex = {
toString: function() { print('toString() lastIndex'); return '123'; },
valueOf: function() { print('valueOf() lastIndex'); return 1; }
};
test('foobarfoobar', re);
// Global match, match not found -> null.
//
// lastIndex should be left as zero (always for global matches).
re = /zoo/g;
re.lastIndex = '1'; // lastIndex start position is ignored
test('foobar', re);
// Global match, single match found -> array with one element.
test('foobar', /foo/g);
// Other global match cases
test('foobarfoobar', /foo/g);
test('FoobarfOObar', /foo/gi);
re = /foo/gi;
re.lastIndex = '1'; // lastIndex start position is ignored
test('FoobarfOObarFOObar', re);
// FIXME: RegExp.prototype.exec() replaced; still calls original function
}
try {
regExpTest();
} catch (e) {
print(e);
}
/*===
coercion
TypeError
TypeError
TypeError
object "true" 1 "rue"
object "false" 2 "lse"
object "123" 1 "23"
object "foobar" 3 "bar"
object "1,2,3" 2 "2,3"
object "[object Object]" 8 "Object"
object "foo" 0 ""
object "foo" 0 ""
object "foonullfoo" 3 "null"
object "footruefoo" 3 "true"
object "foofalsefoo" 3 "false"
object "foo123foo" 3 "123"
object "foobarfoo" 3 "bar"
object "foo1,2foo" 3 "1,2"
object "xxxOyyy" 3 "O"
TypeError
===*/
print('coercion');
function coercionTest() {
function test(this_val, regexp_val, arg_count) {
var t;
try {
if (arg_count === 0) {
t = String.prototype.match.call();
} else if (arg_count === 1) {
t = String.prototype.match.call(this_val);
} else {
t = String.prototype.match.call(this_val, regexp_val);
}
// result is a RegExp match result
print(typeof t, '"' + t.input + '"', t.index, '"' + t[0] + '"');
} catch (e) {
print(e.name);
}
}
// this coercion
test(undefined, undefined, 0);
test(undefined, 'foo');
test(null, 'foo');
test(true, 'rue');
test(false, 'lse');
test(123, '23');
test('foobar', 'bar');
test([1,2,3], '2,3');
test({ foo: 1, bar: 2 }, 'Object');
// regexp argument: if non-regexp, it is given to the RegExp constructor,
// which coerces using ToString() except for undefined, which is explicitly
// coerced to an empty string.
test('foo', undefined, 1); // undefined -> new RegExp('') -> match at index 0
test('foo', undefined);
test('foonullfoo', null);
test('footruefoo', true);
test('foofalsefoo', false);
test('foo123foo', 123);
test('foobarfoo', 'b.r'); // string is always in regexp syntax
test('foo1,2foo', [1,2]);
// object is interesting, it ToString() coerces to "[object Object]" which
// is interpreted as a character class.
test('xxxOyyy', { foo: 1, bar: 2 });
test('xxxAyyy', { foo: 1, bar: 2 }); // no match
}
try {
coercionTest();
} catch (e) {
print(e);
}