зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1120169
- Implement RegExp.prototype.{global, ignoreCase, multiline, source, sticky, unicode}. r=till
This commit is contained in:
Родитель
8fe2b690ec
Коммит
df97a41a15
|
@ -233,29 +233,20 @@ CompileRegExpObject(JSContext *cx, RegExpObjectBuilder &builder, CallArgs args)
|
|||
}
|
||||
|
||||
/*
|
||||
* Only extract the 'flags' out of sourceObj; do not reuse the
|
||||
* RegExpShared since it may be from a different compartment.
|
||||
* Extract the 'source' and the 'flags' out of sourceObj; do not reuse
|
||||
* the RegExpShared since it may be from a different compartment.
|
||||
*/
|
||||
RootedAtom sourceAtom(cx);
|
||||
RegExpFlag flags;
|
||||
{
|
||||
RegExpGuard g(cx);
|
||||
if (!RegExpToShared(cx, sourceObj, &g))
|
||||
return false;
|
||||
|
||||
sourceAtom = g->getSource();
|
||||
flags = g->getFlags();
|
||||
}
|
||||
|
||||
/*
|
||||
* 'toSource' is a permanent read-only property, so this is equivalent
|
||||
* to executing RegExpObject::getSource on the unwrapped object.
|
||||
*/
|
||||
RootedValue v(cx);
|
||||
if (!GetProperty(cx, sourceObj, sourceObj, cx->names().source, &v))
|
||||
return false;
|
||||
|
||||
// For proxies like CPOWs, we can't assume the result of a property get
|
||||
// for 'source' is atomized.
|
||||
Rooted<JSAtom*> sourceAtom(cx, AtomizeString(cx, v.toString()));
|
||||
RegExpObject *reobj = builder.build(sourceAtom, flags);
|
||||
if (!reobj)
|
||||
return false;
|
||||
|
@ -431,8 +422,138 @@ regexp_flags(JSContext *cx, unsigned argc, JS::Value *vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
/* ES6 draft rev32 21.2.5.4. */
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
regexp_global_impl(JSContext *cx, CallArgs args)
|
||||
{
|
||||
MOZ_ASSERT(IsRegExp(args.thisv()));
|
||||
Rooted<RegExpObject*> reObj(cx, &args.thisv().toObject().as<RegExpObject>());
|
||||
|
||||
/* Steps 4-6. */
|
||||
args.rval().setBoolean(reObj->global());
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
regexp_global(JSContext *cx, unsigned argc, JS::Value *vp)
|
||||
{
|
||||
/* Steps 1-3. */
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return CallNonGenericMethod<IsRegExp, regexp_global_impl>(cx, args);
|
||||
}
|
||||
|
||||
/* ES6 draft rev32 21.2.5.5. */
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
regexp_ignoreCase_impl(JSContext *cx, CallArgs args)
|
||||
{
|
||||
MOZ_ASSERT(IsRegExp(args.thisv()));
|
||||
Rooted<RegExpObject*> reObj(cx, &args.thisv().toObject().as<RegExpObject>());
|
||||
|
||||
/* Steps 4-6. */
|
||||
args.rval().setBoolean(reObj->ignoreCase());
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
regexp_ignoreCase(JSContext *cx, unsigned argc, JS::Value *vp)
|
||||
{
|
||||
/* Steps 1-3. */
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return CallNonGenericMethod<IsRegExp, regexp_ignoreCase_impl>(cx, args);
|
||||
}
|
||||
|
||||
/* ES6 draft rev32 21.2.5.7. */
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
regexp_multiline_impl(JSContext *cx, CallArgs args)
|
||||
{
|
||||
MOZ_ASSERT(IsRegExp(args.thisv()));
|
||||
Rooted<RegExpObject*> reObj(cx, &args.thisv().toObject().as<RegExpObject>());
|
||||
|
||||
/* Steps 4-6. */
|
||||
args.rval().setBoolean(reObj->multiline());
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
regexp_multiline(JSContext *cx, unsigned argc, JS::Value *vp)
|
||||
{
|
||||
/* Steps 1-3. */
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return CallNonGenericMethod<IsRegExp, regexp_multiline_impl>(cx, args);
|
||||
}
|
||||
|
||||
/* ES6 draft rev32 21.2.5.10. */
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
regexp_source_impl(JSContext *cx, CallArgs args)
|
||||
{
|
||||
MOZ_ASSERT(IsRegExp(args.thisv()));
|
||||
Rooted<RegExpObject*> reObj(cx, &args.thisv().toObject().as<RegExpObject>());
|
||||
|
||||
/* Step 5. */
|
||||
RootedAtom src(cx, reObj->getSource());
|
||||
if (!src)
|
||||
return false;
|
||||
|
||||
args.rval().setString(src);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
regexp_source(JSContext *cx, unsigned argc, JS::Value *vp)
|
||||
{
|
||||
/* Steps 1-4. */
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return CallNonGenericMethod<IsRegExp, regexp_source_impl>(cx, args);
|
||||
}
|
||||
|
||||
/* ES6 draft rev32 21.2.5.12. */
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
regexp_sticky_impl(JSContext *cx, CallArgs args)
|
||||
{
|
||||
MOZ_ASSERT(IsRegExp(args.thisv()));
|
||||
Rooted<RegExpObject*> reObj(cx, &args.thisv().toObject().as<RegExpObject>());
|
||||
|
||||
/* Steps 4-6. */
|
||||
args.rval().setBoolean(reObj->sticky());
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
regexp_sticky(JSContext *cx, unsigned argc, JS::Value *vp)
|
||||
{
|
||||
/* Steps 1-3. */
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return CallNonGenericMethod<IsRegExp, regexp_sticky_impl>(cx, args);
|
||||
}
|
||||
|
||||
/* ES6 draft rev32 21.2.5.15. */
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
regexp_unicode_impl(JSContext *cx, CallArgs args)
|
||||
{
|
||||
MOZ_ASSERT(IsRegExp(args.thisv()));
|
||||
|
||||
/* Steps 4-6. */
|
||||
/* FIXME: When the /u flags is supported, return correct value. */
|
||||
args.rval().setBoolean(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
regexp_unicode(JSContext *cx, unsigned argc, JS::Value *vp)
|
||||
{
|
||||
/* Steps 1-3. */
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return CallNonGenericMethod<IsRegExp, regexp_unicode_impl>(cx, args);
|
||||
}
|
||||
|
||||
static const JSPropertySpec regexp_properties[] = {
|
||||
JS_PSG("flags", regexp_flags, 0),
|
||||
JS_PSG("global", regexp_global, 0),
|
||||
JS_PSG("ignoreCase", regexp_ignoreCase, 0),
|
||||
JS_PSG("multiline", regexp_multiline, 0),
|
||||
JS_PSG("source", regexp_source, 0),
|
||||
JS_PSG("sticky", regexp_sticky, 0),
|
||||
JS_PSG("unicode", regexp_unicode, 0),
|
||||
JS_PS_END
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
load(libdir + "asserts.js");
|
||||
var g = newGlobal();
|
||||
var a = g.RegExp("x");
|
||||
assertThrowsInstanceOf(function () { Object.defineProperty(a, "ignoreCase", {value: undefined}); },
|
||||
g.TypeError);
|
||||
Object.defineProperty(a, "ignoreCase", {value: undefined});
|
||||
a.toString();
|
||||
|
|
|
@ -267,50 +267,6 @@ expectDescriptor(pd, expected);
|
|||
|
||||
o = /foo/im;
|
||||
|
||||
pd = Object.getOwnPropertyDescriptor(o, "source");
|
||||
expected =
|
||||
{
|
||||
value: "foo",
|
||||
writable: false,
|
||||
enumerable: false,
|
||||
configurable: false
|
||||
};
|
||||
|
||||
expectDescriptor(pd, expected);
|
||||
|
||||
pd = Object.getOwnPropertyDescriptor(o, "global");
|
||||
expected =
|
||||
{
|
||||
value: false,
|
||||
writable: false,
|
||||
enumerable: false,
|
||||
configurable: false
|
||||
};
|
||||
|
||||
expectDescriptor(pd, expected);
|
||||
|
||||
pd = Object.getOwnPropertyDescriptor(o, "ignoreCase");
|
||||
expected =
|
||||
{
|
||||
value: true,
|
||||
writable: false,
|
||||
enumerable: false,
|
||||
configurable: false
|
||||
};
|
||||
|
||||
expectDescriptor(pd, expected);
|
||||
|
||||
pd = Object.getOwnPropertyDescriptor(o, "multiline");
|
||||
expected =
|
||||
{
|
||||
value: true,
|
||||
writable: false,
|
||||
enumerable: false,
|
||||
configurable: false
|
||||
};
|
||||
|
||||
expectDescriptor(pd, expected);
|
||||
|
||||
pd = Object.getOwnPropertyDescriptor(o, "lastIndex");
|
||||
expected =
|
||||
{
|
||||
|
|
|
@ -16,7 +16,7 @@ print(BUGNUMBER + ": " + summary);
|
|||
**************/
|
||||
|
||||
var actual = Object.getOwnPropertyNames(/a/);
|
||||
var expected = ["lastIndex", "source", "global", "ignoreCase", "multiline"];
|
||||
var expected = ["lastIndex"];
|
||||
|
||||
for (var i = 0; i < expected.length; i++)
|
||||
{
|
||||
|
|
|
@ -47,33 +47,18 @@ var choices = [{ msg: "RegExp.prototype",
|
|||
{ msg: "/(?:)/",
|
||||
get: Function("return /(?:)/;") }];
|
||||
|
||||
function checkRegExp(r, msg, lastIndex, global, ignoreCase, multiline)
|
||||
function checkRegExp(r, msg, lastIndex)
|
||||
{
|
||||
var expect;
|
||||
|
||||
expect = { value: lastIndex, enumerable: false, configurable: false, writable: true };
|
||||
checkDataProperty(r, "lastIndex", expect, msg);
|
||||
|
||||
// check source specially: its value is under-defined in the spec
|
||||
var d = Object.getOwnPropertyDescriptor(r, "source");
|
||||
assertEq(d.writable, false, "bad writable: " + msg);
|
||||
assertEq(d.enumerable, false, "bad enumerable: " + msg);
|
||||
assertEq(d.configurable, false, "bad configurable: " + msg);
|
||||
|
||||
expect = { value: global, enumerable: false, configurable: false, writable: false };
|
||||
checkDataProperty(r, "global", expect, msg);
|
||||
|
||||
expect = { value: ignoreCase, enumerable: false, configurable: false, writable: false };
|
||||
checkDataProperty(r, "ignoreCase", expect, msg);
|
||||
|
||||
expect = { value: multiline, enumerable: false, configurable: false, writable: false };
|
||||
checkDataProperty(r, "multiline", expect, msg);
|
||||
}
|
||||
|
||||
checkRegExp(RegExp.prototype, "RegExp.prototype", 0, false, false, false);
|
||||
checkRegExp(new RegExp(), "new RegExp()", 0, false, false, false);
|
||||
checkRegExp(/(?:)/, "/(?:)/", 0, false, false, false);
|
||||
checkRegExp(Function("return /(?:)/;")(), 'Function("return /(?:)/;")()', 0, false, false, false);
|
||||
checkRegExp(RegExp.prototype, "RegExp.prototype", 0);
|
||||
checkRegExp(new RegExp(), "new RegExp()", 0);
|
||||
checkRegExp(/(?:)/, "/(?:)/", 0);
|
||||
checkRegExp(Function("return /(?:)/;")(), 'Function("return /(?:)/;")()', 0);
|
||||
|
||||
for (var i = 0; i < choices.length; i++)
|
||||
{
|
||||
|
@ -81,67 +66,60 @@ for (var i = 0; i < choices.length; i++)
|
|||
var msg = choice.msg;
|
||||
var r = choice.get();
|
||||
|
||||
checkRegExp(r, msg, 0, false, false, false);
|
||||
checkRegExp(r, msg, 0);
|
||||
}
|
||||
|
||||
// Now test less generic regular expressions
|
||||
|
||||
checkRegExp(/a/gim, "/a/gim", 0, true, true, true);
|
||||
checkRegExp(/a/gim, "/a/gim", 0);
|
||||
|
||||
var r;
|
||||
|
||||
do
|
||||
{
|
||||
r = /abcd/mg;
|
||||
checkRegExp(r, "/abcd/mg initially", 0, true, false, true);
|
||||
checkRegExp(r, "/abcd/mg initially", 0);
|
||||
r.exec("abcdefg");
|
||||
checkRegExp(r, "/abcd/mg step 1", 4, true, false, true);
|
||||
checkRegExp(r, "/abcd/mg step 1", 4);
|
||||
r.exec("abcdabcd");
|
||||
checkRegExp(r, "/abcd/mg step 2", 8, true, false, true);
|
||||
checkRegExp(r, "/abcd/mg step 2", 8);
|
||||
r.exec("abcdabcd");
|
||||
checkRegExp(r, "/abcd/mg end", 0, true, false, true);
|
||||
checkRegExp(r, "/abcd/mg end", 0);
|
||||
|
||||
r = /cde/ig;
|
||||
checkRegExp(r, "/cde/ig initially", 0, true, true, false);
|
||||
checkRegExp(r, "/cde/ig initially", 0);
|
||||
var obj = r.lastIndex = { valueOf: function() { return 2; } };
|
||||
checkRegExp(r, "/cde/ig after lastIndex", obj, true, true, false);
|
||||
checkRegExp(r, "/cde/ig after lastIndex", obj);
|
||||
r.exec("aaacdef");
|
||||
checkRegExp(r, "/cde/ig after exec", 6, true, true, false);
|
||||
checkRegExp(r, "/cde/ig after exec", 6);
|
||||
Object.defineProperty(r, "lastIndex", { value: 3 });
|
||||
checkRegExp(r, "/cde/ig after define 3", 3, true, true, false);
|
||||
checkRegExp(r, "/cde/ig after define 3", 3);
|
||||
Object.defineProperty(r, "lastIndex", { value: obj });
|
||||
checkRegExp(r, "/cde/ig after lastIndex", obj, true, true, false);
|
||||
checkRegExp(r, "/cde/ig after lastIndex", obj);
|
||||
|
||||
|
||||
// Tricky bits of testing: make sure that redefining lastIndex doesn't change
|
||||
// the slot where the lastIndex property is initially stored, even if
|
||||
// the redefinition also changes writability.
|
||||
r = /a/g;
|
||||
checkRegExp(r, "/a/g initially", 0, true, false, false);
|
||||
checkRegExp(r, "/a/g initially", 0);
|
||||
Object.defineProperty(r, "lastIndex", { value: 2 });
|
||||
r.exec("aabbbba");
|
||||
checkRegExp(r, "/a/g after first exec", 7, true, false, false);
|
||||
checkRegExp(r, "/a/g after first exec", 7);
|
||||
assertEq(r.lastIndex, 7);
|
||||
r.lastIndex = 2;
|
||||
checkRegExp(r, "/a/g after assign", 2, true, false, false);
|
||||
checkRegExp(r, "/a/g after assign", 2);
|
||||
r.exec("aabbbba");
|
||||
assertEq(r.lastIndex, 7); // check in reverse order
|
||||
checkRegExp(r, "/a/g after second exec", 7, true, false, false);
|
||||
checkRegExp(r, "/a/g after second exec", 7);
|
||||
|
||||
r = /c/g;
|
||||
r.lastIndex = 2;
|
||||
checkRegExp(r, "/c/g initially", 2, true, false, false);
|
||||
checkRegExp(r, "/c/g initially", 2);
|
||||
Object.defineProperty(r, "lastIndex", { writable: false });
|
||||
assertEq(Object.getOwnPropertyDescriptor(r, "lastIndex").writable, false);
|
||||
try { r.exec("aabbbba"); } catch (e) { /* swallow error if thrown */ }
|
||||
assertEq(Object.getOwnPropertyDescriptor(r, "lastIndex").writable, false);
|
||||
assertEq(Object.getOwnPropertyDescriptor(r, "source").writable, false);
|
||||
assertEq(Object.getOwnPropertyDescriptor(r, "global").value, true);
|
||||
assertEq(Object.getOwnPropertyDescriptor(r, "global").writable, false);
|
||||
assertEq(Object.getOwnPropertyDescriptor(r, "ignoreCase").value, false);
|
||||
assertEq(Object.getOwnPropertyDescriptor(r, "ignoreCase").writable, false);
|
||||
assertEq(Object.getOwnPropertyDescriptor(r, "multiline").value, false);
|
||||
assertEq(Object.getOwnPropertyDescriptor(r, "multiline").writable, false);
|
||||
}
|
||||
while (Math.random() > 17); // fake loop to discourage RegExp object caching
|
||||
|
||||
|
|
|
@ -5,34 +5,6 @@
|
|||
* http://creativecommons.org/licenses/publicdomain/
|
||||
*/
|
||||
|
||||
assertEq(testLenientAndStrict('var r = /foo/; r.source = "bar"; r.source',
|
||||
returns("foo"), raisesException(TypeError)),
|
||||
true);
|
||||
assertEq(testLenientAndStrict('var r = /foo/; delete r.source',
|
||||
returns(false), raisesException(TypeError)),
|
||||
true);
|
||||
|
||||
assertEq(testLenientAndStrict('var r = /foo/; r.global = true; r.global',
|
||||
returns(false), raisesException(TypeError)),
|
||||
true);
|
||||
assertEq(testLenientAndStrict('var r = /foo/; delete r.global',
|
||||
returns(false), raisesException(TypeError)),
|
||||
true);
|
||||
|
||||
assertEq(testLenientAndStrict('var r = /foo/; r.ignoreCase = true; r.ignoreCase',
|
||||
returns(false), raisesException(TypeError)),
|
||||
true);
|
||||
assertEq(testLenientAndStrict('var r = /foo/; delete r.ignoreCase',
|
||||
returns(false), raisesException(TypeError)),
|
||||
true);
|
||||
|
||||
assertEq(testLenientAndStrict('var r = /foo/; r.multiline = true; r.multiline',
|
||||
returns(false), raisesException(TypeError)),
|
||||
true);
|
||||
assertEq(testLenientAndStrict('var r = /foo/; delete r.multiline',
|
||||
returns(false), raisesException(TypeError)),
|
||||
true);
|
||||
|
||||
assertEq(testLenientAndStrict('var r = /foo/; r.lastIndex = 42; r.lastIndex',
|
||||
returns(42), returns(42)),
|
||||
true);
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
var BUGNUMBER = 1130860;
|
||||
var summary = "RegExp constructor shouldn't invoke source/flags getters on argument RegExp instance.";
|
||||
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// same-compartment
|
||||
var a = /foo/;
|
||||
var flagsCalled = false;
|
||||
var sourceCalled = false;
|
||||
Object.defineProperty(a, "source", { get: () => {
|
||||
sourceCalled = true;
|
||||
return "bar";
|
||||
}});
|
||||
Object.defineProperty(a, "flags", { get: () => {
|
||||
flagsCalled = true;
|
||||
return "i";
|
||||
}});
|
||||
|
||||
assertEq(a.source, "bar");
|
||||
assertEq(a.flags, "i");
|
||||
assertEq(sourceCalled, true);
|
||||
assertEq(flagsCalled, true);
|
||||
|
||||
sourceCalled = false;
|
||||
flagsCalled = false;
|
||||
assertEq(new RegExp(a).source, "foo");
|
||||
assertEq(sourceCalled, false);
|
||||
assertEq(flagsCalled, false);
|
||||
|
||||
// cross-compartment
|
||||
var g = newGlobal();
|
||||
var b = g.eval(`
|
||||
var b = /foo2/;
|
||||
var flagsCalled = false;
|
||||
var sourceCalled = false;
|
||||
Object.defineProperty(b, "source", { get: () => {
|
||||
sourceCalled = true;
|
||||
return "bar2";
|
||||
}});
|
||||
Object.defineProperty(b, "flags", { get: () => {
|
||||
flagsCalled = true;
|
||||
return "i";
|
||||
}});
|
||||
b;
|
||||
`);
|
||||
|
||||
assertEq(b.source, "bar2");
|
||||
assertEq(b.flags, "i");
|
||||
assertEq(g.eval("sourceCalled;"), true);
|
||||
assertEq(g.eval("flagsCalled;"), true);
|
||||
|
||||
g.eval(`
|
||||
sourceCalled = false;
|
||||
flagsCalled = false;
|
||||
`);
|
||||
assertEq(new RegExp(b).source, "foo2");
|
||||
assertEq(g.eval("sourceCalled;"), false);
|
||||
assertEq(g.eval("flagsCalled;"), false);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
|
@ -0,0 +1,25 @@
|
|||
var BUGNUMBER = 1120169;
|
||||
var summary = "Implement RegExp.prototype.{global, ignoreCase, multiline, sticky, unicode} - property descriptor";
|
||||
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
var getters = [
|
||||
"flags",
|
||||
"global",
|
||||
"ignoreCase",
|
||||
"multiline",
|
||||
"source",
|
||||
"sticky",
|
||||
"unicode",
|
||||
];
|
||||
|
||||
for (var name of getters) {
|
||||
var desc = Object.getOwnPropertyDescriptor(RegExp.prototype, name);
|
||||
assertEq(desc.configurable, true);
|
||||
assertEq(desc.enumerable, false);
|
||||
assertEq("writable" in desc, false);
|
||||
assertEq("get" in desc, true);
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
|
@ -0,0 +1,52 @@
|
|||
var BUGNUMBER = 1120169;
|
||||
var summary = "Implement RegExp.prototype.{global, ignoreCase, multiline, sticky, unicode}";
|
||||
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
var props = [
|
||||
"global",
|
||||
"ignoreCase",
|
||||
"multiline",
|
||||
"sticky",
|
||||
"unicode",
|
||||
];
|
||||
|
||||
testThrows(RegExp.prototype);
|
||||
test(/foo/iymg, [true, true, true, true, false]);
|
||||
test(RegExp(""), [false, false, false, false, false]);
|
||||
test(RegExp("", "mygi"), [true, true, true, true, false]);
|
||||
// When the /u flag is supported, remove the following line and uncomment the
|
||||
// next line.
|
||||
assertThrowsInstanceOf(() => RegExp("", "mygui").flags, SyntaxError);
|
||||
// test(RegExp("", "mygiu"), [true, true, true, true, true]);
|
||||
|
||||
testThrowsGeneric();
|
||||
testThrowsGeneric(1);
|
||||
testThrowsGeneric("");
|
||||
testThrowsGeneric({});
|
||||
testThrowsGeneric(new Proxy({}, {get(){ return true; }}));
|
||||
|
||||
function test(obj, expects) {
|
||||
for (var i = 0; i < props.length; i++) {
|
||||
assertEq(obj[props[i]], expects[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function testThrows(obj) {
|
||||
for (var prop of props) {
|
||||
assertThrowsInstanceOf(obj[prop], TypeError);
|
||||
}
|
||||
}
|
||||
|
||||
function testThrowsGeneric(obj) {
|
||||
for (var prop of props) {
|
||||
assertThrowsInstanceOf(() => genericGet(obj, prop), TypeError);
|
||||
}
|
||||
}
|
||||
|
||||
function genericGet(obj, prop) {
|
||||
return Object.getOwnPropertyDescriptor(RegExp.prototype, prop).get.call(obj);
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
|
@ -0,0 +1,22 @@
|
|||
var BUGNUMBER = 1120169;
|
||||
var summary = "Implement RegExp.prototype.source";
|
||||
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
assertEq(/foo/.source, "foo");
|
||||
assertEq(/foo/iymg.source, "foo");
|
||||
assertEq(/\//.source, "\\/");
|
||||
assertEq(RegExp("/").source, "\\/");
|
||||
|
||||
assertThrowsInstanceOf(() => genericSource(), TypeError);
|
||||
assertThrowsInstanceOf(() => genericSource(1), TypeError);
|
||||
assertThrowsInstanceOf(() => genericSource(""), TypeError);
|
||||
assertThrowsInstanceOf(() => genericSource({}), TypeError);
|
||||
assertThrowsInstanceOf(() => genericSource(new Proxy(/foo/, {get(){ return true; }})), TypeError);
|
||||
|
||||
function genericSource(obj) {
|
||||
return Object.getOwnPropertyDescriptor(RegExp.prototype, "source").get.call(obj);
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
|
@ -27,7 +27,7 @@ for (var constructor of constructors) {
|
|||
assertEq(obj instanceof constructor, false);
|
||||
assertEq(Object.getPrototypeOf(obj), RegExp.prototype);
|
||||
assertEq(Object.getOwnPropertyNames(obj).join(","),
|
||||
"0,lastIndex,source,global,ignoreCase,multiline,sticky");
|
||||
"0,lastIndex");
|
||||
assertEq(obj.length, undefined);
|
||||
|
||||
// Or any JS function.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// Any copyright is dedicated to the Public Domain.
|
||||
// http://creativecommons.org/licenses/publicdomain/
|
||||
|
||||
function testRegExp(b) {
|
||||
function testRegExp(b, c=b) {
|
||||
var a = deserialize(serialize(b));
|
||||
assertEq(a === b, false);
|
||||
assertEq(Object.getPrototypeOf(a), RegExp.prototype);
|
||||
|
@ -11,11 +11,11 @@ function testRegExp(b) {
|
|||
for (p in a)
|
||||
throw new Error("cloned RegExp should have no enumerable properties");
|
||||
|
||||
assertEq(a.source, b.source);
|
||||
assertEq(a.global, b.global);
|
||||
assertEq(a.ignoreCase, b.ignoreCase);
|
||||
assertEq(a.multiline, b.multiline);
|
||||
assertEq(a.sticky, b.sticky);
|
||||
assertEq(a.source, c.source);
|
||||
assertEq(a.global, c.global);
|
||||
assertEq(a.ignoreCase, c.ignoreCase);
|
||||
assertEq(a.multiline, c.multiline);
|
||||
assertEq(a.sticky, c.sticky);
|
||||
assertEq("expando" in a, false);
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,11 @@ testRegExp(RegExp.prototype);
|
|||
var re = /\bx\b/gi;
|
||||
re.expando = true;
|
||||
testRegExp(re);
|
||||
// `source` and the flag accessors are defined on RegExp.prototype, so they're
|
||||
// not available after re.__proto__ has been changed. We solve that by passing
|
||||
// in an additional copy of the same RegExp to compare the
|
||||
// serialized-then-deserialized clone with."
|
||||
re.__proto__ = {};
|
||||
testRegExp(re);
|
||||
testRegExp(re, /\bx\b/gi);
|
||||
|
||||
reportCompare(0, 0, 'ok');
|
||||
|
|
|
@ -337,27 +337,9 @@ RegExpObject::assignInitialShape(ExclusiveContext *cx, Handle<RegExpObject*> sel
|
|||
MOZ_ASSERT(self->empty());
|
||||
|
||||
JS_STATIC_ASSERT(LAST_INDEX_SLOT == 0);
|
||||
JS_STATIC_ASSERT(SOURCE_SLOT == LAST_INDEX_SLOT + 1);
|
||||
JS_STATIC_ASSERT(GLOBAL_FLAG_SLOT == SOURCE_SLOT + 1);
|
||||
JS_STATIC_ASSERT(IGNORE_CASE_FLAG_SLOT == GLOBAL_FLAG_SLOT + 1);
|
||||
JS_STATIC_ASSERT(MULTILINE_FLAG_SLOT == IGNORE_CASE_FLAG_SLOT + 1);
|
||||
JS_STATIC_ASSERT(STICKY_FLAG_SLOT == MULTILINE_FLAG_SLOT + 1);
|
||||
|
||||
/* The lastIndex property alone is writable but non-configurable. */
|
||||
if (!self->addDataProperty(cx, cx->names().lastIndex, LAST_INDEX_SLOT, JSPROP_PERMANENT))
|
||||
return nullptr;
|
||||
|
||||
/* Remaining instance properties are non-writable and non-configurable. */
|
||||
unsigned attrs = JSPROP_PERMANENT | JSPROP_READONLY;
|
||||
if (!self->addDataProperty(cx, cx->names().source, SOURCE_SLOT, attrs))
|
||||
return nullptr;
|
||||
if (!self->addDataProperty(cx, cx->names().global, GLOBAL_FLAG_SLOT, attrs))
|
||||
return nullptr;
|
||||
if (!self->addDataProperty(cx, cx->names().ignoreCase, IGNORE_CASE_FLAG_SLOT, attrs))
|
||||
return nullptr;
|
||||
if (!self->addDataProperty(cx, cx->names().multiline, MULTILINE_FLAG_SLOT, attrs))
|
||||
return nullptr;
|
||||
return self->addDataProperty(cx, cx->names().sticky, STICKY_FLAG_SLOT, attrs);
|
||||
return self->addDataProperty(cx, cx->names().lastIndex, LAST_INDEX_SLOT, JSPROP_PERMANENT);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -370,16 +352,6 @@ RegExpObject::init(ExclusiveContext *cx, HandleAtom source, RegExpFlag flags)
|
|||
|
||||
MOZ_ASSERT(self->lookup(cx, NameToId(cx->names().lastIndex))->slot() ==
|
||||
LAST_INDEX_SLOT);
|
||||
MOZ_ASSERT(self->lookup(cx, NameToId(cx->names().source))->slot() ==
|
||||
SOURCE_SLOT);
|
||||
MOZ_ASSERT(self->lookup(cx, NameToId(cx->names().global))->slot() ==
|
||||
GLOBAL_FLAG_SLOT);
|
||||
MOZ_ASSERT(self->lookup(cx, NameToId(cx->names().ignoreCase))->slot() ==
|
||||
IGNORE_CASE_FLAG_SLOT);
|
||||
MOZ_ASSERT(self->lookup(cx, NameToId(cx->names().multiline))->slot() ==
|
||||
MULTILINE_FLAG_SLOT);
|
||||
MOZ_ASSERT(self->lookup(cx, NameToId(cx->names().sticky))->slot() ==
|
||||
STICKY_FLAG_SLOT);
|
||||
|
||||
/*
|
||||
* If this is a re-initialization with an existing RegExpShared, 'flags'
|
||||
|
|
Загрузка…
Ссылка в новой задаче