diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index 7e605c770d99c4b63e64d5e1d4bdfb1ba5ba8a53..b05c5fb785a2db626b4433a68233b8a641956a60 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -1268,17 +1268,14 @@ bool WrapObject(JSContext* cx, const WindowProxyHolder& p, return ToJSValue(cx, p, rval); } -static int CompareIdsAtIndices(const void* aElement1, const void* aElement2, - void* aClosure) { +static int ComparePropertyInfosAtIndices(const void* aElement1, + const void* aElement2, + void* aClosure) { const uint16_t index1 = *static_cast<const uint16_t*>(aElement1); const uint16_t index2 = *static_cast<const uint16_t*>(aElement2); const PropertyInfo* infos = static_cast<PropertyInfo*>(aClosure); - uintptr_t rawBits1 = infos[index1].Id().asRawBits(); - uintptr_t rawBits2 = infos[index2].Id().asRawBits(); - MOZ_ASSERT(rawBits1 != rawBits2); - - return rawBits1 < rawBits2 ? -1 : 1; + return PropertyInfo::Compare(infos[index1], infos[index2]); } // {JSPropertySpec,JSFunctionSpec} use {JSPropertySpec,JSFunctionSpec}::Name @@ -1351,10 +1348,11 @@ static bool InitPropertyInfos(JSContext* cx, for (unsigned int i = 0; i < nativeProperties->propertyInfoCount; ++i) { indices[i] = i; } - // CompareIdsAtIndices() doesn't actually modify the PropertyInfo array, so - // the const_cast here is OK in spite of the signature of NS_QuickSort(). + // ComparePropertyInfosAtIndices() doesn't actually modify the PropertyInfo + // array, so the const_cast here is OK in spite of the signature of + // NS_QuickSort(). NS_QuickSort(indices, nativeProperties->propertyInfoCount, sizeof(uint16_t), - CompareIdsAtIndices, + ComparePropertyInfosAtIndices, const_cast<PropertyInfo*>(nativeProperties->PropertyInfos())); return true; @@ -1476,23 +1474,42 @@ static JSObject* XrayCreateFunction(JSContext* cx, struct IdToIndexComparator { // The id we're searching for. const jsid& mId; + // Whether we're searching for static operations. + const bool mStatic; // The list of ids we're searching in. const PropertyInfo* mInfos; - explicit IdToIndexComparator(const jsid& aId, const PropertyInfo* aInfos) - : mId(aId), mInfos(aInfos) {} + IdToIndexComparator(const jsid& aId, DOMObjectType aType, + const PropertyInfo* aInfos) + : mId(aId), + mStatic(aType == eInterface || aType == eNamespace), + mInfos(aInfos) {} int operator()(const uint16_t aIndex) const { - if (mId.asRawBits() == mInfos[aIndex].Id().asRawBits()) { - return 0; + const PropertyInfo& info = mInfos[aIndex]; + if (mId.asRawBits() == info.Id().asRawBits()) { + if (info.type != eMethod && info.type != eStaticMethod) { + return 0; + } + + if (mStatic == info.IsStaticMethod()) { + // We're looking for static properties and we've found a static one for + // the right name. + return 0; + } + + // Static operations are sorted before others by PropertyInfo::Compare. + return mStatic ? -1 : 1; } - return mId.asRawBits() < mInfos[aIndex].Id().asRawBits() ? -1 : 1; + + return mId.asRawBits() < info.Id().asRawBits() ? -1 : 1; } }; static const PropertyInfo* XrayFindOwnPropertyInfo( - JSContext* cx, JS::Handle<jsid> id, + JSContext* cx, DOMObjectType type, JS::Handle<jsid> id, const NativeProperties* nativeProperties) { - if (MOZ_UNLIKELY(nativeProperties->iteratorAliasMethodIndex >= 0) && + if ((type == eInterfacePrototype || type == eGlobalInstance) && + MOZ_UNLIKELY(nativeProperties->iteratorAliasMethodIndex >= 0) && id.isWellKnownSymbol(JS::SymbolCode::iterator)) { return nativeProperties->MethodPropertyInfos() + nativeProperties->iteratorAliasMethodIndex; @@ -1505,7 +1522,7 @@ static const PropertyInfo* XrayFindOwnPropertyInfo( if (BinarySearchIf(sortedPropertyIndices, 0, nativeProperties->propertyInfoCount, - IdToIndexComparator(id, propertyInfos), &idx)) { + IdToIndexComparator(id, type, propertyInfos), &idx)) { return propertyInfos + sortedPropertyIndices[idx]; } @@ -1737,11 +1754,11 @@ static bool ResolvePrototypeOrConstructor( const PropertyInfo* found = nullptr; if ((nativeProperties = nativePropertiesHolder.regular)) { - found = XrayFindOwnPropertyInfo(cx, id, nativeProperties); + found = XrayFindOwnPropertyInfo(cx, type, id, nativeProperties); } if (!found && (nativeProperties = nativePropertiesHolder.chromeOnly) && xpc::AccessCheck::isChrome(JS::GetCompartment(wrapper))) { - found = XrayFindOwnPropertyInfo(cx, id, nativeProperties); + found = XrayFindOwnPropertyInfo(cx, type, id, nativeProperties); } if (IsInstance(type)) { diff --git a/dom/bindings/DOMJSClass.h b/dom/bindings/DOMJSClass.h index dd445792ef591b2a63e0985b8fc6f265e5355be9..7cf7bc824c85df7b29e7ed28ec9dbab5ee3c25e4 100644 --- a/dom/bindings/DOMJSClass.h +++ b/dom/bindings/DOMJSClass.h @@ -221,6 +221,25 @@ struct PropertyInfo { mIdBits = aId.asRawBits(); } MOZ_ALWAYS_INLINE jsid Id() const { return jsid::fromRawBits(mIdBits); } + + bool IsStaticMethod() const { return type == eStaticMethod; } + + static int Compare(const PropertyInfo& aInfo1, const PropertyInfo& aInfo2) { + // IdToIndexComparator needs to be updated if the order here is changed! + if (MOZ_UNLIKELY(aInfo1.mIdBits == aInfo2.mIdBits)) { + MOZ_ASSERT((aInfo1.type == eMethod || aInfo1.type == eStaticMethod) && + (aInfo2.type == eMethod || aInfo2.type == eStaticMethod)); + + bool isStatic1 = aInfo1.IsStaticMethod(); + + MOZ_ASSERT(isStatic1 != aInfo2.IsStaticMethod(), + "We shouldn't have 2 static methods with the same name!"); + + return isStatic1 ? -1 : 1; + } + + return aInfo1.mIdBits < aInfo2.mIdBits ? -1 : 1; + } }; static_assert( diff --git a/dom/bindings/test/TestFunctions.h b/dom/bindings/test/TestFunctions.h index 9030c454f80f820418565ae5a016857926ad32ef..16560be0c1f466b71f8dab8b2ba2ff53f52128d7 100644 --- a/dom/bindings/test/TestFunctions.h +++ b/dom/bindings/test/TestFunctions.h @@ -110,6 +110,12 @@ class TestFunctions : public NonRefcountedDOMObject { void TestUnionOfAllowSharedBuffferSource( const MaybeSharedArrayBufferOrMaybeSharedArrayBufferView& aUnion); + bool StaticAndNonStaticOverload() { return false; } + static bool StaticAndNonStaticOverload(GlobalObject& aGlobal, + const Optional<uint32_t>& aLength) { + return true; + } + static bool ObjectFromAboutBlank(JSContext* aCx, JSObject* aObj); WrapperCachedNonISupportsTestInterface* WrapperCachedNonISupportsObject(); diff --git a/dom/bindings/test/chrome.ini b/dom/bindings/test/chrome.ini index e96c2c1209927f36576e763ef5f50e431b33d6ab..fc633d9ceb39d00da6ee981e93c3f33211de4107 100644 --- a/dom/bindings/test/chrome.ini +++ b/dom/bindings/test/chrome.ini @@ -8,11 +8,12 @@ support-files = [test_bug775543.html] [test_document_location_set_via_xray.html] [test_dom_xrays.html] +skip-if = debug == false # TestFunctions is only available in debug builds [test_proxies_via_xray.html] [test_document_location_via_xray_cached.html] [test_bug1123516_maplikesetlikechrome.xhtml] -skip-if = debug == false +skip-if = debug == false # TestFunctions is only available in debug builds [test_bug1287912.html] [test_bug1457051.html] [test_interfaceLength_chrome.html] -skip-if = debug == false +skip-if = debug == false # TestFunctions is only available in debug builds diff --git a/dom/bindings/test/test_dom_xrays.html b/dom/bindings/test/test_dom_xrays.html index 12d8d230db25de10d2f5d6ce584f1c9a13288968..9b565526459b8b2add7ba4c5ff3b3c8dfa0e2d70 100644 --- a/dom/bindings/test/test_dom_xrays.html +++ b/dom/bindings/test/test_dom_xrays.html @@ -293,6 +293,34 @@ function test() { isnot(typeof Object.getOwnPropertyDescriptor(win.Node.prototype, "ELEMENT_NODE"), "undefined", "Should see constant property on prototype objects"); + // Interfaces can have both static and non-static properties with the same name. + isnot(typeof win.TestFunctions.staticAndNonStaticOverload, "undefined", + "Should see static property on interface objects (even with non-static property with the same name)"); + isnot(typeof Object.getOwnPropertyDescriptor(win.TestFunctions, "staticAndNonStaticOverload"), "undefined", + "Should see static property on interface objects (even with non-static property with the same name)"); + isnot(Object.getOwnPropertyNames(win.TestFunctions).indexOf("staticAndNonStaticOverload"), -1, + "Should see static property on interface objects (even with non-static property with the same name)"); + isnot(typeof (new win.TestFunctions("")).staticAndNonStaticOverload, "undefined", + "Should see non-static property on prototype objects (even with static property with the same name)"); + let testFunctions = new win.TestFunctions(); + is(Object.getOwnPropertyDescriptor(testFunctions, "staticAndNonStaticOverload"), undefined, + "Shouldn't see non-static property on instances (even with static property with the same name)"); + ok(!testFunctions.staticAndNonStaticOverload(), + "Should call the non-static overload on the instance"); + ok(win.TestFunctions.staticAndNonStaticOverload(), + "Should call the static overload on the interface object"); + isnot(typeof Object.getOwnPropertyDescriptor(win.TestFunctions.prototype, "staticAndNonStaticOverload"), "undefined", + "Should see non-static property on prototype objects (even with static property with the same name)"); + is(Object.getOwnPropertyDescriptor(win.TestFunctions, "staticAndNonStaticOverload").value, + Object.getOwnPropertyDescriptor(win.TestFunctions, "staticAndNonStaticOverload").value, + "Should get the same value when getting the static property twice"); + is(Object.getOwnPropertyDescriptor(win.TestFunctions.prototype, "staticAndNonStaticOverload").value, + Object.getOwnPropertyDescriptor(win.TestFunctions.prototype, "staticAndNonStaticOverload").value, + "Should get the same value when getting the non-static property twice"); + isnot(Object.getOwnPropertyDescriptor(win.TestFunctions, "staticAndNonStaticOverload").value, + Object.getOwnPropertyDescriptor(win.TestFunctions.prototype, "staticAndNonStaticOverload").value, + "Should get different values for static and non-static properties with the same name"); + // Adopting nodes should not lose expandos. elem = document.createElement("span"); elem.expando = 5; @@ -331,7 +359,12 @@ function test() { } SimpleTest.waitForExplicitFinish(); -addLoadEvent(test); +SimpleTest.requestLongerTimeout(2); + +addLoadEvent(() => { + SpecialPowers.pushPrefEnv({set: [["dom.expose_test_interfaces", true]]}, + test); +}); </script> </pre> diff --git a/dom/webidl/TestFunctions.webidl b/dom/webidl/TestFunctions.webidl index c1229435ee2c0ad470e3ff5b7e59a2b386db47e2..fcc52016a1543372352c1fc2ea73690fbf6ba0f4 100644 --- a/dom/webidl/TestFunctions.webidl +++ b/dom/webidl/TestFunctions.webidl @@ -141,6 +141,9 @@ interface TestFunctions { undefined testDictWithAllowShared(optional DictWithAllowSharedBufferSource buffer = {}); undefined testUnionOfBuffferSource((ArrayBuffer or ArrayBufferView or DOMString) foo); undefined testUnionOfAllowSharedBuffferSource(([AllowShared] ArrayBuffer or [AllowShared] ArrayBufferView) foo); + + boolean staticAndNonStaticOverload(); + static boolean staticAndNonStaticOverload(optional unsigned long foo); }; dictionary DictWithAllowSharedBufferSource {