From fed7084085e6d778c98bd8278e9eaaa1faa1ca8a Mon Sep 17 00:00:00 2001 From: roytam1 Date: Thu, 4 Nov 2021 09:20:44 +0800 Subject: [PATCH] import changes from `dev' branch of rmottola/Arctic-Fox: - Bug 1190733 - Test initializedLength() instead of length() during the fast path for reversing unboxed arrays, r=jandem. (b5dcbd0e3) - Bug 1070767 - Enable {Array, %TypedArray%}.prototype.includes in all builds. r=lth (de595c002) - Bug 1195298 - Fix NewDenseArray intrinsic to work when the first argument is a double. r=till (1f551ada2) - Bug 1190727 - Make initialization of temporary results array resilient against Array.prototype setters in self-hosted Map#next implementation. r=jandem (d705c623c) - Bug 1200108 - Remove NewDenseArray intrinsic, use std_Array instead. r=till (e5c4126c6) - Bug 1199822 - Turn self-hosting's cycle-check into an assertion; r=till (804600283) - Bug 1194148 - Self-host Array.prototype.toString. r=till (4ffb4712c) - pointer style (7b1a9900c) - Bug 1200809 part 5 - Convert self-hosting intrinsics to new InlinableNatives system. r=till (8dd5eb0b9) - pointer style (7974610a2) - Bug 1200809 part 6 - Convert various natives to new InlinableNatives system. r=nbp (81d75199c) - Bug 1200809 part 7 - Convert SIMD natives to new InlinableNatives system. r=nbp (5e67097e0) - Bug 1200809 part 8 - Don't call shouldAbortOnPreliminaryGroups if we have an uninlinable native. r=bhackett (bd20f201f) - Bug 1114507 - Part 1: Add/release the appId's refcnt if frame is in main process. r=kanru (741889791) - Bug 1190903 - Don't send StopIMEStateManagement message after TabParent has been destroyed (r=masayuki) (97bba211b) - Bug 1166592 - Remove ParentIdleListener from idle service when ActorDestroy() to avoid leaking ContentParent. r=khuey (771549a18) - Bug 1114507 - Part 3: Remove PContetBridge channel when grandchild-process is killed. r=kanru (a84f888b3) - Bug 1114507 - Part 2: Add/release the appId's refcnt in oop case. r=kanru (1fdb788b0) - Bug 1114507 - Part 4: Test cases. r=kanru (889a770c7) - pointer style (80bd2082b) - Bug 1159347 - Make BaseProxyHandler::getPropertyDescriptor not-pure virtual. r=efaust (56de51919) - Bug 1166847 - Implement OpaqueCrossCompartmentWrapper;r=evilpies (d762e785e) --- dom/base/nsFrameLoader.cpp | 8 +- dom/bindings/DOMJSProxyHandler.cpp | 25 - dom/bindings/DOMJSProxyHandler.h | 4 - dom/ipc/ContentBridgeChild.cpp | 24 +- dom/ipc/ContentBridgeChild.h | 3 - dom/ipc/ContentBridgeParent.cpp | 11 + dom/ipc/ContentBridgeParent.h | 2 + dom/ipc/ContentParent.cpp | 178 ++++-- dom/ipc/ContentParent.h | 37 +- dom/ipc/ContentProcessManager.cpp | 36 ++ dom/ipc/ContentProcessManager.h | 16 + dom/ipc/PContent.ipdl | 9 +- dom/ipc/TabParent.cpp | 58 +- dom/ipc/TabParent.h | 1 + dom/ipc/nsIContentParent.cpp | 8 + dom/ipc/nsIContentParent.h | 3 + dom/ipc/tests/file_app.sjs | 55 ++ dom/ipc/tests/file_app.template.webapp | 6 + dom/ipc/tests/mochitest.ini | 36 ++ dom/ipc/tests/test_permission_embed.html | 51 ++ .../test_permission_for_in_process_app.html | 43 ++ .../test_permission_for_nested_oop_app.html | 75 +++ .../tests/test_permission_for_oop_app.html | 45 ++ .../test_permission_for_two_oop_apps.html | 88 +++ dom/ipc/tests/test_permission_framescript.js | 31 + dom/ipc/tests/test_permission_helper.js | 362 ++++++++++++ .../test_permission_when_oop_app_crashes.html | 67 +++ dom/workers/RuntimeService.cpp | 25 +- js/public/Proxy.h | 2 +- js/src/builtin/Array.js | 22 +- js/src/builtin/Map.js | 7 +- js/src/builtin/Object.cpp | 7 +- js/src/builtin/Object.h | 3 + js/src/builtin/SIMD.cpp | 5 +- js/src/builtin/TestingFunctions.cpp | 15 +- js/src/builtin/TestingFunctions.h | 9 - js/src/jit-test/tests/basic/bug1190733.js | 7 + js/src/jit-test/tests/basic/bug1200108.js | 5 + js/src/jit/InlinableNatives.h | 48 +- js/src/jit/IonBuilder.h | 3 + js/src/jit/MCallOptimize.cpp | 530 ++++++++++-------- js/src/jsarray.cpp | 48 +- js/src/jscntxt.h | 39 +- js/src/jsfriendapi.cpp | 3 + js/src/jsfriendapi.h | 6 +- js/src/jsfun.cpp | 3 + js/src/jsstr.cpp | 3 +- js/src/jswrapper.h | 51 ++ js/src/moz.build | 1 + js/src/proxy/BaseProxyHandler.cpp | 22 + js/src/proxy/DeadObjectProxy.cpp | 8 - js/src/proxy/DeadObjectProxy.h | 29 +- .../proxy/OpaqueCrossCompartmentWrapper.cpp | 183 ++++++ js/src/proxy/ScriptedDirectProxyHandler.cpp | 22 - js/src/proxy/ScriptedDirectProxyHandler.h | 44 +- js/src/shell/js.cpp | 6 +- js/src/tests/ecma_6/Date/toPrimitive.js | 62 ++ .../ecma_6/Object/toPrimitive-callers.js | 57 ++ js/src/tests/ecma_6/Object/toPrimitive.js | 101 ++++ js/src/tests/ecma_6/Symbol/toPrimitive.js | 39 ++ js/src/tests/ecma_6/TypedArray/includes.js | 3 - js/src/tests/ecma_7/Array/includes.js | 64 +-- js/src/vm/GlobalObject.cpp | 19 +- js/src/vm/GlobalObject.h | 7 +- js/src/vm/SelfHosting.cpp | 261 ++++----- js/src/vm/TypedArrayObject.cpp | 3 - js/xpconnect/tests/chrome/test_xrayToJS.xul | 18 +- 67 files changed, 2312 insertions(+), 760 deletions(-) create mode 100644 dom/ipc/tests/file_app.sjs create mode 100644 dom/ipc/tests/file_app.template.webapp create mode 100644 dom/ipc/tests/test_permission_embed.html create mode 100644 dom/ipc/tests/test_permission_for_in_process_app.html create mode 100644 dom/ipc/tests/test_permission_for_nested_oop_app.html create mode 100644 dom/ipc/tests/test_permission_for_oop_app.html create mode 100644 dom/ipc/tests/test_permission_for_two_oop_apps.html create mode 100644 dom/ipc/tests/test_permission_framescript.js create mode 100644 dom/ipc/tests/test_permission_helper.js create mode 100644 dom/ipc/tests/test_permission_when_oop_app_crashes.html create mode 100644 js/src/jit-test/tests/basic/bug1190733.js create mode 100644 js/src/jit-test/tests/basic/bug1200108.js create mode 100644 js/src/proxy/OpaqueCrossCompartmentWrapper.cpp create mode 100644 js/src/tests/ecma_6/Date/toPrimitive.js create mode 100644 js/src/tests/ecma_6/Object/toPrimitive-callers.js create mode 100644 js/src/tests/ecma_6/Object/toPrimitive.js create mode 100644 js/src/tests/ecma_6/Symbol/toPrimitive.js diff --git a/dom/base/nsFrameLoader.cpp b/dom/base/nsFrameLoader.cpp index 3dedf74290..c7145c6772 100644 --- a/dom/base/nsFrameLoader.cpp +++ b/dom/base/nsFrameLoader.cpp @@ -155,7 +155,6 @@ nsFrameLoader::nsFrameLoader(Element* aOwner, bool aNetworkCreated) , mObservingOwnerContent(false) , mVisible(true) { - ResetPermissionManagerStatus(); mRemoteFrame = ShouldUseRemoteProcess(); } @@ -395,6 +394,9 @@ nsFrameLoader::ReallyStartLoadingInternal() mURIToLoad = nullptr; NS_ENSURE_SUCCESS(rv, rv); + // Track the appId's reference count if this frame is in-process + ResetPermissionManagerStatus(); + return NS_OK; } @@ -2663,7 +2665,9 @@ nsFrameLoader::ResetPermissionManagerStatus() { // The resetting of the permissions status can run only // in the main process. - if (XRE_IsContentProcess()) { + // only in-main-process && in-process frame is handled here and all other + // cases are handled by ContentParent. + if (XRE_IsContentProcess() || mRemoteFrame) { return; } diff --git a/dom/bindings/DOMJSProxyHandler.cpp b/dom/bindings/DOMJSProxyHandler.cpp index 0af76e2a1b..0ad4b207aa 100644 --- a/dom/bindings/DOMJSProxyHandler.cpp +++ b/dom/bindings/DOMJSProxyHandler.cpp @@ -162,31 +162,6 @@ DOMProxyHandler::isExtensible(JSContext *cx, JS::Handle proxy, bool * return true; } -bool -BaseDOMProxyHandler::getPropertyDescriptor(JSContext* cx, - JS::Handle proxy, - JS::Handle id, - MutableHandle desc) const -{ - if (!getOwnPropertyDescriptor(cx, proxy, id, desc)) { - return false; - } - if (desc.object()) { - return true; - } - - JS::Rooted proto(cx); - if (!js::GetObjectProto(cx, proxy, &proto)) { - return false; - } - if (!proto) { - desc.object().set(nullptr); - return true; - } - - return JS_GetPropertyDescriptorById(cx, proto, id, desc); -} - bool BaseDOMProxyHandler::getOwnPropertyDescriptor(JSContext* cx, JS::Handle proxy, diff --git a/dom/bindings/DOMJSProxyHandler.h b/dom/bindings/DOMJSProxyHandler.h index 8820397f12..9d8742571a 100644 --- a/dom/bindings/DOMJSProxyHandler.h +++ b/dom/bindings/DOMJSProxyHandler.h @@ -59,10 +59,6 @@ public: virtual bool ownPropertyKeys(JSContext* cx, JS::Handle proxy, JS::AutoIdVector &props) const override; - bool getPropertyDescriptor(JSContext* cx, JS::Handle proxy, - JS::Handle id, - JS::MutableHandle desc) const override; - virtual bool enumerate(JSContext *cx, JS::Handle proxy, JS::MutableHandle objp) const override; diff --git a/dom/ipc/ContentBridgeChild.cpp b/dom/ipc/ContentBridgeChild.cpp index 0c8dce8614..fc72ea5097 100644 --- a/dom/ipc/ContentBridgeChild.cpp +++ b/dom/ipc/ContentBridgeChild.cpp @@ -12,7 +12,6 @@ #include "mozilla/dom/ipc/BlobChild.h" #include "mozilla/jsipc/CrossProcessObjectWrappers.h" #include "mozilla/ipc/InputStreamUtils.h" -#include "nsIObserverService.h" using namespace mozilla::ipc; using namespace mozilla::jsipc; @@ -21,8 +20,7 @@ namespace mozilla { namespace dom { NS_IMPL_ISUPPORTS(ContentBridgeChild, - nsIContentChild, - nsIObserver) + nsIContentChild) ContentBridgeChild::ContentBridgeChild(Transport* aTransport) : mTransport(aTransport) @@ -36,10 +34,6 @@ ContentBridgeChild::~ContentBridgeChild() void ContentBridgeChild::ActorDestroy(ActorDestroyReason aWhy) { - nsCOMPtr os = mozilla::services::GetObserverService(); - if (os) { - os->RemoveObserver(this, "content-child-shutdown"); - } MessageLoop::current()->PostTask( FROM_HERE, NewRunnableMethod(this, &ContentBridgeChild::DeferredDestroy)); @@ -55,11 +49,6 @@ ContentBridgeChild::Create(Transport* aTransport, ProcessId aOtherPid) DebugOnly ok = bridge->Open(aTransport, aOtherPid, XRE_GetIOMessageLoop()); MOZ_ASSERT(ok); - nsCOMPtr os = mozilla::services::GetObserverService(); - if (os) { - os->AddObserver(bridge, "content-child-shutdown", false); - } - return bridge; } @@ -180,16 +169,5 @@ ContentBridgeChild::DeallocPBlobChild(PBlobChild* aActor) return nsIContentChild::DeallocPBlobChild(aActor); } -NS_IMETHODIMP -ContentBridgeChild::Observe(nsISupports* aSubject, - const char* aTopic, - const char16_t* aData) -{ - if (!strcmp(aTopic, "content-child-shutdown")) { - Close(); - } - return NS_OK; -} - } // namespace dom } // namespace mozilla diff --git a/dom/ipc/ContentBridgeChild.h b/dom/ipc/ContentBridgeChild.h index b04c2f137c..44a1981939 100644 --- a/dom/ipc/ContentBridgeChild.h +++ b/dom/ipc/ContentBridgeChild.h @@ -9,20 +9,17 @@ #include "mozilla/dom/PContentBridgeChild.h" #include "mozilla/dom/nsIContentChild.h" -#include "nsIObserver.h" namespace mozilla { namespace dom { class ContentBridgeChild final : public PContentBridgeChild , public nsIContentChild - , public nsIObserver { public: explicit ContentBridgeChild(Transport* aTransport); NS_DECL_ISUPPORTS - NS_DECL_NSIOBSERVER static ContentBridgeChild* Create(Transport* aTransport, ProcessId aOtherProcess); diff --git a/dom/ipc/ContentBridgeParent.cpp b/dom/ipc/ContentBridgeParent.cpp index c21a8e4c06..b269928b60 100644 --- a/dom/ipc/ContentBridgeParent.cpp +++ b/dom/ipc/ContentBridgeParent.cpp @@ -163,6 +163,17 @@ ContentBridgeParent::DeallocPBrowserParent(PBrowserParent* aParent) return nsIContentParent::DeallocPBrowserParent(aParent); } +void +ContentBridgeParent::NotifyTabDestroyed() +{ + int32_t numLiveTabs = ManagedPBrowserParent().Length(); + if (numLiveTabs == 1) { + MessageLoop::current()->PostTask( + FROM_HERE, + NewRunnableMethod(this, &ContentBridgeParent::Close)); + } +} + // This implementation is identical to ContentParent::GetCPOWManager but we can't // move it to nsIContentParent because it calls ManagedPJavaScriptParent() which // only exists in PContentParent and PContentBridgeParent. diff --git a/dom/ipc/ContentBridgeParent.h b/dom/ipc/ContentBridgeParent.h index af2fbc1acd..3b2c0e3807 100644 --- a/dom/ipc/ContentBridgeParent.h +++ b/dom/ipc/ContentBridgeParent.h @@ -28,6 +28,8 @@ public: virtual void ActorDestroy(ActorDestroyReason aWhy) override; void DeferredDestroy(); + virtual bool IsContentBridgeParent() override { return true; } + void NotifyTabDestroyed(); static ContentBridgeParent* Create(Transport* aTransport, ProcessId aOtherProcess); diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 74246f56dd..82118e9fb6 100755 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -1420,7 +1420,9 @@ ContentParent::Init() } Preferences::AddStrongObserver(this, ""); if (obs) { - obs->NotifyObservers(static_cast(this), "ipc:content-created", nullptr); + nsAutoString cpId; + cpId.AppendInt(static_cast(this->ChildID())); + obs->NotifyObservers(static_cast(this), "ipc:content-created", cpId.get()); } #ifdef ACCESSIBILITY @@ -1989,9 +1991,20 @@ ContentParent::ActorDestroy(ActorDestroyReason why) props->SetPropertyAsBool(NS_LITERAL_STRING("abnormal"), true); } - obs->NotifyObservers((nsIPropertyBag2*) props, "ipc:content-shutdown", nullptr); + nsAutoString cpId; + cpId.AppendInt(static_cast(this->ChildID())); + obs->NotifyObservers((nsIPropertyBag2*) props, "ipc:content-shutdown", cpId.get()); } + // Remove any and all idle listeners. + nsCOMPtr idleService = + do_GetService("@mozilla.org/widget/idleservice;1"); + MOZ_ASSERT(idleService); + nsRefPtr listener; + for (int32_t i = mIdleListeners.Length() - 1; i >= 0; --i) { + listener = static_cast(mIdleListeners[i].get()); + idleService->RemoveIdleObserver(listener, listener->mTime); + } mIdleListeners.Clear(); MessageLoop::current()-> @@ -2008,10 +2021,35 @@ ContentParent::ActorDestroy(ActorDestroyReason why) // least until after the current task finishes running. NS_DispatchToCurrentThread(new DelayedDeleteContentParentTask(this)); - // Destroy any processes created by this ContentParent - ContentProcessManager *cpm = ContentProcessManager::GetSingleton(); + // Release the appId's reference count of any processes + // created by this ContentParent and the frame opened by this ContentParent + // if this ContentParent crashes. + ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); nsTArray childIDArray = cpm->GetAllChildProcessById(this->ChildID()); + if (why == AbnormalShutdown) { + nsCOMPtr permMgr = services::GetPermissionManager(); + if(permMgr) { + // Release the appId's reference count of its child-processes + for (uint32_t i = 0; i < childIDArray.Length(); i++) { + nsTArray tabCtxs = cpm->GetTabContextByContentProcess(childIDArray[i]); + for (uint32_t j = 0 ; j < tabCtxs.Length() ; j++) { + if (tabCtxs[j].OwnOrContainingAppId() != nsIScriptSecurityManager::NO_APP_ID) { + permMgr->ReleaseAppId(tabCtxs[j].OwnOrContainingAppId()); + } + } + } + // Release the appId's reference count belong to itself + nsTArray tabCtxs = cpm->GetTabContextByContentProcess(mChildID); + for (uint32_t i = 0; i < tabCtxs.Length() ; i++) { + if (tabCtxs[i].OwnOrContainingAppId()!= nsIScriptSecurityManager::NO_APP_ID) { + permMgr->ReleaseAppId(tabCtxs[i].OwnOrContainingAppId()); + } + } + } + } + + // Destroy any processes created by this ContentParent for(uint32_t i = 0; i < childIDArray.Length(); i++) { ContentParent* cp = cpm->GetContentProcessById(childIDArray[i]); MessageLoop::current()->PostTask( @@ -2027,23 +2065,33 @@ ContentParent::ActorDestroy(ActorDestroyReason why) } void -ContentParent::NotifyTabDestroying(PBrowserParent* aTab) +ContentParent::NotifyTabDestroying(const TabId& aTabId, + const ContentParentId& aCpId) { + if (XRE_IsParentProcess()) { // There can be more than one PBrowser for a given app process // because of popup windows. PBrowsers can also destroy // concurrently. When all the PBrowsers are destroying, kick off // another task to ensure the child process *really* shuts down, // even if the PBrowsers themselves never finish destroying. - int32_t numLiveTabs = ManagedPBrowserParent().Length(); - ++mNumDestroyingTabs; - if (mNumDestroyingTabs != numLiveTabs) { + ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); + ContentParent* cp = cpm->GetContentProcessById(aCpId); + if (!cp) { + return; + } + ++cp->mNumDestroyingTabs; + nsTArray tabIds = cpm->GetTabParentsByProcessId(aCpId); + if (static_cast(cp->mNumDestroyingTabs) != tabIds.Length()) { return; } // We're dying now, so prevent this content process from being // recycled during its shutdown procedure. - MarkAsDead(); - StartForceKillTimer(); + cp->MarkAsDead(); + cp->StartForceKillTimer(); + } else { + ContentChild::GetSingleton()->SendNotifyTabDestroying(aTabId, aCpId); + } } static int32_t @@ -2071,16 +2119,15 @@ ContentParent::StartForceKillTimer() } void -ContentParent::NotifyTabDestroyed(PBrowserParent* aTab, +ContentParent::NotifyTabDestroyed(const TabId& aTabId, bool aNotifiedDestroying) { if (aNotifiedDestroying) { --mNumDestroyingTabs; } - TabId id = static_cast(aTab)->GetTabId(); nsTArray parentArray = - nsContentPermissionUtils::GetContentPermissionRequestParentById(id); + nsContentPermissionUtils::GetContentPermissionRequestParentById(aTabId); // Need to close undeleted ContentPermissionRequestParents before tab is closed. for (auto& permissionRequestParent : parentArray) { @@ -2093,7 +2140,9 @@ ContentParent::NotifyTabDestroyed(PBrowserParent* aTab, // There can be more than one PBrowser for a given app process // because of popup windows. When the last one closes, shut // us down. - if (ManagedPBrowserParent().Length() == 1) { + ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); + nsTArray tabIds = cpm->GetTabParentsByProcessId(this->ChildID()); + if (tabIds.Length() == 1) { // In the case of normal shutdown, send a shutdown message to child to // allow it to perform shutdown tasks. MessageLoop::current()->PostTask( @@ -4542,30 +4591,34 @@ ContentParent::RecvAddIdleObserver(const uint64_t& aObserver, const uint32_t& aI { nsresult rv; nsCOMPtr idleService = - do_GetService("@mozilla.org/widget/idleservice;1", &rv); + do_GetService("@mozilla.org/widget/idleservice;1", &rv); NS_ENSURE_SUCCESS(rv, false); - nsRefPtr listener = new ParentIdleListener(this, aObserver); - mIdleListeners.Put(aObserver, listener); - idleService->AddIdleObserver(listener, aIdleTimeInS); + nsRefPtr listener = + new ParentIdleListener(this, aObserver, aIdleTimeInS); + rv = idleService->AddIdleObserver(listener, aIdleTimeInS); + NS_ENSURE_SUCCESS(rv, false); + mIdleListeners.AppendElement(listener); return true; } bool ContentParent::RecvRemoveIdleObserver(const uint64_t& aObserver, const uint32_t& aIdleTimeInS) { - nsresult rv; - nsCOMPtr idleService = - do_GetService("@mozilla.org/widget/idleservice;1", &rv); - NS_ENSURE_SUCCESS(rv, false); - nsRefPtr listener; - bool found = mIdleListeners.Get(aObserver, &listener); - if (found) { - mIdleListeners.Remove(aObserver); - idleService->RemoveIdleObserver(listener, aIdleTimeInS); + for (int32_t i = mIdleListeners.Length() - 1; i >= 0; --i) { + listener = static_cast(mIdleListeners[i].get()); + if (listener->mObserver == aObserver && + listener->mTime == aIdleTimeInS) { + nsresult rv; + nsCOMPtr idleService = + do_GetService("@mozilla.org/widget/idleservice;1", &rv); + NS_ENSURE_SUCCESS(rv, false); + idleService->RemoveIdleObserver(listener, aIdleTimeInS); + mIdleListeners.RemoveElementAt(i); + break; + } } - return true; } @@ -4767,8 +4820,12 @@ ContentParent::AllocateTabId(const TabId& aOpenerTabId, { TabId tabId; if (XRE_IsParentProcess()) { - ContentProcessManager *cpm = ContentProcessManager::GetSingleton(); + ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); tabId = cpm->AllocateTabId(aOpenerTabId, aContext, aCpId); + // Add appId's reference count in oop case + if (tabId) { + PermissionManagerAddref(aCpId, tabId); + } } else { ContentChild::GetSingleton()->SendAllocateTabId(aOpenerTabId, @@ -4781,14 +4838,27 @@ ContentParent::AllocateTabId(const TabId& aOpenerTabId, /*static*/ void ContentParent::DeallocateTabId(const TabId& aTabId, - const ContentParentId& aCpId) + const ContentParentId& aCpId, + bool aMarkedDestroying) { if (XRE_IsParentProcess()) { + // Release appId's reference count in oop case + if (aTabId) { + PermissionManagerRelease(aCpId, aTabId); + } + + ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); + ContentParent* cp = cpm->GetContentProcessById(aCpId); + + cp->NotifyTabDestroyed(aTabId, aMarkedDestroying); + ContentProcessManager::GetSingleton()->DeallocateTabId(aCpId, aTabId); } else { - ContentChild::GetSingleton()->SendDeallocateTabId(aTabId); + ContentChild::GetSingleton()->SendDeallocateTabId(aTabId, + aCpId, + aMarkedDestroying); } } @@ -4806,9 +4876,19 @@ ContentParent::RecvAllocateTabId(const TabId& aOpenerTabId, } bool -ContentParent::RecvDeallocateTabId(const TabId& aTabId) +ContentParent::RecvDeallocateTabId(const TabId& aTabId, + const ContentParentId& aCpId, + const bool& aMarkedDestroying) { - DeallocateTabId(aTabId, this->ChildID()); + DeallocateTabId(aTabId, aCpId, aMarkedDestroying); + return true; +} + +bool +ContentParent::RecvNotifyTabDestroying(const TabId& aTabId, + const ContentParentId& aCpId) +{ + NotifyTabDestroying(aTabId, aCpId); return true; } @@ -4979,6 +5059,38 @@ ContentParent::DeallocPContentPermissionRequestParent(PContentPermissionRequestP return true; } +/* static */ bool +ContentParent::PermissionManagerAddref(const ContentParentId& aCpId, + const TabId& aTabId) +{ + MOZ_ASSERT(XRE_IsParentProcess(), + "Call PermissionManagerAddref in content process!"); + ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); + uint32_t appId = cpm->GetAppIdByProcessAndTabId(aCpId, aTabId); + nsCOMPtr permMgr = services::GetPermissionManager(); + if (appId != nsIScriptSecurityManager::NO_APP_ID && permMgr) { + permMgr->AddrefAppId(appId); + return true; + } + return false; +} + +/* static */ bool +ContentParent::PermissionManagerRelease(const ContentParentId& aCpId, + const TabId& aTabId) +{ + MOZ_ASSERT(XRE_IsParentProcess(), + "Call PermissionManagerRelease in content process!"); + ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); + uint32_t appId = cpm->GetAppIdByProcessAndTabId(aCpId, aTabId); + nsCOMPtr permMgr = services::GetPermissionManager(); + if (appId != nsIScriptSecurityManager::NO_APP_ID && permMgr) { + permMgr->ReleaseAppId(appId); + return true; + } + return false; +} + bool ContentParent::RecvGetBrowserConfiguration(const nsCString& aURI, BrowserConfiguration* aConfig) { diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index b2c71c5b6d..8a92af31c5 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -208,9 +208,10 @@ public: virtual bool KillChild() override; /** Notify that a tab is beginning its destruction sequence. */ - void NotifyTabDestroying(PBrowserParent* aTab); + static void NotifyTabDestroying(const TabId& aTabId, + const ContentParentId& aCpId); /** Notify that a tab was destroyed during normal operation. */ - void NotifyTabDestroyed(PBrowserParent* aTab, + void NotifyTabDestroyed(const TabId& aTabId, bool aNotifiedDestroying); TestShellParent* CreateTestShell(); @@ -223,7 +224,21 @@ public: const IPCTabContext& aContext, const ContentParentId& aCpId); static void - DeallocateTabId(const TabId& aTabId, const ContentParentId& aCpId); + DeallocateTabId(const TabId& aTabId, + const ContentParentId& aCpId, + bool aMarkedDestroying); + + /* + * Add the appId's reference count by the given ContentParentId and TabId + */ + static bool + PermissionManagerAddref(const ContentParentId& aCpId, const TabId& aTabId); + + /* + * Release the appId's reference count by the given ContentParentId and TabId + */ + static bool + PermissionManagerRelease(const ContentParentId& aCpId, const TabId& aTabId); static bool GetBrowserConfiguration(const nsCString& aURI, BrowserConfiguration& aConfig); @@ -344,7 +359,12 @@ public: const ContentParentId& aCpId, TabId* aTabId) override; - virtual bool RecvDeallocateTabId(const TabId& aTabId) override; + virtual bool RecvDeallocateTabId(const TabId& aTabId, + const ContentParentId& aCpId, + const bool& aMarkedDestroying) override; + + virtual bool RecvNotifyTabDestroying(const TabId& aTabId, + const ContentParentId& aCpId) override; nsTArray GetManagedTabContext(); @@ -911,7 +931,7 @@ private: nsRefPtr mConsoleService; nsConsoleService* GetConsoleService(); - nsDataHashtable > mIdleListeners; + nsTArray> mIdleListeners; #ifdef MOZ_X11 // Dup of child's X socket, used to scope its resources to this @@ -942,17 +962,20 @@ private: } // namespace mozilla class ParentIdleListener : public nsIObserver { + friend class mozilla::dom::ContentParent; + public: NS_DECL_ISUPPORTS NS_DECL_NSIOBSERVER - ParentIdleListener(mozilla::dom::ContentParent* aParent, uint64_t aObserver) - : mParent(aParent), mObserver(aObserver) + ParentIdleListener(mozilla::dom::ContentParent* aParent, uint64_t aObserver, uint32_t aTime) + : mParent(aParent), mObserver(aObserver), mTime(aTime) {} private: virtual ~ParentIdleListener() {} nsRefPtr mParent; uint64_t mObserver; + uint32_t mTime; }; #endif diff --git a/dom/ipc/ContentProcessManager.cpp b/dom/ipc/ContentProcessManager.cpp index 51c3588c08..8937c777fd 100644 --- a/dom/ipc/ContentProcessManager.cpp +++ b/dom/ipc/ContentProcessManager.cpp @@ -12,6 +12,7 @@ #include "mozilla/ClearOnShutdown.h" #include "nsPrintfCString.h" +#include "nsIScriptSecurityManager.h" // XXX need another bug to move this to a common header. #ifdef DISABLE_ASSERTS_FOR_FUZZING @@ -334,5 +335,40 @@ ContentProcessManager::GetTopLevelTabParentByProcessAndTabId(const ContentParent return GetTabParentByProcessAndTabId(currentCpId, currentTabId); } +nsTArray +ContentProcessManager::GetTabParentsByProcessId(const ContentParentId& aChildCpId) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsTArray tabIdList; + auto iter = mContentParentMap.find(aChildCpId); + if (NS_WARN_IF(iter == mContentParentMap.end())) { + ASSERT_UNLESS_FUZZING(); + return Move(tabIdList); + } + + for (auto remoteFrameIter = iter->second.mRemoteFrames.begin(); + remoteFrameIter != iter->second.mRemoteFrames.end(); + ++remoteFrameIter) { + tabIdList.AppendElement(remoteFrameIter->first); + } + + return Move(tabIdList); +} + +uint32_t +ContentProcessManager::GetAppIdByProcessAndTabId(const ContentParentId& aChildCpId, + const TabId& aChildTabId) +{ + uint32_t appId = nsIScriptSecurityManager::NO_APP_ID; + if (aChildCpId && aChildTabId) { + TabContext tabContext; + if (GetTabContextByProcessAndTabId(aChildCpId, aChildTabId, &tabContext)) { + appId = tabContext.OwnOrContainingAppId(); + } + } + return appId; +} + } // namespace dom } // namespace mozilla diff --git a/dom/ipc/ContentProcessManager.h b/dom/ipc/ContentProcessManager.h index ea5b36e96e..4821af50f9 100644 --- a/dom/ipc/ContentProcessManager.h +++ b/dom/ipc/ContentProcessManager.h @@ -110,6 +110,13 @@ public: const TabId& aChildTabId, /*out*/ TabId* aOpenerTabId); + /** + * Get all TabParents' Ids managed by the givent content process. + * Return empty array when TabParent couldn't be found via aChildCpId + */ + nsTArray + GetTabParentsByProcessId(const ContentParentId& aChildCpId); + /** * Get the TabParent by the given content process and tab id. * Return nullptr when TabParent couldn't be found via aChildCpId @@ -135,6 +142,15 @@ public: GetTopLevelTabParentByProcessAndTabId(const ContentParentId& aChildCpId, const TabId& aChildTabId); + /** + * Return appId by given TabId and ContentParentId. + * It will return nsIScriptSecurityManager::NO_APP_ID + * if the given tab is not an app. + */ + uint32_t + GetAppIdByProcessAndTabId(const ContentParentId& aChildCpId, + const TabId& aChildTabId); + private: static StaticAutoPtr sSingleton; TabId mUniqueId; diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index 63e7f9eab1..1424513d3e 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -976,8 +976,15 @@ parent: */ sync AllocateTabId(TabId openerTabId, IPCTabContext context, ContentParentId cpId) returns (TabId tabId); - async DeallocateTabId(TabId tabId); + async DeallocateTabId(TabId tabId, + ContentParentId cpId, + bool aMarkedDestroying); + /** + * Tell the chrome process there is a destruction of PBrowser(Tab) + */ + async NotifyTabDestroying(TabId tabId, + ContentParentId cpId); /** * Starts an offline application cache update. * @param manifestURI diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index 86c8b78da0..5465f1bded 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -16,8 +16,10 @@ #include "nsAccessibilityService.h" #endif #include "mozilla/BrowserElementParent.h" +#include "mozilla/dom/ContentBridgeParent.h" #include "mozilla/dom/ContentParent.h" #include "mozilla/dom/DataTransfer.h" +#include "mozilla/dom/Event.h" #include "mozilla/dom/indexedDB/ActorsParent.h" #include "mozilla/plugins/PluginWidgetParent.h" #include "mozilla/EventStateManager.h" @@ -430,11 +432,9 @@ TabParent::IsVisible() } void -TabParent::Destroy() +TabParent::DestroyInternal() { - if (mIsDestroyed) { - return; - } + IMEStateManager::OnTabParentDestroying(this); RemoveWindowListeners(); @@ -447,11 +447,6 @@ TabParent::Destroy() RemoveTabParentFromTable(frame->GetLayersId()); frame->Destroy(); } - mIsDestroyed = true; - - if (XRE_IsParentProcess()) { - Manager()->AsContentParent()->NotifyTabDestroying(this); - } // Let all PluginWidgets know we are tearing down. Prevents // these objects from sending async events after the child side @@ -460,6 +455,24 @@ TabParent::Destroy() for (uint32_t idx = 0; idx < kids.Length(); ++idx) { static_cast(kids[idx])->ParentDestroy(); } +} + +void +TabParent::Destroy() +{ + if (mIsDestroyed) { + return; + } + + DestroyInternal(); + + mIsDestroyed = true; + + if (XRE_IsParentProcess()) { + ContentParent::NotifyTabDestroying(this->GetTabId(), Manager()->AsContentParent()->ChildID()); + } else { + ContentParent::NotifyTabDestroying(this->GetTabId(), Manager()->ChildID()); + } mMarkedDestroying = true; } @@ -468,12 +481,15 @@ bool TabParent::Recv__delete__() { if (XRE_IsParentProcess()) { - Manager()->AsContentParent()->NotifyTabDestroyed(this, mMarkedDestroying); ContentParent::DeallocateTabId(mTabId, - Manager()->AsContentParent()->ChildID()); + Manager()->AsContentParent()->ChildID(), + mMarkedDestroying); } else { - ContentParent::DeallocateTabId(mTabId, ContentParentId(0)); + Manager()->AsContentBridgeParent()->NotifyTabDestroyed(); + ContentParent::DeallocateTabId(mTabId, + Manager()->ChildID(), + mMarkedDestroying); } return true; @@ -482,8 +498,26 @@ TabParent::Recv__delete__() void TabParent::ActorDestroy(ActorDestroyReason why) { + // Even though TabParent::Destroy calls this, we need to do it here too in + // case of a crash. IMEStateManager::OnTabParentDestroying(this); + // Prevent executing ContentParent::NotifyTabDestroying in + // TabParent::Destroy() called by frameLoader->DestroyComplete() below + // when tab crashes in contentprocess because ContentParent::ActorDestroy() + // in main process will be triggered before this function + // and remove the process information that + // ContentParent::NotifyTabDestroying need from mContentParentMap. + + // When tab crashes in content process, + // there is no need to call ContentParent::NotifyTabDestroying + // because the jobs in ContentParent::NotifyTabDestroying + // will be done by ContentParent::ActorDestroy. + if (XRE_IsContentProcess() && why == AbnormalShutdown && !mIsDestroyed) { + DestroyInternal(); + mIsDestroyed = true; + } + nsRefPtr frameLoader = GetFrameLoader(true); nsCOMPtr os = services::GetObserverService(); if (frameLoader) { diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h index ce621d9177..16eeecabae 100644 --- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -478,6 +478,7 @@ protected: LayoutDeviceIntPoint mChromeOffset; private: + void DestroyInternal(); already_AddRefed GetFrameLoader(bool aUseCachedFrameLoaderAfterDestroy = false) const; nsRefPtr mManager; void TryCacheDPIAndScale(); diff --git a/dom/ipc/nsIContentParent.cpp b/dom/ipc/nsIContentParent.cpp index 1ec21a1fb4..71b4cdc747 100644 --- a/dom/ipc/nsIContentParent.cpp +++ b/dom/ipc/nsIContentParent.cpp @@ -10,6 +10,7 @@ #include "mozilla/Preferences.h" #include "mozilla/dom/File.h" #include "mozilla/dom/ContentParent.h" +#include "mozilla/dom/ContentBridgeParent.h" #include "mozilla/dom/PTabContext.h" #include "mozilla/dom/PermissionMessageUtils.h" #include "mozilla/dom/StructuredCloneUtils.h" @@ -46,6 +47,13 @@ nsIContentParent::AsContentParent() return static_cast(this); } +ContentBridgeParent* +nsIContentParent::AsContentBridgeParent() +{ + MOZ_ASSERT(IsContentBridgeParent()); + return static_cast(this); +} + PJavaScriptParent* nsIContentParent::AllocPJavaScriptParent() { diff --git a/dom/ipc/nsIContentParent.h b/dom/ipc/nsIContentParent.h index 758f8efdf4..29a50f25d1 100644 --- a/dom/ipc/nsIContentParent.h +++ b/dom/ipc/nsIContentParent.h @@ -38,6 +38,7 @@ class BlobConstructorParams; class BlobImpl; class BlobParent; class ContentParent; +class ContentBridgeParent; class IPCTabContext; class PBlobParent; class PBrowserParent; @@ -77,6 +78,8 @@ public: virtual bool IsContentParent() { return false; } ContentParent* AsContentParent(); + virtual bool IsContentBridgeParent() { return false; } + ContentBridgeParent* AsContentBridgeParent(); protected: // methods bool CanOpenBrowser(const IPCTabContext& aContext); diff --git a/dom/ipc/tests/file_app.sjs b/dom/ipc/tests/file_app.sjs new file mode 100644 index 0000000000..45c002d99e --- /dev/null +++ b/dom/ipc/tests/file_app.sjs @@ -0,0 +1,55 @@ +var gBasePath = "tests/dom/ipc/tests/"; + +function handleRequest(request, response) { + var query = getQuery(request); + + var testToken = ''; + if ('testToken' in query) { + testToken = query.testToken; + } + + var template = ''; + if ('template' in query) { + template = query.template; + } + var template = gBasePath + template; + response.setHeader("Content-Type", "application/x-web-app-manifest+json", false); + response.write(readTemplate(template).replace(/TESTTOKEN/g, testToken)); +} + +// Copy-pasted incantations. There ought to be a better way to synchronously read +// a file into a string, but I guess we're trying to discourage that. +function readTemplate(path) { + var file = Components.classes["@mozilla.org/file/directory_service;1"]. + getService(Components.interfaces.nsIProperties). + get("CurWorkD", Components.interfaces.nsILocalFile); + var fis = Components.classes['@mozilla.org/network/file-input-stream;1']. + createInstance(Components.interfaces.nsIFileInputStream); + var cis = Components.classes["@mozilla.org/intl/converter-input-stream;1"]. + createInstance(Components.interfaces.nsIConverterInputStream); + var split = path.split("/"); + for(var i = 0; i < split.length; ++i) { + file.append(split[i]); + } + fis.init(file, -1, -1, false); + cis.init(fis, "UTF-8", 0, 0); + + var data = ""; + let str = {}; + let read = 0; + do { + read = cis.readString(0xffffffff, str); // read as much as we can and put it in str.value + data += str.value; + } while (read != 0); + cis.close(); + return data; +} + +function getQuery(request) { + var query = {}; + request.queryString.split('&').forEach(function (val) { + var [name, value] = val.split('='); + query[name] = unescape(value); + }); + return query; +} diff --git a/dom/ipc/tests/file_app.template.webapp b/dom/ipc/tests/file_app.template.webapp new file mode 100644 index 0000000000..8e7ae0750b --- /dev/null +++ b/dom/ipc/tests/file_app.template.webapp @@ -0,0 +1,6 @@ +{ + "name": "Nested-OOP", + "description": "Nested-OOP test app used for mochitest.", + "launch_path": "/tests/dom/ipc/tests/TESTTOKEN", + "icons": { "128": "default_icon" } +} diff --git a/dom/ipc/tests/mochitest.ini b/dom/ipc/tests/mochitest.ini index 05091716da..8d83e6b80a 100644 --- a/dom/ipc/tests/mochitest.ini +++ b/dom/ipc/tests/mochitest.ini @@ -12,3 +12,39 @@ skip-if = toolkit != 'gonk' run-if = toolkit == 'gonk' [test_child_docshell.html] run-if = toolkit != 'cocoa' # disabled due to hangs, see changeset 6852e7c47edf +[test_permission_for_in_process_app.html] +skip-if = e10s || os == "android" || toolkit == "gonk" # embed-apps doesn't work in mochitest app +support-files = + test_permission_helper.js +[test_permission_for_oop_app.html] +skip-if = buildapp == 'mulet' || os == "android" || toolkit == "gonk" # embed-apps doesn't work in mochitest app +support-files = + file_app.sjs + file_app.template.webapp + test_permission_helper.js + test_permission_embed.html + test_permission_framescript.js +[test_permission_for_nested_oop_app.html] +skip-if = buildapp == 'mulet' || os == "android" || toolkit == "gonk" # embed-apps doesn't work in mochitest app +support-files = + file_app.sjs + file_app.template.webapp + test_permission_helper.js + test_permission_embed.html + test_permission_framescript.js +[test_permission_for_two_oop_apps.html] +skip-if = buildapp == 'mulet' || os == "android" || toolkit == "gonk" # embed-apps doesn't work in mochitest app +support-files = + file_app.sjs + file_app.template.webapp + test_permission_helper.js + test_permission_embed.html + test_permission_framescript.js +[test_permission_when_oop_app_crashes.html] +skip-if = buildapp == 'mulet' || os == "android" || toolkit == "gonk" # embed-apps doesn't work in mochitest app +support-files = + file_app.sjs + file_app.template.webapp + test_permission_helper.js + test_permission_embed.html + test_permission_framescript.js diff --git a/dom/ipc/tests/test_permission_embed.html b/dom/ipc/tests/test_permission_embed.html new file mode 100644 index 0000000000..88c0051d6f --- /dev/null +++ b/dom/ipc/tests/test_permission_embed.html @@ -0,0 +1,51 @@ + + + + + oop-test apps + + + + + + diff --git a/dom/ipc/tests/test_permission_for_in_process_app.html b/dom/ipc/tests/test_permission_for_in_process_app.html new file mode 100644 index 0000000000..7112efc611 --- /dev/null +++ b/dom/ipc/tests/test_permission_for_in_process_app.html @@ -0,0 +1,43 @@ + + + + + + Test for Bug 1114507 + + + + +Mozilla Bug1114507 +

+ + + + + diff --git a/dom/ipc/tests/test_permission_for_nested_oop_app.html b/dom/ipc/tests/test_permission_for_nested_oop_app.html new file mode 100644 index 0000000000..461ddd16ee --- /dev/null +++ b/dom/ipc/tests/test_permission_for_nested_oop_app.html @@ -0,0 +1,75 @@ + + + + + + Test for Bug 1114507 + + + + +Mozilla Bug1114507 +

+ + + + + diff --git a/dom/ipc/tests/test_permission_for_oop_app.html b/dom/ipc/tests/test_permission_for_oop_app.html new file mode 100644 index 0000000000..781715aa59 --- /dev/null +++ b/dom/ipc/tests/test_permission_for_oop_app.html @@ -0,0 +1,45 @@ + + + + + + Test for Bug 1114507 + + + + +Mozilla Bug1114507 +

+ + + + + diff --git a/dom/ipc/tests/test_permission_for_two_oop_apps.html b/dom/ipc/tests/test_permission_for_two_oop_apps.html new file mode 100644 index 0000000000..9e2182515f --- /dev/null +++ b/dom/ipc/tests/test_permission_for_two_oop_apps.html @@ -0,0 +1,88 @@ + + + + + + Test for Bug 1114507 + + + + +Mozilla Bug1114507 +

+ + + + + diff --git a/dom/ipc/tests/test_permission_framescript.js b/dom/ipc/tests/test_permission_framescript.js new file mode 100644 index 0000000000..d2bd63b846 --- /dev/null +++ b/dom/ipc/tests/test_permission_framescript.js @@ -0,0 +1,31 @@ +Components.utils.import("resource://gre/modules/Services.jsm"); +let cm = Services.crashmanager; +var cpIdList = []; + +var shutdownObs = { + observe: function(subject, topic, data) { + if (topic == 'ipc:content-shutdown') { + var index = cpIdList.indexOf(parseInt(data)); + if (index > -1) { + cpIdList.splice(index, 1); + sendAsyncMessage('content-shutdown', ''); + } + } + } +}; + +var createdObs = { + observe: function(subject, topic, data) { + if (topic == 'ipc:content-created') { + cpIdList.push(parseInt(data)); + sendAsyncMessage('content-created', ''); + } + } +}; + +addMessageListener('crashreporter-status', function(message) { + sendAsyncMessage('crashreporter-enable', !!cm); +}); + +Services.obs.addObserver(shutdownObs, 'ipc:content-shutdown', false); +Services.obs.addObserver(createdObs, 'ipc:content-created', false); diff --git a/dom/ipc/tests/test_permission_helper.js b/dom/ipc/tests/test_permission_helper.js new file mode 100644 index 0000000000..125da91390 --- /dev/null +++ b/dom/ipc/tests/test_permission_helper.js @@ -0,0 +1,362 @@ +"use strict"; + +// Used to access the DOM node in this test +const DOM_PARENT_ID = "container"; +const DOM_PARENT = document.getElementById(DOM_PARENT_ID); +const APP_IFRAME_ID = "appFrame"; +const APP_IFRAME_ID2 = "appFrame2"; + +// Settings for testing the permission +const SESSION_PERSIST_MINUTES = 10; +const PERMISSION_TYPE = "geolocation"; +const permManager = SpecialPowers.Cc["@mozilla.org/permissionmanager;1"] + .getService(SpecialPowers.Ci.nsIPermissionManager); + +// Used to access App's information like appId +const gAppsService = SpecialPowers.Cc["@mozilla.org/AppsService;1"] + .getService(SpecialPowers.Ci.nsIAppsService); + +// Target-app for this testing +const APP_URL = "http://example.org"; +const APP_MANIFEST = "http://example.org/manifest.webapp"; + +// Used to embed a remote app that open the test-target-app above +const embedAppHostedManifestURL = window.location.origin + + '/tests/dom/ipc/tests/file_app.sjs?testToken=test_permission_embed.html&template=file_app.template.webapp'; +var embedApp; + +// Used to add listener for ipc:content-shutdown that +// will be triggered after ContentParent::DeallocateTabId +var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('test_permission_framescript.js')); + +// Delay reporting a result until after finish() got called +SimpleTest.waitForExplicitFinish(); +// Allow tests to disable the per platform app validity checks +SpecialPowers.setAllAppsLaunchable(true); + +// Run tests in order +function runTests() { + if (!tests.length) { + ok(true, "pass all tests!"); + SimpleTest.finish(); + return; + } + + var test = tests.shift(); + test(); +} + +function test1() { + allocateAppFrame(APP_IFRAME_ID, DOM_PARENT, APP_URL, APP_MANIFEST); + + var appId = gAppsService.getAppLocalIdByManifestURL(APP_MANIFEST); + + if (!SpecialPowers.hasPermission( PERMISSION_TYPE, + { url: APP_URL, + appId: appId, + isInBrowserElement: false })) { + errorHandler('[test 1] App should have permission: ' + PERMISSION_TYPE); + } + + removeAppFrame(APP_IFRAME_ID); + + // We expect there is no permission for the test-target-app + // after removing the in-process iframe embedding this app + runNextIfAppHasPermission(1, false, APP_URL, APP_MANIFEST); +} + +function test2() { + var afterShutdown = function () { + // We expect there is no permission for the test-target-app + // after removing the remote iframe embedding this app + runNextIfAppHasPermission(2, false, APP_URL, APP_MANIFEST); + }; + + // Test permission after the child-process containing + // test-target-app is killed + afterContentShutdown(1, afterShutdown); + + allocateAppFrame(APP_IFRAME_ID, DOM_PARENT, APP_URL, APP_MANIFEST, true); + + var appId = gAppsService.getAppLocalIdByManifestURL(APP_MANIFEST); + + if (!SpecialPowers.hasPermission( PERMISSION_TYPE, + { url: APP_URL, + appId: appId, + isInBrowserElement: false })) { + errorHandler('[test 2] App should have permission: ' + PERMISSION_TYPE); + } + + removeAppFrame(APP_IFRAME_ID); +} + +function test3() { + var afterGrandchildShutdown = function () { + // We expect there is no permission for the test-target-app + // after removing the remote iframe embedding this app + runNextIfAppHasPermission(3, false, APP_URL, APP_MANIFEST); + }; + + // Test permission after the grandchild-process + // containing test-target-app is killed + afterContentShutdown(1, afterGrandchildShutdown); + + allocateAppFrame(APP_IFRAME_ID, + DOM_PARENT, + embedApp.manifest.launch_path, + embedApp.manifestURL, + true); + + var appId = gAppsService.getAppLocalIdByManifestURL(APP_MANIFEST); + + if (!SpecialPowers.hasPermission(PERMISSION_TYPE, + { url: APP_URL, + appId: appId, + isInBrowserElement: false })) { + errorHandler('[test 3] App should have permission: ' + PERMISSION_TYPE); + } +} + +function test4() { + var afterGrandchildShutdown = function () { + // We expect there is still a permission for the test-target-app + // after killing test-target-app on 3rd-level process + runNextIfAppHasPermission(4, true, APP_URL, APP_MANIFEST); + }; + + // Test permission after the grandchild-process + // containing test-target-app is killed + afterContentShutdown(1, afterGrandchildShutdown); + + // Open a child process(2nd level) and a grandchild process(3rd level) + // that contains a test-target-app + allocateAppFrame(APP_IFRAME_ID, + DOM_PARENT, + embedApp.manifest.launch_path, + embedApp.manifestURL, + true); + + // Open another child process that contains + // another test-target-app(2nd level) + allocateAppFrame(APP_IFRAME_ID2, + DOM_PARENT, + APP_URL, + APP_MANIFEST, + true); + + var appId = gAppsService.getAppLocalIdByManifestURL(APP_MANIFEST); + + if (!SpecialPowers.hasPermission(PERMISSION_TYPE, + { url: APP_URL, + appId: appId, + isInBrowserElement: false })) { + errorHandler('[test 4] App should have permission: ' + PERMISSION_TYPE); + } +} + +function test5() { + var afterShutdown = function () { + // We expect there is no permission for the test-target-app + // after crashing its parent-process + runNextIfAppHasPermission(5, false, APP_URL, APP_MANIFEST); + }; + + // Test permission after the parent-process of test-target-app is crashed. + afterContentShutdown(2, afterShutdown); + + allocateAppFrame(APP_IFRAME_ID, + DOM_PARENT, + embedApp.manifest.launch_path + '#' + encodeURIComponent('crash'), + embedApp.manifestURL, + true); + + var appId = gAppsService.getAppLocalIdByManifestURL(APP_MANIFEST); + + if (!SpecialPowers.hasPermission( PERMISSION_TYPE, + { url: APP_URL, + appId: appId, + isInBrowserElement: false })) { + errorHandler('[test 5] App should have permission: ' + PERMISSION_TYPE); + } + + // Crash the child-process on 2nd level after + // the grandchild process on 3rd is allocated + var handler = {'crash': function() { + gScript.sendAsyncMessage("crashreporter-status", {}); + + getCrashReporterStatus(function(enable) { + if (enable) { + SimpleTest.expectChildProcessCrash(); + } + crashChildProcess(APP_IFRAME_ID); + }); + } + }; + + receiveMessageFromChildFrame(APP_IFRAME_ID, handler); +} + +// Setup the prefrences and permissions. +// This function should be called before any tests +function setupPrefsAndPermissions() { + SpecialPowers.pushPrefEnv({"set": [ + ["dom.mozBrowserFramesEnabled", true], + ["dom.ipc.tabs.nested.enabled", true], + ["dom.ipc.tabs.disabled", false]]}, + function() { + var autoManageApp = function () { + SpecialPowers.setAllAppsLaunchable(true); + // No confirmation needed when an app is installed and uninstalled. + SpecialPowers.autoConfirmAppInstall(() => { + // This callback should trigger the first test + SpecialPowers.autoConfirmAppUninstall(runTests); + }); + }; + + setupOpenAppPermission(document, autoManageApp); + } + ); +} + +function setupOpenAppPermission(ctx, callback) { + SpecialPowers.pushPermissions([ + { "type": "browser", "allow": true, "context": ctx}, // for mozbrowser + { "type": "embed-apps", "allow": true, "context": ctx }, // for mozapp + { "type": "webapps-manage", "allow": true, "context": ctx }], // for (un)installing apps + function checkPermissions() { + if (SpecialPowers.hasPermission("browser", ctx) && + SpecialPowers.hasPermission("embed-apps", ctx) && + SpecialPowers.hasPermission("webapps-manage", ctx)) { + callback(); + } else { + errorHandler(">> At least one of required permissions to open app is disallowed!\n"); + } + }); +} + +function installApp() { + // Install App from manifest + var request = navigator.mozApps.install(embedAppHostedManifestURL); + request.onerror = cbError; + request.onsuccess = function() { + // Get installed app + embedApp = request.result; // Assign to global variable + runTests(); + } +} + +function uninstallApp() { + var request = navigator.mozApps.mgmt.uninstall(embedApp); + request.onerror = cbError; + request.onsuccess = function() { + info("uninstall app susseccfully!"); + runTests(); + } +} + +function removeAppFrame(id) { + var ifr = document.getElementById(id); + ifr.remove(); +} + +function allocateAppFrame(id, parentNode, appSRC, manifestURL, useRemote = false) { + var ifr = document.createElement('iframe'); + ifr.setAttribute('id', id); + ifr.setAttribute('remote', useRemote? "true" : "false"); + ifr.setAttribute('mozbrowser', 'true'); + ifr.setAttribute('mozapp', manifestURL); + ifr.setAttribute('src', appSRC); + parentNode.appendChild(ifr); +} + +function receiveMessageFromChildFrame(id, handlers) { + var ifr = document.getElementById(id); + ifr.addEventListener('mozbrowsershowmodalprompt', function (recvMsg) { + var msg = recvMsg.detail.message; + handlers[msg](); + }); +} + +function addPermissionToApp(appURL, manifestURL) { + var appId = gAppsService.getAppLocalIdByManifestURL(manifestURL); + + // Get the time now. This is used for permission manager's expire_time + var now = Number(Date.now()); + + // Add app's permission asynchronously + SpecialPowers.pushPermissions([ + { "type":PERMISSION_TYPE, + "allow": 1, + "expireType":permManager.EXPIRE_SESSION, + "expireTime":now + SESSION_PERSIST_MINUTES*60*1000, + "context": { url: appURL, + appId: appId, + isInBrowserElement:false } + } + ], function() { + runTests(); + }); +} + +function runNextIfAppHasPermission(round, expect, appURL, manifestURL) { + var appId = gAppsService.getAppLocalIdByManifestURL(manifestURL); + + var hasPerm = SpecialPowers.hasPermission(PERMISSION_TYPE, + { url: appURL, + appId: appId, + isInBrowserElement: false }); + var result = (expect==hasPerm); + if (result) { + runTests(); + } else { + errorHandler( '[test ' + round + '] App should ' + ((expect)? '':'NOT ') + + 'have permission: ' + PERMISSION_TYPE); + } +} + +function afterContentShutdown(times, callback) { + // handle the message from test_permission_framescript.js + var num = 0; + gScript.addMessageListener('content-shutdown', function onShutdown(data) { + num += 1; + if (num >= times) { + gScript.removeMessageListener('content-shutdown', onShutdown); + callback(); + } + }); +} + +function getCrashReporterStatus(callback) { + gScript.addMessageListener('crashreporter-enable', function getStatus(data) { + gScript.removeMessageListener('crashreporter-enable', getStatus); + callback(data); + }); +} + +// Inject a frame script that crashes the content process +function crashChildProcess(frameId) { + var child = document.getElementById(frameId); + var mm = SpecialPowers.getBrowserFrameMessageManager(child); + var childFrameScriptStr = + 'function ContentScriptScope() {' + + 'var Cu = Components.utils;' + + 'Cu.import("resource://gre/modules/ctypes.jsm");' + + 'var crash = function() {' + + 'var zero = new ctypes.intptr_t(8);' + + 'var badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));' + + 'badptr.contents;' + + '};' + + 'privateNoteIntentionalCrash();' + + 'crash();' + + '}'; + mm.loadFrameScript('data:,new ' + childFrameScriptStr, false); +} + +function errorHandler(errMsg) { + ok(false, errMsg); + SimpleTest.finish(); +} + +function cbError(e) { + errorHandler("Error callback invoked: " + this.error.name); +} diff --git a/dom/ipc/tests/test_permission_when_oop_app_crashes.html b/dom/ipc/tests/test_permission_when_oop_app_crashes.html new file mode 100644 index 0000000000..f1f5dd52bb --- /dev/null +++ b/dom/ipc/tests/test_permission_when_oop_app_crashes.html @@ -0,0 +1,67 @@ + + + + + + Test for Bug 1114507 + + + + +Mozilla Bug1114507 +

+ + + + + diff --git a/dom/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp index 98498d1cd7..6a89508770 100644 --- a/dom/workers/RuntimeService.cpp +++ b/dom/workers/RuntimeService.cpp @@ -853,25 +853,6 @@ PreserveWrapper(JSContext *cx, JSObject *obj) return mozilla::dom::TryPreserveWrapper(obj); } -class DebuggeeGlobalSecurityWrapper : public js::CrossCompartmentSecurityWrapper { -public: - DebuggeeGlobalSecurityWrapper() - : js::CrossCompartmentSecurityWrapper(CROSS_COMPARTMENT, false) - { - } - - bool enter(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id, - js::Wrapper::Action act, bool* bp) const - { - *bp = false; - return false; - } - - static const DebuggeeGlobalSecurityWrapper singleton; -}; - -const DebuggeeGlobalSecurityWrapper DebuggeeGlobalSecurityWrapper::singleton; - JSObject* Wrap(JSContext *cx, JS::HandleObject existing, JS::HandleObject obj) { @@ -886,11 +867,7 @@ Wrap(JSContext *cx, JS::HandleObject existing, JS::HandleObject obj) if (IsDebuggerGlobal(originGlobal) || IsDebuggerSandbox(originGlobal)) { wrapper = &js::CrossCompartmentWrapper::singleton; } else { - if (obj != originGlobal) { - MOZ_CRASH("The should be only edges from the debugger to the debuggee global."); - } - - wrapper = &DebuggeeGlobalSecurityWrapper::singleton; + wrapper = &js::OpaqueCrossCompartmentWrapper::singleton; } if (existing) { diff --git a/js/public/Proxy.h b/js/public/Proxy.h index 7b54d983aa..5e0fca51c8 100644 --- a/js/public/Proxy.h +++ b/js/public/Proxy.h @@ -314,7 +314,7 @@ class JS_FRIEND_API(BaseProxyHandler) /* SpiderMonkey extensions. */ virtual bool getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id, - MutableHandle desc) const = 0; + MutableHandle desc) const; virtual bool hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const; virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props) const; diff --git a/js/src/builtin/Array.js b/js/src/builtin/Array.js index 03707fd06b..79646e2b22 100644 --- a/js/src/builtin/Array.js +++ b/js/src/builtin/Array.js @@ -253,7 +253,7 @@ function ArrayMap(callbackfn/*, thisArg*/) { var T = arguments.length > 1 ? arguments[1] : void 0; /* Step 6. */ - var A = NewDenseArray(len); + var A = std_Array(len); /* Step 7-8. */ /* Step a (implicit), and d. */ @@ -714,9 +714,7 @@ function ArrayIteratorNext() { } if (itemKind === ITEM_KIND_KEY_AND_VALUE) { - var pair = NewDenseArray(2); - pair[0] = index; - pair[1] = a[index]; + var pair = [index, a[index]]; result.value = pair; return result; } @@ -804,7 +802,7 @@ function ArrayFrom(items, mapfn=undefined, thisArg=undefined) { var len = ToLength(arrayLike.length); // Steps 12-14. - var A = IsConstructor(C) ? new C(len) : NewDenseArray(len); + var A = IsConstructor(C) ? new C(len) : std_Array(len); // Steps 15-16. for (var k = 0; k < len; k++) { @@ -824,3 +822,17 @@ function ArrayFrom(items, mapfn=undefined, thisArg=undefined) { // Step 19. return A; } + +// ES2015 22.1.3.27 Array.prototype.toString. +function ArrayToString() { + // Steps 1-2. + var array = ToObject(this); + + // Steps 3-4. + var func = array.join; + + // Steps 5-6. + if (!IsCallable(func)) + return callFunction(std_Object_toString, array); + return callFunction(func, array); +} diff --git a/js/src/builtin/Map.js b/js/src/builtin/Map.js index ba05b41912..852e1895bd 100644 --- a/js/src/builtin/Map.js +++ b/js/src/builtin/Map.js @@ -47,11 +47,8 @@ function MapIteratorNext() { // Steps 8-9 (omitted). var mapIterationResultPair = iteratorTemp.mapIterationResultPair; - if (!mapIterationResultPair) { - mapIterationResultPair = iteratorTemp.mapIterationResultPair = NewDenseArray(2); - mapIterationResultPair[0] = null; - mapIterationResultPair[1] = null; - } + if (!mapIterationResultPair) + mapIterationResultPair = iteratorTemp.mapIterationResultPair = [null, null]; var retVal = {value: undefined, done: true}; diff --git a/js/src/builtin/Object.cpp b/js/src/builtin/Object.cpp index e691c1e860..768a07ceb0 100644 --- a/js/src/builtin/Object.cpp +++ b/js/src/builtin/Object.cpp @@ -13,6 +13,7 @@ #include "builtin/Eval.h" #include "frontend/BytecodeCompiler.h" +#include "jit/InlinableNatives.h" #include "vm/StringBuffer.h" #include "jsobjinlines.h" @@ -331,8 +332,8 @@ JS_BasicObjectToString(JSContext* cx, HandleObject obj) } /* ES5 15.2.4.2. Note steps 1 and 2 are errata. */ -static bool -obj_toString(JSContext* cx, unsigned argc, Value* vp) +bool +js::obj_toString(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); @@ -1026,7 +1027,7 @@ static const JSFunctionSpec object_static_methods[] = { JS_FN("is", obj_is, 2, 0), JS_FN("defineProperty", obj_defineProperty, 3, 0), JS_FN("defineProperties", obj_defineProperties, 2, 0), - JS_FN("create", obj_create, 2, 0), + JS_INLINABLE_FN("create", obj_create, 2, 0, ObjectCreate), JS_FN("getOwnPropertyNames", obj_getOwnPropertyNames, 1, 0), JS_FN("getOwnPropertySymbols", obj_getOwnPropertySymbols, 1, 0), JS_SELF_HOSTED_FN("isExtensible", "ObjectIsExtensible", 1, JSPROP_DEFINE_LATE), diff --git a/js/src/builtin/Object.h b/js/src/builtin/Object.h index de42404c5b..fa03f3ad01 100644 --- a/js/src/builtin/Object.h +++ b/js/src/builtin/Object.h @@ -57,6 +57,9 @@ obj_hasOwnProperty(JSContext* cx, unsigned argc, JS::Value* vp); bool obj_isExtensible(JSContext* cx, unsigned argc, JS::Value* vp); +bool +obj_toString(JSContext* cx, unsigned argc, JS::Value* vp); + // Exposed so SelfHosting.cpp can use it in the OwnPropertyKeys intrinsic bool GetOwnPropertyKeys(JSContext* cx, const JS::CallArgs& args, unsigned flags); diff --git a/js/src/builtin/SIMD.cpp b/js/src/builtin/SIMD.cpp index daf775a929..a078d376cd 100644 --- a/js/src/builtin/SIMD.cpp +++ b/js/src/builtin/SIMD.cpp @@ -21,6 +21,7 @@ #include "jsprf.h" #include "builtin/TypedObject.h" +#include "jit/InlinableNatives.h" #include "js/Value.h" #include "jsobjinlines.h" @@ -244,7 +245,7 @@ const JSFunctionSpec Float32x4Defn::TypedObjectMethods[] = { const JSFunctionSpec Float32x4Defn::Methods[] = { #define SIMD_FLOAT32X4_FUNCTION_ITEM(Name, Func, Operands) \ - JS_FN(#Name, js::simd_float32x4_##Name, Operands, 0), + JS_INLINABLE_FN(#Name, js::simd_float32x4_##Name, Operands, 0, SimdFloat32x4), FLOAT32X4_FUNCTION_LIST(SIMD_FLOAT32X4_FUNCTION_ITEM) #undef SIMD_FLOAT32x4_FUNCTION_ITEM JS_FS_END @@ -344,7 +345,7 @@ const JSFunctionSpec Int32x4Defn::TypedObjectMethods[] = { const JSFunctionSpec Int32x4Defn::Methods[] = { #define SIMD_INT32X4_FUNCTION_ITEM(Name, Func, Operands) \ - JS_FN(#Name, js::simd_int32x4_##Name, Operands, 0), + JS_INLINABLE_FN(#Name, js::simd_int32x4_##Name, Operands, 0, SimdInt32x4), INT32X4_FUNCTION_LIST(SIMD_INT32X4_FUNCTION_ITEM) #undef SIMD_INT32X4_FUNCTION_ITEM JS_FS_END diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index 3dacd17b89..d758e19098 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -19,6 +19,7 @@ #include "asmjs/AsmJSLink.h" #include "asmjs/AsmJSValidate.h" +#include "jit/InlinableNatives.h" #include "jit/JitFrameIterator.h" #include "js/Debug.h" #include "js/HashTable.h" @@ -1404,8 +1405,8 @@ GetObjectMetadata(JSContext* cx, unsigned argc, Value* vp) return true; } -bool -js::testingFunc_bailout(JSContext* cx, unsigned argc, Value* vp) +static bool +testingFunc_bailout(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); @@ -1414,8 +1415,8 @@ js::testingFunc_bailout(JSContext* cx, unsigned argc, Value* vp) return true; } -bool -js::testingFunc_inJit(JSContext* cx, unsigned argc, Value* vp) +static bool +testingFunc_inJit(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); @@ -1442,8 +1443,8 @@ js::testingFunc_inJit(JSContext* cx, unsigned argc, Value* vp) return true; } -bool -js::testingFunc_inIon(JSContext* cx, unsigned argc, Value* vp) +static bool +testingFunc_inIon(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); @@ -3014,7 +3015,7 @@ gc::ZealModeHelpText), "getObjectMetadata(obj)", " Get the metadata for an object."), - JS_FN_HELP("bailout", testingFunc_bailout, 0, 0, + JS_INLINABLE_FN_HELP("bailout", testingFunc_bailout, 0, 0, TestBailout, "bailout()", " Force a bailout out of ionmonkey (if running in ionmonkey)."), diff --git a/js/src/builtin/TestingFunctions.h b/js/src/builtin/TestingFunctions.h index 325c690e95..5e7a6e1a3b 100644 --- a/js/src/builtin/TestingFunctions.h +++ b/js/src/builtin/TestingFunctions.h @@ -14,21 +14,12 @@ namespace js { bool DefineTestingFunctions(JSContext* cx, HandleObject obj, bool fuzzingSafe); -bool -testingFunc_bailout(JSContext* cx, unsigned argc, Value* vp); - bool testingFunc_assertFloat32(JSContext* cx, unsigned argc, Value* vp); bool testingFunc_assertRecoveredOnBailout(JSContext* cx, unsigned argc, Value* vp); -bool -testingFunc_inJit(JSContext* cx, unsigned argc, Value* vp); - -bool -testingFunc_inIon(JSContext* cx, unsigned argc, Value* vp); - } /* namespace js */ #endif /* builtin_TestingFunctions_h */ diff --git a/js/src/jit-test/tests/basic/bug1190733.js b/js/src/jit-test/tests/basic/bug1190733.js new file mode 100644 index 0000000000..2b3f4a419d --- /dev/null +++ b/js/src/jit-test/tests/basic/bug1190733.js @@ -0,0 +1,7 @@ + +x = []; +Array.prototype.push.call(x, Uint8ClampedArray); +(function() { + x.length = 9; +})(); +Array.prototype.reverse.call(x); diff --git a/js/src/jit-test/tests/basic/bug1200108.js b/js/src/jit-test/tests/basic/bug1200108.js new file mode 100644 index 0000000000..1af8826364 --- /dev/null +++ b/js/src/jit-test/tests/basic/bug1200108.js @@ -0,0 +1,5 @@ +// |jit-test| error: 987 +var obj = {length: -1, 0: 0}; +Array.prototype.map.call(obj, function () { + throw 987; +}); diff --git a/js/src/jit/InlinableNatives.h b/js/src/jit/InlinableNatives.h index ef04c2b033..d3df3460e1 100644 --- a/js/src/jit/InlinableNatives.h +++ b/js/src/jit/InlinableNatives.h @@ -8,6 +8,7 @@ #define jit_InlinableNatives_h #define INLINABLE_NATIVE_LIST(_) \ + _(Array) \ _(ArrayIsArray) \ _(ArrayPop) \ _(ArrayShift) \ @@ -67,11 +68,56 @@ _(RegExpExec) \ _(RegExpTest) \ \ + _(String) \ _(StringSplit) \ _(StringCharCodeAt) \ _(StringFromCharCode) \ _(StringCharAt) \ - _(StringReplace) + _(StringReplace) \ + \ + _(ObjectCreate) \ + \ + _(CallBoundFunction) \ + \ + _(SimdInt32x4) \ + _(SimdFloat32x4) \ + \ + _(TestBailout) \ + _(TestAssertFloat32) \ + _(TestAssertRecoveredOnBailout) \ + \ + _(IntrinsicUnsafeSetReservedSlot) \ + _(IntrinsicUnsafeGetReservedSlot) \ + _(IntrinsicUnsafeGetObjectFromReservedSlot) \ + _(IntrinsicUnsafeGetInt32FromReservedSlot) \ + _(IntrinsicUnsafeGetStringFromReservedSlot) \ + _(IntrinsicUnsafeGetBooleanFromReservedSlot) \ + \ + _(IntrinsicIsCallable) \ + _(IntrinsicToObject) \ + _(IntrinsicIsObject) \ + _(IntrinsicToInteger) \ + _(IntrinsicToString) \ + _(IntrinsicIsConstructing) \ + _(IntrinsicSubstringKernel) \ + _(IntrinsicDefineDataProperty) \ + \ + _(IntrinsicIsArrayIterator) \ + _(IntrinsicIsMapIterator) \ + _(IntrinsicIsStringIterator) \ + \ + _(IntrinsicIsTypedArray) \ + _(IntrinsicIsPossiblyWrappedTypedArray) \ + _(IntrinsicTypedArrayLength) \ + _(IntrinsicSetDisjointTypedElements) \ + \ + _(IntrinsicObjectIsTypedObject) \ + _(IntrinsicObjectIsTransparentTypedObject) \ + _(IntrinsicObjectIsOpaqueTypedObject) \ + _(IntrinsicObjectIsTypeDescr) \ + _(IntrinsicTypeDescrIsSimpleType) \ + _(IntrinsicTypeDescrIsArrayType)\ + _(IntrinsicSetTypedObjectOffset) struct JSJitInfo; diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index 8baf2b9caf..d9044a8f62 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -842,6 +842,9 @@ class IonBuilder IonBuilder::InliningStatus boxSimd(CallInfo& callInfo, MInstruction* ins, InlineTypedObject* templateObj); + InliningStatus inlineSimdInt32x4(CallInfo& callInfo, JSNative native); + InliningStatus inlineSimdFloat32x4(CallInfo& callInfo, JSNative native); + template InliningStatus inlineBinarySimd(CallInfo& callInfo, JSNative native, typename T::Operation op, SimdTypeDescr::Type type); diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 65ad5bcc8b..af0fe13856 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -39,13 +39,19 @@ IonBuilder::InliningStatus IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target) { MOZ_ASSERT(target->isNative()); - JSNative native = target->native(); if (!optimizationInfo().inlineNative()) { trackOptimizationOutcome(TrackedOutcome::CantInlineDisabledIon); return InliningStatus_NotInlined; } + if (!target->jitInfo() || target->jitInfo()->type() != JSJitInfo::InlinableNative) { + // Reaching here means we tried to inline a native for which there is no + // Ion specialization. + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeNoSpecialization); + return InliningStatus_NotInlined; + } + // Default failure reason is observing an unsupported type. trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadType); @@ -56,255 +62,10 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target) return InliningStatus_NotInlined; } - if (native == ArrayConstructor) - return inlineArray(callInfo); - - if (native == StringConstructor) - return inlineStringObject(callInfo); - - // Object natives. - if (native == obj_create) - return inlineObjectCreate(callInfo); - if (native == intrinsic_DefineDataProperty) - return inlineDefineDataProperty(callInfo); - - // Slot intrinsics. - if (native == intrinsic_UnsafeSetReservedSlot) - return inlineUnsafeSetReservedSlot(callInfo); - if (native == intrinsic_UnsafeGetReservedSlot) - return inlineUnsafeGetReservedSlot(callInfo, MIRType_Value); - if (native == intrinsic_UnsafeGetObjectFromReservedSlot) - return inlineUnsafeGetReservedSlot(callInfo, MIRType_Object); - if (native == intrinsic_UnsafeGetInt32FromReservedSlot) - return inlineUnsafeGetReservedSlot(callInfo, MIRType_Int32); - if (native == intrinsic_UnsafeGetStringFromReservedSlot) - return inlineUnsafeGetReservedSlot(callInfo, MIRType_String); - if (native == intrinsic_UnsafeGetBooleanFromReservedSlot) - return inlineUnsafeGetReservedSlot(callInfo, MIRType_Boolean); - - // Utility intrinsics. - if (native == intrinsic_IsCallable) - return inlineIsCallable(callInfo); - if (native == intrinsic_ToObject) - return inlineToObject(callInfo); - if (native == intrinsic_IsObject) - return inlineIsObject(callInfo); - if (native == intrinsic_ToInteger) - return inlineToInteger(callInfo); - if (native == intrinsic_ToString) - return inlineToString(callInfo); - if (native == intrinsic_IsConstructing) - return inlineIsConstructing(callInfo); - if (native == intrinsic_SubstringKernel) - return inlineSubstringKernel(callInfo); - if (native == intrinsic_IsArrayIterator) - return inlineHasClass(callInfo, &ArrayIteratorObject::class_); - if (native == intrinsic_IsMapIterator) - return inlineHasClass(callInfo, &MapIteratorObject::class_); - if (native == intrinsic_IsStringIterator) - return inlineHasClass(callInfo, &StringIteratorObject::class_); - - // TypedArray intrinsics. - if (native == intrinsic_IsTypedArray) - return inlineIsTypedArray(callInfo); - if (native == intrinsic_IsPossiblyWrappedTypedArray) - return inlineIsPossiblyWrappedTypedArray(callInfo); - if (native == intrinsic_TypedArrayLength) - return inlineTypedArrayLength(callInfo); - if (native == intrinsic_SetDisjointTypedElements) - return inlineSetDisjointTypedElements(callInfo); - - // TypedObject intrinsics. - if (native == js::ObjectIsTypedObject) - return inlineHasClass(callInfo, - &OutlineTransparentTypedObject::class_, - &OutlineOpaqueTypedObject::class_, - &InlineTransparentTypedObject::class_, - &InlineOpaqueTypedObject::class_); - if (native == js::ObjectIsTransparentTypedObject) - return inlineHasClass(callInfo, - &OutlineTransparentTypedObject::class_, - &InlineTransparentTypedObject::class_); - if (native == js::ObjectIsOpaqueTypedObject) - return inlineHasClass(callInfo, - &OutlineOpaqueTypedObject::class_, - &InlineOpaqueTypedObject::class_); - if (native == js::ObjectIsTypeDescr) - return inlineObjectIsTypeDescr(callInfo); - if (native == js::TypeDescrIsSimpleType) - return inlineHasClass(callInfo, - &ScalarTypeDescr::class_, &ReferenceTypeDescr::class_); - if (native == js::TypeDescrIsArrayType) - return inlineHasClass(callInfo, &ArrayTypeDescr::class_); - if (native == js::SetTypedObjectOffset) - return inlineSetTypedObjectOffset(callInfo); - - // Testing Functions - if (native == testingFunc_bailout) - return inlineBailout(callInfo); - if (native == testingFunc_assertFloat32) - return inlineAssertFloat32(callInfo); - if (native == testingFunc_assertRecoveredOnBailout) - return inlineAssertRecoveredOnBailout(callInfo); - - // Bound function - if (native == js::CallOrConstructBoundFunction) - return inlineBoundFunction(callInfo, target); - - // Simd functions -#define INLINE_FLOAT32X4_SIMD_ARITH_(OP) \ - if (native == js::simd_float32x4_##OP) \ - return inlineBinarySimd(callInfo, native, MSimdBinaryArith::Op_##OP, \ - SimdTypeDescr::Float32x4); - -#define INLINE_INT32X4_SIMD_ARITH_(OP) \ - if (native == js::simd_int32x4_##OP) \ - return inlineBinarySimd(callInfo, native, MSimdBinaryArith::Op_##OP, \ - SimdTypeDescr::Int32x4); - - ARITH_COMMONX4_SIMD_OP(INLINE_INT32X4_SIMD_ARITH_) - ARITH_COMMONX4_SIMD_OP(INLINE_FLOAT32X4_SIMD_ARITH_) - BINARY_ARITH_FLOAT32X4_SIMD_OP(INLINE_FLOAT32X4_SIMD_ARITH_) -#undef INLINE_SIMD_ARITH_ -#undef INLINE_FLOAT32X4_SIMD_ARITH_ - -#define INLINE_SIMD_BITWISE_(OP) \ - if (native == js::simd_int32x4_##OP) \ - return inlineBinarySimd(callInfo, native, MSimdBinaryBitwise::OP##_, \ - SimdTypeDescr::Int32x4); \ - if (native == js::simd_float32x4_##OP) \ - return inlineBinarySimd(callInfo, native, MSimdBinaryBitwise::OP##_, \ - SimdTypeDescr::Float32x4); - - BITWISE_COMMONX4_SIMD_OP(INLINE_SIMD_BITWISE_) -#undef INLINE_SIMD_BITWISE_ - - if (native == js::simd_int32x4_shiftLeftByScalar) - return inlineBinarySimd(callInfo, native, MSimdShift::lsh, SimdTypeDescr::Int32x4); - if (native == js::simd_int32x4_shiftRightArithmeticByScalar) - return inlineBinarySimd(callInfo, native, MSimdShift::rsh, SimdTypeDescr::Int32x4); - if (native == js::simd_int32x4_shiftRightLogicalByScalar) - return inlineBinarySimd(callInfo, native, MSimdShift::ursh, SimdTypeDescr::Int32x4); - -#define INLINE_SIMD_COMPARISON_(OP) \ - if (native == js::simd_int32x4_##OP) \ - return inlineCompSimd(callInfo, native, MSimdBinaryComp::OP, SimdTypeDescr::Int32x4); \ - if (native == js::simd_float32x4_##OP) \ - return inlineCompSimd(callInfo, native, MSimdBinaryComp::OP, SimdTypeDescr::Float32x4); - - COMP_COMMONX4_TO_INT32X4_SIMD_OP(INLINE_SIMD_COMPARISON_) -#undef INLINE_SIMD_COMPARISON_ - - if (native == js::simd_int32x4_extractLane) - return inlineSimdExtractLane(callInfo, native, SimdTypeDescr::Int32x4); - if (native == js::simd_float32x4_extractLane) - return inlineSimdExtractLane(callInfo, native, SimdTypeDescr::Float32x4); - - if (native == js::simd_int32x4_replaceLane) - return inlineSimdReplaceLane(callInfo, native, SimdTypeDescr::Int32x4); - if (native == js::simd_float32x4_replaceLane) - return inlineSimdReplaceLane(callInfo, native, SimdTypeDescr::Float32x4); - - if (native == js::simd_int32x4_not) - return inlineUnarySimd(callInfo, native, MSimdUnaryArith::not_, SimdTypeDescr::Int32x4); - if (native == js::simd_int32x4_neg) - return inlineUnarySimd(callInfo, native, MSimdUnaryArith::neg, SimdTypeDescr::Int32x4); - -#define INLINE_SIMD_FLOAT32X4_UNARY_(OP) \ - if (native == js::simd_float32x4_##OP) \ - return inlineUnarySimd(callInfo, native, MSimdUnaryArith::OP, SimdTypeDescr::Float32x4); - - UNARY_ARITH_FLOAT32X4_SIMD_OP(INLINE_SIMD_FLOAT32X4_UNARY_) - INLINE_SIMD_FLOAT32X4_UNARY_(neg) -#undef INLINE_SIMD_FLOAT32X4_UNARY_ - - if (native == js::simd_float32x4_not) - return inlineUnarySimd(callInfo, native, MSimdUnaryArith::not_, SimdTypeDescr::Float32x4); - - typedef bool IsCast; - if (native == js::simd_float32x4_fromInt32x4) - return inlineSimdConvert(callInfo, native, IsCast(false), SimdTypeDescr::Int32x4, SimdTypeDescr::Float32x4); - if (native == js::simd_int32x4_fromFloat32x4) - return inlineSimdConvert(callInfo, native, IsCast(false), SimdTypeDescr::Float32x4, SimdTypeDescr::Int32x4); - if (native == js::simd_float32x4_fromInt32x4Bits) - return inlineSimdConvert(callInfo, native, IsCast(true), SimdTypeDescr::Int32x4, SimdTypeDescr::Float32x4); - if (native == js::simd_int32x4_fromFloat32x4Bits) - return inlineSimdConvert(callInfo, native, IsCast(true), SimdTypeDescr::Float32x4, SimdTypeDescr::Int32x4); - - if (native == js::simd_int32x4_splat) - return inlineSimdSplat(callInfo, native, SimdTypeDescr::Int32x4); - if (native == js::simd_float32x4_splat) - return inlineSimdSplat(callInfo, native, SimdTypeDescr::Float32x4); - - if (native == js::simd_int32x4_check) - return inlineSimdCheck(callInfo, native, SimdTypeDescr::Int32x4); - if (native == js::simd_float32x4_check) - return inlineSimdCheck(callInfo, native, SimdTypeDescr::Float32x4); - - typedef bool IsElementWise; - if (native == js::simd_int32x4_select) - return inlineSimdSelect(callInfo, native, IsElementWise(true), SimdTypeDescr::Int32x4); - if (native == js::simd_int32x4_selectBits) - return inlineSimdSelect(callInfo, native, IsElementWise(false), SimdTypeDescr::Int32x4); - if (native == js::simd_float32x4_select) - return inlineSimdSelect(callInfo, native, IsElementWise(true), SimdTypeDescr::Float32x4); - - if (native == js::simd_int32x4_swizzle) - return inlineSimdShuffle(callInfo, native, SimdTypeDescr::Int32x4, 1, 4); - if (native == js::simd_float32x4_swizzle) - return inlineSimdShuffle(callInfo, native, SimdTypeDescr::Float32x4, 1, 4); - if (native == js::simd_int32x4_shuffle) - return inlineSimdShuffle(callInfo, native, SimdTypeDescr::Int32x4, 2, 4); - if (native == js::simd_float32x4_shuffle) - return inlineSimdShuffle(callInfo, native, SimdTypeDescr::Float32x4, 2, 4); - - if (native == js::simd_int32x4_load) - return inlineSimdLoad(callInfo, native, SimdTypeDescr::Int32x4, 4); - if (native == js::simd_int32x4_load1) - return inlineSimdLoad(callInfo, native, SimdTypeDescr::Int32x4, 1); - if (native == js::simd_int32x4_load2) - return inlineSimdLoad(callInfo, native, SimdTypeDescr::Int32x4, 2); - if (native == js::simd_int32x4_load3) - return inlineSimdLoad(callInfo, native, SimdTypeDescr::Int32x4, 3); - - if (native == js::simd_float32x4_load) - return inlineSimdLoad(callInfo, native, SimdTypeDescr::Float32x4, 4); - if (native == js::simd_float32x4_load1) - return inlineSimdLoad(callInfo, native, SimdTypeDescr::Float32x4, 1); - if (native == js::simd_float32x4_load2) - return inlineSimdLoad(callInfo, native, SimdTypeDescr::Float32x4, 2); - if (native == js::simd_float32x4_load3) - return inlineSimdLoad(callInfo, native, SimdTypeDescr::Float32x4, 3); - - if (native == js::simd_int32x4_store) - return inlineSimdStore(callInfo, native, SimdTypeDescr::Int32x4, 4); - if (native == js::simd_int32x4_store1) - return inlineSimdStore(callInfo, native, SimdTypeDescr::Int32x4, 1); - if (native == js::simd_int32x4_store2) - return inlineSimdStore(callInfo, native, SimdTypeDescr::Int32x4, 2); - if (native == js::simd_int32x4_store3) - return inlineSimdStore(callInfo, native, SimdTypeDescr::Int32x4, 3); - if (native == js::simd_float32x4_store) - return inlineSimdStore(callInfo, native, SimdTypeDescr::Float32x4, 4); - if (native == js::simd_float32x4_store1) - return inlineSimdStore(callInfo, native, SimdTypeDescr::Float32x4, 1); - if (native == js::simd_float32x4_store2) - return inlineSimdStore(callInfo, native, SimdTypeDescr::Float32x4, 2); - if (native == js::simd_float32x4_store3) - return inlineSimdStore(callInfo, native, SimdTypeDescr::Float32x4, 3); - - if (native == js::simd_int32x4_bool) - return inlineSimdBool(callInfo, native, SimdTypeDescr::Int32x4); - - if (!target->jitInfo() || target->jitInfo()->type() != JSJitInfo::InlinableNative) { - // Reaching here means we tried to inline a native for which there is no - // Ion specialization. - trackOptimizationOutcome(TrackedOutcome::CantInlineNativeNoSpecialization); - return InliningStatus_NotInlined; - } - switch (InlinableNative inlNative = target->jitInfo()->inlinableNative) { // Array natives. + case InlinableNative::Array: + return inlineArray(callInfo); case InlinableNative::ArrayIsArray: return inlineArrayIsArray(callInfo); case InlinableNative::ArrayPop: @@ -419,6 +180,8 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target) return inlineRegExpTest(callInfo); // String natives. + case InlinableNative::String: + return inlineStringObject(callInfo); case InlinableNative::StringSplit: return inlineStringSplit(callInfo); case InlinableNative::StringCharCodeAt: @@ -429,6 +192,101 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target) return inlineStrCharAt(callInfo); case InlinableNative::StringReplace: return inlineStrReplace(callInfo); + + // Object natives. + case InlinableNative::ObjectCreate: + return inlineObjectCreate(callInfo); + + // Bound function. + case InlinableNative::CallBoundFunction: + return inlineBoundFunction(callInfo, target); + + // SIMD natives. + case InlinableNative::SimdInt32x4: + return inlineSimdInt32x4(callInfo, target->native()); + case InlinableNative::SimdFloat32x4: + return inlineSimdFloat32x4(callInfo, target->native()); + + // Testing functions. + case InlinableNative::TestBailout: + return inlineBailout(callInfo); + case InlinableNative::TestAssertFloat32: + return inlineAssertFloat32(callInfo); + case InlinableNative::TestAssertRecoveredOnBailout: + return inlineAssertRecoveredOnBailout(callInfo); + + // Slot intrinsics. + case InlinableNative::IntrinsicUnsafeSetReservedSlot: + return inlineUnsafeSetReservedSlot(callInfo); + case InlinableNative::IntrinsicUnsafeGetReservedSlot: + return inlineUnsafeGetReservedSlot(callInfo, MIRType_Value); + case InlinableNative::IntrinsicUnsafeGetObjectFromReservedSlot: + return inlineUnsafeGetReservedSlot(callInfo, MIRType_Object); + case InlinableNative::IntrinsicUnsafeGetInt32FromReservedSlot: + return inlineUnsafeGetReservedSlot(callInfo, MIRType_Int32); + case InlinableNative::IntrinsicUnsafeGetStringFromReservedSlot: + return inlineUnsafeGetReservedSlot(callInfo, MIRType_String); + case InlinableNative::IntrinsicUnsafeGetBooleanFromReservedSlot: + return inlineUnsafeGetReservedSlot(callInfo, MIRType_Boolean); + + // Utility intrinsics. + case InlinableNative::IntrinsicIsCallable: + return inlineIsCallable(callInfo); + case InlinableNative::IntrinsicToObject: + return inlineToObject(callInfo); + case InlinableNative::IntrinsicIsObject: + return inlineIsObject(callInfo); + case InlinableNative::IntrinsicToInteger: + return inlineToInteger(callInfo); + case InlinableNative::IntrinsicToString: + return inlineToString(callInfo); + case InlinableNative::IntrinsicIsConstructing: + return inlineIsConstructing(callInfo); + case InlinableNative::IntrinsicSubstringKernel: + return inlineSubstringKernel(callInfo); + case InlinableNative::IntrinsicIsArrayIterator: + return inlineHasClass(callInfo, &ArrayIteratorObject::class_); + case InlinableNative::IntrinsicIsMapIterator: + return inlineHasClass(callInfo, &MapIteratorObject::class_); + case InlinableNative::IntrinsicIsStringIterator: + return inlineHasClass(callInfo, &StringIteratorObject::class_); + case InlinableNative::IntrinsicDefineDataProperty: + return inlineDefineDataProperty(callInfo); + + // TypedArray intrinsics. + case InlinableNative::IntrinsicIsTypedArray: + return inlineIsTypedArray(callInfo); + case InlinableNative::IntrinsicIsPossiblyWrappedTypedArray: + return inlineIsPossiblyWrappedTypedArray(callInfo); + case InlinableNative::IntrinsicTypedArrayLength: + return inlineTypedArrayLength(callInfo); + case InlinableNative::IntrinsicSetDisjointTypedElements: + return inlineSetDisjointTypedElements(callInfo); + + // TypedObject intrinsics. + case InlinableNative::IntrinsicObjectIsTypedObject: + return inlineHasClass(callInfo, + &OutlineTransparentTypedObject::class_, + &OutlineOpaqueTypedObject::class_, + &InlineTransparentTypedObject::class_, + &InlineOpaqueTypedObject::class_); + case InlinableNative::IntrinsicObjectIsTransparentTypedObject: + return inlineHasClass(callInfo, + &OutlineTransparentTypedObject::class_, + &InlineTransparentTypedObject::class_); + case InlinableNative::IntrinsicObjectIsOpaqueTypedObject: + return inlineHasClass(callInfo, + &OutlineOpaqueTypedObject::class_, + &InlineOpaqueTypedObject::class_); + case InlinableNative::IntrinsicObjectIsTypeDescr: + return inlineObjectIsTypeDescr(callInfo); + case InlinableNative::IntrinsicTypeDescrIsSimpleType: + return inlineHasClass(callInfo, + &ScalarTypeDescr::class_, &ReferenceTypeDescr::class_); + case InlinableNative::IntrinsicTypeDescrIsArrayType: + return inlineHasClass(callInfo, &ArrayTypeDescr::class_); + case InlinableNative::IntrinsicSetTypedObjectOffset: + return inlineSetTypedObjectOffset(callInfo); } MOZ_CRASH("Shouldn't get here"); @@ -3170,6 +3028,182 @@ IonBuilder::inlineConstructTypedObject(CallInfo& callInfo, TypeDescr* descr) return InliningStatus_Inlined; } +IonBuilder::InliningStatus +IonBuilder::inlineSimdInt32x4(CallInfo& callInfo, JSNative native) +{ +#define INLINE_INT32X4_SIMD_ARITH_(OP) \ + if (native == js::simd_int32x4_##OP) \ + return inlineBinarySimd(callInfo, native, MSimdBinaryArith::Op_##OP, \ + SimdTypeDescr::Int32x4); + + ARITH_COMMONX4_SIMD_OP(INLINE_INT32X4_SIMD_ARITH_) +#undef INLINE_INT32X4_SIMD_ARITH_ + +#define INLINE_SIMD_BITWISE_(OP) \ + if (native == js::simd_int32x4_##OP) \ + return inlineBinarySimd(callInfo, native, MSimdBinaryBitwise::OP##_, \ + SimdTypeDescr::Int32x4); + + BITWISE_COMMONX4_SIMD_OP(INLINE_SIMD_BITWISE_) +#undef INLINE_SIMD_BITWISE_ + + if (native == js::simd_int32x4_shiftLeftByScalar) + return inlineBinarySimd(callInfo, native, MSimdShift::lsh, SimdTypeDescr::Int32x4); + if (native == js::simd_int32x4_shiftRightArithmeticByScalar) + return inlineBinarySimd(callInfo, native, MSimdShift::rsh, SimdTypeDescr::Int32x4); + if (native == js::simd_int32x4_shiftRightLogicalByScalar) + return inlineBinarySimd(callInfo, native, MSimdShift::ursh, SimdTypeDescr::Int32x4); + +#define INLINE_SIMD_COMPARISON_(OP) \ + if (native == js::simd_int32x4_##OP) \ + return inlineCompSimd(callInfo, native, MSimdBinaryComp::OP, SimdTypeDescr::Int32x4); + + COMP_COMMONX4_TO_INT32X4_SIMD_OP(INLINE_SIMD_COMPARISON_) +#undef INLINE_SIMD_COMPARISON_ + + if (native == js::simd_int32x4_extractLane) + return inlineSimdExtractLane(callInfo, native, SimdTypeDescr::Int32x4); + if (native == js::simd_int32x4_replaceLane) + return inlineSimdReplaceLane(callInfo, native, SimdTypeDescr::Int32x4); + + if (native == js::simd_int32x4_not) + return inlineUnarySimd(callInfo, native, MSimdUnaryArith::not_, SimdTypeDescr::Int32x4); + if (native == js::simd_int32x4_neg) + return inlineUnarySimd(callInfo, native, MSimdUnaryArith::neg, SimdTypeDescr::Int32x4); + + typedef bool IsCast; + if (native == js::simd_int32x4_fromFloat32x4) + return inlineSimdConvert(callInfo, native, IsCast(false), SimdTypeDescr::Float32x4, SimdTypeDescr::Int32x4); + if (native == js::simd_int32x4_fromFloat32x4Bits) + return inlineSimdConvert(callInfo, native, IsCast(true), SimdTypeDescr::Float32x4, SimdTypeDescr::Int32x4); + + if (native == js::simd_int32x4_splat) + return inlineSimdSplat(callInfo, native, SimdTypeDescr::Int32x4); + + if (native == js::simd_int32x4_check) + return inlineSimdCheck(callInfo, native, SimdTypeDescr::Int32x4); + + typedef bool IsElementWise; + if (native == js::simd_int32x4_select) + return inlineSimdSelect(callInfo, native, IsElementWise(true), SimdTypeDescr::Int32x4); + if (native == js::simd_int32x4_selectBits) + return inlineSimdSelect(callInfo, native, IsElementWise(false), SimdTypeDescr::Int32x4); + + if (native == js::simd_int32x4_swizzle) + return inlineSimdShuffle(callInfo, native, SimdTypeDescr::Int32x4, 1, 4); + if (native == js::simd_int32x4_shuffle) + return inlineSimdShuffle(callInfo, native, SimdTypeDescr::Int32x4, 2, 4); + + if (native == js::simd_int32x4_load) + return inlineSimdLoad(callInfo, native, SimdTypeDescr::Int32x4, 4); + if (native == js::simd_int32x4_load1) + return inlineSimdLoad(callInfo, native, SimdTypeDescr::Int32x4, 1); + if (native == js::simd_int32x4_load2) + return inlineSimdLoad(callInfo, native, SimdTypeDescr::Int32x4, 2); + if (native == js::simd_int32x4_load3) + return inlineSimdLoad(callInfo, native, SimdTypeDescr::Int32x4, 3); + + if (native == js::simd_int32x4_store) + return inlineSimdStore(callInfo, native, SimdTypeDescr::Int32x4, 4); + if (native == js::simd_int32x4_store1) + return inlineSimdStore(callInfo, native, SimdTypeDescr::Int32x4, 1); + if (native == js::simd_int32x4_store2) + return inlineSimdStore(callInfo, native, SimdTypeDescr::Int32x4, 2); + if (native == js::simd_int32x4_store3) + return inlineSimdStore(callInfo, native, SimdTypeDescr::Int32x4, 3); + + if (native == js::simd_int32x4_bool) + return inlineSimdBool(callInfo, native, SimdTypeDescr::Int32x4); + + return InliningStatus_NotInlined; +} + +IonBuilder::InliningStatus +IonBuilder::inlineSimdFloat32x4(CallInfo& callInfo, JSNative native) +{ + // Simd functions +#define INLINE_FLOAT32X4_SIMD_ARITH_(OP) \ + if (native == js::simd_float32x4_##OP) \ + return inlineBinarySimd(callInfo, native, MSimdBinaryArith::Op_##OP, \ + SimdTypeDescr::Float32x4); + + ARITH_COMMONX4_SIMD_OP(INLINE_FLOAT32X4_SIMD_ARITH_) + BINARY_ARITH_FLOAT32X4_SIMD_OP(INLINE_FLOAT32X4_SIMD_ARITH_) +#undef INLINE_FLOAT32X4_SIMD_ARITH_ + +#define INLINE_SIMD_BITWISE_(OP) \ + if (native == js::simd_float32x4_##OP) \ + return inlineBinarySimd(callInfo, native, MSimdBinaryBitwise::OP##_, \ + SimdTypeDescr::Float32x4); + + BITWISE_COMMONX4_SIMD_OP(INLINE_SIMD_BITWISE_) +#undef INLINE_SIMD_BITWISE_ + +#define INLINE_SIMD_COMPARISON_(OP) \ + if (native == js::simd_float32x4_##OP) \ + return inlineCompSimd(callInfo, native, MSimdBinaryComp::OP, SimdTypeDescr::Float32x4); + + COMP_COMMONX4_TO_INT32X4_SIMD_OP(INLINE_SIMD_COMPARISON_) +#undef INLINE_SIMD_COMPARISON_ + + if (native == js::simd_float32x4_extractLane) + return inlineSimdExtractLane(callInfo, native, SimdTypeDescr::Float32x4); + if (native == js::simd_float32x4_replaceLane) + return inlineSimdReplaceLane(callInfo, native, SimdTypeDescr::Float32x4); + +#define INLINE_SIMD_FLOAT32X4_UNARY_(OP) \ + if (native == js::simd_float32x4_##OP) \ + return inlineUnarySimd(callInfo, native, MSimdUnaryArith::OP, SimdTypeDescr::Float32x4); + + UNARY_ARITH_FLOAT32X4_SIMD_OP(INLINE_SIMD_FLOAT32X4_UNARY_) + INLINE_SIMD_FLOAT32X4_UNARY_(neg) +#undef INLINE_SIMD_FLOAT32X4_UNARY_ + + if (native == js::simd_float32x4_not) + return inlineUnarySimd(callInfo, native, MSimdUnaryArith::not_, SimdTypeDescr::Float32x4); + + typedef bool IsCast; + if (native == js::simd_float32x4_fromInt32x4) + return inlineSimdConvert(callInfo, native, IsCast(false), SimdTypeDescr::Int32x4, SimdTypeDescr::Float32x4); + if (native == js::simd_float32x4_fromInt32x4Bits) + return inlineSimdConvert(callInfo, native, IsCast(true), SimdTypeDescr::Int32x4, SimdTypeDescr::Float32x4); + + if (native == js::simd_float32x4_splat) + return inlineSimdSplat(callInfo, native, SimdTypeDescr::Float32x4); + + if (native == js::simd_float32x4_check) + return inlineSimdCheck(callInfo, native, SimdTypeDescr::Float32x4); + + typedef bool IsElementWise; + if (native == js::simd_float32x4_select) + return inlineSimdSelect(callInfo, native, IsElementWise(true), SimdTypeDescr::Float32x4); + + if (native == js::simd_float32x4_swizzle) + return inlineSimdShuffle(callInfo, native, SimdTypeDescr::Float32x4, 1, 4); + if (native == js::simd_float32x4_shuffle) + return inlineSimdShuffle(callInfo, native, SimdTypeDescr::Float32x4, 2, 4); + + if (native == js::simd_float32x4_load) + return inlineSimdLoad(callInfo, native, SimdTypeDescr::Float32x4, 4); + if (native == js::simd_float32x4_load1) + return inlineSimdLoad(callInfo, native, SimdTypeDescr::Float32x4, 1); + if (native == js::simd_float32x4_load2) + return inlineSimdLoad(callInfo, native, SimdTypeDescr::Float32x4, 2); + if (native == js::simd_float32x4_load3) + return inlineSimdLoad(callInfo, native, SimdTypeDescr::Float32x4, 3); + + if (native == js::simd_float32x4_store) + return inlineSimdStore(callInfo, native, SimdTypeDescr::Float32x4, 4); + if (native == js::simd_float32x4_store1) + return inlineSimdStore(callInfo, native, SimdTypeDescr::Float32x4, 1); + if (native == js::simd_float32x4_store2) + return inlineSimdStore(callInfo, native, SimdTypeDescr::Float32x4, 2); + if (native == js::simd_float32x4_store3) + return inlineSimdStore(callInfo, native, SimdTypeDescr::Float32x4, 3); + + return InliningStatus_NotInlined; +} + IonBuilder::InliningStatus IonBuilder::inlineConstructSimdObject(CallInfo& callInfo, SimdTypeDescr* descr) { diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index d55a1a2a7e..bb2b3b1f5e 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -1149,43 +1149,6 @@ ArrayJoin(JSContext* cx, CallArgs& args) return true; } -/* ES5 15.4.4.2. NB: The algorithm here differs from the one in ES3. */ -static bool -array_toString(JSContext* cx, unsigned argc, Value* vp) -{ - JS_CHECK_RECURSION(cx, return false); - - CallArgs args = CallArgsFromVp(argc, vp); - RootedObject obj(cx, ToObject(cx, args.thisv())); - if (!obj) - return false; - - RootedValue join(cx, args.calleev()); - if (!GetProperty(cx, obj, obj, cx->names().join, &join)) - return false; - - if (!IsCallable(join)) { - JSString* str = JS_BasicObjectToString(cx, obj); - if (!str) - return false; - args.rval().setString(str); - return true; - } - - InvokeArgs args2(cx); - if (!args2.init(0)) - return false; - - args2.setCallee(join); - args2.setThis(ObjectValue(*obj)); - - /* Do the call. */ - if (!Invoke(cx, args2)) - return false; - args.rval().set(args2.rval()); - return true; -} - /* ES5 15.4.4.3 */ static bool array_toLocaleString(JSContext* cx, unsigned argc, Value* vp) @@ -1282,10 +1245,10 @@ ArrayReverseDenseKernel(JSContext* cx, HandleObject obj, uint32_t length) /* Fill out the array's initialized length to its proper length. */ obj->as().ensureDenseInitializedLength(cx, length, 0); } else { - // Unboxed arrays can only be reversed if their initialized length + // Unboxed arrays can only be reversed here if their initialized length // matches their actual length. Otherwise the reversal will place holes // at the beginning of the array, which we don't support. - if (length != obj->as().length()) + if (length != obj->as().initializedLength()) return DenseElementResult::Incomplete; } @@ -3107,8 +3070,8 @@ static const JSFunctionSpec array_methods[] = { #if JS_HAS_TOSOURCE JS_FN(js_toSource_str, array_toSource, 0,0), #endif - JS_FN(js_toString_str, array_toString, 0,0), - JS_FN(js_toLocaleString_str,array_toLocaleString,0,0), + JS_SELF_HOSTED_FN(js_toString_str, "ArrayToString", 0,0), + JS_FN(js_toLocaleString_str, array_toLocaleString, 0,0), /* Perl-ish methods. */ JS_FN("join", array_join, 1,JSFUN_GENERIC_NATIVE), @@ -3147,7 +3110,6 @@ static const JSFunctionSpec array_methods[] = { /* ES7 additions */ JS_SELF_HOSTED_FN("includes", "ArrayIncludes", 2,0), - JS_FS_END }; @@ -3299,7 +3261,7 @@ const Class ArrayObject::class_ = { nullptr, /* construct */ nullptr, /* trace */ { - GenericCreateConstructor, + GenericCreateConstructor, CreateArrayPrototype, array_static_methods, nullptr, diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 323163aeaa..89b7c822c9 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -746,45 +746,8 @@ class AutoAssertNoException } }; -/* Exposed intrinsics so that Ion may inline them. */ -bool intrinsic_ToObject(JSContext* cx, unsigned argc, Value* vp); -bool intrinsic_IsObject(JSContext* cx, unsigned argc, Value* vp); -bool intrinsic_ToInteger(JSContext* cx, unsigned argc, Value* vp); -bool intrinsic_ToString(JSContext* cx, unsigned argc, Value* vp); -bool intrinsic_IsCallable(JSContext* cx, unsigned argc, Value* vp); -bool intrinsic_ThrowRangeError(JSContext* cx, unsigned argc, Value* vp); -bool intrinsic_ThrowTypeError(JSContext* cx, unsigned argc, Value* vp); -bool intrinsic_NewDenseArray(JSContext* cx, unsigned argc, Value* vp); -bool intrinsic_IsConstructing(JSContext* cx, unsigned argc, Value* vp); -bool intrinsic_SubstringKernel(JSContext* cx, unsigned argc, Value* vp); - -bool intrinsic_DefineDataProperty(JSContext* cx, unsigned argc, Value* vp); -bool intrinsic_UnsafeSetReservedSlot(JSContext* cx, unsigned argc, Value* vp); -bool intrinsic_UnsafeGetReservedSlot(JSContext* cx, unsigned argc, Value* vp); -bool intrinsic_UnsafeGetObjectFromReservedSlot(JSContext* cx, unsigned argc, Value* vp); -bool intrinsic_UnsafeGetInt32FromReservedSlot(JSContext* cx, unsigned argc, Value* vp); -bool intrinsic_UnsafeGetStringFromReservedSlot(JSContext* cx, unsigned argc, Value* vp); -bool intrinsic_UnsafeGetBooleanFromReservedSlot(JSContext* cx, unsigned argc, Value* vp); -bool intrinsic_IsPackedArray(JSContext* cx, unsigned argc, Value* vp); - +/* Exposed intrinsics for the JITs. */ bool intrinsic_IsSuspendedStarGenerator(JSContext* cx, unsigned argc, Value* vp); -bool intrinsic_IsArrayIterator(JSContext* cx, unsigned argc, Value* vp); -bool intrinsic_IsMapIterator(JSContext* cx, unsigned argc, Value* vp); -bool intrinsic_IsStringIterator(JSContext* cx, unsigned argc, Value* vp); - -bool intrinsic_IsArrayBuffer(JSContext* cx, unsigned argc, Value* vp); - -bool intrinsic_IsTypedArray(JSContext* cx, unsigned argc, Value* vp); -bool intrinsic_IsPossiblyWrappedTypedArray(JSContext* cx, unsigned argc, Value* vp); -bool intrinsic_TypedArrayBuffer(JSContext* cx, unsigned argc, Value* vp); -bool intrinsic_TypedArrayByteOffset(JSContext* cx, unsigned argc, Value* vp); -bool intrinsic_TypedArrayElementShift(JSContext* cx, unsigned argc, Value* vp); -bool intrinsic_TypedArrayLength(JSContext* cx, unsigned argc, Value* vp); - -bool intrinsic_MoveTypedArrayElements(JSContext* cx, unsigned argc, Value* vp); -bool intrinsic_SetFromTypedArrayApproach(JSContext* cx, unsigned argc, Value* vp); -bool intrinsic_SetDisjointTypedElements(JSContext* cx, unsigned argc, Value* vp); -bool intrinsic_SetOverlappingTypedElements(JSContext* cx, unsigned argc, Value* vp); class AutoLockForExclusiveAccess { diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index ce7667ac5e..2440b854ca 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -244,6 +244,9 @@ JS_DefineFunctionsWithHelp(JSContext* cx, HandleObject obj, const JSFunctionSpec if (!fun) return false; + if (fs->jitInfo) + fun->setJitInfo(fs->jitInfo); + if (fs->usage) { if (!DefineHelpProperty(cx, fun, "usage", fs->usage)) return false; diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 660e79b475..766976053a 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -250,12 +250,16 @@ struct JSFunctionSpecWithHelp { JSNative call; uint16_t nargs; uint16_t flags; + const JSJitInfo* jitInfo; const char* usage; const char* help; }; #define JS_FN_HELP(name,call,nargs,flags,usage,help) \ - {name, call, nargs, (flags) | JSPROP_ENUMERATE | JSFUN_STUB_GSOPS, usage, help} + {name, call, nargs, (flags) | JSPROP_ENUMERATE | JSFUN_STUB_GSOPS, nullptr, usage, help} +#define JS_INLINABLE_FN_HELP(name,call,nargs,flags,native,usage,help) \ + {name, call, nargs, (flags) | JSPROP_ENUMERATE | JSFUN_STUB_GSOPS, &js::jit::JitInfo_##native,\ + usage, help} #define JS_FS_HELP_END \ {nullptr, nullptr, 0, 0, nullptr, nullptr} diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index c98db704b9..86245c58c9 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -31,6 +31,7 @@ #include "frontend/BytecodeCompiler.h" #include "frontend/TokenStream.h" #include "gc/Marking.h" +#include "jit/InlinableNatives.h" #include "jit/Ion.h" #include "jit/JitFrameIterator.h" #include "js/CallNonGenericMethod.h" @@ -1377,6 +1378,8 @@ JSFunction::initBoundFunction(JSContext* cx, HandleObject target, HandleValue th self->initSlotRange(BOUND_FUNCTION_RESERVED_SLOTS, args, argslen); + self->setJitInfo(&jit::JitInfo_CallBoundFunction); + return true; } diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index 4c0b773da6..c9a40b0b01 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -4193,7 +4193,8 @@ js::InitStringClass(JSContext* cx, HandleObject obj) /* Now create the String function. */ RootedFunction ctor(cx); - ctor = global->createConstructor(cx, StringConstructor, cx->names().String, 1); + ctor = global->createConstructor(cx, StringConstructor, cx->names().String, 1, + AllocKind::FUNCTION, &jit::JitInfo_String); if (!ctor) return nullptr; diff --git a/js/src/jswrapper.h b/js/src/jswrapper.h index 66fc93dde4..4744f72eb3 100644 --- a/js/src/jswrapper.h +++ b/js/src/jswrapper.h @@ -162,6 +162,57 @@ class JS_FRIEND_API(CrossCompartmentWrapper) : public Wrapper static const CrossCompartmentWrapper singletonWithPrototype; }; +class JS_FRIEND_API(OpaqueCrossCompartmentWrapper) : public CrossCompartmentWrapper +{ + public: + explicit MOZ_CONSTEXPR OpaqueCrossCompartmentWrapper() : CrossCompartmentWrapper(0) + { } + + /* Standard internal methods. */ + virtual bool getOwnPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id, + MutableHandle desc) const override; + virtual bool defineProperty(JSContext* cx, HandleObject wrapper, HandleId id, + Handle desc, + ObjectOpResult& result) const override; + virtual bool ownPropertyKeys(JSContext* cx, HandleObject wrapper, + AutoIdVector& props) const override; + virtual bool delete_(JSContext* cx, HandleObject wrapper, HandleId id, + ObjectOpResult& result) const override; + virtual bool enumerate(JSContext* cx, HandleObject wrapper, + MutableHandleObject objp) const override; + virtual bool getPrototype(JSContext* cx, HandleObject wrapper, + MutableHandleObject protop) const override; + virtual bool setPrototype(JSContext* cx, HandleObject wrapper, HandleObject proto, + ObjectOpResult& result) const override; + virtual bool setImmutablePrototype(JSContext* cx, HandleObject wrapper, + bool* succeeded) const override; + virtual bool preventExtensions(JSContext* cx, HandleObject wrapper, + ObjectOpResult& result) const override; + virtual bool isExtensible(JSContext* cx, HandleObject wrapper, bool* extensible) const override; + virtual bool has(JSContext* cx, HandleObject wrapper, HandleId id, + bool* bp) const override; + virtual bool get(JSContext* cx, HandleObject wrapper, HandleObject receiver, + HandleId id, MutableHandleValue vp) const override; + virtual bool set(JSContext* cx, HandleObject wrapper, HandleId id, HandleValue v, + HandleValue receiver, ObjectOpResult& result) const override; + virtual bool call(JSContext* cx, HandleObject wrapper, const CallArgs& args) const override; + virtual bool construct(JSContext* cx, HandleObject wrapper, const CallArgs& args) const override; + + /* SpiderMonkey extensions. */ + virtual bool getPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id, + MutableHandle desc) const override; + virtual bool hasOwn(JSContext* cx, HandleObject wrapper, HandleId id, + bool* bp) const override; + virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject wrapper, + AutoIdVector& props) const override; + virtual bool objectClassIs(HandleObject obj, ESClassValue classValue, JSContext* cx) const; + virtual const char* className(JSContext* cx, HandleObject wrapper) const override; + virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const; + virtual bool defaultValue(JSContext* cx, HandleObject obj, JSType hint, MutableHandleValue vp) const; + + static const OpaqueCrossCompartmentWrapper singleton; +}; + /* * Base class for security wrappers. A security wrapper is potentially hiding * all or part of some wrapped object thus SecurityWrapper defaults to denying diff --git a/js/src/moz.build b/js/src/moz.build index 1ce99155a5..bf5ea7be9a 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -275,6 +275,7 @@ UNIFIED_SOURCES += [ 'proxy/CrossCompartmentWrapper.cpp', 'proxy/DeadObjectProxy.cpp', 'proxy/DirectProxyHandler.cpp', + 'proxy/OpaqueCrossCompartmentWrapper.cpp', 'proxy/Proxy.cpp', 'proxy/ScriptedDirectProxyHandler.cpp', 'proxy/ScriptedIndirectProxyHandler.cpp', diff --git a/js/src/proxy/BaseProxyHandler.cpp b/js/src/proxy/BaseProxyHandler.cpp index f4fb49721b..82ce07851f 100644 --- a/js/src/proxy/BaseProxyHandler.cpp +++ b/js/src/proxy/BaseProxyHandler.cpp @@ -31,6 +31,28 @@ BaseProxyHandler::has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) return true; } +bool +BaseProxyHandler::getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id, + MutableHandle desc) const +{ + assertEnteredPolicy(cx, proxy, id, GET | SET | GET_PROPERTY_DESCRIPTOR); + + if (!getOwnPropertyDescriptor(cx, proxy, id, desc)) + return false; + if (desc.object()) + return true; + + RootedObject proto(cx); + if (!GetPrototype(cx, proxy, &proto)) + return false; + if (!proto) { + MOZ_ASSERT(!desc.object()); + return true; + } + return GetPropertyDescriptor(cx, proto, id, desc); +} + + bool BaseProxyHandler::hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const { diff --git a/js/src/proxy/DeadObjectProxy.cpp b/js/src/proxy/DeadObjectProxy.cpp index c89065c4a1..77296c3c2c 100644 --- a/js/src/proxy/DeadObjectProxy.cpp +++ b/js/src/proxy/DeadObjectProxy.cpp @@ -14,14 +14,6 @@ using namespace js; using namespace js::gc; -bool -DeadObjectProxy::getPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id, - MutableHandle desc) const -{ - JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); - return false; -} - bool DeadObjectProxy::getOwnPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id, MutableHandle desc) const diff --git a/js/src/proxy/DeadObjectProxy.h b/js/src/proxy/DeadObjectProxy.h index 6e7176089a..c7639dae2a 100644 --- a/js/src/proxy/DeadObjectProxy.h +++ b/js/src/proxy/DeadObjectProxy.h @@ -19,27 +19,26 @@ class DeadObjectProxy : public BaseProxyHandler { } /* Standard internal methods. */ - virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, + virtual bool getOwnPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id, MutableHandle desc) const override; - virtual bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id, + virtual bool defineProperty(JSContext* cx, HandleObject wrapper, HandleId id, Handle desc, - ObjectOpResult &result) const override; - virtual bool ownPropertyKeys(JSContext *cx, HandleObject wrapper, + ObjectOpResult& result) const override; + virtual bool ownPropertyKeys(JSContext* cx, HandleObject wrapper, AutoIdVector& props) const override; - virtual bool delete_(JSContext *cx, HandleObject wrapper, HandleId id, - ObjectOpResult &result) const override; - virtual bool enumerate(JSContext *cx, HandleObject wrapper, MutableHandleObject objp) const override; - virtual bool getPrototype(JSContext *cx, HandleObject proxy, + virtual bool delete_(JSContext* cx, HandleObject wrapper, HandleId id, + ObjectOpResult& result) const override; + virtual bool enumerate(JSContext* cx, HandleObject wrapper, MutableHandleObject objp) const override; + virtual bool getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject protop) const override; - virtual bool preventExtensions(JSContext *cx, HandleObject proxy, - ObjectOpResult &result) const override; - virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const override; - virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const override; - virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const override; + virtual bool preventExtensions(JSContext* cx, HandleObject proxy, + ObjectOpResult& result) const override; + virtual bool isExtensible(JSContext* cx, HandleObject proxy, bool* extensible) const override; + virtual bool call(JSContext* cx, HandleObject proxy, const CallArgs& args) const override; + virtual bool construct(JSContext* cx, HandleObject proxy, const CallArgs& args) const override; /* SpiderMonkey extensions. */ - virtual bool getPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id, - MutableHandle desc) const override; + // BaseProxyHandler::getPropertyDescriptor will throw by calling getOwnPropertyDescriptor. virtual bool nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl, const CallArgs& args) const override; virtual bool hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, diff --git a/js/src/proxy/OpaqueCrossCompartmentWrapper.cpp b/js/src/proxy/OpaqueCrossCompartmentWrapper.cpp new file mode 100644 index 0000000000..f92e48618b --- /dev/null +++ b/js/src/proxy/OpaqueCrossCompartmentWrapper.cpp @@ -0,0 +1,183 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jswrapper.h" + +#include "jsobjinlines.h" + +using namespace js; + +bool +OpaqueCrossCompartmentWrapper::getOwnPropertyDescriptor(JSContext* cx, + HandleObject wrapper, + HandleId id, + MutableHandle desc) const +{ + desc.object().set(nullptr); + return true; +} + +bool +OpaqueCrossCompartmentWrapper::defineProperty(JSContext* cx, HandleObject wrapper, HandleId id, + Handle desc, + ObjectOpResult& result) const +{ + return result.succeed(); +} + +bool +OpaqueCrossCompartmentWrapper::ownPropertyKeys(JSContext* cx, HandleObject wrapper, + AutoIdVector& props) const +{ + return true; +} + +bool +OpaqueCrossCompartmentWrapper::delete_(JSContext* cx, HandleObject wrapper, HandleId id, + ObjectOpResult& result) const +{ + return result.succeed(); +} + +bool +OpaqueCrossCompartmentWrapper::enumerate(JSContext* cx, HandleObject wrapper, + MutableHandleObject objp) const +{ + return BaseProxyHandler::enumerate(cx, wrapper, objp); +} + +bool +OpaqueCrossCompartmentWrapper::getPrototype(JSContext* cx, HandleObject proxy, + MutableHandleObject protop) const +{ + protop.set(nullptr); + return true; +} + +bool +OpaqueCrossCompartmentWrapper::setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto, + ObjectOpResult& result) const +{ + return result.succeed(); +} + +bool +OpaqueCrossCompartmentWrapper::setImmutablePrototype(JSContext* cx, HandleObject proxy, + bool* succeeded) const +{ + *succeeded = false; + return true; +} + +bool +OpaqueCrossCompartmentWrapper::preventExtensions(JSContext* cx, HandleObject wrapper, + ObjectOpResult& result) const +{ + return result.failCantPreventExtensions(); +} + +bool +OpaqueCrossCompartmentWrapper::isExtensible(JSContext* cx, HandleObject wrapper, + bool* extensible) const +{ + *extensible = true; + return true; +} + +bool +OpaqueCrossCompartmentWrapper::has(JSContext* cx, HandleObject wrapper, HandleId id, + bool* bp) const +{ + return BaseProxyHandler::has(cx, wrapper, id, bp); +} + +bool +OpaqueCrossCompartmentWrapper::get(JSContext* cx, HandleObject wrapper, HandleObject receiver, + HandleId id, MutableHandleValue vp) const +{ + return BaseProxyHandler::get(cx, wrapper, receiver, id, vp); +} + +bool +OpaqueCrossCompartmentWrapper::set(JSContext* cx, HandleObject wrapper, HandleId id, + HandleValue v, HandleValue receiver, + ObjectOpResult& result) const +{ + return BaseProxyHandler::set(cx, wrapper, id, v, receiver, result); +} + +bool +OpaqueCrossCompartmentWrapper::call(JSContext* cx, HandleObject wrapper, + const CallArgs& args) const +{ + RootedValue v(cx, ObjectValue(*wrapper)); + ReportIsNotFunction(cx, v); + return false; +} + +bool +OpaqueCrossCompartmentWrapper::construct(JSContext* cx, HandleObject wrapper, + const CallArgs& args) const +{ + RootedValue v(cx, ObjectValue(*wrapper)); + ReportIsNotFunction(cx, v); + return false; +} + +bool +OpaqueCrossCompartmentWrapper::getPropertyDescriptor(JSContext* cx, + HandleObject wrapper, + HandleId id, + MutableHandle desc) const +{ + return BaseProxyHandler::getPropertyDescriptor(cx, wrapper, id, desc); +} + +bool +OpaqueCrossCompartmentWrapper::hasOwn(JSContext* cx, HandleObject wrapper, HandleId id, + bool* bp) const +{ + return BaseProxyHandler::hasOwn(cx, wrapper, id, bp); +} + +bool +OpaqueCrossCompartmentWrapper::getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject wrapper, + AutoIdVector& props) const +{ + return BaseProxyHandler::getOwnEnumerablePropertyKeys(cx, wrapper, props); +} + +bool +OpaqueCrossCompartmentWrapper::objectClassIs(HandleObject obj, ESClassValue classValue, + JSContext* cx) const +{ + return false; +} + +const char* +OpaqueCrossCompartmentWrapper::className(JSContext* cx, + HandleObject proxy) const +{ + return "Opaque"; +} + +JSString* +OpaqueCrossCompartmentWrapper::fun_toString(JSContext* cx, HandleObject proxy, + unsigned indent) const +{ + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO, js_Function_str, + js_toString_str, "object"); + return nullptr; +} + +bool +OpaqueCrossCompartmentWrapper::defaultValue(JSContext* cx, HandleObject wrapper, JSType hint, + MutableHandleValue vp) const +{ + return OrdinaryToPrimitive(cx, wrapper, hint, vp); +} + +const OpaqueCrossCompartmentWrapper OpaqueCrossCompartmentWrapper::singleton; diff --git a/js/src/proxy/ScriptedDirectProxyHandler.cpp b/js/src/proxy/ScriptedDirectProxyHandler.cpp index 6a476697a7..9a70e2b515 100644 --- a/js/src/proxy/ScriptedDirectProxyHandler.cpp +++ b/js/src/proxy/ScriptedDirectProxyHandler.cpp @@ -289,28 +289,6 @@ ScriptedDirectProxyHandler::isExtensible(JSContext* cx, HandleObject proxy, bool return true; } -// Corresponds to the "standard" property descriptor getOwn/getPrototype dance. It's so explicit -// here because ScriptedDirectProxyHandler allows script visibility for this operation. -bool -ScriptedDirectProxyHandler::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, - MutableHandle desc) const -{ - JS_CHECK_RECURSION(cx, return false); - - if (!GetOwnPropertyDescriptor(cx, proxy, id, desc)) - return false; - if (desc.object()) - return true; - RootedObject proto(cx); - if (!GetPrototype(cx, proxy, &proto)) - return false; - if (!proto) { - MOZ_ASSERT(!desc.object()); - return true; - } - return GetPropertyDescriptor(cx, proto, id, desc); -} - // ES6 (5 April 2014) 9.5.5 Proxy.[[GetOwnProperty]](P) bool ScriptedDirectProxyHandler::getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id, diff --git a/js/src/proxy/ScriptedDirectProxyHandler.h b/js/src/proxy/ScriptedDirectProxyHandler.h index fdc671b2f3..93708e4923 100644 --- a/js/src/proxy/ScriptedDirectProxyHandler.h +++ b/js/src/proxy/ScriptedDirectProxyHandler.h @@ -19,41 +19,39 @@ class ScriptedDirectProxyHandler : public BaseProxyHandler { { } /* Standard internal methods. */ - virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, + virtual bool getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id, MutableHandle desc) const override; - virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id, + virtual bool defineProperty(JSContext* cx, HandleObject proxy, HandleId id, Handle desc, - ObjectOpResult &result) const override; - virtual bool ownPropertyKeys(JSContext *cx, HandleObject proxy, - AutoIdVector &props) const override; - virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id, - ObjectOpResult &result) const override; - virtual bool enumerate(JSContext *cx, HandleObject proxy, MutableHandleObject objp) const override; + ObjectOpResult& result) const override; + virtual bool ownPropertyKeys(JSContext* cx, HandleObject proxy, + AutoIdVector& props) const override; + virtual bool delete_(JSContext* cx, HandleObject proxy, HandleId id, + ObjectOpResult& result) const override; + virtual bool enumerate(JSContext* cx, HandleObject proxy, MutableHandleObject objp) const override; /* These two are standard internal methods but aren't implemented to spec yet. */ - virtual bool getPrototype(JSContext *cx, HandleObject proxy, + virtual bool getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject protop) const override; - virtual bool setPrototype(JSContext *cx, HandleObject proxy, HandleObject proto, - ObjectOpResult &result) const override; + virtual bool setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto, + ObjectOpResult& result) const override; /* Non-standard, but needed to handle revoked proxies. */ - virtual bool setImmutablePrototype(JSContext *cx, HandleObject proxy, - bool *succeeded) const override; + virtual bool setImmutablePrototype(JSContext* cx, HandleObject proxy, + bool* succeeded) const override; - virtual bool preventExtensions(JSContext *cx, HandleObject proxy, + virtual bool preventExtensions(JSContext* cx, HandleObject proxy, ObjectOpResult &result) const override; - virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const override; + virtual bool isExtensible(JSContext* cx, HandleObject proxy, bool* extensible) const override; - virtual bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const override; - virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, + virtual bool has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const override; + virtual bool get(JSContext* cx, HandleObject proxy, HandleObject receiver, HandleId id, MutableHandleValue vp) const override; - virtual bool set(JSContext *cx, HandleObject proxy, HandleId id, HandleValue v, - HandleValue receiver, ObjectOpResult &result) const override; - virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const override; - virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const override; + virtual bool set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v, + HandleValue receiver, ObjectOpResult& result) const override; + virtual bool call(JSContext* cx, HandleObject proxy, const CallArgs& args) const override; + virtual bool construct(JSContext* cx, HandleObject proxy, const CallArgs& args) const override; /* SpiderMonkey extensions. */ - virtual bool getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id, - MutableHandle desc) const override; virtual bool hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const override { return BaseProxyHandler::hasOwn(cx, proxy, id, bp); } diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index a4090f7846..5d0f4efc94 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -59,6 +59,7 @@ #include "frontend/Parser.h" #include "gc/GCInternals.h" #include "jit/arm/Simulator-arm.h" +#include "jit/InlinableNatives.h" #include "jit/Ion.h" #include "jit/JitcodeMap.h" #include "jit/OptimizationTracking.h" @@ -4817,11 +4818,12 @@ static const JSFunctionSpecWithHelp fuzzing_unsafe_functions[] = { " arguments[0] (of the call to nestedShell) will be argv[1], arguments[1] will\n" " be argv[2], etc."), - JS_FN_HELP("assertFloat32", testingFunc_assertFloat32, 2, 0, + JS_INLINABLE_FN_HELP("assertFloat32", testingFunc_assertFloat32, 2, 0, TestAssertFloat32, "assertFloat32(value, isFloat32)", " In IonMonkey only, asserts that value has (resp. hasn't) the MIRType_Float32 if isFloat32 is true (resp. false)."), - JS_FN_HELP("assertRecoveredOnBailout", testingFunc_assertRecoveredOnBailout, 2, 0, + JS_INLINABLE_FN_HELP("assertRecoveredOnBailout", testingFunc_assertRecoveredOnBailout, 2, 0, +TestAssertRecoveredOnBailout, "assertRecoveredOnBailout(var)", " In IonMonkey only, asserts that variable has RecoveredOnBailout flag."), diff --git a/js/src/tests/ecma_6/Date/toPrimitive.js b/js/src/tests/ecma_6/Date/toPrimitive.js new file mode 100644 index 0000000000..11346b1ae8 --- /dev/null +++ b/js/src/tests/ecma_6/Date/toPrimitive.js @@ -0,0 +1,62 @@ +// ES6 20.3.4.45 Date.prototype[@@toPrimitive](hint) + +// The toPrimitive method throws if the this value isn't an object. +var toPrimitive = Date.prototype[Symbol.toPrimitive]; +assertThrowsInstanceOf(() => toPrimitive.call(undefined, "default"), TypeError); +assertThrowsInstanceOf(() => toPrimitive.call(3, "default"), TypeError); + +// It doesn't have to be a Date object, though. +var obj = { + toString() { return "str"; }, + valueOf() { return "val"; } +}; +assertEq(toPrimitive.call(obj, "number"), "val"); +assertEq(toPrimitive.call(obj, "string"), "str"); +assertEq(toPrimitive.call(obj, "default"), "str"); + +// It throws if the hint argument is missing or not one of the three allowed values. +assertThrowsInstanceOf(() => toPrimitive.call(obj), TypeError); +assertThrowsInstanceOf(() => toPrimitive.call(obj, undefined), TypeError); +assertThrowsInstanceOf(() => toPrimitive.call(obj, "boolean"), TypeError); +assertThrowsInstanceOf(() => toPrimitive.call(obj, ["number"]), TypeError); +assertThrowsInstanceOf(() => toPrimitive.call(obj, {toString() { throw "FAIL"; }}), TypeError); + +// The next few tests cover the OrdinaryToPrimitive algorithm, specified in +// ES6 7.1.1 ToPrimitive(input [, PreferredType]). + +// Date.prototype.toString or .valueOf can be overridden. +var dateobj = new Date(); +Date.prototype.toString = function () { + assertEq(this, dateobj); + return 14; +}; +Date.prototype.valueOf = function () { + return "92"; +}; +assertEq(dateobj[Symbol.toPrimitive]("number"), "92"); +assertEq(dateobj[Symbol.toPrimitive]("string"), 14); +assertEq(dateobj[Symbol.toPrimitive]("default"), 14); +assertEq(dateobj == 14, true); // equality comparison: passes "default" + +// If this.toString is a non-callable value, this.valueOf is called instead. +Date.prototype.toString = {}; +assertEq(dateobj[Symbol.toPrimitive]("string"), "92"); +assertEq(dateobj[Symbol.toPrimitive]("default"), "92"); + +// And vice versa. +Date.prototype.toString = function () { return 15; }; +Date.prototype.valueOf = "ponies"; +assertEq(dateobj[Symbol.toPrimitive]("number"), 15); + +// If neither is callable, it throws a TypeError. +Date.prototype.toString = "ponies"; +assertThrowsInstanceOf(() => dateobj[Symbol.toPrimitive]("default"), TypeError); + +// Surface features. +assertEq(toPrimitive.name, "[Symbol.toPrimitive]"); +var desc = Object.getOwnPropertyDescriptor(Date.prototype, Symbol.toPrimitive); +assertEq(desc.configurable, true); +assertEq(desc.enumerable, false); +assertEq(desc.writable, false); + +reportCompare(0, 0); diff --git a/js/src/tests/ecma_6/Object/toPrimitive-callers.js b/js/src/tests/ecma_6/Object/toPrimitive-callers.js new file mode 100644 index 0000000000..ffeef2225c --- /dev/null +++ b/js/src/tests/ecma_6/Object/toPrimitive-callers.js @@ -0,0 +1,57 @@ +// Check all the algorithms that call ToPrimitive. Confirm that they're passing +// the correct hint, per spec. + +var STRING = "xyzzy"; +var NUMBER = 42; + +function assertCallsToPrimitive(f, expectedHint, expectedResult) { + var hint = undefined; + var testObj = { + [Symbol.toPrimitive](h) { + assertEq(hint, undefined); + hint = h; + return h === "number" ? NUMBER : STRING; + } + }; + var result = f(testObj); + assertEq(hint, expectedHint, String(f)); + assertEq(result, expectedResult, String(f)); +} + +// ToNumber +assertCallsToPrimitive(Number, "number", NUMBER); + +// ToString +assertCallsToPrimitive(String, "string", STRING); + +// ToPropertyKey +var obj = {[STRING]: "pass"}; +assertCallsToPrimitive(key => obj[key], "string", "pass"); + +// Abstract Relational Comparison +assertCallsToPrimitive(x => x >= 42, "number", true); +assertCallsToPrimitive(x => x > "42", "number", false); + +// Abstract Equality Comparison +assertCallsToPrimitive(x => x != STRING, "default", false); +assertCallsToPrimitive(x => STRING == x, "default", true); +assertCallsToPrimitive(x => x == NUMBER, "default", false); +assertCallsToPrimitive(x => NUMBER != x, "default", true); + +// Addition +assertCallsToPrimitive(x => 1 + x, "default", "1" + STRING); +assertCallsToPrimitive(x => "" + x, "default", STRING); + +// Date constructor +assertCallsToPrimitive(x => (new Date(x)).valueOf(), "default", Number(STRING)); + +// Date.prototype.toJSON +var expected = "a suffusion of yellow"; +function testJSON(x) { + x.toJSON = Date.prototype.toJSON; + x.toISOString = function () { return expected; }; + return JSON.stringify(x); +} +assertCallsToPrimitive(testJSON, "number", JSON.stringify(expected)); + +reportCompare(0, 0); diff --git a/js/src/tests/ecma_6/Object/toPrimitive.js b/js/src/tests/ecma_6/Object/toPrimitive.js new file mode 100644 index 0000000000..0904bd19a9 --- /dev/null +++ b/js/src/tests/ecma_6/Object/toPrimitive.js @@ -0,0 +1,101 @@ +// ES6 7.1.1 ToPrimitive(input [, PreferredType]) specifies a new extension +// point in the language. Objects can override the behavior of ToPrimitive +// somewhat by supporting the method obj[@@toPrimitive](hint). +// +// (Rationale: ES5 had a [[DefaultValue]] internal method, overridden only by +// Date objects. The change in ES6 is to make [[DefaultValue]] a plain old +// method. This allowed ES6 to eliminate the [[DefaultValue]] internal method, +// simplifying the meta-object protocol and thus proxies.) + +// obj[Symbol.toPrimitive]() is called whenever the ToPrimitive algorithm is invoked. +var expectedThis, expectedHint; +var obj = { + [Symbol.toPrimitive](hint, ...rest) { + assertEq(this, expectedThis); + assertEq(hint, expectedHint); + assertEq(rest.length, 0); + return 2015; + } +}; +expectedThis = obj; +expectedHint = "string"; +assertEq(String(obj), "2015"); +expectedHint = "number"; +assertEq(Number(obj), 2015); + +// It is called even through proxies. +var proxy = new Proxy(obj, {}); +expectedThis = proxy; +expectedHint = "default"; +assertEq("ES" + proxy, "ES2015"); + +// It is called even through additional proxies and the prototype chain. +proxy = new Proxy(Object.create(proxy), {}); +expectedThis = proxy; +expectedHint = "default"; +assertEq("ES" + (proxy + 1), "ES2016"); + +// It is not called if the operand is already a primitive. +var ok = true; +for (var constructor of [Boolean, Number, String, Symbol]) { + constructor.prototype[Symbol.toPrimitive] = function () { + ok = false; + throw "FAIL"; + }; +} +assertEq(Number(true), 1); +assertEq(Number(77.7), 77.7); +assertEq(Number("123"), 123); +assertThrowsInstanceOf(() => Number(Symbol.iterator), TypeError); +assertEq(String(true), "true"); +assertEq(String(77.7), "77.7"); +assertEq(String("123"), "123"); +assertEq(String(Symbol.iterator), "Symbol(Symbol.iterator)"); +assertEq(ok, true); + +// Converting a primitive symbol to another primitive type throws even if you +// delete the @@toPrimitive method from Symbol.prototype. +delete Symbol.prototype[Symbol.toPrimitive]; +var sym = Symbol("ok"); +assertThrowsInstanceOf(() => `${sym}`, TypeError); +assertThrowsInstanceOf(() => Number(sym), TypeError); +assertThrowsInstanceOf(() => "" + sym, TypeError); + +// However, having deleted that method, converting a Symbol wrapper object does +// work: it calls Symbol.prototype.toString(). +obj = Object(sym); +assertEq(String(obj), "Symbol(ok)"); +assertEq(`${obj}`, "Symbol(ok)"); + +// Deleting valueOf as well makes numeric conversion also call toString(). +delete Symbol.prototype.valueOf; +delete Object.prototype.valueOf; +assertEq(Number(obj), NaN); +Symbol.prototype.toString = function () { return "2060"; }; +assertEq(Number(obj), 2060); + +// Deleting Date.prototype[Symbol.toPrimitive] changes the result of addition +// involving Date objects. +var d = new Date; +assertEq(0 + d, 0 + d.toString()); +delete Date.prototype[Symbol.toPrimitive]; +assertEq(0 + d, 0 + d.valueOf()); + +// If @@toPrimitive, .toString, and .valueOf are all missing, we get a +// particular sequence of property accesses, followed by a TypeError exception. +var log = []; +function doGet(target, propertyName, receiver) { + log.push(propertyName); +} +var handler = new Proxy({}, { + get(target, trapName, receiver) { + if (trapName !== "get") + throw `FAIL: system tried to access handler method: ${uneval(trapName)}`; + return doGet; + } +}); +proxy = new Proxy(Object.create(null), handler); +assertThrowsInstanceOf(() => proxy == 0, TypeError); +assertDeepEq(log, [Symbol.toPrimitive, "valueOf", "toString"]); + +reportCompare(0, 0); diff --git a/js/src/tests/ecma_6/Symbol/toPrimitive.js b/js/src/tests/ecma_6/Symbol/toPrimitive.js new file mode 100644 index 0000000000..06262f99c6 --- /dev/null +++ b/js/src/tests/ecma_6/Symbol/toPrimitive.js @@ -0,0 +1,39 @@ +// ES6 19.4.3.4 Symbol.prototype[@@toPrimitive](hint) + +// This method gets the primitive symbol from a Symbol wrapper object. +var sym = Symbol.for("truth") +var obj = Object(sym); +assertEq(obj[Symbol.toPrimitive]("default"), sym); + +// The hint argument is ignored. +assertEq(obj[Symbol.toPrimitive]("number"), sym); +assertEq(obj[Symbol.toPrimitive]("string"), sym); +assertEq(obj[Symbol.toPrimitive](), sym); +assertEq(obj[Symbol.toPrimitive](Math.atan2), sym); + +// The this value can also be a primitive symbol. +assertEq(sym[Symbol.toPrimitive](), sym); + +// Or a wrapper to a Symbol object in another compartment. +var obj2 = newGlobal().Object(sym); +assertEq(obj2[Symbol.toPrimitive]("default"), sym); + +// Otherwise a TypeError is thrown. +var symbolToPrimitive = Symbol.prototype[Symbol.toPrimitive]; +var nonSymbols = [ + undefined, null, true, 13, NaN, "justice", {}, [sym], + symbolToPrimitive, + new Proxy(obj, {}) +]; +for (var value of nonSymbols) { + assertThrowsInstanceOf(() => symbolToPrimitive.call(value, "string"), TypeError); +} + +// Surface features: +assertEq(symbolToPrimitive.name, "[Symbol.toPrimitive]"); +var desc = Object.getOwnPropertyDescriptor(Symbol.prototype, Symbol.toPrimitive); +assertEq(desc.configurable, true); +assertEq(desc.enumerable, false); +assertEq(desc.writable, false); + +reportCompare(0, 0); diff --git a/js/src/tests/ecma_6/TypedArray/includes.js b/js/src/tests/ecma_6/TypedArray/includes.js index 15e98f73ca..fcc274f2c4 100644 --- a/js/src/tests/ecma_6/TypedArray/includes.js +++ b/js/src/tests/ecma_6/TypedArray/includes.js @@ -11,9 +11,6 @@ const constructors = [ ]; for (var constructor of constructors) { - if (!("includes" in constructor.prototype)) - break; - assertEq(constructor.prototype.includes.length, 1); assertEq(new constructor([1, 2, 3]).includes(1), true); diff --git a/js/src/tests/ecma_7/Array/includes.js b/js/src/tests/ecma_7/Array/includes.js index f7daebf05d..aa439571ce 100644 --- a/js/src/tests/ecma_7/Array/includes.js +++ b/js/src/tests/ecma_7/Array/includes.js @@ -8,42 +8,40 @@ var summary = "Implement Array.prototype.includes"; print(BUGNUMBER + ": " + summary); -if ('includes' in []) { - assertEq(typeof [].includes, "function"); - assertEq([].includes.length, 1); +assertEq(typeof [].includes, "function"); +assertEq([].includes.length, 1); - assertTrue([1, 2, 3].includes(2)); - assertTrue([1,,2].includes(2)); - assertTrue([1, 2, 3].includes(2, 1)); - assertTrue([1, 2, 3].includes(2, -2)); - assertTrue([1, 2, 3].includes(2, -100)); - assertTrue([Object, Function, Array].includes(Function)); - assertTrue([-0].includes(0)); - assertTrue([NaN].includes(NaN)); - assertTrue([,].includes()); - assertTrue(staticIncludes("123", "2")); - assertTrue(staticIncludes({length: 3, 1: 2}, 2)); - assertTrue(staticIncludes({length: 3, 1: 2, get 3(){throw ""}}, 2)); - assertTrue(staticIncludes({length: 3, get 1() {return 2}}, 2)); - assertTrue(staticIncludes({__proto__: {1: 2}, length: 3}, 2)); - assertTrue(staticIncludes(new Proxy([1], {get(){return 2}}), 2)); +assertTrue([1, 2, 3].includes(2)); +assertTrue([1,,2].includes(2)); +assertTrue([1, 2, 3].includes(2, 1)); +assertTrue([1, 2, 3].includes(2, -2)); +assertTrue([1, 2, 3].includes(2, -100)); +assertTrue([Object, Function, Array].includes(Function)); +assertTrue([-0].includes(0)); +assertTrue([NaN].includes(NaN)); +assertTrue([,].includes()); +assertTrue(staticIncludes("123", "2")); +assertTrue(staticIncludes({length: 3, 1: 2}, 2)); +assertTrue(staticIncludes({length: 3, 1: 2, get 3(){throw ""}}, 2)); +assertTrue(staticIncludes({length: 3, get 1() {return 2}}, 2)); +assertTrue(staticIncludes({__proto__: {1: 2}, length: 3}, 2)); +assertTrue(staticIncludes(new Proxy([1], {get(){return 2}}), 2)); - assertFalse([1, 2, 3].includes("2")); - assertFalse([1, 2, 3].includes(2, 2)); - assertFalse([1, 2, 3].includes(2, -1)); - assertFalse([undefined].includes(NaN)); - assertFalse([{}].includes({})); - assertFalse(staticIncludes({length: 3, 1: 2}, 2, 2)); - assertFalse(staticIncludes({length: 3, get 0(){delete this[1]}, 1: 2}, 2)); - assertFalse(staticIncludes({length: -100, 0: 1}, 1)); +assertFalse([1, 2, 3].includes("2")); +assertFalse([1, 2, 3].includes(2, 2)); +assertFalse([1, 2, 3].includes(2, -1)); +assertFalse([undefined].includes(NaN)); +assertFalse([{}].includes({})); +assertFalse(staticIncludes({length: 3, 1: 2}, 2, 2)); +assertFalse(staticIncludes({length: 3, get 0(){delete this[1]}, 1: 2}, 2)); +assertFalse(staticIncludes({length: -100, 0: 1}, 1)); - assertThrowsInstanceOf(() => staticIncludes(), TypeError); - assertThrowsInstanceOf(() => staticIncludes(null), TypeError); - assertThrowsInstanceOf(() => staticIncludes({get length(){throw TypeError()}}), TypeError); - assertThrowsInstanceOf(() => staticIncludes({length: 3, get 1() {throw TypeError()}}, 2), TypeError); - assertThrowsInstanceOf(() => staticIncludes({__proto__: {get 1() {throw TypeError()}}, length: 3}, 2), TypeError); - assertThrowsInstanceOf(() => staticIncludes(new Proxy([1], {get(){throw TypeError()}})), TypeError); -} +assertThrowsInstanceOf(() => staticIncludes(), TypeError); +assertThrowsInstanceOf(() => staticIncludes(null), TypeError); +assertThrowsInstanceOf(() => staticIncludes({get length(){throw TypeError()}}), TypeError); +assertThrowsInstanceOf(() => staticIncludes({length: 3, get 1() {throw TypeError()}}, 2), TypeError); +assertThrowsInstanceOf(() => staticIncludes({__proto__: {get 1() {throw TypeError()}}, length: 3}, 2), TypeError); +assertThrowsInstanceOf(() => staticIncludes(new Proxy([1], {get(){throw TypeError()}})), TypeError); function assertTrue(v) { assertEq(v, true); diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp index f20d08df78..90a30a4625 100644 --- a/js/src/vm/GlobalObject.cpp +++ b/js/src/vm/GlobalObject.cpp @@ -448,16 +448,23 @@ GlobalObject::warnOnceAbout(JSContext* cx, HandleObject obj, WarnOnceFlag flag, return true; } -JSFunction * -GlobalObject::createConstructor(JSContext *cx, Native ctor, JSAtom *nameArg, unsigned length, - gc::AllocKind kind) +JSFunction* +GlobalObject::createConstructor(JSContext* cx, Native ctor, JSAtom* nameArg, unsigned length, + gc::AllocKind kind, const JSJitInfo* jitInfo) { RootedAtom name(cx, nameArg); - return NewNativeConstructor(cx, ctor, length, name, kind); + JSFunction* fun = NewNativeConstructor(cx, ctor, length, name, kind); + if (!fun) + return nullptr; + + if (jitInfo) + fun->setJitInfo(jitInfo); + + return fun; } -static NativeObject * -CreateBlankProto(JSContext *cx, const Class *clasp, HandleObject proto, HandleObject global) +static NativeObject* +CreateBlankProto(JSContext* cx, const Class* clasp, HandleObject proto, HandleObject global) { MOZ_ASSERT(clasp != &JSFunction::class_); diff --git a/js/src/vm/GlobalObject.h b/js/src/vm/GlobalObject.h index e8a6db4974..c0d9d82ba4 100644 --- a/js/src/vm/GlobalObject.h +++ b/js/src/vm/GlobalObject.h @@ -306,7 +306,8 @@ class GlobalObject : public NativeObject */ JSFunction* createConstructor(JSContext* cx, JSNative ctor, JSAtom* name, unsigned length, - gc::AllocKind kind = gc::AllocKind::FUNCTION); + gc::AllocKind kind = gc::AllocKind::FUNCTION, + const JSJitInfo* jitInfo = nullptr); /* * Create an object to serve as [[Prototype]] for instances of the given @@ -899,14 +900,14 @@ typedef HashSet, SystemAllocPolicy> * for ClassSpecs. */ -template +template JSObject* GenericCreateConstructor(JSContext* cx, JSProtoKey key) { // Note - We duplicate the trick from ClassName() so that we don't need to // include jsatominlines.h here. PropertyName* name = (&cx->names().Null)[key]; - return cx->global()->createConstructor(cx, ctor, name, length, kind); + return cx->global()->createConstructor(cx, ctor, name, length, kind, jitInfo); } inline JSObject* diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index dc20f17935..7c04e629cc 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -29,6 +29,7 @@ #include "builtin/TypedObject.h" #include "builtin/WeakSetObject.h" #include "gc/Marking.h" +#include "jit/InlinableNatives.h" #include "js/Date.h" #include "vm/Compression.h" #include "vm/GeneratorObject.h" @@ -58,8 +59,8 @@ selfHosting_ErrorReporter(JSContext* cx, const char* message, JSErrorReport* rep PrintError(cx, stderr, message, report, true); } -bool -js::intrinsic_ToObject(JSContext* cx, unsigned argc, Value* vp) +static bool +intrinsic_ToObject(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); RootedValue val(cx, args[0]); @@ -70,8 +71,8 @@ js::intrinsic_ToObject(JSContext* cx, unsigned argc, Value* vp) return true; } -bool -js::intrinsic_IsObject(JSContext* cx, unsigned argc, Value* vp) +static bool +intrinsic_IsObject(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); Value val = args[0]; @@ -80,8 +81,8 @@ js::intrinsic_IsObject(JSContext* cx, unsigned argc, Value* vp) return true; } -bool -js::intrinsic_ToInteger(JSContext* cx, unsigned argc, Value* vp) +static bool +intrinsic_ToInteger(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); double result; @@ -91,8 +92,8 @@ js::intrinsic_ToInteger(JSContext* cx, unsigned argc, Value* vp) return true; } -bool -js::intrinsic_ToString(JSContext* cx, unsigned argc, Value* vp) +static bool +intrinsic_ToString(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); RootedString str(cx); @@ -103,7 +104,7 @@ js::intrinsic_ToString(JSContext* cx, unsigned argc, Value* vp) return true; } -bool +static bool intrinsic_ToPropertyKey(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); @@ -115,8 +116,8 @@ intrinsic_ToPropertyKey(JSContext* cx, unsigned argc, Value* vp) return true; } -bool -js::intrinsic_IsCallable(JSContext* cx, unsigned argc, Value* vp) +static bool +intrinsic_IsCallable(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); args.rval().setBoolean(IsCallable(args[0])); @@ -166,8 +167,8 @@ intrinsic_GetBuiltinConstructor(JSContext* cx, unsigned argc, Value* vp) return true; } -bool -js::intrinsic_SubstringKernel(JSContext* cx, unsigned argc, Value* vp) +static bool +intrinsic_SubstringKernel(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); MOZ_ASSERT(args[0].isString()); @@ -231,8 +232,8 @@ ThrowErrorWithType(JSContext* cx, JSExnType type, const CallArgs& args) errorArgs[0].ptr(), errorArgs[1].ptr(), errorArgs[2].ptr()); } -bool -js::intrinsic_ThrowRangeError(JSContext* cx, unsigned argc, Value* vp) +static bool +intrinsic_ThrowRangeError(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); MOZ_ASSERT(args.length() >= 1); @@ -241,8 +242,8 @@ js::intrinsic_ThrowRangeError(JSContext* cx, unsigned argc, Value* vp) return false; } -bool -js::intrinsic_ThrowTypeError(JSContext* cx, unsigned argc, Value* vp) +static bool +intrinsic_ThrowTypeError(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); MOZ_ASSERT(args.length() >= 1); @@ -324,33 +325,8 @@ intrinsic_DecompileArg(JSContext* cx, unsigned argc, Value* vp) return true; } -/* - * NewDenseArray(length): Allocates and returns a new dense array with - * the given length where all values are initialized to holes. - */ -bool -js::intrinsic_NewDenseArray(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - - // Check that index is an int32 - if (!args[0].isInt32()) { - JS_ReportError(cx, "Expected int32 as second argument"); - return false; - } - uint32_t length = args[0].toInt32(); - - // Make a new buffer and initialize it up to length. - RootedObject buffer(cx, NewFullyAllocatedArrayForCallingAllocationSite(cx, length)); - if (!buffer) - return false; - - args.rval().setObject(*buffer); - return true; -} - -bool -js::intrinsic_DefineDataProperty(JSContext* cx, unsigned argc, Value* vp) +static bool +intrinsic_DefineDataProperty(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); @@ -397,8 +373,8 @@ js::intrinsic_DefineDataProperty(JSContext* cx, unsigned argc, Value* vp) return true; } -bool -js::intrinsic_UnsafeSetReservedSlot(JSContext* cx, unsigned argc, Value* vp) +static bool +intrinsic_UnsafeSetReservedSlot(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); MOZ_ASSERT(args.length() == 3); @@ -410,8 +386,8 @@ js::intrinsic_UnsafeSetReservedSlot(JSContext* cx, unsigned argc, Value* vp) return true; } -bool -js::intrinsic_UnsafeGetReservedSlot(JSContext* cx, unsigned argc, Value* vp) +static bool +intrinsic_UnsafeGetReservedSlot(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); MOZ_ASSERT(args.length() == 2); @@ -422,8 +398,8 @@ js::intrinsic_UnsafeGetReservedSlot(JSContext* cx, unsigned argc, Value* vp) return true; } -bool -js::intrinsic_UnsafeGetObjectFromReservedSlot(JSContext* cx, unsigned argc, Value* vp) +static bool +intrinsic_UnsafeGetObjectFromReservedSlot(JSContext* cx, unsigned argc, Value* vp) { if (!intrinsic_UnsafeGetReservedSlot(cx, argc, vp)) return false; @@ -431,8 +407,8 @@ js::intrinsic_UnsafeGetObjectFromReservedSlot(JSContext* cx, unsigned argc, Valu return true; } -bool -js::intrinsic_UnsafeGetInt32FromReservedSlot(JSContext* cx, unsigned argc, Value* vp) +static bool +intrinsic_UnsafeGetInt32FromReservedSlot(JSContext* cx, unsigned argc, Value* vp) { if (!intrinsic_UnsafeGetReservedSlot(cx, argc, vp)) return false; @@ -440,8 +416,8 @@ js::intrinsic_UnsafeGetInt32FromReservedSlot(JSContext* cx, unsigned argc, Value return true; } -bool -js::intrinsic_UnsafeGetStringFromReservedSlot(JSContext* cx, unsigned argc, Value* vp) +static bool +intrinsic_UnsafeGetStringFromReservedSlot(JSContext* cx, unsigned argc, Value* vp) { if (!intrinsic_UnsafeGetReservedSlot(cx, argc, vp)) return false; @@ -449,8 +425,8 @@ js::intrinsic_UnsafeGetStringFromReservedSlot(JSContext* cx, unsigned argc, Valu return true; } -bool -js::intrinsic_UnsafeGetBooleanFromReservedSlot(JSContext* cx, unsigned argc, Value* vp) +static bool +intrinsic_UnsafeGetBooleanFromReservedSlot(JSContext* cx, unsigned argc, Value* vp) { if (!intrinsic_UnsafeGetReservedSlot(cx, argc, vp)) return false; @@ -458,14 +434,14 @@ js::intrinsic_UnsafeGetBooleanFromReservedSlot(JSContext* cx, unsigned argc, Val return true; } -bool -js::intrinsic_IsPackedArray(JSContext* cx, unsigned argc, Value* vp) +static bool +intrinsic_IsPackedArray(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); MOZ_ASSERT(args.length() == 1); MOZ_ASSERT(args[0].isObject()); - JSObject *obj = &args[0].toObject(); + JSObject* obj = &args[0].toObject(); bool isPacked = obj->is() && !obj->hasLazyGroup() && !obj->group()->hasAllFlags(OBJECT_FLAG_NON_PACKED) && obj->as().getDenseInitializedLength() == @@ -499,7 +475,7 @@ intrinsic_NewArrayIterator(JSContext* cx, unsigned argc, Value* vp) if (!proto) return false; - JSObject *obj = NewObjectWithGivenProto(cx, &ArrayIteratorObject::class_, proto); + JSObject* obj = NewObjectWithGivenProto(cx, &ArrayIteratorObject::class_, proto); if (!obj) return false; @@ -507,8 +483,8 @@ intrinsic_NewArrayIterator(JSContext* cx, unsigned argc, Value* vp) return true; } -bool -js::intrinsic_IsArrayIterator(JSContext* cx, unsigned argc, Value* vp) +static bool +intrinsic_IsArrayIterator(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); MOZ_ASSERT(args.length() == 1); @@ -518,8 +494,8 @@ js::intrinsic_IsArrayIterator(JSContext* cx, unsigned argc, Value* vp) return true; } -bool -js::intrinsic_IsMapIterator(JSContext* cx, unsigned argc, Value* vp) +static bool +intrinsic_IsMapIterator(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); MOZ_ASSERT(args.length() == 1); @@ -529,7 +505,7 @@ js::intrinsic_IsMapIterator(JSContext* cx, unsigned argc, Value* vp) return true; } -bool +static bool intrinsic_GetNextMapEntryForIterator(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); @@ -554,7 +530,7 @@ intrinsic_NewStringIterator(JSContext* cx, unsigned argc, Value* vp) if (!proto) return false; - JSObject *obj = NewObjectWithGivenProto(cx, &StringIteratorObject::class_, proto); + JSObject* obj = NewObjectWithGivenProto(cx, &StringIteratorObject::class_, proto); if (!obj) return false; @@ -562,8 +538,8 @@ intrinsic_NewStringIterator(JSContext* cx, unsigned argc, Value* vp) return true; } -bool -js::intrinsic_IsStringIterator(JSContext* cx, unsigned argc, Value* vp) +static bool +intrinsic_IsStringIterator(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); MOZ_ASSERT(args.length() == 1); @@ -616,8 +592,8 @@ intrinsic_StarGeneratorObjectIsClosed(JSContext* cx, unsigned argc, Value* vp) return true; } -bool -js::intrinsic_MoveTypedArrayElements(JSContext *cx, unsigned argc, Value *vp) +static bool +intrinsic_MoveTypedArrayElements(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); MOZ_ASSERT(args.length() == 4); @@ -706,8 +682,8 @@ DangerouslyUnwrapTypedArray(JSContext* cx, JSObject* obj) } // ES6 draft 20150403 22.2.3.22.2, steps 12-24, 29. -bool -js::intrinsic_SetFromTypedArrayApproach(JSContext* cx, unsigned argc, Value* vp) +static bool +intrinsic_SetFromTypedArrayApproach(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); MOZ_ASSERT(args.length() == 4); @@ -951,8 +927,8 @@ js::SetDisjointTypedElements(TypedArrayObject* target, uint32_t targetOffset, unsafeSrcDataCrossCompartment, unsafeSrcTypeCrossCompartment, count); } -bool -js::intrinsic_SetDisjointTypedElements(JSContext* cx, unsigned argc, Value* vp) +static bool +intrinsic_SetDisjointTypedElements(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); MOZ_ASSERT(args.length() == 3); @@ -977,8 +953,8 @@ js::intrinsic_SetDisjointTypedElements(JSContext* cx, unsigned argc, Value* vp) return true; } -bool -js::intrinsic_SetOverlappingTypedElements(JSContext* cx, unsigned argc, Value* vp) +static bool +intrinsic_SetOverlappingTypedElements(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); MOZ_ASSERT(args.length() == 3); @@ -1106,8 +1082,8 @@ intrinsic_GeneratorSetClosed(JSContext* cx, unsigned argc, Value* vp) return true; } -bool -js::intrinsic_IsArrayBuffer(JSContext* cx, unsigned argc, Value* vp) +static bool +intrinsic_IsArrayBuffer(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); MOZ_ASSERT(args.length() == 1); @@ -1117,8 +1093,8 @@ js::intrinsic_IsArrayBuffer(JSContext* cx, unsigned argc, Value* vp) return true; } -bool -js::intrinsic_IsTypedArray(JSContext* cx, unsigned argc, Value* vp) +static bool +intrinsic_IsTypedArray(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); MOZ_ASSERT(args.length() == 1); @@ -1128,8 +1104,8 @@ js::intrinsic_IsTypedArray(JSContext* cx, unsigned argc, Value* vp) return true; } -bool -js::intrinsic_IsPossiblyWrappedTypedArray(JSContext* cx, unsigned argc, Value* vp) +static bool +intrinsic_IsPossiblyWrappedTypedArray(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); MOZ_ASSERT(args.length() == 1); @@ -1149,8 +1125,8 @@ js::intrinsic_IsPossiblyWrappedTypedArray(JSContext* cx, unsigned argc, Value* v return true; } -bool -js::intrinsic_TypedArrayBuffer(JSContext* cx, unsigned argc, Value* vp) +static bool +intrinsic_TypedArrayBuffer(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); MOZ_ASSERT(args.length() == 1); @@ -1164,8 +1140,8 @@ js::intrinsic_TypedArrayBuffer(JSContext* cx, unsigned argc, Value* vp) return true; } -bool -js::intrinsic_TypedArrayByteOffset(JSContext* cx, unsigned argc, Value* vp) +static bool +intrinsic_TypedArrayByteOffset(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); MOZ_ASSERT(args.length() == 1); @@ -1175,8 +1151,8 @@ js::intrinsic_TypedArrayByteOffset(JSContext* cx, unsigned argc, Value* vp) return true; } -bool -js::intrinsic_TypedArrayElementShift(JSContext* cx, unsigned argc, Value* vp) +static bool +intrinsic_TypedArrayElementShift(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); MOZ_ASSERT(args.length() == 1); @@ -1190,8 +1166,8 @@ js::intrinsic_TypedArrayElementShift(JSContext* cx, unsigned argc, Value* vp) } // Return the value of [[ArrayLength]] internal slot of the TypedArray -bool -js::intrinsic_TypedArrayLength(JSContext* cx, unsigned argc, Value* vp) +static bool +intrinsic_TypedArrayLength(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); MOZ_ASSERT(args.length() == 1); @@ -1285,8 +1261,8 @@ intrinsic_RuntimeDefaultLocale(JSContext* cx, unsigned argc, Value* vp) return true; } -bool -js::intrinsic_IsConstructing(JSContext* cx, unsigned argc, Value* vp) +static bool +intrinsic_IsConstructing(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); MOZ_ASSERT(args.length() == 0); @@ -1327,6 +1303,7 @@ intrinsic_ConstructorForTypedArray(JSContext* cx, unsigned argc, Value* vp) // Additionally, a set of C++-implemented helper functions is defined on the // self-hosting global. static const JSFunctionSpec intrinsic_functions[] = { + JS_FN("std_Array", ArrayConstructor, 1,0), JS_FN("std_Array_join", array_join, 1,0), JS_FN("std_Array_push", array_push, 1,0), JS_FN("std_Array_pop", array_pop, 0,0), @@ -1359,6 +1336,7 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_FN("std_Object_getOwnPropertyNames", obj_getOwnPropertyNames, 1,0), JS_FN("std_Object_getOwnPropertyDescriptor", obj_getOwnPropertyDescriptor, 2,0), JS_FN("std_Object_hasOwnProperty", obj_hasOwnProperty, 1,0), + JS_FN("std_Object_toString", obj_toString, 0,0), JS_FN("std_Reflect_getPrototypeOf", Reflect_getPrototypeOf, 1,0), JS_FN("std_Reflect_isExtensible", Reflect_isExtensible, 1,0), @@ -1388,12 +1366,12 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_FN("std_SIMD_Float64x2_extractLane", simd_float64x2_extractLane, 2,0), // Helper funtions after this point. - JS_FN("ToObject", intrinsic_ToObject, 1,0), - JS_FN("IsObject", intrinsic_IsObject, 1,0), - JS_FN("ToInteger", intrinsic_ToInteger, 1,0), - JS_FN("ToString", intrinsic_ToString, 1,0), + JS_INLINABLE_FN("ToObject", intrinsic_ToObject, 1,0, IntrinsicToObject), + JS_INLINABLE_FN("IsObject", intrinsic_IsObject, 1,0, IntrinsicIsObject), + JS_INLINABLE_FN("ToInteger", intrinsic_ToInteger, 1,0, IntrinsicToInteger), + JS_INLINABLE_FN("ToString", intrinsic_ToString, 1,0, IntrinsicToString), JS_FN("ToPropertyKey", intrinsic_ToPropertyKey, 1,0), - JS_FN("IsCallable", intrinsic_IsCallable, 1,0), + JS_INLINABLE_FN("IsCallable", intrinsic_IsCallable, 1,0, IntrinsicIsCallable), JS_FN("IsConstructor", intrinsic_IsConstructor, 1,0), JS_FN("OwnPropertyKeys", intrinsic_OwnPropertyKeys, 1,0), JS_FN("ThrowRangeError", intrinsic_ThrowRangeError, 4,0), @@ -1401,41 +1379,51 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_FN("AssertionFailed", intrinsic_AssertionFailed, 1,0), JS_FN("GetBuiltinConstructorImpl", intrinsic_GetBuiltinConstructor, 1,0), JS_FN("MakeConstructible", intrinsic_MakeConstructible, 2,0), - JS_FN("_IsConstructing", intrinsic_IsConstructing, 0,0), JS_FN("_ConstructorForTypedArray", intrinsic_ConstructorForTypedArray, 1,0), JS_FN("DecompileArg", intrinsic_DecompileArg, 2,0), JS_FN("RuntimeDefaultLocale", intrinsic_RuntimeDefaultLocale, 0,0), - JS_FN("SubstringKernel", intrinsic_SubstringKernel, 3,0), - JS_FN("_DefineDataProperty", intrinsic_DefineDataProperty, 4,0), - JS_FN("UnsafeSetReservedSlot", intrinsic_UnsafeSetReservedSlot, 3,0), - JS_FN("UnsafeGetReservedSlot", intrinsic_UnsafeGetReservedSlot, 2,0), - JS_FN("UnsafeGetObjectFromReservedSlot", - intrinsic_UnsafeGetObjectFromReservedSlot, 2, 0), - JS_FN("UnsafeGetInt32FromReservedSlot", - intrinsic_UnsafeGetInt32FromReservedSlot, 2, 0), - JS_FN("UnsafeGetStringFromReservedSlot", - intrinsic_UnsafeGetStringFromReservedSlot, 2, 0), - JS_FN("UnsafeGetBooleanFromReservedSlot", - intrinsic_UnsafeGetBooleanFromReservedSlot, 2, 0), + JS_INLINABLE_FN("_IsConstructing", intrinsic_IsConstructing, 0,0, + IntrinsicIsConstructing), + JS_INLINABLE_FN("SubstringKernel", intrinsic_SubstringKernel, 3,0, + IntrinsicSubstringKernel), + JS_INLINABLE_FN("_DefineDataProperty", intrinsic_DefineDataProperty, 4,0, + IntrinsicDefineDataProperty), + JS_INLINABLE_FN("UnsafeSetReservedSlot", intrinsic_UnsafeSetReservedSlot, 3,0, + IntrinsicUnsafeSetReservedSlot), + JS_INLINABLE_FN("UnsafeGetReservedSlot", intrinsic_UnsafeGetReservedSlot, 2,0, + IntrinsicUnsafeGetReservedSlot), + JS_INLINABLE_FN("UnsafeGetObjectFromReservedSlot", intrinsic_UnsafeGetObjectFromReservedSlot, 2,0, + IntrinsicUnsafeGetObjectFromReservedSlot), + JS_INLINABLE_FN("UnsafeGetInt32FromReservedSlot", intrinsic_UnsafeGetInt32FromReservedSlot, 2,0, + IntrinsicUnsafeGetInt32FromReservedSlot), + JS_INLINABLE_FN("UnsafeGetStringFromReservedSlot", intrinsic_UnsafeGetStringFromReservedSlot, 2,0, + IntrinsicUnsafeGetStringFromReservedSlot), + JS_INLINABLE_FN("UnsafeGetBooleanFromReservedSlot", intrinsic_UnsafeGetBooleanFromReservedSlot,2,0, + IntrinsicUnsafeGetBooleanFromReservedSlot), + JS_FN("IsPackedArray", intrinsic_IsPackedArray, 1,0), JS_FN("GetIteratorPrototype", intrinsic_GetIteratorPrototype, 0,0), JS_FN("NewArrayIterator", intrinsic_NewArrayIterator, 0,0), JS_FN("_SetCanonicalName", intrinsic_SetCanonicalName, 2,0), - JS_FN("IsArrayIterator", intrinsic_IsArrayIterator, 1,0), JS_FN("CallArrayIteratorMethodIfWrapped", CallNonGenericSelfhostedMethod>, 2,0), - JS_FN("IsMapIterator", intrinsic_IsMapIterator, 1,0), + JS_INLINABLE_FN("IsArrayIterator", intrinsic_IsArrayIterator, 1,0, + IntrinsicIsArrayIterator), + JS_INLINABLE_FN("IsMapIterator", intrinsic_IsMapIterator, 1,0, + IntrinsicIsMapIterator), + JS_INLINABLE_FN("IsStringIterator",intrinsic_IsStringIterator, 1,0, + IntrinsicIsStringIterator), + JS_FN("_GetNextMapEntryForIterator", intrinsic_GetNextMapEntryForIterator, 3,0), JS_FN("CallMapIteratorMethodIfWrapped", CallNonGenericSelfhostedMethod>, 2,0), JS_FN("NewStringIterator", intrinsic_NewStringIterator, 0,0), - JS_FN("IsStringIterator", intrinsic_IsStringIterator, 1,0), JS_FN("CallStringIteratorMethodIfWrapped", CallNonGenericSelfhostedMethod>, 2,0), @@ -1453,18 +1441,25 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_FN("IsArrayBuffer", intrinsic_IsArrayBuffer, 1,0), - JS_FN("IsTypedArray", intrinsic_IsTypedArray, 1,0), - JS_FN("IsPossiblyWrappedTypedArray",intrinsic_IsPossiblyWrappedTypedArray,1,0), + JS_INLINABLE_FN("IsTypedArray", intrinsic_IsTypedArray, 1,0, + IntrinsicIsTypedArray), + JS_INLINABLE_FN("IsPossiblyWrappedTypedArray",intrinsic_IsPossiblyWrappedTypedArray,1,0, + IntrinsicIsPossiblyWrappedTypedArray), + JS_FN("TypedArrayBuffer", intrinsic_TypedArrayBuffer, 1,0), JS_FN("TypedArrayByteOffset", intrinsic_TypedArrayByteOffset, 1,0), JS_FN("TypedArrayElementShift", intrinsic_TypedArrayElementShift, 1,0), - JS_FN("TypedArrayLength", intrinsic_TypedArrayLength, 1,0), + + JS_INLINABLE_FN("TypedArrayLength", intrinsic_TypedArrayLength, 1,0, + IntrinsicTypedArrayLength), JS_FN("MoveTypedArrayElements", intrinsic_MoveTypedArrayElements, 4,0), JS_FN("SetFromTypedArrayApproach",intrinsic_SetFromTypedArrayApproach, 4, 0), - JS_FN("SetDisjointTypedElements",intrinsic_SetDisjointTypedElements,3,0), JS_FN("SetOverlappingTypedElements",intrinsic_SetOverlappingTypedElements,3,0), + JS_INLINABLE_FN("SetDisjointTypedElements",intrinsic_SetDisjointTypedElements,3,0, + IntrinsicSetDisjointTypedElements), + JS_FN("CallTypedArrayMethodIfWrapped", CallNonGenericSelfhostedMethod>, 2, 0), @@ -1475,23 +1470,14 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_FN("IsWeakSet", intrinsic_IsWeakSet, 1,0), - JS_FN("NewDenseArray", intrinsic_NewDenseArray, 1,0), - // See builtin/TypedObject.h for descriptors of the typedobj functions. JS_FN("NewOpaqueTypedObject", js::NewOpaqueTypedObject, 1, 0), JS_FN("NewDerivedTypedObject", js::NewDerivedTypedObject, 3, 0), JS_FN("TypedObjectBuffer", TypedObject::GetBuffer, 1, 0), JS_FN("TypedObjectByteOffset", TypedObject::GetByteOffset, 1, 0), JS_FN("AttachTypedObject", js::AttachTypedObject, 3, 0), - JS_FN("SetTypedObjectOffset", js::SetTypedObjectOffset, 2, 0), - JS_FN("ObjectIsTypeDescr" , js::ObjectIsTypeDescr, 1, 0), - JS_FN("ObjectIsTypedObject", js::ObjectIsTypedObject, 1, 0), - JS_FN("ObjectIsTransparentTypedObject", js::ObjectIsTransparentTypedObject, 1, 0), JS_FN("TypedObjectIsAttached", js::TypedObjectIsAttached, 1, 0), JS_FN("TypedObjectTypeDescr", js::TypedObjectTypeDescr, 1, 0), - JS_FN("ObjectIsOpaqueTypedObject", js::ObjectIsOpaqueTypedObject, 1, 0), - JS_FN("TypeDescrIsArrayType", js::TypeDescrIsArrayType, 1, 0), - JS_FN("TypeDescrIsSimpleType", js::TypeDescrIsSimpleType, 1, 0), JS_FN("ClampToUint8", js::ClampToUint8, 1, 0), JS_FN("GetTypedObjectModule", js::GetTypedObjectModule, 0, 0), JS_FN("GetFloat32x4TypeDescr", js::GetFloat32x4TypeDescr, 0, 0), @@ -1500,6 +1486,21 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_FN("GetInt16x8TypeDescr", js::GetInt16x8TypeDescr, 0, 0), JS_FN("GetInt32x4TypeDescr", js::GetInt32x4TypeDescr, 0, 0), + JS_INLINABLE_FN("ObjectIsTypeDescr" , js::ObjectIsTypeDescr, 1, 0, + IntrinsicObjectIsTypeDescr), + JS_INLINABLE_FN("ObjectIsTypedObject", js::ObjectIsTypedObject, 1, 0, + IntrinsicObjectIsTypedObject), + JS_INLINABLE_FN("ObjectIsOpaqueTypedObject", js::ObjectIsOpaqueTypedObject, 1, 0, + IntrinsicObjectIsOpaqueTypedObject), + JS_INLINABLE_FN("ObjectIsTransparentTypedObject", js::ObjectIsTransparentTypedObject, 1, 0, + IntrinsicObjectIsTransparentTypedObject), + JS_INLINABLE_FN("TypeDescrIsArrayType", js::TypeDescrIsArrayType, 1, 0, + IntrinsicTypeDescrIsArrayType), + JS_INLINABLE_FN("TypeDescrIsSimpleType", js::TypeDescrIsSimpleType, 1, 0, + IntrinsicTypeDescrIsSimpleType), + JS_INLINABLE_FN("SetTypedObjectOffset", js::SetTypedObjectOffset, 2, 0, + IntrinsicSetTypedObjectOffset), + #define LOAD_AND_STORE_SCALAR_FN_DECLS(_constant, _type, _name) \ JS_FN("Store_" #_name, js::StoreScalar##_type::Func, 3, 0), \ JS_FN("Load_" #_name, js::LoadScalar##_type::Func, 3, 0), @@ -1815,13 +1816,13 @@ CloneString(JSContext* cx, JSFlatString* selfHostedString) static JSObject* CloneObject(JSContext* cx, HandleNativeObject selfHostedObject) { +#ifdef DEBUG AutoCycleDetector detect(cx, selfHostedObject); if (!detect.init()) return nullptr; - if (detect.foundCycle()) { - JS_ReportError(cx, "SelfHosted cloning cannot handle cyclic object graphs."); - return nullptr; - } + if (detect.foundCycle()) + MOZ_CRASH("SelfHosted cloning cannot handle cyclic object graphs."); +#endif RootedObject clone(cx); if (selfHostedObject->is()) { diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp index 703fa41628..44d561ecc4 100644 --- a/js/src/vm/TypedArrayObject.cpp +++ b/js/src/vm/TypedArrayObject.cpp @@ -802,10 +802,7 @@ TypedArrayObject::protoFunctions[] = { // Both of these are actually defined to the same object in FinishTypedArrayInit. JS_SELF_HOSTED_FN("values", "TypedArrayValues", 0, JSPROP_DEFINE_LATE), JS_SELF_HOSTED_SYM_FN(iterator, "TypedArrayValues", 0, JSPROP_DEFINE_LATE), - - /* ES7 additions */ JS_SELF_HOSTED_FN("includes", "TypedArrayIncludes", 2, 0), - JS_FS_END }; diff --git a/js/xpconnect/tests/chrome/test_xrayToJS.xul b/js/xpconnect/tests/chrome/test_xrayToJS.xul index ee05da1ffe..cc0b2def9e 100644 --- a/js/xpconnect/tests/chrome/test_xrayToJS.xul +++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul @@ -168,22 +168,16 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681 gPrototypeProperties['Array'] = ["length", "toSource", "toString", "toLocaleString", "join", "reverse", "sort", "push", "pop", "shift", "unshift", "splice", "concat", "slice", "lastIndexOf", "indexOf", - "forEach", "map", "reduce", "reduceRight", "filter", "some", "every", "find", + "includes", "forEach", "map", "reduce", "reduceRight", "filter", "some", "every", "find", "findIndex", "copyWithin", "fill", Symbol.iterator, "entries", "keys", "constructor"]; - if (isNightlyBuild) { - gPrototypeProperties['Array'].push('includes'); - } for (var c of typedArrayClasses) { gPrototypeProperties[c] = ["constructor", "BYTES_PER_ELEMENT"]; } gPrototypeProperties['TypedArray'] = ["length", "buffer", "byteLength", "byteOffset", Symbol.iterator, "subarray", - "set", "copyWithin", "find", "findIndex", "forEach","indexOf", "lastIndexOf", "reverse", - "join", "every", "some", "reduce", "reduceRight", "entries", "keys", "values", "slice", - "map", "filter"]; - if (isNightlyBuild) { - gPrototypeProperties['TypedArray'].push('includes'); - } + "set", "copyWithin", "find", "findIndex", "forEach","indexOf", "lastIndexOf", "includes", + "reverse", "join", "every", "some", "reduce", "reduceRight", "entries", "keys", "values", + "slice", "map", "filter"]; for (var c of errorObjectClasses) { gPrototypeProperties[c] = ["constructor", "name", // We don't actually resolve these empty data properties @@ -207,10 +201,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681 "flags", "global", "ignoreCase", "multiline", "source", "sticky", "lastIndex"]; - if (isNightlyBuild) { - gPrototypeProperties['TypedArray'].push('includes'); - } - // Sort an array that may contain symbols as well as strings. function sortProperties(arr) { function sortKey(prop) {