Bug 1518753 part 4 - Fix IsRegExpPrototype to return false for cross-realm regexp prototypes. r=anba
authorJan de Mooij <jdemooij@mozilla.com>
Sat, 12 Jan 2019 10:50:00 +0000
changeset 453630 1d49da4facd722bc4da5ef9d7a0bff92c6e7d940
parent 453629 b32c2548fa6bf1e642bd6bb5eacd550a94792638
child 453631 cfa1c48c717048f00eb4811b5719cd716eb1e5b3
push id35362
push userncsoregi@mozilla.com
push dateSat, 12 Jan 2019 21:35:38 +0000
treeherdermozilla-central@877169d8ef49 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersanba
bugs1518753
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1518753 part 4 - Fix IsRegExpPrototype to return false for cross-realm regexp prototypes. r=anba Differential Revision: https://phabricator.services.mozilla.com/D16169
js/src/builtin/RegExp.cpp
js/src/tests/non262/RegExp/prototype-different-global.js
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -603,40 +603,36 @@ bool js::regexp_construct_raw_flags(JSCo
   }
 
   // Step 8.
   regexp->initAndZeroLastIndex(sourceAtom, RegExpFlag(flags), cx);
   args.rval().setObject(*regexp);
   return true;
 }
 
-MOZ_ALWAYS_INLINE bool IsRegExpPrototype(HandleValue v) {
-  if (IsRegExpObject(v) || !v.isObject()) {
-    return false;
-  }
-
-  // Note: The prototype shares its JSClass with instances.
-  return StandardProtoKeyOrNull(&v.toObject()) == JSProto_RegExp;
+MOZ_ALWAYS_INLINE bool IsRegExpPrototype(HandleValue v, JSContext* cx) {
+  return (v.isObject() &&
+          cx->global()->maybeGetRegExpPrototype() == &v.toObject());
 }
 
 // ES 2017 draft 21.2.5.4.
 MOZ_ALWAYS_INLINE bool regexp_global_impl(JSContext* cx, const CallArgs& args) {
   MOZ_ASSERT(IsRegExpObject(args.thisv()));
 
   // Steps 4-6.
   RegExpObject* reObj = &args.thisv().toObject().as<RegExpObject>();
   args.rval().setBoolean(reObj->global());
   return true;
 }
 
 bool js::regexp_global(JSContext* cx, unsigned argc, JS::Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   // Step 3.a.
-  if (IsRegExpPrototype(args.thisv())) {
+  if (IsRegExpPrototype(args.thisv(), cx)) {
     args.rval().setUndefined();
     return true;
   }
 
   // Steps 1-3.
   return CallNonGenericMethod<IsRegExpObject, regexp_global_impl>(cx, args);
 }
 
@@ -650,17 +646,17 @@ MOZ_ALWAYS_INLINE bool regexp_ignoreCase
   args.rval().setBoolean(reObj->ignoreCase());
   return true;
 }
 
 bool js::regexp_ignoreCase(JSContext* cx, unsigned argc, JS::Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   // Step 3.a.
-  if (IsRegExpPrototype(args.thisv())) {
+  if (IsRegExpPrototype(args.thisv(), cx)) {
     args.rval().setUndefined();
     return true;
   }
 
   // Steps 1-3.
   return CallNonGenericMethod<IsRegExpObject, regexp_ignoreCase_impl>(cx, args);
 }
 
@@ -674,17 +670,17 @@ MOZ_ALWAYS_INLINE bool regexp_multiline_
   args.rval().setBoolean(reObj->multiline());
   return true;
 }
 
 bool js::regexp_multiline(JSContext* cx, unsigned argc, JS::Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   // Step 3.a.
-  if (IsRegExpPrototype(args.thisv())) {
+  if (IsRegExpPrototype(args.thisv(), cx)) {
     args.rval().setUndefined();
     return true;
   }
 
   // Steps 1-3.
   return CallNonGenericMethod<IsRegExpObject, regexp_multiline_impl>(cx, args);
 }
 
@@ -708,17 +704,17 @@ MOZ_ALWAYS_INLINE bool regexp_source_imp
   args.rval().setString(str);
   return true;
 }
 
 static bool regexp_source(JSContext* cx, unsigned argc, JS::Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   // Step 3.a.
-  if (IsRegExpPrototype(args.thisv())) {
+  if (IsRegExpPrototype(args.thisv(), cx)) {
     args.rval().setString(cx->names().emptyRegExp);
     return true;
   }
 
   // Steps 1-4.
   return CallNonGenericMethod<IsRegExpObject, regexp_source_impl>(cx, args);
 }
 
@@ -731,17 +727,17 @@ MOZ_ALWAYS_INLINE bool regexp_sticky_imp
   args.rval().setBoolean(reObj->sticky());
   return true;
 }
 
 bool js::regexp_sticky(JSContext* cx, unsigned argc, JS::Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   // Step 3.a.
-  if (IsRegExpPrototype(args.thisv())) {
+  if (IsRegExpPrototype(args.thisv(), cx)) {
     args.rval().setUndefined();
     return true;
   }
 
   // Steps 1-3.
   return CallNonGenericMethod<IsRegExpObject, regexp_sticky_impl>(cx, args);
 }
 
@@ -755,17 +751,17 @@ MOZ_ALWAYS_INLINE bool regexp_unicode_im
   args.rval().setBoolean(reObj->unicode());
   return true;
 }
 
 bool js::regexp_unicode(JSContext* cx, unsigned argc, JS::Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   // Step 3.a.
-  if (IsRegExpPrototype(args.thisv())) {
+  if (IsRegExpPrototype(args.thisv(), cx)) {
     args.rval().setUndefined();
     return true;
   }
 
   // Steps 1-3.
   return CallNonGenericMethod<IsRegExpObject, regexp_unicode_impl>(cx, args);
 }
 
--- a/js/src/tests/non262/RegExp/prototype-different-global.js
+++ b/js/src/tests/non262/RegExp/prototype-different-global.js
@@ -1,19 +1,28 @@
-
-var otherGlobal = newGlobal();
-var otherRegExp = otherGlobal.RegExp;
+function test(otherGlobal) {
+    var otherRegExp = otherGlobal.RegExp;
 
-for (let name of ["global", "ignoreCase", "multiline", "sticky", "unicode", "source"]) {
-    let getter = Object.getOwnPropertyDescriptor(RegExp.prototype, name).get;
-    assertEq(typeof getter, "function");
+    for (let name of ["global", "ignoreCase", "multiline", "sticky", "unicode", "source"]) {
+        let getter = Object.getOwnPropertyDescriptor(RegExp.prototype, name).get;
+        assertEq(typeof getter, "function");
 
-    // Note: TypeError gets reported from wrong global!
-    assertThrowsInstanceOf(() => getter.call(otherRegExp.prototype), otherGlobal.TypeError);
-}
+        // Note: TypeError gets reported from wrong global if cross-compartment,
+        // so we test both cases.
+        let ex;
+        try {
+            getter.call(otherRegExp.prototype);
+        } catch (e) {
+            ex = e;
+        }
+        assertEq(ex instanceof TypeError || ex instanceof otherGlobal.TypeError, true);
+    }
 
-let flagsGetter = Object.getOwnPropertyDescriptor(RegExp.prototype, "flags").get;
-assertEq(flagsGetter.call(otherRegExp.prototype), "");
+    let flagsGetter = Object.getOwnPropertyDescriptor(RegExp.prototype, "flags").get;
+    assertEq(flagsGetter.call(otherRegExp.prototype), "");
 
-assertEq(RegExp.prototype.toString.call(otherRegExp.prototype), "/(?:)/");
+    assertEq(RegExp.prototype.toString.call(otherRegExp.prototype), "/(?:)/");
+}
+test(newGlobal());
+test(newGlobal({newCompartment: true}));
 
 if (typeof reportCompare === "function")
     reportCompare(0, 0);