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.
 
 
 
 
 
 

621 lines
13 KiB

/*
* Exec() lastIndex behavior is a bit peculiar, especially for non-global
* regexps (= regexps whose global flag is false).
*
* For regexps with global=true, the behavior is quite straightforward:
* read lastIndex, and write it after matching regardless of success or
* failure of matching.
*
* For regexps with global=false, it is a bit odd (E5 Section 15.10.6.2):
*
* 1. lastIndex is read from the object and coerced to integer (steps 4 and 5)
* 2. the index is reset to zero if global=false (step 7)
* 3. if the input is exhausted during matching, lastIndex is *written* to
* zero and a null is returned (step 9.a)
* 4. if a match occurs, lastIndex is *not* written (step 11)
*/
var r, t;
var a, b;
/*
* Non-global regexp, normal use.
*/
/*===
0
0
0
0
0
===*/
try {
r = /foo/;
print(r.lastIndex);
t = r.exec('bar'); /* no match -> reset to zero (but stays the same here, since it is already 0) */
print(r.lastIndex);
t = r.exec('foo'); /* match -> don't touch */
print(r.lastIndex);
t = r.exec('foofoo'); /* match -> don't touch */
print(r.lastIndex);
t = r.exec('foofoo'); /* match -> don't touch */
print(r.lastIndex);
} catch (e) {
print(e.name);
}
/*
* Non-global regexp, lastIndex should have no effect on matching
* and should only change if a match is not found.
*/
/*===
0
-1
foo
-1
0
9999
foo
9999
===*/
try {
r = /foo/;
print(r.lastIndex);
r.lastIndex = -1;
print(r.lastIndex);
t = r.exec('foo'); /* match -> don't touch */
print(t[0]);
print(r.lastIndex);
r = /foo/;
print(r.lastIndex);
r.lastIndex = 9999;
print(r.lastIndex);
t = r.exec('foo'); /* match -> don't touch */
print(t[0]);
print(r.lastIndex);
} catch (e) {
print(e.name);
}
/*
* Non-global regexp, lastIndex is not written to if a match is found,
* even if lastIndex needs coercion.
*
* Note: Smjs and Rhino seems to coerce lastIndex to number when
* it is written, so they fail this test. lastIndex is just a
* normal writable property, see E5 Section 15.10.7.5. It is
* coerced to integer *when used*.
*/
/*===
0 number
1 string
1 string
1 string
1 string
0 number
1 object
1 object
1 object
1 object
true
===*/
try {
r = /foo/;
print(r.lastIndex, typeof r.lastIndex);
r.lastIndex = "1";
print(r.lastIndex, typeof r.lastIndex);
r.exec('foo');
print(r.lastIndex, typeof r.lastIndex);
r.exec('foofoo');
print(r.lastIndex, typeof r.lastIndex);
r.exec('foofoo');
print(r.lastIndex, typeof r.lastIndex);
r = /foo/;
print(r.lastIndex, typeof r.lastIndex);
a = new String("1");
r.lastIndex = a;
print(r.lastIndex, typeof r.lastIndex);
r.exec('foo');
print(r.lastIndex, typeof r.lastIndex);
r.exec('foofoo');
print(r.lastIndex, typeof r.lastIndex);
r.exec('foofoo');
print(r.lastIndex, typeof r.lastIndex);
print(r.lastIndex === a); /* instance should still be the same */
} catch (e) {
print(e.name);
}
/*
* Non-global regexp, more lastIndex behavior when match is found
* and when it is not.
*/
/*===
0 number
0 number
0 number
1 string
1 string
0 number
-1 number
-1 number
0 number
1000000000000 number
1000000000000 number
0 number
1 string
0 number
===*/
/* match: zero is not written to */
try {
r = /foo/;
print(r.lastIndex, typeof r.lastIndex);
t = r.exec('foobar');
print(r.lastIndex, typeof r.lastIndex);
} catch (e) {
print(e.name);
}
/* match: string '1' is not written to, and is ignored */
try {
r = /foo/;
print(r.lastIndex, typeof r.lastIndex);
r.lastIndex = '1';
print(r.lastIndex, typeof r.lastIndex);
t = r.exec('foobar');
print(r.lastIndex, typeof r.lastIndex);
} catch (e) {
print(e.name);
}
/* match: number -1 is not written to, and is ignored */
try {
r = /foo/;
print(r.lastIndex, typeof r.lastIndex);
r.lastIndex = -1;
print(r.lastIndex, typeof r.lastIndex);
t = r.exec('foobar');
print(r.lastIndex, typeof r.lastIndex);
} catch (e) {
print(e.name);
}
/* match: a large number is not written to, and is ignored */
try {
r = /foo/;
print(r.lastIndex, typeof r.lastIndex);
r.lastIndex = 1000000000000; /* above 2^32 */
print(r.lastIndex, typeof r.lastIndex);
t = r.exec('foobar');
print(r.lastIndex, typeof r.lastIndex);
} catch (e) {
print(e.name);
}
/* no match: previous value is overwritten with zero (step 9.a.i),
* regardless of the global flag!
*
* Rhino and smjs fail this test.
*/
try {
r = /foo/;
print(r.lastIndex, typeof r.lastIndex);
r.lastIndex = '1';
print(r.lastIndex, typeof r.lastIndex);
t = r.exec('bar');
print(r.lastIndex, typeof r.lastIndex);
} catch (e) {
print(e.name);
}
/*
* Global regexp, normal use.
*/
/*===
0
foo
3
null
0
0
foo
3
Foo
6
FOO
9
null
0
===*/
try {
r = /foo/g;
print(r.lastIndex);
t = r.exec('foo');
print(t[0]);
print(r.lastIndex);
t = r.exec('foo');
print(t);
print(r.lastIndex);
r = /foo/gi;
print(r.lastIndex);
t = r.exec('fooFooFOO');
print(t[0]);
print(r.lastIndex);
t = r.exec('fooFooFOO');
print(t[0]);
print(r.lastIndex);
t = r.exec('fooFooFOO');
print(t[0]);
print(r.lastIndex);
t = r.exec('fooFooFOO');
print(t);
print(r.lastIndex);
} catch (e) {
print(e.name);
}
/*
* Global regexp, change input string while lastIndex changes,
* just a sanity check.
*/
/*===
0
foo
3
FOO
6
null
0
===*/
try {
r = /foo/gi;
print(r.lastIndex);
t = r.exec('foo'); /* match, leave at 3 */
print(t[0]);
print(r.lastIndex);
t = r.exec('barFOO');
print(t[0]);
print(r.lastIndex);
t = r.exec('foo'); /* starts at 6, out of bounds => null and 0 */
print(t);
print(r.lastIndex);
} catch (e) {
print(e.name);
}
/*
* Coercion and update of a non-number lastIndex. Out of bounds lastIndex
* causes exec() to return null, and reset lastIndex to zero.
*/
/*===
0
-1
null
0
999
null
0
object
2
Foo
3
6
number
===*/
try {
r = /foo/gi;
print(r.lastIndex);
r.lastIndex = -1;
print(r.lastIndex);
t = r.exec('foo');
print(t);
print(r.lastIndex);
r.lastIndex = 999;
print(r.lastIndex);
t = r.exec('foo');
print(t);
print(r.lastIndex);
a = {};
a.valueOf = function() { return 2; };
r.lastIndex = a;
print(typeof r.lastIndex);
print(r.lastIndex.valueOf());
t = r.exec('fooFoo'); /* start matching at 2, find match at 3 for 'Foo' */
print(t[0]);
print(t.index);
print(r.lastIndex);
print(typeof r.lastIndex);
} catch (e) {
print(e.name);
}
/*
* lastIndex is coerced with ToInteger() which -allows- values
* larger than 32 bits. For instance, 0x100000000 must NOT be
* confused with 0x00000000 as would happen if lastIndex were
* coerced with ToUint32() for instance.
*/
/*===
0 number
foo
4294967297 number
0 number
null
0 number
===*/
try {
/* Non-global regexp: lastIndex is ignored (matching starts from char
* index 0) -> match. lastIndex is not updated for non-global regexps
* when a match happens.
*/
r = /foo/;
print(r.lastIndex, typeof r.lastIndex);
r.lastIndex = 4294967297.0; /* 0x100000001 */
t = r.exec('foofoofoo');
print(t);
print(r.lastIndex, typeof r.lastIndex);
} catch (e) {
print(e.name);
}
try {
/* Global regexp: respects lastIndex -> no match. On a non-match
* lastIndex is zeroed.
*/
r = /foo/g;
print(r.lastIndex, typeof r.lastIndex);
r.lastIndex = 4294967297.0; /* 0x100000001 */
t = r.exec('foofoofoo'); /* no match, lastIndex is reset to zero */
print(t);
print(r.lastIndex, typeof r.lastIndex);
} catch (e) {
print(e.name);
}
/*
* lastIndex can be NAN or +/- Infinity. These have well defined
* behavior. If the global flag is not set, they are ignored (but
* overwritten if there is no match). If the global flag is set,
* NAN is coerced to +0.0 by ToInteger(), while +/- Infinity is used
* as is. It will cause match to fail at step 9.a, and lastIndex to
* be overwritten with zero.
*
* The sign of a zero is preserved by ToInteger(). It must be preserved
* for non-global regexps which -do match-.
*/
/*===
0 number
NaN number
foo
NaN number
null
0 number Infinity
0 number
NaN number
foo
3 number
null
0 number Infinity
===*/
try {
r = /foo/;
print(r.lastIndex, typeof r.lastIndex);
r.lastIndex = NaN;
print(r.lastIndex, typeof r.lastIndex);
t = r.exec('foo'); /* match -> don't update */
print(t[0]);
print(r.lastIndex, typeof r.lastIndex);
t = r.exec('bar'); /* no match -> overwrite with zero */
print(t);
print(r.lastIndex, typeof r.lastIndex, 1.0 / r.lastIndex); /* check sign too */
} catch (e) {
print(e.name);
}
try {
r = /foo/g;
print(r.lastIndex, typeof r.lastIndex);
r.lastIndex = NaN;
print(r.lastIndex, typeof r.lastIndex);
t = r.exec('foo'); /* match -> update */
print(t[0]);
print(r.lastIndex, typeof r.lastIndex);
t = r.exec('bar'); /* no match -> overwrite with zero */
print(t);
print(r.lastIndex, typeof r.lastIndex, 1.0 / r.lastIndex); /* check sign too */
} catch (e) {
print(e.name);
}
/*===
0 number
Infinity number
foo
Infinity number
null
0 number Infinity
0 number
Infinity number
null
0 number Infinity
===*/
try {
r = /foo/;
print(r.lastIndex, typeof r.lastIndex);
r.lastIndex = Infinity;
print(r.lastIndex, typeof r.lastIndex);
t = r.exec('foo'); /* match -> don't update */
print(t[0]);
print(r.lastIndex, typeof r.lastIndex);
t = r.exec('bar'); /* no match -> overwrite with zero */
print(t);
print(r.lastIndex, typeof r.lastIndex, 1.0 / r.lastIndex); /* check sign too */
} catch (e) {
print(e.name);
}
try {
r = /foo/g;
print(r.lastIndex, typeof r.lastIndex);
r.lastIndex = Infinity;
print(r.lastIndex, typeof r.lastIndex);
t = r.exec('foo'); /* no match (since we start from after end of string -> overwrite */
print(t);
print(r.lastIndex, typeof r.lastIndex, 1.0 / r.lastIndex); /* check sign too */
} catch (e) {
print(e.name);
}
/*===
0 number
-Infinity number
foo
-Infinity number
null
0 number Infinity
0 number
-Infinity number
null
0 number Infinity
===*/
try {
r = /foo/;
print(r.lastIndex, typeof r.lastIndex);
r.lastIndex = -Infinity;
print(r.lastIndex, typeof r.lastIndex);
t = r.exec('foo'); /* match -> don't update */
print(t[0]);
print(r.lastIndex, typeof r.lastIndex);
t = r.exec('bar'); /* no match -> overwrite with zero */
print(t);
print(r.lastIndex, typeof r.lastIndex, 1.0 / r.lastIndex); /* check sign too */
} catch (e) {
print(e.name);
}
try {
r = /foo/g;
print(r.lastIndex, typeof r.lastIndex);
r.lastIndex = -Infinity;
print(r.lastIndex, typeof r.lastIndex);
t = r.exec('foo'); /* no match (since we start from after end of string -> overwrite */
print(t);
print(r.lastIndex, typeof r.lastIndex, 1.0 / r.lastIndex); /* check sign too */
} catch (e) {
print(e.name);
}
/*===
0 number Infinity
0 number -Infinity
foo
0 number -Infinity
null
0 number Infinity
0 number Infinity
0 number -Infinity
foo
3 number
null
0 number Infinity
===*/
try {
r = /foo/;
print(r.lastIndex, typeof r.lastIndex, 1.0 / r.lastIndex);
r.lastIndex = -0.0;
print(r.lastIndex, typeof r.lastIndex, 1.0 / r.lastIndex);
t = r.exec('foo'); /* match -> don't update */
print(t[0]);
print(r.lastIndex, typeof r.lastIndex, 1.0 / r.lastIndex);
t = r.exec('bar'); /* no match -> overwrite with zero (positive) */
print(t);
print(r.lastIndex, typeof r.lastIndex, 1.0 / r.lastIndex); /* check sign too */
} catch (e) {
print(e.name);
}
try {
r = /foo/g;
print(r.lastIndex, typeof r.lastIndex, 1.0 / r.lastIndex);
r.lastIndex = -0.0;
print(r.lastIndex, typeof r.lastIndex, 1.0 / r.lastIndex);
t = r.exec('foo'); /* match -> update */
print(t[0]);
print(r.lastIndex, typeof r.lastIndex);
t = r.exec('bar'); /* no match -> overwrite with zero (positive) */
print(t);
print(r.lastIndex, typeof r.lastIndex, 1.0 / r.lastIndex); /* check sign too */
} catch (e) {
print(e.name);
}
/*
* The lastIndex value is floored.
*/
/*===
0 number
0.9 number
foo
0.9 number
0 number
0.9 number
foo
3 number
===*/
try {
r = /foo/;
print(r.lastIndex, typeof r.lastIndex);
r.lastIndex = 0.9;
print(r.lastIndex, typeof r.lastIndex);
t = r.exec('foo');
print(t[0]);
print(r.lastIndex, typeof r.lastIndex);
} catch (e) {
print(e.name);
}
try {
r = /foo/g;
print(r.lastIndex, typeof r.lastIndex);
r.lastIndex = 0.9;
print(r.lastIndex, typeof r.lastIndex);
t = r.exec('foo');
print(t[0]);
print(r.lastIndex, typeof r.lastIndex);
} catch (e) {
print(e.name);
}