diff --git a/build/autoconf/compiler-opts.m4 b/build/autoconf/compiler-opts.m4 index 0c333ad8070..4ae7ccb983a 100644 --- a/build/autoconf/compiler-opts.m4 +++ b/build/autoconf/compiler-opts.m4 @@ -223,7 +223,8 @@ if test "$GNU_CC"; then CFLAGS="$CFLAGS -ffunction-sections -fdata-sections" CXXFLAGS="$CXXFLAGS -ffunction-sections -fdata-sections" fi - CXXFLAGS="$CXXFLAGS -fno-exceptions" + CFLAGS="$CFLAGS -fno-math-errno" + CXXFLAGS="$CXXFLAGS -fno-exceptions -fno-math-errno" fi dnl ======================================================== diff --git a/configure.in b/configure.in index 36b1e713850..6385921fe37 100644 --- a/configure.in +++ b/configure.in @@ -9227,19 +9227,32 @@ fi dist=$MOZ_BUILD_ROOT/dist ac_configure_args="$_SUBDIR_CONFIG_ARGS" ac_configure_args="$ac_configure_args --enable-threadsafe" + +MOZ_ARG_WITH_STRING(intl-api, +[ --with-intl-api, --with-intl-api=build, --without-intl-api + Determine the status of the ECMAScript Internationalization API. The first + (or lack of any of these) builds and exposes the API. The second builds it + but doesn't use ICU or expose the API to script. The third doesn't build + ICU at all.], + WITH_INTL="--with-intl-api=$withval" +]) +if test -z "$WITH_INTL"; then if test "$NIGHTLY_BUILD" = "1" -a "$MOZ_BUILD_APP" = "browser"; then # In desktop nightlies the Internationalization API is disabled, but all # code for it is still built. Bug 853301 will remove this so that it's # built and the API is enabled. - ac_configure_args="$ac_configure_args --with-intl-api=build" + WITH_INTL="--with-intl-api=build" else # Internationalization isn't built or exposed by default in non-desktop and # non-nightly builds. Bugs to enable: # # Android: bug 864843 # B2G: bug 866301 - ac_configure_args="$ac_configure_args --without-intl-api" + WITH_INTL="--without-intl-api" fi +fi +ac_configure_args="$ac_configure_args $WITH_INTL" + if test "$BUILD_CTYPES"; then # Build js-ctypes on the platforms we can. ac_configure_args="$ac_configure_args --enable-ctypes" diff --git a/content/base/public/nsDOMFile.h b/content/base/public/nsDOMFile.h index cd202e55b30..95194fa05ae 100644 --- a/content/base/public/nsDOMFile.h +++ b/content/base/public/nsDOMFile.h @@ -149,6 +149,7 @@ protected: nsString mContentType; nsString mName; + nsString mPath; // The path relative to a directory chosen by the user uint64_t mStart; uint64_t mLength; @@ -300,6 +301,8 @@ public: NS_IMETHOD GetMozFullPathInternal(nsAString& aFullPath) MOZ_OVERRIDE; NS_IMETHOD GetInternalStream(nsIInputStream**) MOZ_OVERRIDE; + void SetPath(const nsAString& aFullPath); + protected: // Create slice nsDOMFileFile(const nsDOMFileFile* aOther, uint64_t aStart, uint64_t aLength, diff --git a/content/base/public/nsIDOMFile.idl b/content/base/public/nsIDOMFile.idl index 0ef73a8e9bf..d5617042b5c 100644 --- a/content/base/public/nsIDOMFile.idl +++ b/content/base/public/nsIDOMFile.idl @@ -59,11 +59,13 @@ interface nsIDOMBlob : nsISupports [notxpcom] FileInfo getFileInfo(in FileManager aFileManager); }; -[scriptable, builtinclass, uuid(6928584d-7d87-4d56-9ce1-1c89c24f2c6a)] +[scriptable, builtinclass, uuid(0acb4135-9f79-4516-ba92-b5fba5203620)] interface nsIDOMFile : nsIDOMBlob { readonly attribute DOMString name; + readonly attribute DOMString path; + [implicit_jscontext] readonly attribute jsval lastModifiedDate; diff --git a/content/base/src/nsContentList.h b/content/base/src/nsContentList.h index ace8b16379a..e8c72ad2a7c 100644 --- a/content/base/src/nsContentList.h +++ b/content/base/src/nsContentList.h @@ -299,6 +299,16 @@ public: mMatchNameSpaceId == aKey.mMatchNameSpaceId; } + /** + * Sets the state to LIST_DIRTY and clears mElements array. + * @note This is the only acceptable way to set state to LIST_DIRTY. + */ + void SetDirty() + { + mState = LIST_DIRTY; + Reset(); + } + protected: /** * Returns whether the element matches our criterion @@ -350,16 +360,6 @@ protected: */ inline void BringSelfUpToDate(bool aDoFlush); - /** - * Sets the state to LIST_DIRTY and clears mElements array. - * @note This is the only acceptable way to set state to LIST_DIRTY. - */ - void SetDirty() - { - mState = LIST_DIRTY; - Reset(); - } - /** * To be called from non-destructor locations that want to remove from caches. * Needed because if subclasses want to have cache behavior they can't just diff --git a/content/base/src/nsDOMFile.cpp b/content/base/src/nsDOMFile.cpp index 8585af47522..f98400ec196 100644 --- a/content/base/src/nsDOMFile.cpp +++ b/content/base/src/nsDOMFile.cpp @@ -126,6 +126,14 @@ nsDOMFileBase::GetName(nsAString &aFileName) return NS_OK; } +NS_IMETHODIMP +nsDOMFileBase::GetPath(nsAString &aPath) +{ + NS_ASSERTION(mIsFile, "Should only be called on files"); + aPath = mPath; + return NS_OK; +} + NS_IMETHODIMP nsDOMFileBase::GetLastModifiedDate(JSContext* cx, JS::Value *aLastModifiedDate) { @@ -591,6 +599,12 @@ nsDOMFileFile::GetInternalStream(nsIInputStream **aStream) -1, -1, sFileStreamFlags); } +void +nsDOMFileFile::SetPath(const nsAString& aPath) +{ + mPath = aPath; +} + //////////////////////////////////////////////////////////////////////////// // nsDOMMemoryFile implementation diff --git a/content/base/src/nsXMLHttpRequest.cpp b/content/base/src/nsXMLHttpRequest.cpp index 89b0afb2a81..f5ee28fb8b8 100644 --- a/content/base/src/nsXMLHttpRequest.cpp +++ b/content/base/src/nsXMLHttpRequest.cpp @@ -289,7 +289,7 @@ nsXMLHttpRequest::nsXMLHttpRequest() mProgressSinceLastProgressEvent(false), mRequestSentTime(0), mTimeoutMilliseconds(0), mErrorLoad(false), mWaitingForOnStopRequest(false), - mProgressTimerIsActive(false), mProgressEventWasDelayed(false), + mProgressTimerIsActive(false), mIsHtml(false), mWarnAboutSyncHtml(false), mLoadLengthComputable(false), mLoadTotal(0), @@ -3752,7 +3752,6 @@ nsXMLHttpRequest::StartProgressEventTimer() mProgressNotifier = do_CreateInstance(NS_TIMER_CONTRACTID); } if (mProgressNotifier) { - mProgressEventWasDelayed = false; mProgressTimerIsActive = true; mProgressNotifier->Cancel(); mProgressNotifier->InitWithCallback(this, NS_PROGRESS_EVENT_INTERVAL, diff --git a/content/base/src/nsXMLHttpRequest.h b/content/base/src/nsXMLHttpRequest.h index 139a9b4df0d..e18dd136560 100644 --- a/content/base/src/nsXMLHttpRequest.h +++ b/content/base/src/nsXMLHttpRequest.h @@ -673,7 +673,6 @@ protected: bool mErrorLoad; bool mWaitingForOnStopRequest; bool mProgressTimerIsActive; - bool mProgressEventWasDelayed; bool mIsHtml; bool mWarnAboutMultipartHtml; bool mWarnAboutSyncHtml; diff --git a/content/html/content/src/HTMLInputElement.cpp b/content/html/content/src/HTMLInputElement.cpp index edd042fdbb3..9503c379d37 100644 --- a/content/html/content/src/HTMLInputElement.cpp +++ b/content/html/content/src/HTMLInputElement.cpp @@ -240,13 +240,13 @@ class HTMLInputElementState MOZ_FINAL : public nsISupports mValue = aValue; } - const nsCOMArray& GetFiles() { + const nsTArray >& GetFiles() { return mFiles; } - void SetFiles(const nsCOMArray& aFiles) { + void SetFiles(const nsTArray >& aFiles) { mFiles.Clear(); - mFiles.AppendObjects(aFiles); + mFiles.AppendElements(aFiles); } HTMLInputElementState() @@ -257,7 +257,7 @@ class HTMLInputElementState MOZ_FINAL : public nsISupports protected: nsString mValue; - nsCOMArray mFiles; + nsTArray > mFiles; bool mChecked; bool mCheckedSet; }; @@ -314,6 +314,221 @@ UploadLastDir::ContentPrefCallback::HandleError(nsresult error) return NS_OK; } +namespace { + +/** + * This enumerator returns nsDOMFileFile objects after wrapping a single + * nsIFile representing a directory. It enumerates the files under that + * directory and its subdirectories as a flat list of files, ignoring/skipping + * over symbolic links. + * + * The enumeration involves I/O, so this class must NOT be used on the main + * thread or else the main thread could be blocked for a very long time. + * + * This enumerator does not walk the directory tree breadth-first, but it also + * is not guaranteed to walk it depth-first either (since it uses + * nsIFile::GetDirectoryEntries, which is not guaranteed to group a directory's + * subdirectories at the beginning of the list that it returns). + */ +class DirPickerRecursiveFileEnumerator MOZ_FINAL + : public nsISimpleEnumerator +{ +public: + NS_DECL_ISUPPORTS + + DirPickerRecursiveFileEnumerator(nsIFile* aTopDir) + : mTopDir(aTopDir) + { + MOZ_ASSERT(!NS_IsMainThread(), "This class blocks on I/O!"); + +#ifdef DEBUG + { + bool isDir; + aTopDir->IsDirectory(&isDir); + MOZ_ASSERT(isDir); + } +#endif + + nsCOMPtr entries; + if (NS_SUCCEEDED(mTopDir->GetDirectoryEntries(getter_AddRefs(entries))) && + entries) { + mDirEnumeratorStack.AppendElement(entries); + LookupAndCacheNext(); + } + } + + NS_IMETHOD + GetNext(nsISupports** aResult) + { + MOZ_ASSERT(!NS_IsMainThread(), + "Walking the directory tree involves I/O, so using this " + "enumerator can block a thread for a long time!"); + + if (!mNextFile) { + return NS_ERROR_FAILURE; + } + nsRefPtr domFile = new nsDOMFileFile(mNextFile); + nsCString relDescriptor; + nsresult rv = mNextFile->GetRelativeDescriptor(mTopDir, relDescriptor); + NS_ENSURE_SUCCESS(rv, rv); + NS_ConvertUTF8toUTF16 path(relDescriptor); + nsAutoString leafName; + mNextFile->GetLeafName(leafName); + MOZ_ASSERT(leafName.Length() <= path.Length()); + int32_t length = path.Length() - leafName.Length() - 1; // -1 for "/" + MOZ_ASSERT(length >= -1); + if (length > 0) { + domFile->SetPath(Substring(path, 0, uint32_t(length))); + } + *aResult = static_cast(domFile.forget().get()); + LookupAndCacheNext(); + return NS_OK; + } + + NS_IMETHOD + HasMoreElements(bool* aResult) + { + *aResult = !!mNextFile; + return NS_OK; + } + +private: + + void + LookupAndCacheNext() + { + for (;;) { + if (mDirEnumeratorStack.IsEmpty()) { + mNextFile = nullptr; + break; + } + + nsISimpleEnumerator* currentDirEntries = + mDirEnumeratorStack.LastElement(); + + bool hasMore; + DebugOnly rv = currentDirEntries->HasMoreElements(&hasMore); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + + if (!hasMore) { + mDirEnumeratorStack.RemoveElementAt(mDirEnumeratorStack.Length() - 1); + continue; + } + + nsCOMPtr entry; + rv = currentDirEntries->GetNext(getter_AddRefs(entry)); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + + nsCOMPtr file = do_QueryInterface(entry); + MOZ_ASSERT(file); + + bool isLink, isSpecial; + file->IsSymlink(&isLink); + file->IsSpecial(&isSpecial); + if (isLink || isSpecial) { + continue; + } + + bool isDir; + file->IsDirectory(&isDir); + if (isDir) { + nsCOMPtr subDirEntries; + rv = file->GetDirectoryEntries(getter_AddRefs(subDirEntries)); + MOZ_ASSERT(NS_SUCCEEDED(rv) && subDirEntries); + mDirEnumeratorStack.AppendElement(subDirEntries); + continue; + } + +#ifdef DEBUG + { + bool isFile; + file->IsFile(&isFile); + MOZ_ASSERT(isFile); + } +#endif + + mNextFile.swap(file); + return; + } + } + +private: + nsCOMPtr mTopDir; + nsCOMPtr mNextFile; + nsTArray > mDirEnumeratorStack; +}; + +NS_IMPL_ISUPPORTS1(DirPickerRecursiveFileEnumerator, nsISimpleEnumerator) + +class DirPickerBuildFileListTask MOZ_FINAL + : public nsRunnable +{ +public: + DirPickerBuildFileListTask(HTMLInputElement* aInput, nsIFile* aTopDir) + : mInput(aInput) + , mTopDir(aTopDir) + {} + + NS_IMETHOD Run() { + if (!NS_IsMainThread()) { + // Build up list of nsDOMFileFile objects on this dedicated thread: + nsCOMPtr iter = + new DirPickerRecursiveFileEnumerator(mTopDir); + bool hasMore = true; + nsCOMPtr tmp; + while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) { + iter->GetNext(getter_AddRefs(tmp)); + nsCOMPtr domFile = do_QueryInterface(tmp); + MOZ_ASSERT(domFile); + mFileList.AppendElement(domFile); + } + return NS_DispatchToMainThread(this); + } + + // Now back on the main thread, set the list on our HTMLInputElement: + if (mFileList.IsEmpty()) { + return NS_OK; + } + // The text control frame (if there is one) isn't going to send a change + // event because it will think this is done by a script. + // So, we can safely send one by ourself. + mInput->SetFiles(mFileList, true); + nsresult rv = + nsContentUtils::DispatchTrustedEvent(mInput->OwnerDoc(), + static_cast(mInput.get()), + NS_LITERAL_STRING("change"), true, + false); + // Clear mInput to make sure that it can't lose its last strong ref off the + // main thread (which may happen if our dtor runs off the main thread)! + mInput = nullptr; + return rv; + } + +private: + nsRefPtr mInput; + nsCOMPtr mTopDir; + nsTArray > mFileList; +}; + +static already_AddRefed +DOMFileToLocalFile(nsIDOMFile* aDomFile) +{ + nsString path; + nsresult rv = aDomFile->GetMozFullPathInternal(path); + if (NS_FAILED(rv) || path.IsEmpty()) { + return nullptr; + } + + nsCOMPtr localFile; + rv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(path), true, + getter_AddRefs(localFile)); + NS_ENSURE_SUCCESS(rv, nullptr); + + return localFile.forget(); +} + +} // anonymous namespace + NS_IMETHODIMP HTMLInputElement::nsFilePickerShownCallback::Done(int16_t aResult) { @@ -323,11 +538,43 @@ HTMLInputElement::nsFilePickerShownCallback::Done(int16_t aResult) int16_t mode; mFilePicker->GetMode(&mode); - bool multi = mode == static_cast(nsIFilePicker::modeOpenMultiple); + + if (mode == static_cast(nsIFilePicker::modeGetFolder)) { + // Directory picking is different, since we still need to do more I/O to + // build up the list of nsDOMFileFile objects. Since this may block for a + // long time, we need to build the list off on another dedicated thread to + // avoid blocking any other activities that the browser is carrying out. + + // The user selected this directory, so we always save this dir, even if + // no files are found under it. + nsCOMPtr pickedDir; + mFilePicker->GetFile(getter_AddRefs(pickedDir)); + +#ifdef DEBUG + { + bool isDir; + pickedDir->IsDirectory(&isDir); + MOZ_ASSERT(isDir); + } +#endif + + HTMLInputElement::gUploadLastDir->StoreLastUsedDirectory( + mInput->OwnerDoc(), pickedDir); + + nsCOMPtr target + = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); + NS_ASSERTION(target, "Must have stream transport service"); + + // DirPickerBuildFileListTask takes care of calling SetFiles() and + // dispatching the "change" event. + nsRefPtr event = + new DirPickerBuildFileListTask(mInput.get(), pickedDir.get()); + return target->Dispatch(event, NS_DISPATCH_NORMAL); + } // Collect new selected filenames - nsCOMArray newFiles; - if (multi) { + nsTArray > newFiles; + if (mode == static_cast(nsIFilePicker::modeOpenMultiple)) { nsCOMPtr iter; nsresult rv = mFilePicker->GetDomfiles(getter_AddRefs(iter)); NS_ENSURE_SUCCESS(rv, rv); @@ -337,41 +584,35 @@ HTMLInputElement::nsFilePickerShownCallback::Done(int16_t aResult) } nsCOMPtr tmp; - bool prefSaved = false; - bool loop = true; + bool hasMore = true; - while (NS_SUCCEEDED(iter->HasMoreElements(&loop)) && loop) { + while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) { iter->GetNext(getter_AddRefs(tmp)); nsCOMPtr domFile = do_QueryInterface(tmp); MOZ_ASSERT(domFile); - - newFiles.AppendObject(domFile); - - if (!prefSaved) { - // Store the last used directory using the content pref service - HTMLInputElement::gUploadLastDir->StoreLastUsedDirectory( - mInput->OwnerDoc(), domFile); - prefSaved = true; - } + newFiles.AppendElement(domFile); } - } - else { + } else { + MOZ_ASSERT(mode == static_cast(nsIFilePicker::modeOpen)); nsCOMPtr domFile; nsresult rv = mFilePicker->GetDomfile(getter_AddRefs(domFile)); NS_ENSURE_SUCCESS(rv, rv); if (domFile) { - newFiles.AppendObject(domFile); - - // Store the last used directory using the content pref service - HTMLInputElement::gUploadLastDir->StoreLastUsedDirectory( - mInput->OwnerDoc(), domFile); + newFiles.AppendElement(domFile); } } - if (!newFiles.Count()) { + if (newFiles.IsEmpty()) { return NS_OK; } + // Store the last used directory using the content pref service: + nsCOMPtr file = DOMFileToLocalFile(newFiles[0]); + nsCOMPtr lastUsedDir; + file->GetParent(getter_AddRefs(lastUsedDir)); + HTMLInputElement::gUploadLastDir->StoreLastUsedDirectory( + mInput->OwnerDoc(), lastUsedDir); + // The text control frame (if there is one) isn't going to send a change // event because it will think this is done by a script. // So, we can safely send one by ourself. @@ -543,7 +784,7 @@ HTMLInputElement::InitColorPicker() } nsresult -HTMLInputElement::InitFilePicker() +HTMLInputElement::InitFilePicker(FilePickerType aType) { // Get parent nsPIDOMWindow object. nsCOMPtr doc = OwnerDoc(); @@ -567,15 +808,23 @@ HTMLInputElement::InitFilePicker() if (!filePicker) return NS_ERROR_FAILURE; - bool multi = HasAttr(kNameSpaceID_None, nsGkAtoms::multiple); + int16_t mode; - nsresult rv = filePicker->Init(win, title, - multi - ? static_cast(nsIFilePicker::modeOpenMultiple) - : static_cast(nsIFilePicker::modeOpen)); + if (aType == FILE_PICKER_DIRECTORY) { + mode = static_cast(nsIFilePicker::modeGetFolder); + } else if (HasAttr(kNameSpaceID_None, nsGkAtoms::multiple)) { + mode = static_cast(nsIFilePicker::modeOpenMultiple); + } else { + mode = static_cast(nsIFilePicker::modeOpen); + } + + nsresult rv = filePicker->Init(win, title, mode); NS_ENSURE_SUCCESS(rv, rv); - if (HasAttr(kNameSpaceID_None, nsGkAtoms::accept)) { + // Native directory pickers ignore file type filters, so we don't spend + // cycles adding them for FILE_PICKER_DIRECTORY. + if (HasAttr(kNameSpaceID_None, nsGkAtoms::accept) && + aType != FILE_PICKER_DIRECTORY) { SetFilePickerFiltersFromAccept(filePicker); } else { filePicker->AppendFilters(nsIFilePicker::filterAll); @@ -584,12 +833,13 @@ HTMLInputElement::InitFilePicker() // Set default directry and filename nsAutoString defaultName; - const nsCOMArray& oldFiles = GetFilesInternal(); + const nsTArray >& oldFiles = GetFilesInternal(); nsCOMPtr callback = new HTMLInputElement::nsFilePickerShownCallback(this, filePicker); - if (oldFiles.Count()) { + if (!oldFiles.IsEmpty() && + aType != FILE_PICKER_DIRECTORY) { nsString path; oldFiles[0]->GetMozFullPathInternal(path); @@ -608,7 +858,7 @@ HTMLInputElement::InitFilePicker() // Unfortunately nsIFilePicker doesn't allow multiple files to be // default-selected, so only select something by default if exactly // one file was selected before. - if (oldFiles.Count() == 1) { + if (oldFiles.Length() == 1) { nsAutoString leafName; oldFiles[0]->GetName(leafName); if (!leafName.IsEmpty()) { @@ -621,7 +871,6 @@ HTMLInputElement::InitFilePicker() HTMLInputElement::gUploadLastDir->FetchDirectoryAndDisplayPicker(doc, filePicker, callback); return NS_OK; - } #define CPS_PREF_NAME NS_LITERAL_STRING("browser.upload.lastDir") @@ -680,31 +929,16 @@ UploadLastDir::FetchDirectoryAndDisplayPicker(nsIDocument* aDoc, } nsresult -UploadLastDir::StoreLastUsedDirectory(nsIDocument* aDoc, nsIDOMFile* aDomFile) +UploadLastDir::StoreLastUsedDirectory(nsIDocument* aDoc, nsIFile* aDir) { NS_PRECONDITION(aDoc, "aDoc is null"); - NS_PRECONDITION(aDomFile, "aDomFile is null"); - - nsString path; - nsresult rv = aDomFile->GetMozFullPathInternal(path); - if (NS_FAILED(rv) || path.IsEmpty()) { + if (!aDir) { return NS_OK; } - nsCOMPtr localFile; - rv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(path), true, - getter_AddRefs(localFile)); - NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr docURI = aDoc->GetDocumentURI(); NS_PRECONDITION(docURI, "docURI is null"); - nsCOMPtr parentFile; - localFile->GetParent(getter_AddRefs(parentFile)); - if (!parentFile) { - return NS_OK; - } - // Attempt to get the CPS, if it's not present we'll just return nsCOMPtr contentPrefService = do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID); @@ -717,7 +951,7 @@ UploadLastDir::StoreLastUsedDirectory(nsIDocument* aDoc, nsIDOMFile* aDomFile) // Find the parent of aFile, and store it nsString unicodePath; - parentFile->GetPath(unicodePath); + aDir->GetPath(unicodePath); if (unicodePath.IsEmpty()) // nothing to do return NS_OK; nsCOMPtr prefValue = do_CreateInstance(NS_VARIANT_CONTRACTID); @@ -906,7 +1140,7 @@ HTMLInputElement::Clone(nsINodeInfo* aNodeInfo, nsINode** aResult) const GetDisplayFileName(it->mStaticDocFileList); } else { it->mFiles.Clear(); - it->mFiles.AppendObjects(mFiles); + it->mFiles.AppendElements(mFiles); } break; case VALUE_MODE_DEFAULT_ON: @@ -1264,7 +1498,7 @@ HTMLInputElement::GetValueInternal(nsAString& aValue) const case VALUE_MODE_FILENAME: if (nsContentUtils::IsCallerChrome()) { - if (mFiles.Count()) { + if (!mFiles.IsEmpty()) { return mFiles[0]->GetMozFullPath(aValue); } else { @@ -1272,7 +1506,7 @@ HTMLInputElement::GetValueInternal(nsAString& aValue) const } } else { // Just return the leaf name - if (mFiles.Count() == 0 || NS_FAILED(mFiles[0]->GetName(aValue))) { + if (mFiles.IsEmpty() || NS_FAILED(mFiles[0]->GetName(aValue))) { aValue.Truncate(); } } @@ -1818,7 +2052,7 @@ HTMLInputElement::StepUp(int32_t n, uint8_t optional_argc) void HTMLInputElement::MozGetFileNameArray(nsTArray< nsString >& aArray) { - for (int32_t i = 0; i < mFiles.Count(); i++) { + for (uint32_t i = 0; i < mFiles.Length(); i++) { nsString str; mFiles[i]->GetMozFullPathInternal(str); aArray.AppendElement(str); @@ -1857,7 +2091,7 @@ HTMLInputElement::MozGetFileNameArray(uint32_t* aLength, PRUnichar*** aFileNames void HTMLInputElement::MozSetFileNameArray(const Sequence< nsString >& aFileNames) { - nsCOMArray files; + nsTArray > files; for (uint32_t i = 0; i < aFileNames.Length(); ++i) { nsCOMPtr file; @@ -1876,7 +2110,7 @@ HTMLInputElement::MozSetFileNameArray(const Sequence< nsString >& aFileNames) if (file) { nsCOMPtr domFile = new nsDOMFileFile(file); - files.AppendObject(domFile); + files.AppendElement(domFile); } else { continue; // Not much we can do if the file doesn't exist } @@ -2068,14 +2302,14 @@ HTMLInputElement::GetDisplayFileName(nsAString& aValue) const return; } - if (mFiles.Count() == 1) { + if (mFiles.Length() == 1) { mFiles[0]->GetName(aValue); return; } nsXPIDLString value; - if (mFiles.Count() == 0) { + if (mFiles.IsEmpty()) { if (HasAttr(kNameSpaceID_None, nsGkAtoms::multiple)) { nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES, "NoFilesSelected", value); @@ -2085,7 +2319,7 @@ HTMLInputElement::GetDisplayFileName(nsAString& aValue) const } } else { nsString count; - count.AppendInt(mFiles.Count()); + count.AppendInt(mFiles.Length()); const PRUnichar* params[] = { count.get() }; nsContentUtils::FormatLocalizedString(nsContentUtils::eFORMS_PROPERTIES, @@ -2096,11 +2330,11 @@ HTMLInputElement::GetDisplayFileName(nsAString& aValue) const } void -HTMLInputElement::SetFiles(const nsCOMArray& aFiles, +HTMLInputElement::SetFiles(const nsTArray >& aFiles, bool aSetValueChanged) { mFiles.Clear(); - mFiles.AppendObjects(aFiles); + mFiles.AppendElements(aFiles); AfterSetFiles(aSetValueChanged); } @@ -2117,7 +2351,7 @@ HTMLInputElement::SetFiles(nsIDOMFileList* aFiles, for (uint32_t i = 0; i < listLength; i++) { nsCOMPtr file; aFiles->Item(i, getter_AddRefs(file)); - mFiles.AppendObject(file); + mFiles.AppendElement(file); } } @@ -2179,14 +2413,23 @@ HTMLInputElement::GetFiles() return mFileList; } +void +HTMLInputElement::OpenDirectoryPicker(ErrorResult& aRv) +{ + if (mType != NS_FORM_INPUT_FILE) { + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + } + InitFilePicker(FILE_PICKER_DIRECTORY); +} + nsresult HTMLInputElement::UpdateFileList() { if (mFileList) { mFileList->Clear(); - const nsCOMArray& files = GetFilesInternal(); - for (int32_t i = 0; i < files.Count(); ++i) { + const nsTArray >& files = GetFilesInternal(); + for (uint32_t i = 0; i < files.Length(); ++i) { if (!mFileList->Append(files[i])) { return NS_ERROR_FAILURE; } @@ -2890,7 +3133,7 @@ HTMLInputElement::MaybeInitPickers(nsEventChainPostVisitor& aVisitor) if (NS_IS_MOUSE_LEFT_CLICK(aVisitor.mEvent) && !aVisitor.mEvent->mFlags.mDefaultPrevented) { if (mType == NS_FORM_INPUT_FILE) { - return InitFilePicker(); + return InitFilePicker(FILE_PICKER_FILE); } if (mType == NS_FORM_INPUT_COLOR) { return InitColorPicker(); @@ -4574,13 +4817,13 @@ HTMLInputElement::SubmitNamesValues(nsFormSubmission* aFormSubmission) if (mType == NS_FORM_INPUT_FILE) { // Submit files - const nsCOMArray& files = GetFilesInternal(); + const nsTArray >& files = GetFilesInternal(); - for (int32_t i = 0; i < files.Count(); ++i) { + for (uint32_t i = 0; i < files.Length(); ++i) { aFormSubmission->AddNameFilePair(name, files[i], NullString()); } - if (files.Count() == 0) { + if (files.IsEmpty()) { // If no file was selected, pretend we had an empty file with an // empty filename. aFormSubmission->AddNameFilePair(name, nullptr, NullString()); @@ -4617,7 +4860,7 @@ HTMLInputElement::SaveState() } break; case VALUE_MODE_FILENAME: - if (mFiles.Count()) { + if (!mFiles.IsEmpty()) { inputState = new HTMLInputElementState(); inputState->SetFiles(mFiles); } @@ -4790,7 +5033,7 @@ HTMLInputElement::RestoreState(nsPresState* aState) break; case VALUE_MODE_FILENAME: { - const nsCOMArray& files = inputState->GetFiles(); + const nsTArray >& files = inputState->GetFiles(); SetFiles(files, true); } break; @@ -5257,8 +5500,8 @@ HTMLInputElement::IsValueMissing() const return IsValueEmpty(); case VALUE_MODE_FILENAME: { - const nsCOMArray& files = GetFilesInternal(); - return !files.Count(); + const nsTArray >& files = GetFilesInternal(); + return files.IsEmpty(); } case VALUE_MODE_DEFAULT_ON: // This should not be used for type radio. diff --git a/content/html/content/src/HTMLInputElement.h b/content/html/content/src/HTMLInputElement.h index 83f8c0c28cd..ee5bf2da31f 100644 --- a/content/html/content/src/HTMLInputElement.h +++ b/content/html/content/src/HTMLInputElement.h @@ -54,10 +54,9 @@ public: * Store the last used directory for this location using the * content pref service, if it is available * @param aURI URI of the current page - * @param aDomFile file chosen by the user - the path to the parent of this - * file will be stored + * @param aDir Parent directory of the file(s)/directory chosen by the user */ - nsresult StoreLastUsedDirectory(nsIDocument* aDoc, nsIDOMFile* aDomFile); + nsresult StoreLastUsedDirectory(nsIDocument* aDoc, nsIFile* aDir); class ContentPrefCallback MOZ_FINAL : public nsIContentPrefCallback2 { @@ -191,12 +190,12 @@ public: void GetDisplayFileName(nsAString& aFileName) const; - const nsCOMArray& GetFilesInternal() const + const nsTArray >& GetFilesInternal() const { return mFiles; } - void SetFiles(const nsCOMArray& aFiles, bool aSetValueChanged); + void SetFiles(const nsTArray >& aFiles, bool aSetValueChanged); void SetFiles(nsIDOMFileList* aFiles, bool aSetValueChanged); void SetCheckedChangedInternal(bool aCheckedChanged); @@ -393,6 +392,8 @@ public: nsDOMFileList* GetFiles(); + void OpenDirectoryPicker(ErrorResult& aRv); + // XPCOM GetFormAction() is OK void SetFormAction(const nsAString& aValue, ErrorResult& aRv) { @@ -734,7 +735,7 @@ protected: bool IsValueEmpty() const; void ClearFiles(bool aSetValueChanged) { - nsCOMArray files; + nsTArray > files; SetFiles(files, aSetValueChanged); } @@ -1080,7 +1081,11 @@ protected: */ nsresult MaybeInitPickers(nsEventChainPostVisitor& aVisitor); - nsresult InitFilePicker(); + enum FilePickerType { + FILE_PICKER_FILE, + FILE_PICKER_DIRECTORY + }; + nsresult InitFilePicker(FilePickerType aType); nsresult InitColorPicker(); /** @@ -1122,7 +1127,7 @@ protected: * the frame. Whenever the frame wants to change the filename it has to call * SetFileNames to update this member. */ - nsCOMArray mFiles; + nsTArray > mFiles; nsRefPtr mFileList; diff --git a/content/html/content/src/HTMLSelectElement.cpp b/content/html/content/src/HTMLSelectElement.cpp index d3e66bc5096..28d581fe1e0 100644 --- a/content/html/content/src/HTMLSelectElement.cpp +++ b/content/html/content/src/HTMLSelectElement.cpp @@ -13,6 +13,7 @@ #include "mozilla/dom/HTMLSelectElementBinding.h" #include "mozilla/Util.h" #include "nsContentCreatorFunctions.h" +#include "nsContentList.h" #include "nsError.h" #include "nsEventDispatcher.h" #include "nsEventStates.h" @@ -139,10 +140,12 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLSelectElement, nsGenericHTMLFormElementWithState) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mValidity) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOptions) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelectedOptions) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLSelectElement, nsGenericHTMLFormElementWithState) NS_IMPL_CYCLE_COLLECTION_UNLINK(mValidity) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelectedOptions) NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_ADDREF_INHERITED(HTMLSelectElement, Element) @@ -769,6 +772,34 @@ HTMLSelectElement::SetLength(uint32_t aLength, ErrorResult& aRv) } } +/* static */ +bool +HTMLSelectElement::MatchSelectedOptions(nsIContent* aContent, + int32_t /* unused */, + nsIAtom* /* unused */, + void* /* unused*/) +{ + HTMLOptionElement* option = HTMLOptionElement::FromContent(aContent); + return option && option->Selected(); +} + +nsIHTMLCollection* +HTMLSelectElement::SelectedOptions() +{ + if (!mSelectedOptions) { + mSelectedOptions = new nsContentList(this, MatchSelectedOptions, nullptr, + nullptr, /* deep */ true); + } + return mSelectedOptions; +} + +NS_IMETHODIMP +HTMLSelectElement::GetSelectedOptions(nsIDOMHTMLCollection** aSelectedOptions) +{ + NS_ADDREF(*aSelectedOptions = SelectedOptions()); + return NS_OK; +} + //NS_IMPL_INT_ATTR(HTMLSelectElement, SelectedIndex, selectedindex) NS_IMETHODIMP @@ -847,6 +878,7 @@ HTMLSelectElement::OnOptionSelected(nsISelectControlFrame* aSelectFrame, aSelectFrame->OnOptionSelected(aIndex, aSelected); } + UpdateSelectedOptions(); UpdateValueMissingValidityState(); UpdateState(aNotify); } @@ -1848,6 +1880,8 @@ HTMLSelectElement::SetSelectionChanged(bool aValue, bool aNotify) return; } + UpdateSelectedOptions(); + bool previousSelectionChangedValue = mSelectionHasChanged; mSelectionHasChanged = aValue; @@ -1856,6 +1890,14 @@ HTMLSelectElement::SetSelectionChanged(bool aValue, bool aNotify) } } +void +HTMLSelectElement::UpdateSelectedOptions() +{ + if (mSelectedOptions) { + mSelectedOptions->SetDirty(); + } +} + JSObject* HTMLSelectElement::WrapNode(JSContext* aCx, JS::Handle aScope) { diff --git a/content/html/content/src/HTMLSelectElement.h b/content/html/content/src/HTMLSelectElement.h index 80903020b55..1cf42a3607a 100644 --- a/content/html/content/src/HTMLSelectElement.h +++ b/content/html/content/src/HTMLSelectElement.h @@ -18,7 +18,9 @@ #include "nsError.h" #include "mozilla/dom/HTMLFormElement.h" +class nsContentList; class nsIDOMHTMLOptionElement; +class nsIHTMLCollection; class nsISelectControlFrame; class nsPresState; @@ -208,6 +210,11 @@ public: mOptions->IndexedSetter(aIndex, aOption, aRv); } + static bool MatchSelectedOptions(nsIContent* aContent, int32_t, nsIAtom*, + void*); + + nsIHTMLCollection* SelectedOptions(); + int32_t SelectedIndex() const { return mSelectedIndex; @@ -553,6 +560,12 @@ protected: void SetSelectionChanged(bool aValue, bool aNotify); + /** + * Marks the selectedOptions list as dirty, so that it'll populate itself + * again. + */ + void UpdateSelectedOptions(); + /** * Return whether an element should have a validity UI. * (with :-moz-ui-invalid and :-moz-ui-valid pseudo-classes). @@ -618,6 +631,11 @@ protected: * done adding options */ nsCOMPtr mRestoreState; + + /** + * The live list of selected options. + */ + nsRefPtr mSelectedOptions; }; } // namespace dom diff --git a/content/html/content/src/nsFormSubmission.cpp b/content/html/content/src/nsFormSubmission.cpp index a93c96b89b3..372105da25e 100644 --- a/content/html/content/src/nsFormSubmission.cpp +++ b/content/html/content/src/nsFormSubmission.cpp @@ -470,6 +470,13 @@ nsFSMultipartFormData::AddNameFilePair(const nsAString& aName, if (filename16.IsEmpty()) { filename16.AssignLiteral("blob"); + } else { + nsAutoString filepath16; + rv = file->GetPath(filepath16); + NS_ENSURE_SUCCESS(rv, rv); + if (!filepath16.IsEmpty()) { + filename16 = filepath16 + NS_LITERAL_STRING("/") + filename16; + } } rv = EncodeVal(filename16, filename, true); diff --git a/content/html/content/test/forms/Makefile.in b/content/html/content/test/forms/Makefile.in index 97a4da8a90c..33ce3c226dc 100644 --- a/content/html/content/test/forms/Makefile.in +++ b/content/html/content/test/forms/Makefile.in @@ -67,6 +67,7 @@ MOCHITEST_FILES = \ test_input_color_picker_popup.html \ test_input_color_input_change_events.html \ test_restore_form_elements.html \ + test_select_selectedOptions.html \ $(NULL) include $(topsrcdir)/config/rules.mk diff --git a/content/html/content/test/forms/test_select_selectedOptions.html b/content/html/content/test/forms/test_select_selectedOptions.html new file mode 100644 index 00000000000..e93222459a9 --- /dev/null +++ b/content/html/content/test/forms/test_select_selectedOptions.html @@ -0,0 +1,120 @@ + + + + + Test for HTMLSelectElement.selectedOptions + + + + + +Mozilla Bug 596681 +

+
+
+
+ + diff --git a/content/media/plugins/MediaPluginHost.cpp b/content/media/plugins/MediaPluginHost.cpp index 8bbc1f613ff..b3c6a9970c3 100644 --- a/content/media/plugins/MediaPluginHost.cpp +++ b/content/media/plugins/MediaPluginHost.cpp @@ -107,9 +107,6 @@ static bool IsOmxSupported() // nullptr is returned if Omx decoding is not supported on the device, static const char* GetOmxLibraryName() { - if (!IsOmxSupported()) - return nullptr; - #if defined(ANDROID) && !defined(MOZ_WIDGET_GONK) nsCOMPtr infoService = do_GetService("@mozilla.org/system-info;1"); NS_ASSERTION(infoService, "Could not find a system info service"); @@ -138,19 +135,18 @@ static const char* GetOmxLibraryName() ALOG("Android Manufacturer is: %s", NS_LossyConvertUTF16toASCII(manufacturer).get()); } - if (version >= 16 && manufacturer.Find("HTC") == 0) { - return "libomxpluginjb-htc.so"; + nsAutoString hardware; + rv = infoService->GetPropertyAsAString(NS_LITERAL_STRING("hardware"), hardware); + if (NS_SUCCEEDED(rv)) { + ALOG("Android Hardware is: %s", NS_LossyConvertUTF16toASCII(hardware).get()); } - else if (version == 15 && - (device.Find("LT28", false) == 0 || - device.Find("LT26", false) == 0 || - device.Find("LT22", false) == 0 || - device.Find("IS12", false) == 0 || - device.Find("MT27", false) == 0)) { - // Sony Ericsson devices running ICS - return "libomxpluginsony.so"; - } - else if (version == 13 || version == 12 || version == 11) { +#endif + + if (!IsOmxSupported()) + return nullptr; + +#if defined(ANDROID) && !defined(MOZ_WIDGET_GONK) + if (version == 13 || version == 12 || version == 11) { return "libomxpluginhc.so"; } else if (version == 10 && release_version >= NS_LITERAL_STRING("2.3.6")) { diff --git a/content/media/webaudio/test/Makefile.in b/content/media/webaudio/test/Makefile.in index abd4d27ddac..2fe44cb346c 100644 --- a/content/media/webaudio/test/Makefile.in +++ b/content/media/webaudio/test/Makefile.in @@ -61,7 +61,6 @@ MOCHITEST_FILES := \ test_convolverNode_mono_mono.html \ test_currentTime.html \ test_delayNode.html \ - test_delayNodeAtMax.html \ test_delayNodeSmallMaxDelay.html \ test_delayNodeWithGain.html \ test_dynamicsCompressorNode.html \ @@ -113,4 +112,10 @@ MOCHITEST_FILES := \ audio-quad.wav \ $(NULL) +ifneq ($(MOZ_DEBUG)+$(MOZ_WIDGET_TOOLKIT),+gtk2) # bug 911777 +MOCHITEST_FILES += \ + test_delayNodeAtMax.html \ + $(NULL) +endif + include $(topsrcdir)/config/rules.mk diff --git a/dom/interfaces/html/nsIDOMHTMLSelectElement.idl b/dom/interfaces/html/nsIDOMHTMLSelectElement.idl index a4233654ae0..26cb5666855 100644 --- a/dom/interfaces/html/nsIDOMHTMLSelectElement.idl +++ b/dom/interfaces/html/nsIDOMHTMLSelectElement.idl @@ -19,7 +19,7 @@ interface nsIDOMValidityState; -[scriptable, uuid(846578b2-6d4f-4399-86cc-2c05f19469d0)] +[scriptable, uuid(d8914a2d-3556-4b66-911c-a84c4394e7fa)] interface nsIDOMHTMLSelectElement : nsISupports { attribute boolean autofocus; @@ -44,6 +44,7 @@ interface nsIDOMHTMLSelectElement : nsISupports raises(DOMException); void remove(in long index); + readonly attribute nsIDOMHTMLCollection selectedOptions; attribute long selectedIndex; attribute DOMString value; diff --git a/dom/webidl/HTMLInputElement.webidl b/dom/webidl/HTMLInputElement.webidl index 3bc9036366a..d8cf6f33103 100644 --- a/dom/webidl/HTMLInputElement.webidl +++ b/dom/webidl/HTMLInputElement.webidl @@ -33,6 +33,8 @@ interface HTMLInputElement : HTMLElement { readonly attribute HTMLFormElement? form; [Pure] readonly attribute FileList? files; + [Throws, Pref="dom.input.dirpicker"] + void openDirectoryPicker(); [Pure, SetterThrows] attribute DOMString formAction; [Pure, SetterThrows] diff --git a/dom/webidl/HTMLSelectElement.webidl b/dom/webidl/HTMLSelectElement.webidl index 89f410a0597..06876160140 100644 --- a/dom/webidl/HTMLSelectElement.webidl +++ b/dom/webidl/HTMLSelectElement.webidl @@ -38,7 +38,7 @@ interface HTMLSelectElement : HTMLElement { [Throws] setter creator void (unsigned long index, HTMLOptionElement? option); -// NYI: readonly attribute HTMLCollection selectedOptions; + readonly attribute HTMLCollection selectedOptions; [SetterThrows, Pure] attribute long selectedIndex; [Pure] diff --git a/dom/workers/File.cpp b/dom/workers/File.cpp index f7121dd9360..6457e95c894 100644 --- a/dom/workers/File.cpp +++ b/dom/workers/File.cpp @@ -353,6 +353,29 @@ private: return true; } + static bool + GetPath(JSContext* aCx, JS::Handle aObj, JS::Handle aIdval, + JS::MutableHandle aVp) + { + nsIDOMFile* file = GetInstancePrivate(aCx, aObj, "path"); + if (!file) { + return false; + } + + nsString path; + if (NS_FAILED(file->GetPath(path))) { + path.Truncate(); + } + + JSString* jsPath = JS_NewUCStringCopyN(aCx, path.get(), path.Length()); + if (!jsPath) { + return false; + } + + aVp.set(STRING_TO_JSVAL(jsPath)); + return true; + } + static bool GetLastModifiedDate(JSContext* aCx, JS::Handle aObj, JS::Handle aIdval, JS::MutableHandle aVp) @@ -382,6 +405,8 @@ JSClass File::sClass = { const JSPropertySpec File::sProperties[] = { { "name", 0, PROPERTY_FLAGS, JSOP_WRAPPER(GetName), JSOP_WRAPPER(js_GetterOnlyPropertyStub) }, + { "path", 0, PROPERTY_FLAGS, JSOP_WRAPPER(GetPath), + JSOP_WRAPPER(js_GetterOnlyPropertyStub) }, { "lastModifiedDate", 0, PROPERTY_FLAGS, JSOP_WRAPPER(GetLastModifiedDate), JSOP_WRAPPER(js_GetterOnlyPropertyStub) }, { "mozFullPath", 0, PROPERTY_FLAGS, JSOP_WRAPPER(GetMozFullPath), diff --git a/dom/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp index fc63288da6e..8c342c4b4d8 100644 --- a/dom/workers/RuntimeService.cpp +++ b/dom/workers/RuntimeService.cpp @@ -514,13 +514,13 @@ LoadJSGCMemoryOptions(const char* aPrefName, void* /* aClosure */) matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_allocation_threshold_mb"); - if (memPrefName == matchName || (!rts && index == 9)) { + if (memPrefName == matchName || (!rts && index == 8)) { UpdateCommonJSGCMemoryOption(rts, matchName, JSGC_ALLOCATION_THRESHOLD); continue; } matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_incremental_slice_ms"); - if (memPrefName == matchName || (!rts && index == 10)) { + if (memPrefName == matchName || (!rts && index == 9)) { int32_t prefValue = GetWorkerPref(matchName, -1); uint32_t value = (prefValue <= 0 || prefValue >= 100000) ? 0 : uint32_t(prefValue); @@ -529,7 +529,7 @@ LoadJSGCMemoryOptions(const char* aPrefName, void* /* aClosure */) } matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_dynamic_heap_growth"); - if (memPrefName == matchName || (!rts && index == 11)) { + if (memPrefName == matchName || (!rts && index == 10)) { bool prefValue = GetWorkerPref(matchName, false); UpdatOtherJSGCMemoryOption(rts, JSGC_DYNAMIC_HEAP_GROWTH, prefValue ? 0 : 1); @@ -537,7 +537,7 @@ LoadJSGCMemoryOptions(const char* aPrefName, void* /* aClosure */) } matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_dynamic_mark_slice"); - if (memPrefName == matchName || (!rts && index == 12)) { + if (memPrefName == matchName || (!rts && index == 11)) { bool prefValue = GetWorkerPref(matchName, false); UpdatOtherJSGCMemoryOption(rts, JSGC_DYNAMIC_MARK_SLICE, prefValue ? 0 : 1); diff --git a/dom/workers/test/file_worker.js b/dom/workers/test/file_worker.js index 92880ffcd8a..67c4bd50da5 100644 --- a/dom/workers/test/file_worker.js +++ b/dom/workers/test/file_worker.js @@ -9,6 +9,7 @@ onmessage = function(event) { rtnObj.size = file.size; rtnObj.type = file.type; rtnObj.name = file.name; + rtnObj.path = file.path; rtnObj.lastModifiedDate = file.lastModifiedDate; rtnObj.mozFullPath = file.mozFullPath; diff --git a/dom/workers/test/test_file.xul b/dom/workers/test/test_file.xul index e3943c7a672..69f41ca3006 100644 --- a/dom/workers/test/test_file.xul +++ b/dom/workers/test/test_file.xul @@ -69,6 +69,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=123456 is(event.data.size, expectedSize, "size proproperty accessed from worker is not the same as on main thread."); is(event.data.type, expectedType, "type proproperty accessed from worker is incorrect."); is(event.data.name, file.name, "name proproperty accessed from worker is incorrect."); + is(event.data.path, file.path, "path proproperty accessed from worker is incorrect."); is(event.data.lastModifiedDate.toString(), file.lastModifiedDate.toString(), "lastModifiedDate proproperty accessed from worker is incorrect."); is(event.data.mozFullPath, file.mozFullPath, "mozFullPath proproperty accessed from worker is not the same as on main thread."); finish(); diff --git a/gfx/layers/client/ImageClient.cpp b/gfx/layers/client/ImageClient.cpp index 22e12587f5f..0b0abc92465 100644 --- a/gfx/layers/client/ImageClient.cpp +++ b/gfx/layers/client/ImageClient.cpp @@ -256,7 +256,8 @@ ImageClientSingle::CreateBufferTextureClient(gfx::SurfaceFormat aFormat, Texture TemporaryRef ImageClientSingle::CreateBufferTextureClient(gfx::SurfaceFormat aFormat) { - return CompositableClient::CreateBufferTextureClient(aFormat, mTextureFlags); + return CompositableClient::CreateBufferTextureClient(aFormat, + mTextureFlags | TEXTURE_FLAGS_DEFAULT); } void diff --git a/gfx/thebes/gfxFont.cpp b/gfx/thebes/gfxFont.cpp index 81d23e75924..d50ee17ae32 100644 --- a/gfx/thebes/gfxFont.cpp +++ b/gfx/thebes/gfxFont.cpp @@ -4383,7 +4383,36 @@ gfxFontGroup::InitTextRun(gfxContext *aContext, } } +#ifdef PR_LOGGING + PRLogModuleInfo *log = (mStyle.systemFont ? + gfxPlatform::GetLog(eGfxLog_textrunui) : + gfxPlatform::GetLog(eGfxLog_textrun)); +#endif + if (sizeof(T) == sizeof(uint8_t) && !transformedString) { + +#ifdef PR_LOGGING + if (MOZ_UNLIKELY(log)) { + nsAutoCString lang; + mStyle.language->ToUTF8String(lang); + nsAutoCString str((const char*)aString, aLength); + PR_LOG(log, PR_LOG_WARNING,\ + ("(%s) fontgroup: [%s] lang: %s script: %d len %d " + "weight: %d width: %d style: %s size: %6.2f %d-byte " + "TEXTRUN [%s] ENDTEXTRUN\n", + (mStyle.systemFont ? "textrunui" : "textrun"), + NS_ConvertUTF16toUTF8(mFamilies).get(), + lang.get(), MOZ_SCRIPT_LATIN, aLength, + uint32_t(mStyle.weight), uint32_t(mStyle.stretch), + (mStyle.style & NS_FONT_STYLE_ITALIC ? "italic" : + (mStyle.style & NS_FONT_STYLE_OBLIQUE ? "oblique" : + "normal")), + mStyle.size, + sizeof(T), + str.get())); + } +#endif + // the text is still purely 8-bit; bypass the script-run itemizer // and treat it as a single Latin run InitScriptRun(aContext, aTextRun, aString, @@ -4402,12 +4431,6 @@ gfxFontGroup::InitTextRun(gfxContext *aContext, // the font matching process below gfxScriptItemizer scriptRuns(textPtr, aLength); -#ifdef PR_LOGGING - PRLogModuleInfo *log = (mStyle.systemFont ? - gfxPlatform::GetLog(eGfxLog_textrunui) : - gfxPlatform::GetLog(eGfxLog_textrun)); -#endif - uint32_t runStart = 0, runLimit = aLength; int32_t runScript = MOZ_SCRIPT_LATIN; while (scriptRuns.Next(runStart, runLimit, runScript)) { @@ -4419,7 +4442,7 @@ gfxFontGroup::InitTextRun(gfxContext *aContext, uint32_t runLen = runLimit - runStart; PR_LOG(log, PR_LOG_WARNING,\ ("(%s) fontgroup: [%s] lang: %s script: %d len %d " - "weight: %d width: %d style: %s " + "weight: %d width: %d style: %s size: %6.2f %d-byte " "TEXTRUN [%s] ENDTEXTRUN\n", (mStyle.systemFont ? "textrunui" : "textrun"), NS_ConvertUTF16toUTF8(mFamilies).get(), @@ -4428,6 +4451,8 @@ gfxFontGroup::InitTextRun(gfxContext *aContext, (mStyle.style & NS_FONT_STYLE_ITALIC ? "italic" : (mStyle.style & NS_FONT_STYLE_OBLIQUE ? "oblique" : "normal")), + mStyle.size, + sizeof(T), NS_ConvertUTF16toUTF8(textPtr + runStart, runLen).get())); } #endif diff --git a/js/ipc/JavaScriptParent.h b/js/ipc/JavaScriptParent.h index 29be6496d5e..d6d975622c2 100644 --- a/js/ipc/JavaScriptParent.h +++ b/js/ipc/JavaScriptParent.h @@ -39,9 +39,9 @@ class JavaScriptParent JS::MutableHandle desc, unsigned flags); bool defineProperty(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, JS::MutableHandle desc); - bool getOwnPropertyNames(JSContext *cx, JS::HandleObject proxy, js::AutoIdVector &props); + bool getOwnPropertyNames(JSContext *cx, JS::HandleObject proxy, JS::AutoIdVector &props); bool delete_(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, bool *bp); - bool enumerate(JSContext *cx, JS::HandleObject proxy, js::AutoIdVector &props); + bool enumerate(JSContext *cx, JS::HandleObject proxy, JS::AutoIdVector &props); // Derived proxy traps. Implementing these is useful for perfomance. bool has(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, bool *bp); @@ -50,7 +50,7 @@ class JavaScriptParent JS::HandleId id, JS::MutableHandleValue vp); bool set(JSContext *cx, JS::HandleObject proxy, JS::HandleObject receiver, JS::HandleId id, bool strict, JS::MutableHandleValue vp); - bool keys(JSContext *cx, JS::HandleObject proxy, js::AutoIdVector &props); + bool keys(JSContext *cx, JS::HandleObject proxy, JS::AutoIdVector &props); // We use "iterate" provided by the base class here. // SpiderMonkey Extensions. @@ -82,7 +82,8 @@ class JavaScriptParent private: bool makeId(JSContext *cx, JSObject *obj, ObjectId *idp); - bool getPropertyNames(JSContext *cx, JS::HandleObject proxy, uint32_t flags, js::AutoIdVector &props); + bool getPropertyNames(JSContext *cx, JS::HandleObject proxy, uint32_t flags, + JS::AutoIdVector &props); ObjectId idOf(JSObject *obj); // Catastrophic IPC failure. diff --git a/js/src/NamespaceImports.h b/js/src/NamespaceImports.h new file mode 100644 index 00000000000..808912658f9 --- /dev/null +++ b/js/src/NamespaceImports.h @@ -0,0 +1,116 @@ +/* -*- 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/. */ + +// This file imports some common JS:: names into the js namespace so we can +// make unqualified references to them. + +#ifndef NamespaceImports_h +#define NamespaceImports_h + +// These includes are needed these for some typedefs (e.g. HandleValue) and +// functions (e.g. NullValue())... +#include "js/CallNonGenericMethod.h" +#include "js/TypeDecls.h" +#include "js/Value.h" + +// ... but we do forward declarations of the structs and classes not pulled in +// by the headers included above. +namespace JS { + +class Latin1CharsZ; +class StableCharPtr; +class TwoByteChars; + +class AutoFunctionVector; +class AutoIdVector; +class AutoObjectVector; +class AutoScriptVector; +class AutoValueVector; + +class AutoIdArray; + +class AutoGCRooter; +class AutoArrayRooter; +template class AutoVectorRooter; +template class AutoHashMapRooter; +template class AutoHashSetRooter; + +} + +// Do the importing. +namespace js { + +using JS::Value; +using JS::BooleanValue; +using JS::DoubleValue; +using JS::Int32Value; +using JS::IsPoisonedValue; +using JS::MagicValue; +using JS::NullValue; +using JS::NumberValue; +using JS::ObjectOrNullValue; +using JS::ObjectValue; +using JS::PrivateUint32Value; +using JS::PrivateValue; +using JS::StringValue; +using JS::UndefinedValue; + +using JS::IsPoisonedPtr; + +using JS::Latin1CharsZ; +using JS::StableCharPtr; +using JS::TwoByteChars; + +using JS::AutoFunctionVector; +using JS::AutoIdVector; +using JS::AutoObjectVector; +using JS::AutoScriptVector; +using JS::AutoValueVector; + +using JS::AutoIdArray; + +using JS::AutoGCRooter; +using JS::AutoArrayRooter; +using JS::AutoHashMapRooter; +using JS::AutoHashSetRooter; +using JS::AutoVectorRooter; + +using JS::CallArgs; +using JS::CallNonGenericMethod; +using JS::CallReceiver; +using JS::CompileOptions; +using JS::IsAcceptableThis; +using JS::NativeImpl; + +using JS::Rooted; +using JS::RootedFunction; +using JS::RootedId; +using JS::RootedObject; +using JS::RootedScript; +using JS::RootedString; +using JS::RootedValue; + +using JS::Handle; +using JS::HandleFunction; +using JS::HandleId; +using JS::HandleObject; +using JS::HandleScript; +using JS::HandleString; +using JS::HandleValue; + +using JS::MutableHandle; +using JS::MutableHandleFunction; +using JS::MutableHandleId; +using JS::MutableHandleObject; +using JS::MutableHandleScript; +using JS::MutableHandleString; +using JS::MutableHandleValue; + +using JS::Zone; + +} /* namespace js */ + +#endif /* NamespaceImports_h */ diff --git a/js/src/build/autoconf/compiler-opts.m4 b/js/src/build/autoconf/compiler-opts.m4 index 0c333ad8070..4ae7ccb983a 100644 --- a/js/src/build/autoconf/compiler-opts.m4 +++ b/js/src/build/autoconf/compiler-opts.m4 @@ -223,7 +223,8 @@ if test "$GNU_CC"; then CFLAGS="$CFLAGS -ffunction-sections -fdata-sections" CXXFLAGS="$CXXFLAGS -ffunction-sections -fdata-sections" fi - CXXFLAGS="$CXXFLAGS -fno-exceptions" + CFLAGS="$CFLAGS -fno-math-errno" + CXXFLAGS="$CXXFLAGS -fno-exceptions -fno-math-errno" fi dnl ======================================================== diff --git a/js/src/builtin/BinaryData.h b/js/src/builtin/BinaryData.h index c5486b03ce3..d87d2a29418 100644 --- a/js/src/builtin/BinaryData.h +++ b/js/src/builtin/BinaryData.h @@ -7,7 +7,6 @@ #ifndef builtin_BinaryData_h #define builtin_BinaryData_h -#include "jsapi.h" #include "jsobj.h" #include "builtin/TypeRepresentation.h" diff --git a/js/src/builtin/Eval.h b/js/src/builtin/Eval.h index 19e3b060a51..1d15b00c04b 100644 --- a/js/src/builtin/Eval.h +++ b/js/src/builtin/Eval.h @@ -7,8 +7,8 @@ #ifndef builtin_Eval_h #define builtin_Eval_h -#include "jsapi.h" #include "jsbytecode.h" +#include "NamespaceImports.h" namespace js { diff --git a/js/src/builtin/Intl.h b/js/src/builtin/Intl.h index 149fd666082..e44bf612fa4 100644 --- a/js/src/builtin/Intl.h +++ b/js/src/builtin/Intl.h @@ -7,7 +7,7 @@ #ifndef builtin_Intl_h #define builtin_Intl_h -#include "jsapi.h" +#include "NamespaceImports.h" #include "js/RootingAPI.h" diff --git a/js/src/builtin/TestingFunctions.h b/js/src/builtin/TestingFunctions.h index bec7512e004..33472d2d7e5 100644 --- a/js/src/builtin/TestingFunctions.h +++ b/js/src/builtin/TestingFunctions.h @@ -7,7 +7,7 @@ #ifndef builtin_TestingFunctions_h #define builtin_TestingFunctions_h -#include "jsapi.h" +#include "NamespaceImports.h" namespace js { @@ -15,10 +15,10 @@ bool DefineTestingFunctions(JSContext *cx, HandleObject obj); bool -testingFunc_inParallelSection(JSContext *cx, unsigned argc, jsval *vp); +testingFunc_inParallelSection(JSContext *cx, unsigned argc, Value *vp); bool -testingFunc_bailout(JSContext *cx, unsigned argc, jsval *vp); +testingFunc_bailout(JSContext *cx, unsigned argc, Value *vp); } /* namespace js */ diff --git a/js/src/configure.in b/js/src/configure.in index 302dcb49c31..fc79c78a261 100644 --- a/js/src/configure.in +++ b/js/src/configure.in @@ -232,7 +232,7 @@ changequote(,) MOZJS_MAJOR_VERSION=`echo $MOZILLA_VERSION | sed "s|\(^[0-9]*\)\.[0-9]*.*|\1|"` MOZJS_MINOR_VERSION=`echo $MOZILLA_VERSION | sed "s|^[0-9]*\.\([0-9]*\).*|\1|"` MOZJS_PATCH_VERSION=`echo $MOZILLA_VERSION | sed "s|^[0-9]*\.[0-9]*[^0-9]*||"` -IS_ALPHA=`echo $MOZILLA_VERSION | grep [ab]` +IS_ALPHA=`echo $MOZILLA_VERSION | grep '[ab]'` dnl XXX in a temporary bid to avoid developer anger at renaming files dnl XXX before "js" symlinks exist, don't change names. diff --git a/js/src/ds/IdValuePair.h b/js/src/ds/IdValuePair.h index 2b0591c5441..81971abe39b 100644 --- a/js/src/ds/IdValuePair.h +++ b/js/src/ds/IdValuePair.h @@ -7,7 +7,7 @@ #ifndef ds_IdValuePair_h #define ds_IdValuePair_h -#include "jsapi.h" +#include "NamespaceImports.h" namespace js { diff --git a/js/src/frontend/BytecodeCompiler.h b/js/src/frontend/BytecodeCompiler.h index 7cc5f60ecf7..40dcf8ddf5b 100644 --- a/js/src/frontend/BytecodeCompiler.h +++ b/js/src/frontend/BytecodeCompiler.h @@ -7,7 +7,7 @@ #ifndef frontend_BytecodeCompiler_h #define frontend_BytecodeCompiler_h -#include "jsapi.h" +#include "NamespaceImports.h" class JSLinearString; diff --git a/js/src/frontend/TokenStream.h b/js/src/frontend/TokenStream.h index a1ffd0f151d..c223fb0fe6e 100644 --- a/js/src/frontend/TokenStream.h +++ b/js/src/frontend/TokenStream.h @@ -317,6 +317,12 @@ struct CompileError { } ~CompileError(); void throwError(JSContext *cx); + + private: + // CompileError owns raw allocated memory, so disable assignment and copying + // for safety. + void operator=(const CompileError &) MOZ_DELETE; + CompileError(const CompileError &) MOZ_DELETE; }; // Ideally, tokenizing would be entirely independent of context. But the diff --git a/js/src/gc/Barrier.h b/js/src/gc/Barrier.h index 762ce29a08c..688aa629215 100644 --- a/js/src/gc/Barrier.h +++ b/js/src/gc/Barrier.h @@ -8,6 +8,7 @@ #define gc_Barrier_h #include "jsapi.h" +#include "NamespaceImports.h" #include "gc/Heap.h" #include "js/HashTable.h" diff --git a/js/src/gc/Statistics.h b/js/src/gc/Statistics.h index 6c45fabb63d..36008f2769b 100644 --- a/js/src/gc/Statistics.h +++ b/js/src/gc/Statistics.h @@ -10,10 +10,11 @@ #include "mozilla/DebugOnly.h" #include "mozilla/PodOperations.h" -#include "jsapi.h" +#include "jsalloc.h" #include "jspubtd.h" #include "js/GCAPI.h" +#include "js/Vector.h" struct JSCompartment; diff --git a/js/src/gdb/gdb-tests.h b/js/src/gdb/gdb-tests.h index 52a6a363e2e..4fd84b555c4 100644 --- a/js/src/gdb/gdb-tests.h +++ b/js/src/gdb/gdb-tests.h @@ -21,6 +21,7 @@ // actually run.) #include "jsapi.h" +#include "NamespaceImports.h" void breakpoint(); diff --git a/js/src/jit/AsmJS.cpp b/js/src/jit/AsmJS.cpp index 09a07248764..73f29cc8815 100644 --- a/js/src/jit/AsmJS.cpp +++ b/js/src/jit/AsmJS.cpp @@ -5223,7 +5223,7 @@ StackDecrementForCall(MacroAssembler &masm, const VectorT &argTypes, unsigned ex static const unsigned FramePushedAfterSave = NonVolatileRegs.gprs().size() * STACK_SLOT_SIZE + NonVolatileRegs.fpus().size() * sizeof(double); -#ifndef JS_CPU_ARM + static bool GenerateEntry(ModuleCompiler &m, const AsmJSModule::ExportedFunction &exportedFunc) { @@ -5237,72 +5237,85 @@ GenerateEntry(ModuleCompiler &m, const AsmJSModule::ExportedFunction &exportedFu // PushRegsInMask(NonVolatileRegs). masm.setFramePushed(0); masm.PushRegsInMask(NonVolatileRegs); + JS_ASSERT(masm.framePushed() == FramePushedAfterSave); // Remember the stack pointer in the current AsmJSActivation. This will be // used by error exit paths to set the stack pointer back to what it was // right after the (C++) caller's non-volatile registers were saved so that // they can be restored. - JS_ASSERT(masm.framePushed() == FramePushedAfterSave); Register activation = ABIArgGenerator::NonArgReturnVolatileReg0; LoadAsmJSActivationIntoRegister(masm, activation); - masm.movePtr(StackPointer, Operand(activation, AsmJSActivation::offsetOfErrorRejoinSP())); + masm.storePtr(StackPointer, Address(activation, AsmJSActivation::offsetOfErrorRejoinSP())); -#if defined(JS_CPU_X64) - // Install the heap pointer into the globally-pinned HeapReg. The heap - // pointer is stored in the global data section and is patched at dynamic - // link time. - CodeOffsetLabel label = masm.loadRipRelativeInt64(HeapReg); - m.addGlobalAccess(AsmJSGlobalAccess(label.offset(), m.module().heapOffset())); + // ARM has a globally-pinned GlobalReg (x64 uses RIP-relative addressing, + // x86 uses immediates in effective addresses) and NaN register (used as + // part of the out-of-bounds handling in heap loads/stores). +#if defined(JS_CPU_ARM) + masm.movePtr(IntArgReg1, GlobalReg); + masm.ma_vimm(js_NaN, NANReg); #endif + // ARM and x64 have a globally-pinned HeapReg (x86 uses immediates in + // effective addresses). +#if defined(JS_CPU_X64) || defined(JS_CPU_ARM) + masm.loadPtr(Address(IntArgReg1, m.module().heapOffset()), HeapReg); +#endif + + // Get 'argv' into a non-arg register and save it on the stack. Register argv = ABIArgGenerator::NonArgReturnVolatileReg0; Register scratch = ABIArgGenerator::NonArgReturnVolatileReg1; #if defined(JS_CPU_X86) - masm.movl(Operand(StackPointer, NativeFrameSize + masm.framePushed()), argv); -#elif defined(JS_CPU_X64) - masm.movq(IntArgReg0, argv); - masm.Push(argv); + masm.loadPtr(Address(StackPointer, NativeFrameSize + masm.framePushed()), argv); +#else + masm.movePtr(IntArgReg0, argv); #endif + masm.Push(argv); // Bump the stack for the call. const ModuleCompiler::Func &func = *m.lookupFunction(exportedFunc.name()); unsigned stackDec = StackDecrementForCall(masm, func.sig().args()); masm.reserveStack(stackDec); + // Copy parameters out of argv and into the registers/stack-slots specified by + // the system ABI. for (ABIArgTypeIter iter(func.sig().args()); !iter.done(); iter++) { - Operand src(argv, iter.index() * sizeof(uint64_t)); + unsigned argOffset = iter.index() * sizeof(uint64_t); + Address src(argv, argOffset); switch (iter->kind()) { case ABIArg::GPR: masm.load32(src, iter->gpr()); break; case ABIArg::FPU: +#if defined(JS_CPU_ARM) and !defined(JS_CPU_ARM_HARDFP) + masm.ma_dataTransferN(IsLoad, 64, true, argv, Imm32(argOffset), + Register::FromCode(iter->fpu().code()*2)); +#else masm.loadDouble(src, iter->fpu()); +#endif break; case ABIArg::Stack: if (iter.mirType() == MIRType_Int32) { masm.load32(src, scratch); - masm.storePtr(scratch, Operand(StackPointer, iter->offsetFromArgBase())); + masm.storePtr(scratch, Address(StackPointer, iter->offsetFromArgBase())); } else { JS_ASSERT(iter.mirType() == MIRType_Double); masm.loadDouble(src, ScratchFloatReg); - masm.storeDouble(ScratchFloatReg, Operand(StackPointer, iter->offsetFromArgBase())); + masm.storeDouble(ScratchFloatReg, Address(StackPointer, iter->offsetFromArgBase())); } break; } } + // Call into the real function. AssertStackAlignment(masm); masm.call(func.code()); + // Pop the stack and recover the original 'argv' argument passed to the + // trampoline (which was pushed on the stack). masm.freeStack(stackDec); - -#if defined(JS_CPU_X86) - masm.movl(Operand(StackPointer, NativeFrameSize + masm.framePushed()), argv); -#elif defined(JS_CPU_X64) masm.Pop(argv); -#endif - // Store return value in argv[0] + // Store the return value in argv[0] switch (func.sig().retType().which()) { case RetType::Void: break; @@ -5310,119 +5323,7 @@ GenerateEntry(ModuleCompiler &m, const AsmJSModule::ExportedFunction &exportedFu masm.storeValue(JSVAL_TYPE_INT32, ReturnReg, Address(argv, 0)); break; case RetType::Double: - masm.canonicalizeDouble(ReturnFloatReg); - masm.storeDouble(ReturnFloatReg, Address(argv, 0)); - break; - } - - // Restore clobbered registers. - masm.PopRegsInMask(NonVolatileRegs); - JS_ASSERT(masm.framePushed() == 0); - - masm.move32(Imm32(true), ReturnReg); - masm.ret(); - return true; -} -#else -static bool -GenerateEntry(ModuleCompiler &m, const AsmJSModule::ExportedFunction &exportedFunc) -{ - const ModuleCompiler::Func &func = *m.lookupFunction(exportedFunc.name()); - - MacroAssembler &masm = m.masm(); - - // In constrast to the X64 system ABI, the Ion convention is that all - // registers are clobbered by calls. Thus, we must save the caller's - // non-volatile registers. - // - // NB: GenerateExits assumes that masm.framePushed() == 0 before - // PushRegsInMask(NonVolatileRegs). - masm.setFramePushed(0); - masm.PushRegsInMask(NonVolatileRegs); - JS_ASSERT(masm.framePushed() == FramePushedAfterSave); - JS_ASSERT(masm.framePushed() % 8 == 0); - - // Remember the stack pointer in the current AsmJSActivation. This will be - // used by error exit paths to set the stack pointer back to what it was - // right after the (C++) caller's non-volatile registers were saved so that - // they can be restored. - - LoadAsmJSActivationIntoRegister(masm, r9); - masm.ma_str(StackPointer, Address(r9, AsmJSActivation::offsetOfErrorRejoinSP())); - // masm.storeErrorRejoinSp(); - - // Move the parameters into non-argument registers since we are about to - // clobber these registers with the contents of argv. - Register argv = r9; - masm.movePtr(IntArgReg1, GlobalReg); // globalData - masm.movePtr(IntArgReg0, argv); // argv - - masm.ma_ldr(Operand(GlobalReg, Imm32(m.module().heapOffset())), HeapReg); - // Remember argv so that we can load argv[0] after the call. - JS_ASSERT(masm.framePushed() % 8 == 0); - masm.Push(argv); - JS_ASSERT(masm.framePushed() % 8 == 4); - - // Determine how many stack slots we need to hold arguments that don't fit - // in registers. - unsigned numStackArgs = 0; - for (ABIArgTypeIter iter(func.sig().args()); !iter.done(); iter++) { - if (iter->kind() == ABIArg::Stack) - numStackArgs++; - } - - // Before calling, we must ensure sp % 16 == 0. Since (sp % 16) = 8 on - // entry, we need to push 8 (mod 16) bytes. - //JS_ASSERT(AlignmentAtPrologue == 8); - JS_ASSERT(masm.framePushed() % 8 == 4); - unsigned stackDec = numStackArgs * sizeof(double) + (masm.framePushed() >> 2) % 2 * sizeof(uint32_t); - masm.reserveStack(stackDec); - //JS_ASSERT(masm.framePushed() % 8 == 0); - if(getenv("GDB_BREAK")) { - masm.breakpoint(js::jit::Assembler::Always); - } - // Copy parameters out of argv into the registers/stack-slots specified by - // the system ABI. - for (ABIArgTypeIter iter(func.sig().args()); !iter.done(); iter++) { - unsigned argOffset = iter.index() * sizeof(uint64_t); - switch (iter->kind()) { - case ABIArg::GPR: - masm.ma_ldr(Operand(argv, argOffset), iter->gpr()); - break; - case ABIArg::FPU: -#if defined(JS_CPU_ARM_HARDFP) - masm.ma_vldr(Operand(argv, argOffset), iter->fpu()); -#else - // The ABI is expecting a double value in a pair of gpr's. Figure out which gprs it is, - // and use them explicityl. - masm.ma_dataTransferN(IsLoad, 64, true, argv, Imm32(argOffset), Register::FromCode(iter->fpu().code()*2)); -#endif - break; - case ABIArg::Stack: - if (iter.mirType() == MIRType_Int32) { - masm.memMove32(Address(argv, argOffset), Address(StackPointer, iter->offsetFromArgBase())); - } else { - masm.memMove64(Address(argv, argOffset), Address(StackPointer, iter->offsetFromArgBase())); - } - break; - } - } - masm.ma_vimm(js_NaN, NANReg); - masm.call(func.code()); - - // Recover argv. - masm.freeStack(stackDec); - masm.Pop(argv); - - // Store the result in argv[0]. - switch (func.sig().retType().which()) { - case RetType::Void: - break; - case RetType::Signed: - masm.storeValue(JSVAL_TYPE_INT32, ReturnReg, Address(argv, 0)); - break; - case RetType::Double: -#ifndef JS_CPU_ARM_HARDFP +#if defined(JS_CPU_ARM) and !defined(JS_CPU_ARM_HARDFP) masm.ma_vxfer(r0, r1, d0); #endif masm.canonicalizeDouble(ReturnFloatReg); @@ -5430,13 +5331,15 @@ GenerateEntry(ModuleCompiler &m, const AsmJSModule::ExportedFunction &exportedFu break; } + // Restore clobbered non-volatile registers of the caller. masm.PopRegsInMask(NonVolatileRegs); - masm.ma_mov(Imm32(true), ReturnReg); + JS_ASSERT(masm.framePushed() == 0); + + masm.move32(Imm32(true), ReturnReg); masm.abiret(); return true; } -#endif static inline bool TryEnablingIon(JSContext *cx, AsmJSModule &module, HandleFunction fun, uint32_t exitIndex, @@ -6029,16 +5932,9 @@ GenerateStackOverflowExit(ModuleCompiler &m, Label *throwLabel) LoadAsmJSActivationIntoRegister(masm, eax); LoadJSContextFromActivation(masm, eax, eax); masm.storePtr(eax, Address(StackPointer, 0)); -#elif defined(JS_CPU_X64) - LoadAsmJSActivationIntoRegister(masm, IntArgReg0); - LoadJSContextFromActivation(masm, IntArgReg0, IntArgReg0); #else - - // on ARM, we should always be aligned, just do the context manipulation - // and make the call. LoadAsmJSActivationIntoRegister(masm, IntArgReg0); LoadJSContextFromActivation(masm, IntArgReg0, IntArgReg0); - #endif void (*pf)(JSContext*) = js_ReportOverRecursed; masm.call(ImmWord(JS_FUNC_TO_DATA_PTR(void*, pf))); diff --git a/js/src/jit/AsmJS.h b/js/src/jit/AsmJS.h index 2d8e876a3ab..4654743275b 100644 --- a/js/src/jit/AsmJS.h +++ b/js/src/jit/AsmJS.h @@ -7,7 +7,9 @@ #ifndef jit_AsmJS_h #define jit_AsmJS_h -#include "jsapi.h" +#include + +#include "js/TypeDecls.h" namespace js { diff --git a/js/src/jit/AsmJSLink.cpp b/js/src/jit/AsmJSLink.cpp index 09d5638553c..b5a77e246c9 100644 --- a/js/src/jit/AsmJSLink.cpp +++ b/js/src/jit/AsmJSLink.cpp @@ -362,13 +362,8 @@ CallAsmJS(JSContext *cx, unsigned argc, Value *vp) JitActivation jitActivation(cx, /* firstFrameIsConstructing = */ false, /* active */ false); // Call the per-exported-function trampoline created by GenerateEntry. -#ifdef JS_CPU_ARM if (!module.entryTrampoline(func)(coercedArgs.begin(), module.globalData())) return false; -#else - if (!module.entryTrampoline(func)(coercedArgs.begin())) - return false; -#endif } switch (func.returnType()) { diff --git a/js/src/jit/AsmJSLink.h b/js/src/jit/AsmJSLink.h index 426a7e48bfd..51e6fcfe26d 100644 --- a/js/src/jit/AsmJSLink.h +++ b/js/src/jit/AsmJSLink.h @@ -7,7 +7,7 @@ #ifndef jit_AsmJSLink_h #define jit_AsmJSLink_h -#include "jsapi.h" +#include "NamespaceImports.h" namespace js { diff --git a/js/src/jit/AsmJSModule.h b/js/src/jit/AsmJSModule.h index b71eb0ef104..b667916f3da 100644 --- a/js/src/jit/AsmJSModule.h +++ b/js/src/jit/AsmJSModule.h @@ -183,11 +183,7 @@ class AsmJSModule ionCodeOffset_ = off; } }; -#ifdef JS_CPU_ARM typedef int32_t (*CodePtr)(uint64_t *args, uint8_t *global); -#else - typedef int32_t (*CodePtr)(uint64_t *args); -#endif typedef Vector ArgCoercionVector; diff --git a/js/src/jit/C1Spewer.h b/js/src/jit/C1Spewer.h index 3b23e5e37df..522be56016d 100644 --- a/js/src/jit/C1Spewer.h +++ b/js/src/jit/C1Spewer.h @@ -9,7 +9,7 @@ #ifdef DEBUG -#include "jsapi.h" +#include "NamespaceImports.h" #include "js/RootingAPI.h" diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index eba68b7ae43..5e21050cdee 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -1786,6 +1786,9 @@ CodeGenerator::visitCallGeneric(LCallGeneric *call) if (!emitCallToUncompiledScriptPar(call, calleereg)) return false; break; + + default: + MOZ_ASSUME_UNREACHABLE("No such execution mode"); } masm.bind(&end); @@ -1911,6 +1914,9 @@ CodeGenerator::visitCallKnown(LCallKnown *call) if (!emitCallToUncompiledScriptPar(call, calleereg)) return false; break; + + default: + MOZ_ASSUME_UNREACHABLE("No such execution mode"); } masm.bind(&end); @@ -4800,7 +4806,7 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole *ool) masm.jump(ool->rejoin()); return true; - case ParallelExecution: + case ParallelExecution: { ////////////////////////////////////////////////////////////// // If the problem is that we do not have sufficient capacity, // try to reallocate the elements array and then branch back @@ -4855,6 +4861,10 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole *ool) return false; masm.jump(bail1->entry()); return true; + } + + default: + MOZ_ASSUME_UNREACHABLE("No such execution mode"); } JS_ASSERT(false); diff --git a/js/src/jit/CompileInfo.h b/js/src/jit/CompileInfo.h index 42b77e7100e..9e2b76f1e18 100644 --- a/js/src/jit/CompileInfo.h +++ b/js/src/jit/CompileInfo.h @@ -30,11 +30,15 @@ CountArgSlots(JSScript *script, JSFunction *fun) enum ExecutionMode { // Normal JavaScript execution - SequentialExecution = 0, + SequentialExecution, // JavaScript code to be executed in parallel worker threads, // e.g. by ParallelArray - ParallelExecution + ParallelExecution, + + // MIR analysis performed when invoking 'new' on a script, to determine + // definite properties + DefinitePropertiesAnalysis }; // Not as part of the enum so we don't get warnings about unhandled enum diff --git a/js/src/jit/ExecutionModeInlines.h b/js/src/jit/ExecutionModeInlines.h index 519d86ea7ff..47356b6e83d 100644 --- a/js/src/jit/ExecutionModeInlines.h +++ b/js/src/jit/ExecutionModeInlines.h @@ -22,6 +22,7 @@ HasIonScript(JSScript *script, ExecutionMode cmode) switch (cmode) { case SequentialExecution: return script->hasIonScript(); case ParallelExecution: return script->hasParallelIonScript(); + default:; } MOZ_ASSUME_UNREACHABLE("No such execution mode"); } @@ -32,6 +33,7 @@ GetIonScript(JSScript *script, ExecutionMode cmode) switch (cmode) { case SequentialExecution: return script->maybeIonScript(); case ParallelExecution: return script->maybeParallelIonScript(); + default:; } MOZ_ASSUME_UNREACHABLE("No such execution mode"); } @@ -42,6 +44,7 @@ SetIonScript(JSScript *script, ExecutionMode cmode, IonScript *ionScript) switch (cmode) { case SequentialExecution: script->setIonScript(ionScript); return; case ParallelExecution: script->setParallelIonScript(ionScript); return; + default:; } MOZ_ASSUME_UNREACHABLE("No such execution mode"); } @@ -52,6 +55,7 @@ OffsetOfIonInJSScript(ExecutionMode cmode) switch (cmode) { case SequentialExecution: return JSScript::offsetOfIonScript(); case ParallelExecution: return JSScript::offsetOfParallelIonScript(); + default:; } MOZ_ASSUME_UNREACHABLE("No such execution mode"); } @@ -62,6 +66,8 @@ CanIonCompile(JSScript *script, ExecutionMode cmode) switch (cmode) { case SequentialExecution: return script->canIonCompile(); case ParallelExecution: return script->canParallelIonCompile(); + case DefinitePropertiesAnalysis: return true; + default:; } MOZ_ASSUME_UNREACHABLE("No such execution mode"); return false; @@ -79,6 +85,7 @@ CompilingOffThread(JSScript *script, ExecutionMode cmode) switch (cmode) { case SequentialExecution: return script->isIonCompilingOffThread(); case ParallelExecution: return script->isParallelIonCompilingOffThread(); + default:; } MOZ_ASSUME_UNREACHABLE("No such execution mode"); } @@ -89,6 +96,7 @@ CompilingOffThread(HandleScript script, ExecutionMode cmode) switch (cmode) { case SequentialExecution: return script->isIonCompilingOffThread(); case ParallelExecution: return script->isParallelIonCompilingOffThread(); + default:; } MOZ_ASSUME_UNREACHABLE("No such execution mode"); } @@ -99,6 +107,7 @@ CompilerOutputKind(ExecutionMode cmode) switch (cmode) { case SequentialExecution: return types::CompilerOutput::Ion; case ParallelExecution: return types::CompilerOutput::ParallelIon; + default:; } MOZ_ASSUME_UNREACHABLE("No such execution mode"); } diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index f6be53e54e5..7b852acbdee 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -2451,6 +2451,8 @@ jit::Invalidate(JSContext *cx, JSScript *script, ExecutionMode mode, bool resetU if (!scripts.append(script->parallelIonScript()->recompileInfo())) return false; break; + default: + MOZ_ASSUME_UNREACHABLE("No such execution mode"); } Invalidate(cx, scripts, resetUses); @@ -2542,6 +2544,9 @@ jit::ForbidCompilation(JSContext *cx, JSScript *script, ExecutionMode mode) script->setParallelIonScript(ION_DISABLED_SCRIPT); return; + + default: + MOZ_ASSUME_UNREACHABLE("No such execution mode"); } MOZ_ASSUME_UNREACHABLE("No such execution mode"); diff --git a/js/src/jit/IonAnalysis.cpp b/js/src/jit/IonAnalysis.cpp index 306bb821ec2..11dd9c9bdc1 100644 --- a/js/src/jit/IonAnalysis.cpp +++ b/js/src/jit/IonAnalysis.cpp @@ -8,14 +8,20 @@ #include "jsanalyze.h" +#include "jit/BaselineInspector.h" #include "jit/Ion.h" #include "jit/IonBuilder.h" #include "jit/LIR.h" #include "jit/MIRGraph.h" +#include "jsinferinlines.h" +#include "jsscriptinlines.h" + using namespace js; using namespace js::jit; +using mozilla::DebugOnly; + // A critical edge is an edge which is neither its successor's only predecessor // nor its predecessor's only successor. Critical edges must be split to // prevent copy-insertion and code motion from affecting other edges. @@ -1605,3 +1611,263 @@ LinearSum::print(Sprinter &sp) const else if (constant_ < 0) sp.printf("%d", constant_); } + +static bool +AnalyzePoppedThis(JSContext *cx, types::TypeObject *type, + MDefinition *thisValue, MInstruction *ins, bool definitelyExecuted, + HandleObject baseobj, + Vector *initializerList, + Vector *accessedProperties, + bool *phandled) +{ + // Determine the effect that a use of the |this| value when calling |new| + // on a script has on the properties definitely held by the new object. + + if (ins->isCallSetProperty()) { + MCallSetProperty *setprop = ins->toCallSetProperty(); + + if (setprop->obj() != thisValue) + return true; + + // Don't use GetAtomId here, we need to watch for SETPROP on + // integer properties and bail out. We can't mark the aggregate + // JSID_VOID type property as being in a definite slot. + RootedId id(cx, NameToId(setprop->name())); + if (types::IdToTypeId(id) != id || + id == NameToId(cx->names().classPrototype) || + id == NameToId(cx->names().proto) || + id == NameToId(cx->names().constructor)) + { + return true; + } + + // Ignore assignments to properties that were already written to. + if (baseobj->nativeLookup(cx, id)) { + *phandled = true; + return true; + } + + // Don't add definite properties for properties that were already + // read in the constructor. + for (size_t i = 0; i < accessedProperties->length(); i++) { + if ((*accessedProperties)[i] == id) + return true; + } + + if (baseobj->slotSpan() >= (types::TYPE_FLAG_DEFINITE_MASK >> types::TYPE_FLAG_DEFINITE_SHIFT)) { + // Maximum number of definite properties added. + return true; + } + + // Assignments to new properties must always execute. + if (!definitelyExecuted) + return true; + + if (!types::AddClearDefiniteGetterSetterForPrototypeChain(cx, type, id)) { + // The prototype chain already contains a getter/setter for this + // property, or type information is too imprecise. + return true; + } + + DebugOnly slotSpan = baseobj->slotSpan(); + RootedValue value(cx, UndefinedValue()); + if (!DefineNativeProperty(cx, baseobj, id, value, NULL, NULL, + JSPROP_ENUMERATE, 0, 0, DNP_SKIP_TYPE)) + { + return false; + } + JS_ASSERT(baseobj->slotSpan() != slotSpan); + JS_ASSERT(!baseobj->inDictionaryMode()); + + Vector callerResumePoints(cx); + MBasicBlock *block = ins->block(); + for (MResumePoint *rp = block->callerResumePoint(); + rp; + block = rp->block(), rp = block->callerResumePoint()) + { + JSScript *script = rp->block()->info().script(); + types::AddClearDefiniteFunctionUsesInScript(cx, type, script, block->info().script()); + if (!callerResumePoints.append(rp)) + return false; + } + + for (int i = callerResumePoints.length() - 1; i >= 0; i--) { + MResumePoint *rp = callerResumePoints[i]; + JSScript *script = rp->block()->info().script(); + types::TypeNewScript::Initializer entry(types::TypeNewScript::Initializer::SETPROP_FRAME, + rp->pc() - script->code); + if (!initializerList->append(entry)) + return false; + } + + JSScript *script = ins->block()->info().script(); + types::TypeNewScript::Initializer entry(types::TypeNewScript::Initializer::SETPROP, + setprop->resumePoint()->pc() - script->code); + if (!initializerList->append(entry)) + return false; + + *phandled = true; + return true; + } + + if (ins->isCallGetProperty()) { + MCallGetProperty *get = ins->toCallGetProperty(); + + /* + * Properties can be read from the 'this' object if the following hold: + * + * - The read is not on a getter along the prototype chain, which + * could cause 'this' to escape. + * + * - The accessed property is either already a definite property or + * is not later added as one. Since the definite properties are + * added to the object at the point of its creation, reading a + * definite property before it is assigned could incorrectly hit. + */ + RootedId id(cx, NameToId(get->name())); + if (types::IdToTypeId(id) != id) + return true; + if (!baseobj->nativeLookup(cx, id) && !accessedProperties->append(id.get())) + return false; + + if (!types::AddClearDefiniteGetterSetterForPrototypeChain(cx, type, id)) { + // The |this| value can escape if any property reads it does go + // through a getter. + return true; + } + + *phandled = true; + return true; + } + + if (ins->isPostWriteBarrier()) { + *phandled = true; + return true; + } + + return true; +} + +static int +CmpInstructions(const void *a, const void *b) +{ + return (*static_cast(a))->id() - + (*static_cast(b))->id(); +} + +bool +jit::AnalyzeNewScriptProperties(JSContext *cx, JSFunction *fun, + types::TypeObject *type, HandleObject baseobj, + Vector *initializerList) +{ + // When invoking 'new' on the specified script, try to find some properties + // which will definitely be added to the created object before it has a + // chance to escape and be accessed elsewhere. + + if (!fun->nonLazyScript()->compileAndGo) + return true; + + if (!fun->nonLazyScript()->ensureHasTypes(cx)) + return false; + + types::TypeScript::SetThis(cx, fun->nonLazyScript(), types::Type::ObjectType(type)); + + Vector accessedProperties(cx); + + LifoAlloc alloc(JSCompartment::ANALYSIS_LIFO_ALLOC_PRIMARY_CHUNK_SIZE); + + TempAllocator temp(&alloc); + IonContext ictx(cx, &temp); + + types::AutoEnterAnalysis enter(cx); + + if (!fun->nonLazyScript()->ensureRanAnalysis(cx)) + return false; + + MIRGraph graph(&temp); + CompileInfo info(fun->nonLazyScript(), fun, + /* osrPc = */ NULL, /* constructing = */ false, + DefinitePropertiesAnalysis); + + AutoTempAllocatorRooter root(cx, &temp); + + BaselineInspector inspector(cx, fun->nonLazyScript()); + IonBuilder builder(cx, &temp, &graph, &inspector, &info, /* baselineFrame = */ NULL); + + if (!builder.build()) { + if (builder.abortReason() == AbortReason_Alloc) + return false; + return true; + } + + if (!SplitCriticalEdges(graph)) + return false; + + if (!RenumberBlocks(graph)) + return false; + + if (!BuildDominatorTree(graph)) + return false; + + if (!EliminatePhis(&builder, graph, AggressiveObservability)) + return false; + + MDefinition *thisValue = graph.begin()->getSlot(info.thisSlot()); + + // Get a list of instructions using the |this| value in the order they + // appear in the graph. + Vector instructions(cx); + + Vector useWorklist(cx); + for (MUseDefIterator uses(thisValue); uses; uses++) { + MDefinition *use = uses.def(); + + // Don't track |this| through assignments to phis. + if (!use->isInstruction()) + return true; + + if (!instructions.append(use->toInstruction())) + return false; + } + + // Sort the instructions to visit in increasing order. + qsort(instructions.begin(), instructions.length(), + sizeof(MInstruction *), CmpInstructions); + + // Find all exit blocks in the graph. + Vector exitBlocks(cx); + for (MBasicBlockIterator block(graph.begin()); block != graph.end(); block++) { + if (!block->numSuccessors() && !exitBlocks.append(*block)) + return false; + } + + for (size_t i = 0; i < instructions.length(); i++) { + MInstruction *ins = instructions[i]; + + // Track whether the use of |this| is in unconditional code, i.e. + // the block dominates all graph exits. + bool definitelyExecuted = true; + for (size_t i = 0; i < exitBlocks.length(); i++) { + for (MBasicBlock *exit = exitBlocks[i]; + exit != ins->block(); + exit = exit->immediateDominator()) + { + if (exit == exit->immediateDominator()) { + definitelyExecuted = false; + break; + } + } + } + + bool handled = false; + if (!AnalyzePoppedThis(cx, type, thisValue, ins, definitelyExecuted, + baseobj, initializerList, &accessedProperties, &handled)) + { + return false; + } + if (!handled) + return true; + } + + return true; +} diff --git a/js/src/jit/IonAnalysis.h b/js/src/jit/IonAnalysis.h index 2edbb128e0e..1a05ffc8c94 100644 --- a/js/src/jit/IonAnalysis.h +++ b/js/src/jit/IonAnalysis.h @@ -124,6 +124,11 @@ class LinearSum int32_t constant_; }; +bool +AnalyzeNewScriptProperties(JSContext *cx, JSFunction *fun, + types::TypeObject *type, HandleObject baseobj, + Vector *initializerList); + } // namespace jit } // namespace js diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 105eaf574ef..2876914f9eb 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -3929,16 +3929,21 @@ IonBuilder::makeInliningDecision(JSFunction *target, CallInfo &callInfo) return false; } - // Caller must be... somewhat hot. + // Caller must be... somewhat hot. Ignore use counts when inlining for + // the definite properties analysis, as the caller has not run yet. uint32_t callerUses = script()->getUseCount(); - if (callerUses < js_IonOptions.usesBeforeInlining()) { + if (callerUses < js_IonOptions.usesBeforeInlining() && + info().executionMode() != DefinitePropertiesAnalysis) + { IonSpew(IonSpew_Inlining, "%s:%d - Vetoed: caller is insufficiently hot.", targetScript->filename(), targetScript->lineno); return false; } // Callee must be hot relative to the caller. - if (targetScript->getUseCount() * js_IonOptions.inlineUseCountRatio < callerUses) { + if (targetScript->getUseCount() * js_IonOptions.inlineUseCountRatio < callerUses && + info().executionMode() != DefinitePropertiesAnalysis) + { IonSpew(IonSpew_Inlining, "%s:%d - Vetoed: callee is not hot.", targetScript->filename(), targetScript->lineno); return false; @@ -4803,7 +4808,7 @@ IonBuilder::jsop_funapplyarguments(uint32_t argc) // When this script isn't inlined, use MApplyArgs, // to copy the arguments from the stack and call the function - if (inliningDepth_ == 0) { + if (inliningDepth_ == 0 && info().executionMode() != DefinitePropertiesAnalysis) { // Vp MPassArg *passVp = current->pop()->toPassArg(); @@ -4841,7 +4846,9 @@ IonBuilder::jsop_funapplyarguments(uint32_t argc) // When inlining we have the arguments the function gets called with // and can optimize even more, by just calling the functions with the args. - JS_ASSERT(inliningDepth_ > 0); + // We also try this path when doing the definite properties analysis, as we + // can inline the apply() target and don't care about the actual arguments + // that were passed in. CallInfo callInfo(cx, false); @@ -4853,8 +4860,10 @@ IonBuilder::jsop_funapplyarguments(uint32_t argc) // Arguments Vector args(cx); - if (!args.append(inlineCallInfo_->argv().begin(), inlineCallInfo_->argv().end())) - return false; + if (inliningDepth_) { + if (!args.append(inlineCallInfo_->argv().begin(), inlineCallInfo_->argv().end())) + return false; + } callInfo.setArgs(&args); // This @@ -5570,6 +5579,7 @@ IonBuilder::jsop_initprop(HandlePropertyName name) // forkjoin.cpp for more information. switch (info().executionMode()) { case SequentialExecution: + case DefinitePropertiesAnalysis: break; case ParallelExecution: needsBarrier = false; @@ -8019,11 +8029,22 @@ IonBuilder::jsop_getprop(HandlePropertyName name) if (!getPropTryArgumentsLength(&emitted) || emitted) return emitted; + bool barrier = PropertyReadNeedsTypeBarrier(cx, current->peek(-1), name, types); + // Try to hardcode known constants. if (!getPropTryConstant(&emitted, id, types) || emitted) return emitted; - bool barrier = PropertyReadNeedsTypeBarrier(cx, current->peek(-1), name, types); + // Except when loading constants above, always use a call if we are doing + // the definite properties analysis and not actually emitting code, to + // simplify later analysis. + if (info().executionMode() == DefinitePropertiesAnalysis) { + MDefinition *obj = current->pop(); + MCallGetProperty *call = MCallGetProperty::New(obj, name); + current->add(call); + current->push(call); + return resumeAfter(call); + } // Try to emit loads from definite slots. if (!getPropTryDefiniteSlot(&emitted, name, barrier, types) || emitted) @@ -8370,6 +8391,15 @@ IonBuilder::jsop_setprop(HandlePropertyName name) RootedId id(cx, NameToId(name)); bool emitted = false; + // Always use a call if we are doing the definite properties analysis and + // not actually emitting code, to simplify later analysis. + if (info().executionMode() == DefinitePropertiesAnalysis) { + MInstruction *ins = MCallSetProperty::New(obj, value, name, script()->strict); + current->add(ins); + current->push(value); + return resumeAfter(ins); + } + // Add post barrier if needed. if (NeedsPostBarrier(info(), value)) current->add(MPostWriteBarrier::New(obj, value)); @@ -8759,6 +8789,15 @@ IonBuilder::jsop_this() return true; } + // If we are doing a definite properties analysis, we don't yet know the + // |this| type as its type object is being created right now. Instead of + // bailing out just push the |this| slot, as this code won't actually + // execute and it does not matter whether |this| is primitive. + if (info().executionMode() == DefinitePropertiesAnalysis) { + current->pushSlot(info().thisSlot()); + return true; + } + return abort("JSOP_THIS hard case not yet handled"); } diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 6df031fdbb0..06fe5e5ba94 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -1102,7 +1102,7 @@ IonBuilder::inlineForceSequentialOrInParallelSection(CallInfo &callInfo) // access the "in warmup" flag of the runtime. return InliningStatus_NotInlined; - case ParallelExecution: + case ParallelExecution: { // During Parallel Exec, we always force sequential, so // replace with true. This permits UCE to eliminate the // entire path as dead, which is important. @@ -1111,6 +1111,9 @@ IonBuilder::inlineForceSequentialOrInParallelSection(CallInfo &callInfo) current->add(ins); current->push(ins); return InliningStatus_Inlined; + } + + default:; } MOZ_ASSUME_UNREACHABLE("Invalid execution mode"); @@ -1285,6 +1288,7 @@ IonBuilder::inlineNewDenseArray(CallInfo &callInfo) return inlineNewDenseArrayForSequentialExecution(callInfo); case ParallelExecution: return inlineNewDenseArrayForParallelExecution(callInfo); + default:; } MOZ_ASSUME_UNREACHABLE("unknown ExecutionMode"); diff --git a/js/src/jit/PerfSpewer.cpp b/js/src/jit/PerfSpewer.cpp index 62b84df48be..7794c1871e9 100644 --- a/js/src/jit/PerfSpewer.cpp +++ b/js/src/jit/PerfSpewer.cpp @@ -147,7 +147,7 @@ PerfSpewer::writeProfile(JSScript *script, if (size > 0) { fprintf(fp_, "%lx %lx %s:%d: Func%02d\n", - reinterpret_cast(code->raw()), + reinterpret_cast(code->raw()), size, script->filename(), script->lineno, diff --git a/js/src/jsanalyze.cpp b/js/src/jsanalyze.cpp index 38c546c7551..9b9527fed63 100644 --- a/js/src/jsanalyze.cpp +++ b/js/src/jsanalyze.cpp @@ -1787,88 +1787,6 @@ ScriptAnalysis::needsArgsObj(JSContext *cx) return needsArgsObj(cx, seen, SSAValue::PushedValue(pcOff, 0)); } -CrossSSAValue -CrossScriptSSA::foldValue(const CrossSSAValue &cv) -{ - const Frame &frame = getFrame(cv.frame); - const SSAValue &v = cv.v; - - JSScript *parentScript = NULL; - ScriptAnalysis *parentAnalysis = NULL; - if (frame.parent != INVALID_FRAME) { - parentScript = getFrame(frame.parent).script; - parentAnalysis = parentScript->analysis(); - } - - if (v.kind() == SSAValue::VAR && v.varInitial() && parentScript) { - uint32_t slot = v.varSlot(); - if (slot >= ArgSlot(0) && slot < LocalSlot(frame.script, 0)) { - uint32_t argc = GET_ARGC(frame.parentpc); - SSAValue argv = parentAnalysis->poppedValue(frame.parentpc, argc - 1 - (slot - ArgSlot(0))); - return foldValue(CrossSSAValue(frame.parent, argv)); - } - } - - if (v.kind() == SSAValue::PUSHED) { - jsbytecode *pc = frame.script->code + v.pushedOffset(); - - switch (JSOp(*pc)) { - case JSOP_THIS: - if (parentScript) { - uint32_t argc = GET_ARGC(frame.parentpc); - SSAValue thisv = parentAnalysis->poppedValue(frame.parentpc, argc); - return foldValue(CrossSSAValue(frame.parent, thisv)); - } - break; - - case JSOP_CALL: { - /* - * If there is a single inline callee with a single return site, - * propagate back to that. - */ - JSScript *callee = NULL; - uint32_t calleeFrame = INVALID_FRAME; - for (unsigned i = 0; i < numFrames(); i++) { - if (iterFrame(i).parent == cv.frame && iterFrame(i).parentpc == pc) { - if (callee) - return cv; /* Multiple callees */ - callee = iterFrame(i).script; - calleeFrame = iterFrame(i).index; - } - } - if (callee && callee->analysis()->numReturnSites() == 1) { - ScriptAnalysis *analysis = callee->analysis(); - uint32_t offset = 0; - while (offset < callee->length) { - jsbytecode *pc = callee->code + offset; - if (analysis->maybeCode(pc) && JSOp(*pc) == JSOP_RETURN) - return foldValue(CrossSSAValue(calleeFrame, analysis->poppedValue(pc, 0))); - offset += GetBytecodeLength(pc); - } - } - break; - } - - case JSOP_TOID: { - /* - * TOID acts as identity for integers, so to get better precision - * we should propagate its popped values forward if it acted as - * identity. - */ - ScriptAnalysis *analysis = frame.script->analysis(); - SSAValue toidv = analysis->poppedValue(pc, 0); - if (analysis->getValueTypes(toidv)->getKnownTypeTag() == JSVAL_TYPE_INT32) - return foldValue(CrossSSAValue(cv.frame, toidv)); - break; - } - - default:; - } - } - - return cv; -} - #ifdef DEBUG void diff --git a/js/src/jsanalyze.h b/js/src/jsanalyze.h index 5e127e5fa26..c1c3cfb867c 100644 --- a/js/src/jsanalyze.h +++ b/js/src/jsanalyze.h @@ -78,15 +78,6 @@ class Bytecode /* Whether this is a catch/finally entry point. */ bool exceptionEntry : 1; - /* - * Side effects of this bytecode were not determined by type inference. - * Either a property set with unknown lvalue, or call with unknown callee. - */ - bool monitoredTypes : 1; - - /* Call whose result should be monitored. */ - bool monitoredTypesReturn : 1; - /* * Dynamically observed state about the execution of this opcode. These are * hints about the script for use during compilation. @@ -129,14 +120,6 @@ class Bytecode */ Vector *pendingValues; }; - - /* --------- Type inference --------- */ - - /* Types for all values pushed by this bytecode. */ - types::StackTypeSet *pushedTypes; - - /* Any type barriers in place at this bytecode. */ - types::TypeBarrier *typeBarriers; }; /* @@ -471,7 +454,6 @@ class SSAValue uint32_t phiSlot() const; uint32_t phiLength() const; const SSAValue &phiValue(uint32_t i) const; - types::TypeSet *phiTypes() const; /* Offset at which this phi node was created. */ uint32_t phiOffset() const { @@ -571,7 +553,6 @@ class SSAValue */ struct SSAPhiNode { - types::StackTypeSet types; uint32_t slot; uint32_t length; SSAValue *options; @@ -599,13 +580,6 @@ SSAValue::phiValue(uint32_t i) const return u.phi.node->options[i]; } -inline types::TypeSet * -SSAValue::phiTypes() const -{ - JS_ASSERT(kind() == PHI); - return &u.phi.node->types; -} - class SSAUseChain { public: @@ -654,7 +628,6 @@ class ScriptAnalysis bool ranBytecode_; bool ranSSA_; bool ranLifetimes_; - bool ranInference_; #ifdef DEBUG /* Whether the compartment was in debug mode when we performed the analysis. */ @@ -689,15 +662,10 @@ class ScriptAnalysis bool ranBytecode() { return ranBytecode_; } bool ranSSA() { return ranSSA_; } bool ranLifetimes() { return ranLifetimes_; } - bool ranInference() { return ranInference_; } void analyzeBytecode(JSContext *cx); void analyzeSSA(JSContext *cx); void analyzeLifetimes(JSContext *cx); - void analyzeTypes(JSContext *cx); - - /* Analyze the effect of invoking 'new' on script. */ - void analyzeTypesNew(JSContext *cx); bool OOM() const { return outOfMemory; } bool failed() const { return hadFailure; } @@ -760,45 +728,6 @@ class ScriptAnalysis } const SlotValue *newValues(const jsbytecode *pc) { return newValues(pc - script_->code); } - inline types::StackTypeSet *pushedTypes(uint32_t offset, uint32_t which = 0); - inline types::StackTypeSet *pushedTypes(const jsbytecode *pc, uint32_t which); - - bool hasPushedTypes(const jsbytecode *pc) { return getCode(pc).pushedTypes != NULL; } - - types::TypeBarrier *typeBarriers(JSContext *cx, uint32_t offset) { - if (getCode(offset).typeBarriers) - pruneTypeBarriers(cx, offset); - return getCode(offset).typeBarriers; - } - types::TypeBarrier *typeBarriers(JSContext *cx, const jsbytecode *pc) { - return typeBarriers(cx, pc - script_->code); - } - void addTypeBarrier(JSContext *cx, const jsbytecode *pc, - types::TypeSet *target, types::Type type); - void addSingletonTypeBarrier(JSContext *cx, const jsbytecode *pc, - types::TypeSet *target, - HandleObject singleton, HandleId singletonId); - - /* Remove obsolete type barriers at the given offset. */ - void pruneTypeBarriers(JSContext *cx, uint32_t offset); - - /* - * Remove still-active type barriers at the given offset. If 'all' is set, - * then all barriers are removed, otherwise only those deemed excessive - * are removed. - */ - void breakTypeBarriers(JSContext *cx, uint32_t offset, bool all); - - /* Break all type barriers used in computing v. */ - void breakTypeBarriersSSA(JSContext *cx, const SSAValue &v); - - inline void addPushedType(JSContext *cx, uint32_t offset, uint32_t which, types::Type type); - - inline types::StackTypeSet *getValueTypes(const SSAValue &v); - - inline types::StackTypeSet *poppedTypes(uint32_t offset, uint32_t which); - inline types::StackTypeSet *poppedTypes(const jsbytecode *pc, uint32_t which); - bool trackUseChain(const SSAValue &v) { JS_ASSERT_IF(v.kind() == SSAValue::VAR, trackSlot(v.varSlot())); return v.kind() != SSAValue::EMPTY && @@ -912,9 +841,6 @@ class ScriptAnalysis {} }; - /* Type inference helpers */ - bool analyzeTypesBytecode(JSContext *cx, unsigned offset, TypeInferenceState &state); - typedef Vector SeenVector; bool needsArgsObj(JSContext *cx, SeenVector &seen, const SSAValue &v); bool needsArgsObj(JSContext *cx, SeenVector &seen, SSAUseChain *use); @@ -928,84 +854,6 @@ class ScriptAnalysis #endif }; -/* SSA value as used by CrossScriptSSA, identifies the frame it came from. */ -struct CrossSSAValue -{ - unsigned frame; - SSAValue v; - CrossSSAValue(unsigned frame, const SSAValue &v) : frame(frame), v(v) {} -}; - -/* - * Analysis for managing SSA values from multiple call stack frames. These are - * created by the backend compiler when inlining functions, and allow for - * values to be tracked as they flow into or out of the inlined frames. - */ -class CrossScriptSSA -{ - public: - - static const uint32_t OUTER_FRAME = UINT32_MAX; - static const unsigned INVALID_FRAME = uint32_t(-2); - - struct Frame { - uint32_t index; - JSScript *script; - uint32_t depth; /* Distance from outer frame to this frame, in sizeof(Value) */ - uint32_t parent; - jsbytecode *parentpc; - - Frame(uint32_t index, JSScript *script, uint32_t depth, uint32_t parent, - jsbytecode *parentpc) - : index(index), script(script), depth(depth), parent(parent), parentpc(parentpc) - {} - }; - - const Frame &getFrame(uint32_t index) { - if (index == OUTER_FRAME) - return outerFrame; - return inlineFrames[index]; - } - - unsigned numFrames() { return 1 + inlineFrames.length(); } - const Frame &iterFrame(unsigned i) { - if (i == 0) - return outerFrame; - return inlineFrames[i - 1]; - } - - JSScript *outerScript() { return outerFrame.script; } - - /* Total length of scripts preceding a frame. */ - size_t frameLength(uint32_t index) { - if (index == OUTER_FRAME) - return 0; - size_t res = outerFrame.script->length; - for (unsigned i = 0; i < index; i++) - res += inlineFrames[i].script->length; - return res; - } - - inline types::StackTypeSet *getValueTypes(const CrossSSAValue &cv); - - bool addInlineFrame(JSScript *script, uint32_t depth, uint32_t parent, - jsbytecode *parentpc) - { - uint32_t index = inlineFrames.length(); - return inlineFrames.append(Frame(index, script, depth, parent, parentpc)); - } - - CrossScriptSSA(JSContext *cx, JSScript *outer) - : outerFrame(OUTER_FRAME, outer, 0, INVALID_FRAME, NULL), inlineFrames(cx) - {} - - CrossSSAValue foldValue(const CrossSSAValue &cv); - - private: - Frame outerFrame; - Vector inlineFrames; -}; - #ifdef DEBUG void PrintBytecode(JSContext *cx, HandleScript script, jsbytecode *pc); #endif diff --git a/js/src/jsanalyzeinlines.h b/js/src/jsanalyzeinlines.h index 9a5c3b10b9a..a9e329ef197 100644 --- a/js/src/jsanalyzeinlines.h +++ b/js/src/jsanalyzeinlines.h @@ -29,64 +29,6 @@ ScriptAnalysis::poppedValue(const jsbytecode *pc, uint32_t which) return poppedValue(pc - script_->code, which); } -inline types::StackTypeSet * -ScriptAnalysis::pushedTypes(uint32_t offset, uint32_t which) -{ - JS_ASSERT(offset < script_->length); - JS_ASSERT(which < GetDefCount(script_, offset) + - (ExtendedDef(script_->code + offset) ? 1 : 0)); - types::StackTypeSet *array = getCode(offset).pushedTypes; - JS_ASSERT(array); - return array + which; -} - -inline types::StackTypeSet * -ScriptAnalysis::pushedTypes(const jsbytecode *pc, uint32_t which) -{ - return pushedTypes(pc - script_->code, which); -} - -inline types::StackTypeSet * -ScriptAnalysis::getValueTypes(const SSAValue &v) -{ - switch (v.kind()) { - case SSAValue::PUSHED: - return pushedTypes(v.pushedOffset(), v.pushedIndex()); - case SSAValue::VAR: - JS_ASSERT(!slotEscapes(v.varSlot())); - if (v.varInitial()) { - if (v.varSlot() < LocalSlot(script_, 0)) - return types::TypeScript::SlotTypes(script_, v.varSlot()); - return undefinedTypeSet; - } else { - /* - * Results of intermediate assignments have the same type as - * the first type pushed by the assignment op. Note that this - * may not be the exact same value as was pushed, due to - * post-inc/dec ops. - */ - return pushedTypes(v.varOffset(), 0); - } - case SSAValue::PHI: - return &v.phiNode()->types; - default: - /* Cannot compute types for empty SSA values. */ - MOZ_ASSUME_UNREACHABLE("Bad SSA value"); - } -} - -inline types::StackTypeSet * -ScriptAnalysis::poppedTypes(uint32_t offset, uint32_t which) -{ - return getValueTypes(poppedValue(offset, which)); -} - -inline types::StackTypeSet * -ScriptAnalysis::poppedTypes(const jsbytecode *pc, uint32_t which) -{ - return getValueTypes(poppedValue(pc, which)); -} - inline SSAUseChain *& ScriptAnalysis::useChain(const SSAValue &v) { @@ -107,12 +49,6 @@ ScriptAnalysis::getCallPC(jsbytecode *pc) return script_->code + uses->offset; } -inline types::StackTypeSet * -CrossScriptSSA::getValueTypes(const CrossSSAValue &cv) -{ - return getFrame(cv.frame).script->analysis()->getValueTypes(cv.v); -} - } /* namespace analyze */ } /* namespace js */ diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 7e805fcca88..4dce3f78cfb 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -9,11 +9,9 @@ #ifndef jsapi_h #define jsapi_h -#include "mozilla/Compiler.h" #include "mozilla/FloatingPoint.h" #include "mozilla/MemoryReporting.h" #include "mozilla/RangedPtr.h" -#include "mozilla/TypeTraits.h" #include #include @@ -24,7 +22,6 @@ #include "jspubtd.h" #include "js/CallArgs.h" -#include "js/CallNonGenericMethod.h" #include "js/Class.h" #include "js/HashTable.h" #include "js/Id.h" @@ -1358,7 +1355,7 @@ JS_EndRequest(JSContext *cx); extern JS_PUBLIC_API(bool) JS_IsInRequest(JSRuntime *rt); -namespace JS { +namespace js { inline bool IsPoisonedId(jsid iden) @@ -1370,15 +1367,11 @@ IsPoisonedId(jsid iden) return false; } -} /* namespace JS */ - -namespace js { - template <> struct GCMethods { static jsid initial() { return JSID_VOID; } static ThingRootKind kind() { return THING_ROOT_ID; } - static bool poisoned(jsid id) { return JS::IsPoisonedId(id); } + static bool poisoned(jsid id) { return IsPoisonedId(id); } static bool needsPostBarrier(jsid id) { return false; } #ifdef JSGC_GENERATIONAL static void postBarrier(jsid *idp) {} @@ -4586,81 +4579,4 @@ extern JS_PUBLIC_DATA(const Handle) JSID_EMPTYHANDLE; } /* namespace JS */ -namespace js { - -/* - * Import some JS:: names into the js namespace so we can make unqualified - * references to them. - */ - -using JS::Value; -using JS::IsPoisonedValue; -using JS::NullValue; -using JS::UndefinedValue; -using JS::Int32Value; -using JS::DoubleValue; -using JS::StringValue; -using JS::BooleanValue; -using JS::ObjectValue; -using JS::MagicValue; -using JS::NumberValue; -using JS::ObjectOrNullValue; -using JS::PrivateValue; -using JS::PrivateUint32Value; - -using JS::IsPoisonedPtr; -using JS::IsPoisonedId; - -using JS::StableCharPtr; -using JS::TwoByteChars; -using JS::Latin1CharsZ; - -using JS::AutoIdVector; -using JS::AutoValueVector; -using JS::AutoObjectVector; -using JS::AutoFunctionVector; -using JS::AutoScriptVector; -using JS::AutoIdArray; - -using JS::AutoGCRooter; -using JS::AutoArrayRooter; -using JS::AutoVectorRooter; -using JS::AutoHashMapRooter; -using JS::AutoHashSetRooter; - -using JS::CallArgs; -using JS::IsAcceptableThis; -using JS::NativeImpl; -using JS::CallReceiver; -using JS::CompileOptions; -using JS::CallNonGenericMethod; - -using JS::Rooted; -using JS::RootedObject; -using JS::RootedFunction; -using JS::RootedScript; -using JS::RootedString; -using JS::RootedId; -using JS::RootedValue; - -using JS::Handle; -using JS::HandleObject; -using JS::HandleFunction; -using JS::HandleScript; -using JS::HandleString; -using JS::HandleId; -using JS::HandleValue; - -using JS::MutableHandle; -using JS::MutableHandleObject; -using JS::MutableHandleFunction; -using JS::MutableHandleScript; -using JS::MutableHandleString; -using JS::MutableHandleId; -using JS::MutableHandleValue; - -using JS::Zone; - -} /* namespace js */ - #endif /* jsapi_h */ diff --git a/js/src/jsbool.h b/js/src/jsbool.h index 3854d1871a1..6f26f2de44d 100644 --- a/js/src/jsbool.h +++ b/js/src/jsbool.h @@ -6,11 +6,13 @@ #ifndef jsbool_h #define jsbool_h + /* * JS boolean interface. */ #include "jsapi.h" +#include "NamespaceImports.h" extern JSObject * js_InitBooleanClass(JSContext *cx, js::HandleObject obj); diff --git a/js/src/jsexn.h b/js/src/jsexn.h index d4f3695ec11..e32fef0e1be 100644 --- a/js/src/jsexn.h +++ b/js/src/jsexn.h @@ -12,6 +12,7 @@ #define jsexn_h #include "jsapi.h" +#include "NamespaceImports.h" /* * Initialize the exception constructor/prototype hierarchy. diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index 4bc823750a2..c7a8dbf3298 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -29,6 +29,7 @@ #ifdef JS_ION #include "jit/BaselineJIT.h" #include "jit/Ion.h" +#include "jit/IonAnalysis.h" #include "jit/IonCompartment.h" #endif #include "js/MemoryMetrics.h" @@ -594,1082 +595,18 @@ class TypeConstraintSubset : public TypeConstraint } /* anonymous namespace */ void -StackTypeSet::addSubset(JSContext *cx, TypeSet *target) +StackTypeSet::addSubset(JSContext *cx, StackTypeSet *target) { add(cx, cx->analysisLifoAlloc().new_(target)); } void -HeapTypeSet::addSubset(JSContext *cx, TypeSet *target) +HeapTypeSet::addSubset(JSContext *cx, HeapTypeSet *target) { JS_ASSERT(!target->purged()); add(cx, cx->typeLifoAlloc().new_(target)); } -namespace { - -enum PropertyAccessKind { - PROPERTY_WRITE, - PROPERTY_READ, - PROPERTY_READ_EXISTING -}; - -/* Constraints for reads/writes on object properties. */ -template -class TypeConstraintProp : public TypeConstraint -{ - JSScript *script_; - - public: - jsbytecode *pc; - - /* - * If assign is true, the target is used to update a property of the object. - * If assign is false, the target is assigned the value of the property. - */ - StackTypeSet *target; - - /* Property being accessed. This is unrooted. */ - jsid id; - - TypeConstraintProp(JSScript *script, jsbytecode *pc, StackTypeSet *target, jsid id) - : script_(script), pc(pc), target(target), id(id) - { - JS_ASSERT(script && pc && target); - } - - const char *kind() { return "prop"; } - - void newType(JSContext *cx, TypeSet *source, Type type); -}; - -typedef TypeConstraintProp TypeConstraintSetProperty; -typedef TypeConstraintProp TypeConstraintGetProperty; -typedef TypeConstraintProp TypeConstraintGetPropertyExisting; - -} /* anonymous namespace */ - -void -StackTypeSet::addGetProperty(JSContext *cx, JSScript *script, jsbytecode *pc, - StackTypeSet *target, jsid id) -{ - /* - * GetProperty constraints are normally used with property read input type - * sets, except for array_pop/array_shift special casing. - */ - JS_ASSERT(IsCallPC(pc)); - - add(cx, cx->analysisLifoAlloc().new_(script, pc, target, id)); -} - -void -StackTypeSet::addSetProperty(JSContext *cx, JSScript *script, jsbytecode *pc, - StackTypeSet *target, jsid id) -{ - add(cx, cx->analysisLifoAlloc().new_(script, pc, target, id)); -} - -void -HeapTypeSet::addGetProperty(JSContext *cx, JSScript *script, jsbytecode *pc, - StackTypeSet *target, jsid id) -{ - JS_ASSERT(!target->purged()); - add(cx, cx->typeLifoAlloc().new_(script, pc, target, id)); -} - -namespace { - -/* - * Constraints for updating the 'this' types of callees on CALLPROP/CALLELEM. - * These are derived from the types on the properties themselves, rather than - * those pushed in the 'this' slot at the call site, which allows us to retain - * correlations between the type of the 'this' object and the associated - * callee scripts at polymorphic call sites. - */ -template -class TypeConstraintCallProp : public TypeConstraint -{ - JSScript *script_; - - public: - jsbytecode *callpc; - - /* Property being accessed. */ - jsid id; - - TypeConstraintCallProp(JSScript *script, jsbytecode *callpc, jsid id) - : script_(script), callpc(callpc), id(id) - { - JS_ASSERT(script && callpc); - } - - const char *kind() { return "callprop"; } - - void newType(JSContext *cx, TypeSet *source, Type type); -}; - -typedef TypeConstraintCallProp TypeConstraintCallProperty; -typedef TypeConstraintCallProp TypeConstraintCallPropertyExisting; - -} /* anonymous namespace */ - -void -HeapTypeSet::addCallProperty(JSContext *cx, JSScript *script, jsbytecode *pc, jsid id) -{ - /* - * For calls which will go through JSOP_NEW, don't add any constraints to - * modify the 'this' types of callees. The initial 'this' value will be - * outright ignored. - */ - jsbytecode *callpc = script->analysis()->getCallPC(pc); - if (JSOp(*callpc) == JSOP_NEW) - return; - - add(cx, cx->typeLifoAlloc().new_(script, callpc, id)); -} - -namespace { - -/* - * Constraints for generating 'set' property constraints on a SETELEM only if - * the element type may be a number. For SETELEM we only account for integer - * indexes, and if the element cannot be an integer (e.g. it must be a string) - * then we lose precision by treating it like one. - */ -class TypeConstraintSetElement : public TypeConstraint -{ - JSScript *script_; - - public: - jsbytecode *pc; - - StackTypeSet *objectTypes; - StackTypeSet *valueTypes; - - TypeConstraintSetElement(JSScript *script, jsbytecode *pc, - StackTypeSet *objectTypes, StackTypeSet *valueTypes) - : script_(script), pc(pc), - objectTypes(objectTypes), valueTypes(valueTypes) - { - JS_ASSERT(script && pc); - } - - const char *kind() { return "setelement"; } - - void newType(JSContext *cx, TypeSet *source, Type type); -}; - -} /* anonymous namespace */ - -void -StackTypeSet::addSetElement(JSContext *cx, JSScript *script, jsbytecode *pc, - StackTypeSet *objectTypes, StackTypeSet *valueTypes) -{ - add(cx, cx->analysisLifoAlloc().new_(script, pc, objectTypes, - valueTypes)); -} - -namespace { - -/* - * Constraints for watching call edges as they are discovered and invoking native - * function handlers, adding constraints for arguments, receiver objects and the - * return value, and updating script foundOffsets. - */ -class TypeConstraintCall : public TypeConstraint -{ - public: - /* Call site being tracked. */ - TypeCallsite *callsite; - - TypeConstraintCall(TypeCallsite *callsite) - : callsite(callsite) - {} - - const char *kind() { return "call"; } - - void newType(JSContext *cx, TypeSet *source, Type type); -}; - -} /* anonymous namespace */ - -void -StackTypeSet::addCall(JSContext *cx, TypeCallsite *site) -{ - add(cx, cx->analysisLifoAlloc().new_(site)); -} - -namespace { - -/* Constraints for arithmetic operations. */ -class TypeConstraintArith : public TypeConstraint -{ - JSScript *script_; - - public: - jsbytecode *pc; - - /* Type set receiving the result of the arithmetic. */ - TypeSet *target; - - /* For addition operations, the other operand. */ - TypeSet *other; - - TypeConstraintArith(JSScript *script, jsbytecode *pc, TypeSet *target, TypeSet *other) - : script_(script), pc(pc), target(target), other(other) - { - JS_ASSERT(target); - } - - const char *kind() { return "arith"; } - - void newType(JSContext *cx, TypeSet *source, Type type); -}; - -} /* anonymous namespace */ - -void -StackTypeSet::addArith(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target, - TypeSet *other) -{ - add(cx, cx->analysisLifoAlloc().new_(script, pc, target, other)); -} - -namespace { - -/* Subset constraint which transforms primitive values into appropriate objects. */ -class TypeConstraintTransformThis : public TypeConstraint -{ - JSScript *script_; - - public: - TypeSet *target; - - TypeConstraintTransformThis(JSScript *script, TypeSet *target) - : script_(script), target(target) - {} - - const char *kind() { return "transformthis"; } - - void newType(JSContext *cx, TypeSet *source, Type type); -}; - -} /* anonymous namespace */ - -void -StackTypeSet::addTransformThis(JSContext *cx, JSScript *script, TypeSet *target) -{ - add(cx, cx->analysisLifoAlloc().new_(script, target)); -} - -namespace { - -/* - * Constraint which adds a particular type to the 'this' types of all - * discovered scripted functions. - */ -class TypeConstraintPropagateThis : public TypeConstraint -{ - JSScript *script_; - - public: - jsbytecode *callpc; - Type type; - StackTypeSet *types; - - TypeConstraintPropagateThis(JSScript *script, jsbytecode *callpc, Type type, StackTypeSet *types) - : script_(script), callpc(callpc), type(type), types(types) - {} - - const char *kind() { return "propagatethis"; } - - void newType(JSContext *cx, TypeSet *source, Type type); -}; - -} /* anonymous namespace */ - -void -StackTypeSet::addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc, - Type type, StackTypeSet *types) -{ - add(cx, cx->analysisLifoAlloc().new_(script, pc, type, types)); -} - -namespace { - -/* Subset constraint which filters out primitive types. */ -class TypeConstraintFilterPrimitive : public TypeConstraint -{ - public: - TypeSet *target; - - TypeConstraintFilterPrimitive(TypeSet *target) - : target(target) - {} - - const char *kind() { return "filter"; } - - void newType(JSContext *cx, TypeSet *source, Type type) - { - if (type.isPrimitive()) - return; - - target->addType(cx, type); - } -}; - -} /* anonymous namespace */ - -void -HeapTypeSet::addFilterPrimitives(JSContext *cx, TypeSet *target) -{ - add(cx, cx->typeLifoAlloc().new_(target)); -} - -/* If id is a normal slotful 'own' property of an object, get its shape. */ -static inline Shape * -GetSingletonShape(JSContext *cx, JSObject *obj, jsid idArg) -{ - if (!obj->isNative()) - return NULL; - RootedId id(cx, idArg); - Shape *shape = obj->nativeLookup(cx, id); - if (shape && shape->hasDefaultGetter() && shape->hasSlot()) - return shape; - return NULL; -} - -void -ScriptAnalysis::pruneTypeBarriers(JSContext *cx, uint32_t offset) -{ - TypeBarrier **pbarrier = &getCode(offset).typeBarriers; - while (*pbarrier) { - TypeBarrier *barrier = *pbarrier; - if (barrier->target->hasType(barrier->type)) { - /* Barrier is now obsolete, it can be removed. */ - *pbarrier = barrier->next; - continue; - } - if (barrier->singleton) { - JS_ASSERT(barrier->type.isPrimitive(JSVAL_TYPE_UNDEFINED)); - Shape *shape = GetSingletonShape(cx, barrier->singleton, barrier->singletonId); - if (shape && !barrier->singleton->nativeGetSlot(shape->slot()).isUndefined()) { - /* - * When we analyzed the script the singleton had an 'own' - * property which was undefined (probably a 'var' variable - * added to a global object), but now it is defined. The only - * way it can become undefined again is if an explicit assign - * or deletion on the property occurs, which will update the - * type set for the property directly and trigger construction - * of a normal type barrier. - */ - *pbarrier = barrier->next; - continue; - } - } - pbarrier = &barrier->next; - } -} - -/* - * Cheesy limit on the number of objects we will tolerate in an observed type - * set before refusing to add new type barriers for objects. - * :FIXME: this heuristic sucks, and doesn't handle calls. - */ -static const uint32_t BARRIER_OBJECT_LIMIT = 10; - -void ScriptAnalysis::breakTypeBarriers(JSContext *cx, uint32_t offset, bool all) -{ - pruneTypeBarriers(cx, offset); - - bool resetResolving = !cx->compartment()->types.resolving; - if (resetResolving) - cx->compartment()->types.resolving = true; - - TypeBarrier **pbarrier = &getCode(offset).typeBarriers; - while (*pbarrier) { - TypeBarrier *barrier = *pbarrier; - if (barrier->target->hasType(barrier->type) ) { - /* - * Barrier is now obsolete, it can be removed. This is not - * redundant with the pruneTypeBarriers() call above, as breaking - * previous type barriers may have modified the target type set. - */ - *pbarrier = barrier->next; - } else if (all) { - /* Force removal of the barrier. */ - barrier->target->addType(cx, barrier->type); - *pbarrier = barrier->next; - } else if (!barrier->type.isUnknown() && - !barrier->type.isAnyObject() && - barrier->type.isObject() && - barrier->target->getObjectCount() >= BARRIER_OBJECT_LIMIT) { - /* Maximum number of objects in the set exceeded. */ - barrier->target->addType(cx, barrier->type); - *pbarrier = barrier->next; - } else { - pbarrier = &barrier->next; - } - } - - if (resetResolving) { - cx->compartment()->types.resolving = false; - cx->compartment()->types.resolvePending(cx); - } -} - -void ScriptAnalysis::breakTypeBarriersSSA(JSContext *cx, const SSAValue &v) -{ - if (v.kind() != SSAValue::PUSHED) - return; - - uint32_t offset = v.pushedOffset(); - if (JSOp(script_->code[offset]) == JSOP_GETPROP) - breakTypeBarriersSSA(cx, poppedValue(offset, 0)); - - breakTypeBarriers(cx, offset, true); -} - -namespace { - -/* - * Subset constraint for property reads and argument passing which can add type - * barriers on the read instead of passing types along. - */ -class TypeConstraintSubsetBarrier : public TypeConstraint -{ - public: - JSScript *script; - jsbytecode *pc; - TypeSet *target; - - TypeConstraintSubsetBarrier(JSScript *script, jsbytecode *pc, TypeSet *target) - : script(script), pc(pc), target(target) - {} - - const char *kind() { return "subsetBarrier"; } - - void newType(JSContext *cx, TypeSet *source, Type type) - { - if (!target->hasType(type)) { - if (!script->ensureRanAnalysis(cx)) - return; - script->analysis()->addTypeBarrier(cx, pc, target, type); - } - } -}; - -} /* anonymous namespace */ - -void -StackTypeSet::addSubsetBarrier(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target) -{ - add(cx, cx->analysisLifoAlloc().new_(script, pc, target)); -} - -void -HeapTypeSet::addSubsetBarrier(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target) -{ - JS_ASSERT(!target->purged()); - add(cx, cx->typeLifoAlloc().new_(script, pc, target)); -} - -///////////////////////////////////////////////////////////////////// -// TypeConstraint -///////////////////////////////////////////////////////////////////// - -/* Get the object to use for a property access on type. */ -static inline TypeObject * -GetPropertyObject(JSContext *cx, HandleScript script, Type type) -{ - if (type.isTypeObject()) - return type.typeObject(); - - /* Force instantiation of lazy types for singleton objects. */ - if (type.isSingleObject()) - return type.singleObject()->getType(cx); - - /* - * Handle properties attached to primitive types, treating this access as a - * read on the primitive's new object. - */ - TypeObject *object = NULL; - switch (type.primitive()) { - - case JSVAL_TYPE_INT32: - case JSVAL_TYPE_DOUBLE: - object = TypeScript::StandardType(cx, JSProto_Number); - break; - - case JSVAL_TYPE_BOOLEAN: - object = TypeScript::StandardType(cx, JSProto_Boolean); - break; - - case JSVAL_TYPE_STRING: - object = TypeScript::StandardType(cx, JSProto_String); - break; - - default: - /* undefined, null and lazy arguments do not have properties. */ - return NULL; - } - - if (!object) - cx->compartment()->types.setPendingNukeTypes(cx); - return object; -} - -static inline bool -UsePropertyTypeBarrier(jsbytecode *pc) -{ - /* - * At call opcodes, type barriers can only be added for the call bindings, - * which TypeConstraintCall will add barrier constraints for directly. - */ - uint32_t format = js_CodeSpec[*pc].format; - return (format & JOF_TYPESET) && !(format & JOF_INVOKE); -} - -static inline void -MarkPropertyAccessUnknown(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target) -{ - if (UsePropertyTypeBarrier(pc)) - script->analysis()->addTypeBarrier(cx, pc, target, Type::UnknownType()); - else - target->addType(cx, Type::UnknownType()); -} - -/* - * Get a value for reading id from obj or its prototypes according to the - * current VM state, returning the unknown type on failure or an undefined - * property. - */ -static inline Type -GetSingletonPropertyType(JSContext *cx, JSObject *rawObjArg, HandleId id) -{ - RootedObject obj(cx, rawObjArg); // Root this locally because it's assigned to. - - JS_ASSERT(id == IdToTypeId(id)); - - if (JSID_IS_VOID(id)) - return Type::UnknownType(); - - if (obj->is()) { - if (id == id_length(cx)) - return Type::Int32Type(); - obj = obj->getProto(); - } - - while (obj) { - if (!obj->isNative()) - return Type::UnknownType(); - - RootedValue v(cx); - if (HasDataProperty(cx, obj, id, v.address())) { - if (v.isUndefined()) - return Type::UnknownType(); - return GetValueType(v); - } - - obj = obj->getProto(); - } - - return Type::UnknownType(); -} - -/* - * Handle a property access on a specific object. All property accesses go through - * here, whether via x.f, x[f], or global name accesses. - */ -template -static inline void -PropertyAccess(JSContext *cx, JSScript *script, jsbytecode *pc, TypeObject *object, - StackTypeSet *target, jsid idArg) -{ - RootedId id(cx, idArg); - - /* Reads from objects with unknown properties are unknown, writes to such objects are ignored. */ - if (object->unknownProperties()) { - if (access != PROPERTY_WRITE) - MarkPropertyAccessUnknown(cx, script, pc, target); - return; - } - - /* - * Get the possible types of the property. For assignments, we do not - * automatically update the 'own' bit on accessed properties, except for - * indexed elements. This exception allows for JIT fast paths to avoid - * testing the array's type when assigning to dense array elements. - */ - bool markOwn = access == PROPERTY_WRITE && JSID_IS_VOID(id); - HeapTypeSet *types = object->getProperty(cx, id, markOwn); - if (!types) - return; - - /* Capture the effects of a standard property access. */ - if (access == PROPERTY_WRITE) { - target->addSubset(cx, types); - } else { - JS_ASSERT_IF(script->hasAnalysis(), - target == TypeScript::BytecodeTypes(script, pc)); - if (!types->hasPropagatedProperty()) - object->getFromPrototypes(cx, id, types); - if (UsePropertyTypeBarrier(pc)) { - if (access == PROPERTY_READ) { - types->addSubsetBarrier(cx, script, pc, target); - } else { - TypeConstraintSubsetBarrier constraint(script, pc, target); - types->addTypesToConstraint(cx, &constraint); - } - if (object->singleton && !JSID_IS_VOID(id)) { - /* - * Add a singleton type barrier on the object if it has an - * 'own' property which is currently undefined. We'll be able - * to remove the barrier after the property becomes defined, - * even if no undefined value is ever observed at pc. - */ - RootedObject singleton(cx, object->singleton); - RootedShape shape(cx, GetSingletonShape(cx, singleton, id)); - if (shape && singleton->nativeGetSlot(shape->slot()).isUndefined()) - script->analysis()->addSingletonTypeBarrier(cx, pc, target, singleton, id); - } - } else { - JS_ASSERT(access == PROPERTY_READ); - types->addSubset(cx, target); - } - } -} - -/* Whether the JSObject/TypeObject referent of an access on type cannot be determined. */ -static inline bool -UnknownPropertyAccess(HandleScript script, Type type) -{ - return type.isUnknown() - || type.isAnyObject() - || (!type.isObject() && !script->compileAndGo); -} - -template -void -TypeConstraintProp::newType(JSContext *cx, TypeSet *source, Type type) -{ - RootedScript script(cx, script_); - - if (UnknownPropertyAccess(script, type)) { - /* - * Access on an unknown object. Reads produce an unknown result, writes - * need to be monitored. - */ - if (access == PROPERTY_WRITE) - cx->compartment()->types.monitorBytecode(cx, script, pc - script->code); - else - MarkPropertyAccessUnknown(cx, script, pc, target); - return; - } - - if (type.isPrimitive(JSVAL_TYPE_MAGIC)) { - /* Ignore cases which will be accounted for by the followEscapingArguments analysis. */ - if (access == PROPERTY_WRITE || (id != JSID_VOID && id != id_length(cx))) - return; - - if (id == JSID_VOID) - MarkPropertyAccessUnknown(cx, script, pc, target); - return; - } - - TypeObject *object = GetPropertyObject(cx, script, type); - if (object) - PropertyAccess(cx, script, pc, object, target, id); -} - -template -void -TypeConstraintCallProp::newType(JSContext *cx, TypeSet *source, Type type) -{ - RootedScript script(cx, script_); - - /* - * For CALLPROP, we need to update not just the pushed types but also the - * 'this' types of possible callees. If we can't figure out that set of - * callees, monitor the call to make sure discovered callees get their - * 'this' types updated. - */ - - if (UnknownPropertyAccess(script, type)) { - cx->compartment()->types.monitorBytecode(cx, script, callpc - script->code); - return; - } - - TypeObject *object = GetPropertyObject(cx, script, type); - if (object) { - if (object->unknownProperties()) { - cx->compartment()->types.monitorBytecode(cx, script, callpc - script->code); - } else { - TypeSet *types = object->getProperty(cx, id, false); - if (!types) - return; - if (!types->hasPropagatedProperty()) - object->getFromPrototypes(cx, id, types); - if (access == PROPERTY_READ) { - types->add(cx, cx->typeLifoAlloc().new_( - script_, callpc, type, (StackTypeSet *) NULL)); - } else { - TypeConstraintPropagateThis constraint(script, callpc, type, NULL); - types->addTypesToConstraint(cx, &constraint); - } - } - } -} - -void -TypeConstraintSetElement::newType(JSContext *cx, TypeSet *source, Type type) -{ - RootedScript script(cx, script_); - if (type.isUnknown() || - type.isPrimitive(JSVAL_TYPE_INT32) || - type.isPrimitive(JSVAL_TYPE_DOUBLE)) { - objectTypes->addSetProperty(cx, script, pc, valueTypes, JSID_VOID); - } -} - -static inline JSFunction * -CloneCallee(JSContext *cx, HandleFunction fun, HandleScript script, jsbytecode *pc) -{ - /* - * Clone called functions at appropriate callsites to match interpreter - * behavior. - */ - JSFunction *callee = CloneFunctionAtCallsite(cx, fun, script, pc); - if (!callee) - return NULL; - - InferSpew(ISpewOps, "callsiteCloneType: #%u:%05u: %s", - script->id(), pc - script->code, TypeString(Type::ObjectType(callee))); - - return callee; -} - -void -TypeConstraintCall::newType(JSContext *cx, TypeSet *source, Type type) -{ - RootedScript script(cx, callsite->script); - jsbytecode *pc = callsite->pc; - - JS_ASSERT_IF(script->hasAnalysis(), - callsite->returnTypes == TypeScript::BytecodeTypes(script, pc)); - - if (type.isUnknown() || type.isAnyObject()) { - /* Monitor calls on unknown functions. */ - cx->compartment()->types.monitorBytecode(cx, script, pc - script->code); - return; - } - - RootedFunction callee(cx); - - if (type.isSingleObject()) { - RootedObject obj(cx, type.singleObject()); - - if (!obj->is()) { - /* Calls on non-functions are dynamically monitored. */ - return; - } - - if (obj->as().isNative()) { - /* - * The return value and all side effects within native calls should - * be dynamically monitored, except when the compiler is generating - * specialized inline code or stub calls for a specific natives and - * knows about the behavior of that native. - */ - cx->compartment()->types.monitorBytecode(cx, script, pc - script->code, true); - - /* - * Add type constraints capturing the possible behavior of - * specialized natives which operate on properties. :XXX: use - * better factoring for both this and the compiler code itself - * which specializes particular natives. - */ - - Native native = obj->as().native(); - - if (native == js::array_push) { - for (size_t i = 0; i < callsite->argumentCount; i++) { - callsite->thisTypes->addSetProperty(cx, script, pc, - callsite->argumentTypes[i], JSID_VOID); - } - } - - if (native == intrinsic_UnsafePutElements) { - // UnsafePutElements(arr0, idx0, elem0, ..., arrN, idxN, elemN) - // is (basically) equivalent to arri[idxi] = elemi for i = 0...N - JS_ASSERT((callsite->argumentCount % 3) == 0); - for (size_t i = 0; i < callsite->argumentCount; i += 3) { - StackTypeSet *arr = callsite->argumentTypes[i]; - StackTypeSet *elem = callsite->argumentTypes[i+2]; - arr->addSetProperty(cx, script, pc, elem, JSID_VOID); - } - } - - if (native == js::array_pop || native == js::array_shift) - callsite->thisTypes->addGetProperty(cx, script, pc, callsite->returnTypes, JSID_VOID); - - if (native == js_Array) { - TypeObject *res = TypeScript::InitObject(cx, script, pc, JSProto_Array); - if (!res) - return; - - callsite->returnTypes->addType(cx, Type::ObjectType(res)); - - if (callsite->argumentCount >= 2) { - for (unsigned i = 0; i < callsite->argumentCount; i++) { - PropertyAccess(cx, script, pc, res, - callsite->argumentTypes[i], JSID_VOID); - } - } - } - - if (native == js_String && callsite->isNew) { - // Note that "new String()" returns a String object and "String()" - // returns a primitive string. - TypeObject *res = TypeScript::StandardType(cx, JSProto_String); - if (!res) - return; - - callsite->returnTypes->addType(cx, Type::ObjectType(res)); - } - - return; - } - - callee = &obj->as(); - } else if (type.isTypeObject()) { - callee = type.typeObject()->interpretedFunction; - if (!callee) - return; - } else { - /* Calls on non-objects are dynamically monitored. */ - return; - } - - if (callee->isInterpretedLazy() && !callee->getOrCreateScript(cx)) - return; - - /* - * As callsite cloning is a hint, we must propagate to both the original - * and the clone. - */ - if (callee->nonLazyScript()->shouldCloneAtCallsite) { - callee = CloneCallee(cx, callee, script, pc); - if (!callee) - return; - } - - RootedScript calleeScript(cx, callee->nonLazyScript()); - if (!calleeScript->ensureHasTypes(cx)) - return; - - unsigned nargs = callee->nargs; - - /* Add bindings for the arguments of the call. */ - for (unsigned i = 0; i < callsite->argumentCount && i < nargs; i++) { - StackTypeSet *argTypes = callsite->argumentTypes[i]; - StackTypeSet *types = TypeScript::ArgTypes(calleeScript, i); - argTypes->addSubsetBarrier(cx, script, callsite->pc, types); - } - - /* Add void type for any formals in the callee not supplied at the call site. */ - for (unsigned i = callsite->argumentCount; i < nargs; i++) { - TypeSet *types = TypeScript::ArgTypes(calleeScript, i); - types->addType(cx, Type::UndefinedType()); - } - - StackTypeSet *thisTypes = TypeScript::ThisTypes(calleeScript); - HeapTypeSet *returnTypes = TypeScript::ReturnTypes(calleeScript); - - if (callsite->isNew) { - /* - * If the script does not return a value then the pushed value is the - * new object (typical case). Note that we don't model construction of - * the new value, which is done dynamically; we don't keep track of the - * possible 'new' types for a given prototype type object. - */ - thisTypes->addSubset(cx, returnTypes); - returnTypes->addFilterPrimitives(cx, callsite->returnTypes); - } else { - /* - * Add a binding for the return value of the call. We don't add a - * binding for the receiver object, as this is done with PropagateThis - * constraints added by the original JSOP_CALL* op. The type sets we - * manipulate here have lost any correlations between particular types - * in the 'this' and 'callee' sets, which we want to maintain for - * polymorphic JSOP_CALLPROP invocations. - */ - returnTypes->addSubset(cx, callsite->returnTypes); - } -} - -void -TypeConstraintPropagateThis::newType(JSContext *cx, TypeSet *source, Type type) -{ - RootedScript script(cx, script_); - if (type.isUnknown() || type.isAnyObject()) { - /* - * The callee is unknown, make sure the call is monitored so we pick up - * possible this/callee correlations. This only comes into play for - * CALLPROP, for other calls we are past the type barrier and a - * TypeConstraintCall will also monitor the call. - */ - cx->compartment()->types.monitorBytecode(cx, script, callpc - script->code); - return; - } - - /* Ignore calls to natives, these will be handled by TypeConstraintCall. */ - RootedFunction callee(cx); - - if (type.isSingleObject()) { - RootedObject object(cx, type.singleObject()); - if (!object->is() || !object->as().isInterpreted()) - return; - callee = &object->as(); - } else if (type.isTypeObject()) { - TypeObject *object = type.typeObject(); - if (!object->interpretedFunction) - return; - callee = object->interpretedFunction; - } else { - /* Ignore calls to primitives, these will go through a stub. */ - return; - } - - if (callee->isInterpretedLazy() && !callee->getOrCreateScript(cx)) - return; - - /* - * As callsite cloning is a hint, we must propagate to both the original - * and the clone. - */ - if (callee->nonLazyScript()->shouldCloneAtCallsite) { - callee = CloneCallee(cx, callee, script, callpc); - if (!callee) - return; - } - - if (!callee->nonLazyScript()->ensureHasTypes(cx)) - return; - - TypeSet *thisTypes = TypeScript::ThisTypes(callee->nonLazyScript()); - if (this->types) - this->types->addSubset(cx, thisTypes); - else - thisTypes->addType(cx, this->type); -} - -void -TypeConstraintArith::newType(JSContext *cx, TypeSet *source, Type type) -{ - /* - * We only model a subset of the arithmetic behavior that is actually - * possible. The following need to be watched for at runtime: - * - * 1. Operations producing a double where no operand was a double. - * 2. Operations producing a string where no operand was a string (addition only). - * 3. Operations producing a value other than int/double/string. - */ - RootedScript script(cx, script_); - if (other) { - /* - * Addition operation, consider these cases: - * {int,bool} x {int,bool} -> int - * double x {int,bool,double} -> double - * string x any -> string - */ - if (type.isUnknown() || other->unknown()) { - target->addType(cx, Type::UnknownType()); - } else if (type.isPrimitive(JSVAL_TYPE_DOUBLE)) { - if (other->hasAnyFlag(TYPE_FLAG_UNDEFINED | TYPE_FLAG_NULL | - TYPE_FLAG_INT32 | TYPE_FLAG_DOUBLE | TYPE_FLAG_BOOLEAN | - TYPE_FLAG_ANYOBJECT)) { - target->addType(cx, Type::DoubleType()); - } else if (other->getObjectCount() != 0) { - TypeDynamicResult(cx, script, pc, Type::DoubleType()); - } - } else if (type.isPrimitive(JSVAL_TYPE_STRING)) { - target->addType(cx, Type::StringType()); - } else if (other->hasAnyFlag(TYPE_FLAG_DOUBLE)) { - target->addType(cx, Type::DoubleType()); - } else if (other->hasAnyFlag(TYPE_FLAG_UNDEFINED | TYPE_FLAG_NULL | - TYPE_FLAG_INT32 | TYPE_FLAG_BOOLEAN | - TYPE_FLAG_ANYOBJECT)) { - target->addType(cx, Type::Int32Type()); - } else if (other->getObjectCount() != 0) { - TypeDynamicResult(cx, script, pc, Type::Int32Type()); - } - } else { - if (type.isUnknown()) - target->addType(cx, Type::UnknownType()); - else if (type.isPrimitive(JSVAL_TYPE_DOUBLE)) - target->addType(cx, Type::DoubleType()); - else if (!type.isAnyObject() && type.isObject()) - TypeDynamicResult(cx, script, pc, Type::Int32Type()); - else - target->addType(cx, Type::Int32Type()); - } -} - -void -TypeConstraintTransformThis::newType(JSContext *cx, TypeSet *source, Type type) -{ - if (type.isUnknown() || type.isAnyObject() || type.isObject() || script_->strict) { - target->addType(cx, type); - return; - } - - RootedScript script(cx, script_); - - /* - * Builtin scripts do not adhere to normal assumptions about transforming - * 'this'. - */ - if (script->function() && script->function()->isSelfHostedBuiltin()) { - target->addType(cx, type); - return; - } - - /* - * Note: if |this| is null or undefined, the pushed value is the outer window. We - * can't use script->getGlobalType() here because it refers to the inner window. - */ - if (!script->compileAndGo || - type.isPrimitive(JSVAL_TYPE_NULL) || - type.isPrimitive(JSVAL_TYPE_UNDEFINED)) { - target->addType(cx, Type::UnknownType()); - return; - } - - TypeObject *object = NULL; - switch (type.primitive()) { - case JSVAL_TYPE_INT32: - case JSVAL_TYPE_DOUBLE: - object = TypeScript::StandardType(cx, JSProto_Number); - break; - case JSVAL_TYPE_BOOLEAN: - object = TypeScript::StandardType(cx, JSProto_Boolean); - break; - case JSVAL_TYPE_STRING: - object = TypeScript::StandardType(cx, JSProto_String); - break; - default: - return; - } - - if (!object) { - cx->compartment()->types.setPendingNukeTypes(cx); - return; - } - - target->addType(cx, Type::ObjectType(object)); -} - ///////////////////////////////////////////////////////////////////// // Freeze constraints ///////////////////////////////////////////////////////////////////// @@ -1973,6 +910,7 @@ HeapTypeSet::isOwnProperty(JSContext *cx, TypeObject *object, bool configurable) * definite properties be invalidated. */ if (object->flags & OBJECT_FLAG_NEW_SCRIPT_REGENERATE) { + object->flags &= ~OBJECT_FLAG_NEW_SCRIPT_REGENERATE; if (object->hasNewScript()) { Rooted typeObj(cx, object); RootedFunction fun(cx, object->newScript()->fun); @@ -2835,38 +1773,6 @@ TypeCompartment::addPendingRecompile(JSContext *cx, JSScript *script) ObjectStateChange(cx, script->function()->type(), false, true); } -void -TypeCompartment::monitorBytecode(JSContext *cx, JSScript *script, uint32_t offset, - bool returnOnly) -{ - if (!script->ensureRanInference(cx)) - return; - - ScriptAnalysis *analysis = script->analysis(); - jsbytecode *pc = script->code + offset; - - JS_ASSERT_IF(returnOnly, IsCallPC(pc)); - - Bytecode &code = analysis->getCode(pc); - - if (returnOnly ? code.monitoredTypesReturn : code.monitoredTypes) - return; - - InferSpew(ISpewOps, "addMonitorNeeded:%s #%u:%05u", - returnOnly ? " returnOnly" : "", script->id(), offset); - - /* Dynamically monitor this call to keep track of its result types. */ - if (IsCallPC(pc)) - code.monitoredTypesReturn = true; - - if (returnOnly) - return; - - code.monitoredTypes = true; - - AddPendingRecompile(cx, script); -} - void TypeCompartment::markSetsUnknown(JSContext *cx, TypeObject *target) { @@ -2914,121 +1820,9 @@ TypeCompartment::markSetsUnknown(JSContext *cx, TypeObject *target) typeArray[i].addType(cx, Type::AnyObjectType()); } } - if (script->hasAnalysis() && script->analysis()->ranInference()) { - for (unsigned i = 0; i < script->length; i++) { - if (!script->analysis()->maybeCode(i)) - continue; - jsbytecode *pc = script->code + i; - unsigned defCount = GetDefCount(script, i); - if (ExtendedDef(pc)) - defCount++; - for (unsigned j = 0; j < defCount; j++) { - TypeSet *types = script->analysis()->pushedTypes(pc, j); - if (types->hasType(Type::ObjectType(target))) - types->addType(cx, Type::AnyObjectType()); - } - } - } } } -void -ScriptAnalysis::addTypeBarrier(JSContext *cx, const jsbytecode *pc, TypeSet *target, Type type) -{ - Bytecode &code = getCode(pc); - - if (!type.isUnknown() && !type.isAnyObject() && - type.isObject() && target->getObjectCount() >= BARRIER_OBJECT_LIMIT) { - /* Ignore this barrier, just add the type to the target. */ - target->addType(cx, type); - return; - } - - if (!code.typeBarriers) { - /* - * Adding type barriers at a bytecode which did not have them before - * will trigger recompilation. If there were already type barriers, - * however, do not trigger recompilation (the script will be recompiled - * if any of the barriers is ever violated). - */ - AddPendingRecompile(cx, script_); - } - - /* Ignore duplicate barriers. */ - size_t barrierCount = 0; - TypeBarrier *barrier = code.typeBarriers; - while (barrier) { - if (barrier->target == target && !barrier->singleton) { - if (barrier->type == type) - return; - if (barrier->type.isAnyObject() && !type.isUnknown() && - /* type.isAnyObject() must be false, since type != barrier->type */ - type.isObject()) - { - return; - } - } - barrier = barrier->next; - barrierCount++; - } - - /* - * Use a generic object barrier if the number of barriers on an opcode gets - * excessive: it is unlikely that we will be able to completely discharge - * the barrier anyways without the target being marked as a generic object. - */ - if (barrierCount >= BARRIER_OBJECT_LIMIT && - !type.isUnknown() && !type.isAnyObject() && type.isObject()) - { - type = Type::AnyObjectType(); - } - - InferSpew(ISpewOps, "typeBarrier: #%u:%05u: %sT%p%s %s", - script_->id(), pc - script_->code, - InferSpewColor(target), target, InferSpewColorReset(), - TypeString(type)); - - barrier = cx->analysisLifoAlloc().new_(target, type, (JSObject *) NULL, JSID_VOID); - - if (!barrier) { - cx->compartment()->types.setPendingNukeTypes(cx); - return; - } - - barrier->next = code.typeBarriers; - code.typeBarriers = barrier; -} - -void -ScriptAnalysis::addSingletonTypeBarrier(JSContext *cx, const jsbytecode *pc, TypeSet *target, - HandleObject singleton, HandleId singletonId) -{ - JS_ASSERT(singletonId == IdToTypeId(singletonId) && !JSID_IS_VOID(singletonId)); - - Bytecode &code = getCode(pc); - - if (!code.typeBarriers) { - /* Trigger recompilation as for normal type barriers. */ - AddPendingRecompile(cx, script_); - } - - InferSpew(ISpewOps, "singletonTypeBarrier: #%u:%05u: %sT%p%s %p %s", - script_->id(), pc - script_->code, - InferSpewColor(target), target, InferSpewColorReset(), - (void *) singleton.get(), TypeIdString(singletonId)); - - TypeBarrier *barrier = cx->analysisLifoAlloc().new_(target, Type::UndefinedType(), - singleton, singletonId); - - if (!barrier) { - cx->compartment()->types.setPendingNukeTypes(cx); - return; - } - - barrier->next = code.typeBarriers; - code.typeBarriers = barrier; -} - void TypeCompartment::print(JSContext *cx, bool force) { @@ -3442,7 +2236,7 @@ TypeCompartment::newTypedObject(JSContext *cx, IdValuePair *properties, size_t n ///////////////////////////////////////////////////////////////////// void -TypeObject::getFromPrototypes(JSContext *cx, jsid id, TypeSet *types, bool force) +TypeObject::getFromPrototypes(JSContext *cx, jsid id, HeapTypeSet *types, bool force) { if (!force && types->hasPropagatedProperty()) return; @@ -3826,66 +2620,69 @@ TypeObject::clearNewScriptAddendum(ExclusiveContext *cx) Vector pcOffsets(cx); for (ScriptFrameIter iter(cx->asJSContext()); !iter.done(); ++iter) { pcOffsets.append(uint32_t(iter.pc() - iter.script()->code)); - if (iter.isConstructing() && - iter.callee() == newScript()->fun && - iter.thisv().isObject() && - !iter.thisv().toObject().hasLazyType() && - iter.thisv().toObject().type() == this) + if (!iter.isConstructing() || + iter.callee() != newScript()->fun || + !iter.thisv().isObject() || + iter.thisv().toObject().hasLazyType() || + iter.thisv().toObject().type() != this) { - RootedObject obj(cx, &iter.thisv().toObject()); + continue; + } - /* Whether all identified 'new' properties have been initialized. */ - bool finished = false; + // Found a matching frame. + RootedObject obj(cx, &iter.thisv().toObject()); - /* If not finished, number of properties that have been added. */ - uint32_t numProperties = 0; + // Whether all identified 'new' properties have been initialized. + bool finished = false; - /* - * If non-zero, we are scanning initializers in a call which has - * already finished. - */ - size_t depth = 0; - size_t callDepth = pcOffsets.length() - 1; - uint32_t offset = pcOffsets[callDepth]; + // If not finished, number of properties that have been added. + uint32_t numProperties = 0; - for (TypeNewScript::Initializer *init = newScript()->initializerList;; init++) { - if (init->kind == TypeNewScript::Initializer::SETPROP) { - if (!depth && init->offset > offset) { - /* Advanced past all properties which have been initialized. */ - break; - } - numProperties++; - } else if (init->kind == TypeNewScript::Initializer::FRAME_PUSH) { - if (depth) { - depth++; - } else if (init->offset > offset) { - /* Advanced past all properties which have been initialized. */ - break; - } else if (init->offset == offset) { - if (!callDepth) - break; - offset = pcOffsets[--callDepth]; - } else { - /* This call has already finished. */ - depth = 1; - } - } else if (init->kind == TypeNewScript::Initializer::FRAME_POP) { - if (depth) { - depth--; - } else { - /* This call has not finished yet. */ - break; - } - } else { - JS_ASSERT(init->kind == TypeNewScript::Initializer::DONE); - finished = true; + // Whether the current SETPROP is within an inner frame which has + // finished entirely. + bool pastProperty = false; + + // Index in pcOffsets of the outermost frame. + int callDepth = pcOffsets.length() - 1; + + // Index in pcOffsets of the frame currently being checked for a SETPROP. + int setpropDepth = callDepth; + + for (TypeNewScript::Initializer *init = newScript()->initializerList;; init++) { + if (init->kind == TypeNewScript::Initializer::SETPROP) { + if (!pastProperty && pcOffsets[setpropDepth] < init->offset) { + // Have not yet reached this setprop. break; } + // This setprop has executed, reset state for the next one. + numProperties++; + pastProperty = false; + setpropDepth = callDepth; + } else if (init->kind == TypeNewScript::Initializer::SETPROP_FRAME) { + if (!pastProperty) { + if (pcOffsets[setpropDepth] < init->offset) { + // Have not yet reached this inner call. + break; + } else if (pcOffsets[setpropDepth] > init->offset) { + // Have advanced past this inner call. + pastProperty = true; + } else if (setpropDepth == 0) { + // Have reached this call but not yet in it. + break; + } else { + // Somewhere inside this inner call. + setpropDepth--; + } + } + } else { + JS_ASSERT(init->kind == TypeNewScript::Initializer::DONE); + finished = true; + break; } - - if (!finished) - obj->rollbackProperties(cx, numProperties); } + + if (!finished) + obj->rollbackProperties(cx, numProperties); } } else { // Threads with an ExclusiveContext are not allowed to run scripts. @@ -3966,843 +2763,6 @@ GetInitializerType(JSContext *cx, JSScript *script, jsbytecode *pc) return TypeScript::InitObject(cx, script, pc, key); } -/* Analyze type information for a single bytecode. */ -bool -ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset, TypeInferenceState &state) -{ - JSScript *script = this->script_; - - jsbytecode *pc = script->code + offset; - JSOp op = (JSOp)*pc; - - Bytecode &code = getCode(offset); - JS_ASSERT(!code.pushedTypes); - - InferSpew(ISpewOps, "analyze: #%u:%05u", script->id(), offset); - - unsigned defCount = GetDefCount(script, offset); - if (ExtendedDef(pc)) - defCount++; - - StackTypeSet *pushed = cx->analysisLifoAlloc().newArrayUninitialized(defCount); - if (!pushed) - return false; - PodZero(pushed, defCount); - code.pushedTypes = pushed; - - /* - * Add phi nodes introduced at this point to the list of all phi nodes in - * the script. Types for these are not generated until after the script has - * been processed, as types can flow backwards into phi nodes and the - * source sets may not exist if we try to process these eagerly. - */ - if (code.newValues) { - SlotValue *newv = code.newValues; - while (newv->slot) { - if (newv->value.kind() != SSAValue::PHI || newv->value.phiOffset() != offset) { - newv++; - continue; - } - - /* - * The phi nodes at join points should all be unique, and every phi - * node created should be in the phiValues list on some bytecode. - */ - if (!state.phiNodes.append(newv->value.phiNode())) - return false; - TypeSet &types = newv->value.phiNode()->types; - InferSpew(ISpewOps, "typeSet: %sT%p%s phi #%u:%05u:%u", - InferSpewColor(&types), &types, InferSpewColorReset(), - script->id(), offset, newv->slot); - types.setPurged(); - - newv++; - } - } - - for (unsigned i = 0; i < defCount; i++) { - InferSpew(ISpewOps, "typeSet: %sT%p%s pushed%u #%u:%05u", - InferSpewColor(&pushed[i]), &pushed[i], InferSpewColorReset(), - i, script->id(), offset); - pushed[i].setPurged(); - } - - /* Add type constraints for the various opcodes. */ - switch (op) { - - /* Nop bytecodes. */ - case JSOP_POP: - case JSOP_NOP: - case JSOP_NOTEARG: - case JSOP_LOOPHEAD: - case JSOP_LOOPENTRY: - case JSOP_GOTO: - case JSOP_IFEQ: - case JSOP_IFNE: - case JSOP_LINENO: - case JSOP_DEFCONST: - case JSOP_LEAVEWITH: - case JSOP_LEAVEBLOCK: - case JSOP_RETRVAL: - case JSOP_ENDITER: - case JSOP_THROWING: - case JSOP_GOSUB: - case JSOP_RETSUB: - case JSOP_CONDSWITCH: - case JSOP_DEFAULT: - case JSOP_POPN: - case JSOP_POPV: - case JSOP_DEBUGGER: - case JSOP_SETCALL: - case JSOP_TABLESWITCH: - case JSOP_TRY: - case JSOP_LABEL: - case JSOP_RUNONCE: - break; - - /* Bytecodes pushing values of known type. */ - case JSOP_VOID: - case JSOP_UNDEFINED: - pushed[0].addType(cx, Type::UndefinedType()); - break; - case JSOP_ZERO: - case JSOP_ONE: - case JSOP_INT8: - case JSOP_INT32: - case JSOP_UINT16: - case JSOP_UINT24: - case JSOP_BITAND: - case JSOP_BITOR: - case JSOP_BITXOR: - case JSOP_BITNOT: - case JSOP_RSH: - case JSOP_LSH: - case JSOP_URSH: - pushed[0].addType(cx, Type::Int32Type()); - break; - case JSOP_FALSE: - case JSOP_TRUE: - case JSOP_EQ: - case JSOP_NE: - case JSOP_LT: - case JSOP_LE: - case JSOP_GT: - case JSOP_GE: - case JSOP_NOT: - case JSOP_STRICTEQ: - case JSOP_STRICTNE: - case JSOP_IN: - case JSOP_INSTANCEOF: - pushed[0].addType(cx, Type::BooleanType()); - break; - case JSOP_DOUBLE: - pushed[0].addType(cx, Type::DoubleType()); - break; - case JSOP_STRING: - case JSOP_TYPEOF: - case JSOP_TYPEOFEXPR: - pushed[0].addType(cx, Type::StringType()); - break; - case JSOP_NULL: - pushed[0].addType(cx, Type::NullType()); - break; - - case JSOP_REGEXP: - if (script->compileAndGo) { - TypeObject *object = TypeScript::StandardType(cx, JSProto_RegExp); - if (!object) - return false; - pushed[0].addType(cx, Type::ObjectType(object)); - } else { - pushed[0].addType(cx, Type::UnknownType()); - } - break; - - case JSOP_OBJECT: - pushed[0].addType(cx, Type::ObjectType(script->getObject(GET_UINT32_INDEX(pc)))); - break; - - case JSOP_STOP: - /* If a stop is reachable then the return type may be void. */ - if (script->function()) - TypeScript::ReturnTypes(script)->addType(cx, Type::UndefinedType()); - break; - - case JSOP_OR: - case JSOP_AND: - /* OR/AND push whichever operand determined the result. */ - poppedTypes(pc, 0)->addSubset(cx, &pushed[0]); - break; - - case JSOP_DUP: - poppedTypes(pc, 0)->addSubset(cx, &pushed[0]); - poppedTypes(pc, 0)->addSubset(cx, &pushed[1]); - break; - - case JSOP_DUP2: - poppedTypes(pc, 1)->addSubset(cx, &pushed[0]); - poppedTypes(pc, 0)->addSubset(cx, &pushed[1]); - poppedTypes(pc, 1)->addSubset(cx, &pushed[2]); - poppedTypes(pc, 0)->addSubset(cx, &pushed[3]); - break; - - case JSOP_SWAP: - case JSOP_PICK: { - unsigned pickedDepth = (op == JSOP_SWAP ? 1 : GET_UINT8(pc)); - /* The last popped value is the last pushed. */ - poppedTypes(pc, pickedDepth)->addSubset(cx, &pushed[pickedDepth]); - for (unsigned i = 0; i < pickedDepth; i++) - poppedTypes(pc, i)->addSubset(cx, &pushed[pickedDepth - 1 - i]); - break; - } - - case JSOP_GETGNAME: - case JSOP_CALLGNAME: { - jsid id = GetAtomId(cx, script, pc, 0); - - StackTypeSet *seen = TypeScript::BytecodeTypes(script, pc); - seen->addSubset(cx, &pushed[0]); - - /* - * Normally we rely on lazy standard class initialization to fill in - * the types of global properties the script can access. In a few cases - * the method JIT will bypass this, and we need to add the types - * directly. - */ - if (id == NameToId(cx->names().undefined)) - seen->addType(cx, Type::UndefinedType()); - if (id == NameToId(cx->names().NaN)) - seen->addType(cx, Type::DoubleType()); - if (id == NameToId(cx->names().Infinity)) - seen->addType(cx, Type::DoubleType()); - - TypeObject *global = script->global().getType(cx); - if (!global) - return false; - - /* Handle as a property access. */ - if (state.hasPropertyReadTypes) - PropertyAccess(cx, script, pc, global, seen, id); - else - PropertyAccess(cx, script, pc, global, seen, id); - break; - } - - case JSOP_NAME: - case JSOP_GETINTRINSIC: - case JSOP_CALLNAME: - case JSOP_CALLINTRINSIC: { - StackTypeSet *seen = TypeScript::BytecodeTypes(script, pc); - addTypeBarrier(cx, pc, seen, Type::UnknownType()); - seen->addSubset(cx, &pushed[0]); - break; - } - - case JSOP_BINDGNAME: - case JSOP_BINDNAME: - case JSOP_BINDINTRINSIC: - break; - - case JSOP_SETGNAME: { - jsid id = GetAtomId(cx, script, pc, 0); - TypeObject *global = script->global().getType(cx); - if (!global) - return false; - PropertyAccess(cx, script, pc, global, poppedTypes(pc, 0), id); - poppedTypes(pc, 0)->addSubset(cx, &pushed[0]); - break; - } - - case JSOP_SETNAME: - case JSOP_SETINTRINSIC: - case JSOP_SETCONST: - cx->compartment()->types.monitorBytecode(cx, script, offset); - poppedTypes(pc, 0)->addSubset(cx, &pushed[0]); - break; - - case JSOP_GETXPROP: { - StackTypeSet *seen = TypeScript::BytecodeTypes(script, pc); - addTypeBarrier(cx, pc, seen, Type::UnknownType()); - seen->addSubset(cx, &pushed[0]); - break; - } - - case JSOP_GETARG: - case JSOP_CALLARG: - case JSOP_GETLOCAL: - case JSOP_CALLLOCAL: { - uint32_t slot = GetBytecodeSlot(script, pc); - if (trackSlot(slot)) { - /* - * Normally these opcodes don't pop anything, but they are given - * an extended use holding the variable's SSA value before the - * access. Use the types from here. - */ - poppedTypes(pc, 0)->addSubset(cx, &pushed[0]); - } else { - /* Local 'let' variable. Punt on types for these, for now. */ - pushed[0].addType(cx, Type::UnknownType()); - } - break; - } - - case JSOP_SETARG: - case JSOP_SETLOCAL: - /* - * For assignments to non-escaping locals/args, we don't need to update - * the possible types of the var, as for each read of the var SSA gives - * us the writes that could have produced that read. - */ - poppedTypes(pc, 0)->addSubset(cx, &pushed[0]); - break; - - case JSOP_GETALIASEDVAR: - case JSOP_CALLALIASEDVAR: - /* - * Every aliased variable will contain 'undefined' in addition to the - * type of whatever value is written to it. Thus, a dynamic barrier is - * necessary. Since we don't expect the to observe more than 1 type, - * there is little benefit to maintaining a TypeSet for the aliased - * variable. Instead, we monitor/barrier all reads unconditionally. - */ - TypeScript::BytecodeTypes(script, pc)->addSubset(cx, &pushed[0]); - break; - - case JSOP_SETALIASEDVAR: - poppedTypes(pc, 0)->addSubset(cx, &pushed[0]); - break; - - case JSOP_ARGUMENTS: - /* Compute a precise type only when we know the arguments won't escape. */ - if (script->needsArgsObj()) - pushed[0].addType(cx, Type::UnknownType()); - else - pushed[0].addType(cx, Type::MagicArgType()); - break; - - case JSOP_REST: { - StackTypeSet *types = TypeScript::BytecodeTypes(script, pc); - if (script->compileAndGo) { - TypeObject *rest = TypeScript::InitObject(cx, script, pc, JSProto_Array); - if (!rest) - return false; - - // Simulate setting a element. - if (!rest->unknownProperties()) { - HeapTypeSet *propTypes = rest->getProperty(cx, JSID_VOID, true); - if (!propTypes) - return false; - propTypes->addType(cx, Type::UnknownType()); - } - - types->addType(cx, Type::ObjectType(rest)); - } else { - types->addType(cx, Type::UnknownType()); - } - types->addSubset(cx, &pushed[0]); - break; - } - - - case JSOP_SETPROP: { - jsid id = GetAtomId(cx, script, pc, 0); - poppedTypes(pc, 1)->addSetProperty(cx, script, pc, poppedTypes(pc, 0), id); - poppedTypes(pc, 0)->addSubset(cx, &pushed[0]); - break; - } - - case JSOP_LENGTH: - case JSOP_GETPROP: - case JSOP_CALLPROP: { - jsid id = GetAtomId(cx, script, pc, 0); - StackTypeSet *seen = TypeScript::BytecodeTypes(script, pc); - - HeapTypeSet *input = &script->types->propertyReadTypes[state.propertyReadIndex++]; - poppedTypes(pc, 0)->addSubset(cx, input); - - if (state.hasPropertyReadTypes) { - TypeConstraintGetPropertyExisting getProp(script, pc, seen, id); - input->addTypesToConstraint(cx, &getProp); - if (op == JSOP_CALLPROP) { - TypeConstraintCallPropertyExisting callProp(script, pc, id); - input->addTypesToConstraint(cx, &callProp); - } - } else { - input->addGetProperty(cx, script, pc, seen, id); - if (op == JSOP_CALLPROP) - input->addCallProperty(cx, script, pc, id); - } - - seen->addSubset(cx, &pushed[0]); - break; - } - - /* - * We only consider ELEM accesses on integers below. Any element access - * which is accessing a non-integer property must be monitored. - */ - - case JSOP_GETELEM: - case JSOP_CALLELEM: { - StackTypeSet *seen = TypeScript::BytecodeTypes(script, pc); - - /* Don't try to compute a precise callee for CALLELEM. */ - if (op == JSOP_CALLELEM) - seen->addType(cx, Type::AnyObjectType()); - - HeapTypeSet *input = &script->types->propertyReadTypes[state.propertyReadIndex++]; - poppedTypes(pc, 1)->addSubset(cx, input); - - if (state.hasPropertyReadTypes) { - TypeConstraintGetPropertyExisting getProp(script, pc, seen, JSID_VOID); - input->addTypesToConstraint(cx, &getProp); - } else { - input->addGetProperty(cx, script, pc, seen, JSID_VOID); - } - - seen->addSubset(cx, &pushed[0]); - break; - } - - case JSOP_SETELEM: - poppedTypes(pc, 1)->addSetElement(cx, script, pc, poppedTypes(pc, 2), poppedTypes(pc, 0)); - poppedTypes(pc, 0)->addSubset(cx, &pushed[0]); - break; - - case JSOP_TOID: - /* - * This is only used for element inc/dec ops; any id produced which - * is not an integer must be monitored. - */ - pushed[0].addType(cx, Type::Int32Type()); - break; - - case JSOP_THIS: - TypeScript::ThisTypes(script)->addTransformThis(cx, script, &pushed[0]); - break; - - case JSOP_RETURN: - case JSOP_SETRVAL: - if (script->function()) - poppedTypes(pc, 0)->addSubset(cx, TypeScript::ReturnTypes(script)); - break; - - case JSOP_ADD: - poppedTypes(pc, 0)->addArith(cx, script, pc, &pushed[0], poppedTypes(pc, 1)); - poppedTypes(pc, 1)->addArith(cx, script, pc, &pushed[0], poppedTypes(pc, 0)); - break; - - case JSOP_SUB: - case JSOP_MUL: - case JSOP_MOD: - case JSOP_DIV: - poppedTypes(pc, 0)->addArith(cx, script, pc, &pushed[0]); - poppedTypes(pc, 1)->addArith(cx, script, pc, &pushed[0]); - break; - - case JSOP_NEG: - case JSOP_POS: - poppedTypes(pc, 0)->addArith(cx, script, pc, &pushed[0]); - break; - - case JSOP_LAMBDA: { - RootedObject obj(cx, script->getObject(GET_UINT32_INDEX(pc))); - TypeSet *res = &pushed[0]; - - // If the lambda may produce values with different types than the - // original function, despecialize the type produced here. This includes - // functions that are deep cloned at each lambda, as well as inner - // functions to run-once lambdas which may actually execute multiple times. - if (script->compileAndGo && !script->treatAsRunOnce && - !UseNewTypeForClone(&obj->as())) - { - res->addType(cx, Type::ObjectType(obj)); - } else { - res->addType(cx, Type::AnyObjectType()); - } - break; - } - - case JSOP_DEFFUN: - cx->compartment()->types.monitorBytecode(cx, script, offset); - break; - - case JSOP_DEFVAR: - break; - - case JSOP_CALL: - case JSOP_EVAL: - case JSOP_FUNCALL: - case JSOP_FUNAPPLY: - case JSOP_NEW: { - StackTypeSet *seen = TypeScript::BytecodeTypes(script, pc); - seen->addSubset(cx, &pushed[0]); - - /* Construct the base call information about this site. */ - unsigned argCount = GetUseCount(script, offset) - 2; - TypeCallsite *callsite = cx->analysisLifoAlloc().new_( - cx, script, pc, op == JSOP_NEW, argCount); - if (!callsite || (argCount && !callsite->argumentTypes)) { - cx->compartment()->types.setPendingNukeTypes(cx); - break; - } - callsite->thisTypes = poppedTypes(pc, argCount); - callsite->returnTypes = seen; - - for (unsigned i = 0; i < argCount; i++) - callsite->argumentTypes[i] = poppedTypes(pc, argCount - 1 - i); - - /* - * Mark FUNCALL and FUNAPPLY sites as monitored. The method JIT may - * lower these into normal calls, and we need to make sure the - * callee's argument types are checked on entry. - */ - if (op == JSOP_FUNCALL || op == JSOP_FUNAPPLY) - cx->compartment()->types.monitorBytecode(cx, script, pc - script->code); - - StackTypeSet *calleeTypes = poppedTypes(pc, argCount + 1); - - /* - * Propagate possible 'this' types to the callee except when the call - * came through JSOP_CALLPROP (which uses TypeConstraintCallProperty) - * or for JSOP_NEW (where the callee will construct the 'this' object). - */ - SSAValue calleeValue = poppedValue(pc, argCount + 1); - if (*pc != JSOP_NEW && - (calleeValue.kind() != SSAValue::PUSHED || - script->code[calleeValue.pushedOffset()] != JSOP_CALLPROP)) - { - calleeTypes->add(cx, cx->analysisLifoAlloc().new_ - (script, pc, Type::UndefinedType(), callsite->thisTypes)); - } - - calleeTypes->addCall(cx, callsite); - break; - } - - case JSOP_NEWINIT: - case JSOP_NEWARRAY: - case JSOP_NEWOBJECT: { - StackTypeSet *types = TypeScript::BytecodeTypes(script, pc); - types->addSubset(cx, &pushed[0]); - - bool isArray = (op == JSOP_NEWARRAY || (op == JSOP_NEWINIT && GET_UINT8(pc) == JSProto_Array)); - JSProtoKey key = isArray ? JSProto_Array : JSProto_Object; - - if (UseNewTypeForInitializer(cx, script, pc, key)) { - /* Defer types pushed by this bytecode until runtime. */ - break; - } - - TypeObject *initializer = GetInitializerType(cx, script, pc); - if (script->compileAndGo) { - if (!initializer) - return false; - types->addType(cx, Type::ObjectType(initializer)); - } else { - JS_ASSERT(!initializer); - types->addType(cx, Type::UnknownType()); - } - break; - } - - case JSOP_ENDINIT: - break; - - case JSOP_INITELEM: - case JSOP_INITELEM_INC: - case JSOP_INITELEM_ARRAY: - case JSOP_INITELEM_GETTER: - case JSOP_INITELEM_SETTER: - case JSOP_SPREAD: { - const SSAValue &objv = poppedValue(pc, (op == JSOP_INITELEM_ARRAY) ? 1 : 2); - jsbytecode *initpc = script->code + objv.pushedOffset(); - TypeObject *initializer = GetInitializerType(cx, script, initpc); - - if (initializer) { - pushed[0].addType(cx, Type::ObjectType(initializer)); - if (!initializer->unknownProperties()) { - /* - * Assume the initialized element is an integer. INITELEM can be used - * for doubles which don't map to the JSID_VOID property, which must - * be caught with dynamic monitoring. - */ - TypeSet *types = initializer->getProperty(cx, JSID_VOID, true); - if (!types) - return false; - if (op == JSOP_INITELEM_GETTER || op == JSOP_INITELEM_SETTER) { - types->addType(cx, Type::UnknownType()); - } else if (state.hasHole) { - if (!initializer->unknownProperties()) - initializer->setFlags(cx, OBJECT_FLAG_NON_PACKED); - } else if (op == JSOP_SPREAD) { - // Iterator could put arbitrary things into the array. - types->addType(cx, Type::UnknownType()); - } else { - poppedTypes(pc, 0)->addSubset(cx, types); - } - } - } else { - pushed[0].addType(cx, Type::UnknownType()); - } - switch (op) { - case JSOP_SPREAD: - case JSOP_INITELEM_INC: - poppedTypes(pc, 1)->addSubset(cx, &pushed[1]); - break; - default: - break; - } - state.hasHole = false; - break; - } - - case JSOP_HOLE: - state.hasHole = true; - break; - - case JSOP_INITPROP: - case JSOP_INITPROP_GETTER: - case JSOP_INITPROP_SETTER: { - const SSAValue &objv = poppedValue(pc, 1); - jsbytecode *initpc = script->code + objv.pushedOffset(); - TypeObject *initializer = GetInitializerType(cx, script, initpc); - - if (initializer) { - pushed[0].addType(cx, Type::ObjectType(initializer)); - if (!initializer->unknownProperties()) { - jsid id = GetAtomId(cx, script, pc, 0); - TypeSet *types = initializer->getProperty(cx, id, true); - if (!types) - return false; - if (id == id___proto__(cx) || id == id_prototype(cx)) - cx->compartment()->types.monitorBytecode(cx, script, offset); - else if (op == JSOP_INITPROP_GETTER || op == JSOP_INITPROP_SETTER) - types->addType(cx, Type::UnknownType()); - else - poppedTypes(pc, 0)->addSubset(cx, types); - } - } else { - pushed[0].addType(cx, Type::UnknownType()); - } - JS_ASSERT(!state.hasHole); - break; - } - - case JSOP_ENTERWITH: - case JSOP_ENTERBLOCK: - case JSOP_ENTERLET0: - /* - * Scope lookups can occur on the values being pushed here. We don't track - * the value or its properties, and just monitor all name opcodes in the - * script. - */ - break; - - case JSOP_ENTERLET1: - /* - * JSOP_ENTERLET1 enters a let block with an unrelated value on top of - * the stack (such as the condition to a switch) whose constraints must - * be propagated. The other values are ignored for the same reason as - * JSOP_ENTERLET0. - */ - poppedTypes(pc, 0)->addSubset(cx, &pushed[defCount - 1]); - break; - - case JSOP_ITER: { - /* - * Use a per-script type set to unify the possible target types of all - * 'for in' or 'for each' loops in the script. We need to mark the - * value pushed by the ITERNEXT appropriately, but don't track the SSA - * information to connect that ITERNEXT with the appropriate ITER. - * This loses some precision when a script mixes 'for in' and - * 'for each' loops together, oh well. - */ - if (!state.forTypes) { - state.forTypes = StackTypeSet::make(cx, "forTypes"); - if (!state.forTypes) - return false; - } - - if (GET_UINT8(pc) == JSITER_ENUMERATE) - state.forTypes->addType(cx, Type::StringType()); - else - state.forTypes->addType(cx, Type::UnknownType()); - break; - } - - case JSOP_ITERNEXT: - state.forTypes->addSubset(cx, &pushed[0]); - break; - - case JSOP_MOREITER: - pushed[1].addType(cx, Type::BooleanType()); - break; - - case JSOP_ENUMELEM: - case JSOP_ENUMCONSTELEM: - case JSOP_ARRAYPUSH: - cx->compartment()->types.monitorBytecode(cx, script, offset); - break; - - case JSOP_THROW: - /* There will be a monitor on the bytecode catching the exception. */ - break; - - case JSOP_FINALLY: - /* Pushes information about whether an exception was thrown. */ - break; - - case JSOP_IMPLICITTHIS: - case JSOP_EXCEPTION: - pushed[0].addType(cx, Type::UnknownType()); - break; - - case JSOP_DELPROP: - case JSOP_DELELEM: - case JSOP_DELNAME: - pushed[0].addType(cx, Type::BooleanType()); - break; - - case JSOP_LEAVEBLOCKEXPR: - poppedTypes(pc, 0)->addSubset(cx, &pushed[0]); - break; - - case JSOP_LEAVEFORLETIN: - break; - - case JSOP_CASE: - poppedTypes(pc, 1)->addSubset(cx, &pushed[0]); - break; - - case JSOP_GENERATOR: - TypeScript::ReturnTypes(script)->addType(cx, Type::UnknownType()); - break; - - case JSOP_YIELD: - pushed[0].addType(cx, Type::UnknownType()); - break; - - case JSOP_CALLEE: - pushed[0].addType(cx, Type::AnyObjectType()); - break; - - default: - /* Display fine-grained debug information first */ - fprintf(stderr, "Unknown bytecode %02x at #%u:%05u\n", op, script->id(), offset); - TypeFailure(cx, "Unknown bytecode %02x", op); - } - - return true; -} - -void -ScriptAnalysis::analyzeTypes(JSContext *cx) -{ - JS_ASSERT(!ranInference()); - - if (OOM()) { - cx->compartment()->types.setPendingNukeTypes(cx); - return; - } - - if (!ranSSA()) { - analyzeSSA(cx); - if (failed()) - return; - } - - if (!script_->ensureHasBytecodeTypeMap(cx)) - return; - - /* - * Set this early to avoid reentrance. Any failures are OOMs, and will nuke - * all types in the compartment. - */ - ranInference_ = true; - - TypeInferenceState state(cx); - - /* - * Generate type sets for the inputs to property reads in the script, - * unless it already has them. If we purge analysis information and end up - * reanalyzing types in the script, we don't want to regenerate constraints - * on these property inputs as they will be duplicating information on the - * property type sets previously added. - */ - if (script_->types->propertyReadTypes) { - state.hasPropertyReadTypes = true; - } else { - HeapTypeSet *typeArray = - (HeapTypeSet*) cx->typeLifoAlloc().alloc(sizeof(HeapTypeSet) * numPropertyReads()); - if (!typeArray) { - cx->compartment()->types.setPendingNukeTypes(cx); - return; - } - script_->types->propertyReadTypes = typeArray; - PodZero(typeArray, numPropertyReads()); - -#ifdef DEBUG - for (unsigned i = 0; i < numPropertyReads(); i++) { - InferSpew(ISpewOps, "typeSet: %sT%p%s propertyRead%u #%u", - InferSpewColor(&typeArray[i]), &typeArray[i], InferSpewColorReset(), - i, script_->id()); - } -#endif - } - - undefinedTypeSet = cx->analysisLifoAlloc().new_(); - if (!undefinedTypeSet) { - cx->compartment()->types.setPendingNukeTypes(cx); - return; - } - undefinedTypeSet->addType(cx, Type::UndefinedType()); - - unsigned offset = 0; - while (offset < script_->length) { - Bytecode *code = maybeCode(offset); - - jsbytecode *pc = script_->code + offset; - - if (code && !analyzeTypesBytecode(cx, offset, state)) { - cx->compartment()->types.setPendingNukeTypes(cx); - return; - } - - offset += GetBytecodeLength(pc); - } - - JS_ASSERT(state.propertyReadIndex == numPropertyReads()); - - for (unsigned i = 0; i < state.phiNodes.length(); i++) { - SSAPhiNode *node = state.phiNodes[i]; - for (unsigned j = 0; j < node->length; j++) { - const SSAValue &v = node->options[j]; - getValueTypes(v)->addSubset(cx, &node->types); - } - } - - /* - * Replay any dynamic type results which have been generated for the script - * either because we ran the interpreter some before analyzing or because - * we are reanalyzing after a GC. - */ - TypeResult *result = script_->types->dynamicList; - while (result) { - if (result->offset != UINT32_MAX) { - pushedTypes(result->offset)->addType(cx, result->type); - } else { - /* Custom for-in loop iteration has happened in this script. */ - state.forTypes->addType(cx, Type::UnknownType()); - } - result = result->next; - } - - TypeScript::AddFreezeConstraints(cx, script_); -} - -namespace { - /* * Persistent constraint clearing out newScript and definite properties from * an object should a property on another object get a getter or setter. @@ -4835,15 +2795,13 @@ class TypeConstraintClearDefiniteGetterSetter : public TypeConstraint void newType(JSContext *cx, TypeSet *source, Type type) {} }; -} /* anonymous namespace */ - -static bool -AddClearDefiniteGetterSetterForPrototypeChain(JSContext *cx, TypeObject *type, jsid id) +bool +types::AddClearDefiniteGetterSetterForPrototypeChain(JSContext *cx, TypeObject *type, jsid id) { /* * Ensure that if the properties named here could have a getter, setter or * a permanent property in any transitive prototype, the definite - * properties get cleared from the shape. + * properties get cleared from the type. */ RootedObject parent(cx, type->proto); while (parent) { @@ -4859,8 +2817,6 @@ AddClearDefiniteGetterSetterForPrototypeChain(JSContext *cx, TypeObject *type, j return true; } -namespace { - /* * Constraint which clears definite properties on an object should a type set * contain any types other than a single object. @@ -4885,331 +2841,43 @@ class TypeConstraintClearDefiniteSingle : public TypeConstraint } }; -struct NewScriptPropertiesState +void +types::AddClearDefiniteFunctionUsesInScript(JSContext *cx, TypeObject *type, + JSScript *script, JSScript *calleeScript) { - RootedFunction thisFunction; - RootedObject baseobj; - Vector initializerList; - Vector accessedProperties; + // Look for any uses of the specified calleeScript in type sets for + // |script|, and add constraints to ensure that if the type sets' contents + // change then the definite properties are cleared from the type. + // This ensures that the inlining performed when the definite properties + // analysis was done is stable. - NewScriptPropertiesState(JSContext *cx) - : thisFunction(cx), baseobj(cx), initializerList(cx), accessedProperties(cx) - {} -}; + TypeObjectKey *calleeKey = Type::ObjectType(calleeScript->function()).objectKey(); -} /* anonymous namespace */ + unsigned count = TypeScript::NumTypeSets(script); + TypeSet *typeArray = script->types->typeArray(); -static bool -AnalyzePoppedThis(JSContext *cx, SSAUseChain *use, - TypeObject *type, JSFunction *fun, NewScriptPropertiesState &state); - -static bool -AnalyzeNewScriptProperties(JSContext *cx, TypeObject *type, HandleFunction fun, - NewScriptPropertiesState &state) -{ - /* - * When invoking 'new' on the specified script, try to find some properties - * which will definitely be added to the created object before it has a - * chance to escape and be accessed elsewhere. - * - * Returns true if the entire script was analyzed (pbaseobj has been - * preserved), false if we had to bail out part way through (pbaseobj may - * have been cleared). - */ - - if (state.initializerList.length() > 50) { - /* - * Bail out on really long initializer lists (far longer than maximum - * number of properties we can track), we may be recursing. - */ - return false; - } - - RootedScript script(cx, fun->getOrCreateScript(cx)); - if (!script) - return false; - - if (!script->ensureRanAnalysis(cx) || !script->ensureRanInference(cx)) { - state.baseobj = NULL; - cx->compartment()->types.setPendingNukeTypes(cx); - return false; - } - - ScriptAnalysis *analysis = script->analysis(); - - /* - * Offset of the last bytecode which popped 'this' and which we have - * processed. To support compound inline assignments to properties like - * 'this.f = (this.g = ...)' where multiple 'this' values are pushed - * and popped en masse, we keep a stack of 'this' values that have yet to - * be processed. If a 'this' is pushed before the previous 'this' value - * was popped, we defer processing it until we see a 'this' that is popped - * after the previous 'this' was popped, i.e. the end of the compound - * inline assignment, or we encounter a return from the script. - */ - Vector pendingPoppedThis(cx); - - unsigned nextOffset = 0; - while (nextOffset < script->length) { - unsigned offset = nextOffset; - jsbytecode *pc = script->code + offset; - - JSOp op = JSOp(*pc); - - nextOffset += GetBytecodeLength(pc); - - Bytecode *code = analysis->maybeCode(pc); - if (!code) - continue; - - /* - * If offset >= the offset at the top of the pending stack, we either - * encountered the end of a compound inline assignment or a 'this' was - * immediately popped and used. In either case, handle the uses - * consumed before the current offset. - */ - while (!pendingPoppedThis.empty() && offset >= pendingPoppedThis.back()->offset) { - SSAUseChain *use = pendingPoppedThis.popCopy(); - if (!AnalyzePoppedThis(cx, use, type, fun, state)) - return false; - } - - /* - * End analysis after the first return statement from the script, - * returning success if the return is unconditional. - */ - if (op == JSOP_RETURN || op == JSOP_STOP || op == JSOP_RETRVAL) - return code->unconditional; - - /* 'this' can escape through a call to eval. */ - if (op == JSOP_EVAL) - return false; - - /* - * We are only interested in places where 'this' is popped. The new - * 'this' value cannot escape and be accessed except through such uses. - */ - if (op != JSOP_THIS) - continue; - - SSAValue thisv = SSAValue::PushedValue(offset, 0); - SSAUseChain *uses = analysis->useChain(thisv); - - JS_ASSERT(uses); - if (uses->next || !uses->popped) { - /* 'this' value popped in more than one place. */ - return false; - } - - /* Only handle 'this' values popped in unconditional code. */ - Bytecode *poppedCode = analysis->maybeCode(uses->offset); - if (!poppedCode || !poppedCode->unconditional) - return false; - - if (!pendingPoppedThis.append(uses)) - return false; - } - - /* Will have hit a STOP or similar, unless the script always throws. */ - return true; -} - -static bool -AnalyzePoppedThis(JSContext *cx, SSAUseChain *use, - TypeObject *type, JSFunction *fun, NewScriptPropertiesState &state) -{ - RootedScript script(cx, fun->nonLazyScript()); - ScriptAnalysis *analysis = script->analysis(); - - jsbytecode *pc = script->code + use->offset; - JSOp op = JSOp(*pc); - - if (op == JSOP_SETPROP && use->u.which == 1) { - /* - * Don't use GetAtomId here, we need to watch for SETPROP on - * integer properties and bail out. We can't mark the aggregate - * JSID_VOID type property as being in a definite slot. - */ - RootedId id(cx, NameToId(script->getName(GET_UINT32_INDEX(pc)))); - if (IdToTypeId(id) != id) - return false; - if (id_prototype(cx) == id || id___proto__(cx) == id || id_constructor(cx) == id) - return false; - - /* - * Don't add definite properties for properties that were already - * read in the constructor. - */ - for (size_t i = 0; i < state.accessedProperties.length(); i++) { - if (state.accessedProperties[i] == id) - return false; - } - - if (!AddClearDefiniteGetterSetterForPrototypeChain(cx, type, id)) - return false; - - unsigned slotSpan = state.baseobj->slotSpan(); - RootedValue value(cx, UndefinedValue()); - if (!DefineNativeProperty(cx, state.baseobj, id, value, NULL, NULL, - JSPROP_ENUMERATE, 0, 0, DNP_SKIP_TYPE)) - { - cx->compartment()->types.setPendingNukeTypes(cx); - state.baseobj = NULL; - return false; - } - - if (state.baseobj->inDictionaryMode()) { - state.baseobj = NULL; - return false; - } - - if (state.baseobj->slotSpan() == slotSpan) { - /* Set a duplicate property. */ - return false; - } - - TypeNewScript::Initializer setprop(TypeNewScript::Initializer::SETPROP, use->offset); - if (!state.initializerList.append(setprop)) { - cx->compartment()->types.setPendingNukeTypes(cx); - state.baseobj = NULL; - return false; - } - - if (state.baseobj->slotSpan() >= (TYPE_FLAG_DEFINITE_MASK >> TYPE_FLAG_DEFINITE_SHIFT)) { - /* Maximum number of definite properties added. */ - return false; - } - - return true; - } - - if (op == JSOP_GETPROP && use->u.which == 0) { - /* - * Properties can be read from the 'this' object if the following hold: - * - * - The read is not on a getter along the prototype chain, which - * could cause 'this' to escape. - * - * - The accessed property is either already a definite property or - * is not later added as one. Since the definite properties are - * added to the object at the point of its creation, reading a - * definite property before it is assigned could incorrectly hit. - */ - RootedId id(cx, NameToId(script->getName(GET_UINT32_INDEX(pc)))); - if (IdToTypeId(id) != id) - return false; - if (!state.baseobj->nativeLookup(cx, id) && !state.accessedProperties.append(id.get())) { - cx->compartment()->types.setPendingNukeTypes(cx); - state.baseobj = NULL; - return false; - } - - if (!AddClearDefiniteGetterSetterForPrototypeChain(cx, type, id)) - return false; - - /* - * Populate the read with any value from the type's proto, if - * this is being used in a function call and we need to analyze the - * callee's behavior. - */ - Shape *shape = (type->proto && type->proto->isNative()) - ? type->proto->nativeLookup(cx, id) - : NULL; - if (shape && shape->hasSlot()) { - Value protov = type->proto->getSlot(shape->slot()); - TypeSet *types = TypeScript::BytecodeTypes(script, pc); - types->addType(cx, GetValueType(protov)); - } - - return true; - } - - if ((op == JSOP_FUNCALL || op == JSOP_FUNAPPLY) && use->u.which == GET_ARGC(pc) - 1) { - /* - * Passed as the first parameter to Function.call. Follow control - * into the callee, and add any definite properties it assigns to - * the object as well. :TODO: This is narrow pattern matching on - * the inheritance patterns seen in the v8-deltablue benchmark, and - * needs robustness against other ways initialization can cross - * script boundaries. - * - * Add constraints ensuring we are calling Function.call on a - * particular script, removing definite properties from the result - */ - - /* Callee/this must have been pushed by a CALLPROP. */ - SSAValue calleev = analysis->poppedValue(pc, GET_ARGC(pc) + 1); - if (calleev.kind() != SSAValue::PUSHED) - return false; - jsbytecode *calleepc = script->code + calleev.pushedOffset(); - if (JSOp(*calleepc) != JSOP_CALLPROP) - return false; - - /* - * This code may not have run yet, break any type barriers involved - * in performing the call (for the greater good!). - */ - if (cx->compartment()->types.compiledInfo.outputIndex == RecompileInfo::NoCompilerRunning) { - analysis->breakTypeBarriersSSA(cx, analysis->poppedValue(calleepc, 0)); - analysis->breakTypeBarriers(cx, calleepc - script->code, true); - } - - StackTypeSet *funcallTypes = analysis->poppedTypes(pc, GET_ARGC(pc) + 1); - StackTypeSet *scriptTypes = analysis->poppedTypes(pc, GET_ARGC(pc)); - - /* Need to definitely be calling Function.call/apply on a specific script. */ - RootedFunction function(cx); - { - JSObject *funcallObj = funcallTypes->getSingleton(); - JSObject *scriptObj = scriptTypes->getSingleton(); - if (!funcallObj || !funcallObj->is() || - funcallObj->as().isInterpreted() || - !scriptObj || !scriptObj->is() || - !scriptObj->as().isInterpreted()) - { - return false; + for (unsigned i = 0; i < count; i++) { + TypeSet *types = &typeArray[i]; + if (types->getObjectCount() == 1) { + if (calleeKey != types->getObject(0)) { + // Also check if the object is the Function.call or + // Function.apply native. IonBuilder uses the presence of these + // functions during inlining. + JSObject *singleton = types->getSingleObject(0); + if (!singleton || !singleton->is()) + continue; + JSFunction *fun = &singleton->as(); + if (!fun->isNative()) + continue; + if (fun->native() != js_fun_call && fun->native() != js_fun_apply) + continue; } - Native native = funcallObj->as().native(); - if (native != js_fun_call && native != js_fun_apply) - return false; - function = &scriptObj->as(); + // This is a type set that might have been used when inlining + // |calleeScript| into |script|. + types->add(cx, + cx->analysisLifoAlloc().new_(type)); } - - /* - * Generate constraints to clear definite properties from the type - * should the Function.call or callee itself change in the future. - */ - funcallTypes->add(cx, - cx->analysisLifoAlloc().new_(type)); - scriptTypes->add(cx, - cx->analysisLifoAlloc().new_(type)); - - TypeNewScript::Initializer pushframe(TypeNewScript::Initializer::FRAME_PUSH, use->offset); - if (!state.initializerList.append(pushframe)) { - cx->compartment()->types.setPendingNukeTypes(cx); - state.baseobj = NULL; - return false; - } - - if (!AnalyzeNewScriptProperties(cx, type, function, state)) - return false; - - TypeNewScript::Initializer popframe(TypeNewScript::Initializer::FRAME_POP, 0); - if (!state.initializerList.append(popframe)) { - cx->compartment()->types.setPendingNukeTypes(cx); - state.baseobj = NULL; - return false; - } - - /* - * The callee never lets the 'this' value escape, continue looking - * for definite properties in the remainder of this script. - */ - return true; } - - /* Unhandled use of 'this'. */ - return false; } /* @@ -5220,24 +2888,23 @@ AnalyzePoppedThis(JSContext *cx, SSAUseChain *use, static void CheckNewScriptProperties(JSContext *cx, HandleTypeObject type, HandleFunction fun) { +#ifdef JS_ION if (type->unknownProperties()) return; - NewScriptPropertiesState state(cx); - state.thisFunction = fun; - /* Strawman object to add properties to and watch for duplicates. */ - state.baseobj = NewBuiltinClassInstance(cx, &JSObject::class_, gc::FINALIZE_OBJECT16); - if (!state.baseobj) { - if (type->addendum) - type->clearAddendum(cx); + RootedObject baseobj(cx, NewBuiltinClassInstance(cx, &JSObject::class_, gc::FINALIZE_OBJECT16)); + if (!baseobj) + return; + + Vector initializerList(cx); + + if (!jit::AnalyzeNewScriptProperties(cx, fun, type, baseobj, &initializerList)) { cx->compartment()->types.setPendingNukeTypes(cx); return; } - AnalyzeNewScriptProperties(cx, type, fun, state); - if (!state.baseobj || - state.baseobj->slotSpan() == 0 || + if (baseobj->slotSpan() == 0 || !!(type->flags & OBJECT_FLAG_ADDENDUM_CLEARED)) { if (type->addendum) @@ -5251,17 +2918,17 @@ CheckNewScriptProperties(JSContext *cx, HandleTypeObject type, HandleFunction fu * the properties added to baseobj match the type's definite properties. */ if (type->hasNewScript()) { - if (!type->matchDefiniteProperties(state.baseobj)) + if (!type->matchDefiniteProperties(baseobj)) type->clearAddendum(cx); return; } JS_ASSERT(!type->addendum); JS_ASSERT(!(type->flags & OBJECT_FLAG_ADDENDUM_CLEARED)); - gc::AllocKind kind = gc::GetGCObjectKind(state.baseobj->slotSpan()); + gc::AllocKind kind = gc::GetGCObjectKind(baseobj->slotSpan()); /* We should not have overflowed the maximum number of fixed slots for an object. */ - JS_ASSERT(gc::GetGCKindSlots(kind) >= state.baseobj->slotSpan()); + JS_ASSERT(gc::GetGCKindSlots(kind) >= baseobj->slotSpan()); TypeNewScript::Initializer done(TypeNewScript::Initializer::DONE, 0); @@ -5270,17 +2937,18 @@ CheckNewScriptProperties(JSContext *cx, HandleTypeObject type, HandleFunction fu * than we will use for subsequent new objects. Generate an object with the * appropriate final shape. */ - RootedShape shape(cx, state.baseobj->lastProperty()); - state.baseobj = NewReshapedObject(cx, type, state.baseobj->getParent(), kind, shape); - if (!state.baseobj || - !type->addDefiniteProperties(cx, state.baseobj) || - !state.initializerList.append(done)) { + RootedShape shape(cx, baseobj->lastProperty()); + baseobj = NewReshapedObject(cx, type, baseobj->getParent(), kind, shape); + if (!baseobj || + !type->addDefiniteProperties(cx, baseobj) || + !initializerList.append(done)) + { cx->compartment()->types.setPendingNukeTypes(cx); return; } size_t numBytes = sizeof(TypeNewScript) - + (state.initializerList.length() * sizeof(TypeNewScript::Initializer)); + + (initializerList.length() * sizeof(TypeNewScript::Initializer)); TypeNewScript *newScript; #ifdef JSGC_ROOT_ANALYSIS // calloc can legitimately return a pointer that appears to be poisoned. @@ -5302,13 +2970,14 @@ CheckNewScriptProperties(JSContext *cx, HandleTypeObject type, HandleFunction fu newScript->fun = fun; newScript->allocKind = kind; - newScript->shape = state.baseobj->lastProperty(); + newScript->shape = baseobj->lastProperty(); newScript->initializerList = (TypeNewScript::Initializer *) ((char *) newScript + sizeof(TypeNewScript)); PodCopy(newScript->initializerList, - state.initializerList.begin(), - state.initializerList.length()); + initializerList.begin(), + initializerList.length()); +#endif // JS_ION } ///////////////////////////////////////////////////////////////////// @@ -5363,19 +3032,6 @@ types::MarkIteratorUnknownSlow(JSContext *cx) script->types->dynamicList = result; AddPendingRecompile(cx, script); - - if (!script->hasAnalysis() || !script->analysis()->ranInference()) - return; - - ScriptAnalysis *analysis = script->analysis(); - - for (unsigned i = 0; i < script->length; i++) { - jsbytecode *pc = script->code + i; - if (!analysis->maybeCode(pc)) - continue; - if (JSOp(*pc) == JSOP_ITERNEXT) - analysis->pushedTypes(pc, 0)->addType(cx, Type::UnknownType()); - } } void @@ -5437,37 +3093,26 @@ types::TypeDynamicResult(JSContext *cx, JSScript *script, jsbytecode *pc, Type t return; } - if (script->hasAnalysis() && script->analysis()->ranInference()) { - /* - * If the pushed set already has this type, we don't need to ensure - * there is a TypeIntermediate. Either there already is one, or the - * type could be determined from the script's other input type sets. - */ - TypeSet *pushed = script->analysis()->pushedTypes(pc, 0); - if (pushed->hasType(type)) - return; - } else { - /* Scan all intermediate types on the script to check for a dupe. */ - TypeResult *result, **pstart = &script->types->dynamicList, **presult = pstart; - while (*presult) { - result = *presult; - if (result->offset == unsigned(pc - script->code) && result->type == type) { - if (presult != pstart) { - /* Move to the head of the list, maintain LRU order. */ - *presult = result->next; - result->next = *pstart; - *pstart = result; - } - return; + /* Scan all intermediate types on the script to check for a dupe. */ + TypeResult *result, **pstart = &script->types->dynamicList, **presult = pstart; + while (*presult) { + result = *presult; + if (result->offset == unsigned(pc - script->code) && result->type == type) { + if (presult != pstart) { + /* Move to the head of the list, maintain LRU order. */ + *presult = result->next; + result->next = *pstart; + *pstart = result; } - presult = &result->next; + return; } + presult = &result->next; } InferSpew(ISpewOps, "externalType: monitorResult #%u:%05u: %s", script->id(), pc - script->code, TypeString(type)); - TypeResult *result = cx->new_(pc - script->code, type); + result = cx->new_(pc - script->code, type); if (!result) { cx->compartment()->types.setPendingNukeTypes(cx); return; @@ -5476,11 +3121,6 @@ types::TypeDynamicResult(JSContext *cx, JSScript *script, jsbytecode *pc, Type t script->types->dynamicList = result; AddPendingRecompile(cx, script); - - if (script->hasAnalysis() && script->analysis()->ranInference()) { - TypeSet *pushed = script->analysis()->pushedTypes(pc, 0); - pushed->addType(cx, type); - } } void diff --git a/js/src/jsinfer.h b/js/src/jsinfer.h index 884e2d38c04..19784f287f4 100644 --- a/js/src/jsinfer.h +++ b/js/src/jsinfer.h @@ -571,22 +571,8 @@ class StackTypeSet : public TypeSet */ static StackTypeSet *make(JSContext *cx, const char *name); - /* Constraints for type inference. */ - - void addSubset(JSContext *cx, TypeSet *target); - void addGetProperty(JSContext *cx, JSScript *script, jsbytecode *pc, - StackTypeSet *target, jsid id); - void addSetProperty(JSContext *cx, JSScript *script, jsbytecode *pc, - StackTypeSet *target, jsid id); - void addSetElement(JSContext *cx, JSScript *script, jsbytecode *pc, - StackTypeSet *objectTypes, StackTypeSet *valueTypes); - void addCall(JSContext *cx, TypeCallsite *site); - void addArith(JSContext *cx, JSScript *script, jsbytecode *pc, - TypeSet *target, TypeSet *other = NULL); - void addTransformThis(JSContext *cx, JSScript *script, TypeSet *target); - void addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc, - Type type, StackTypeSet *types = NULL); - void addSubsetBarrier(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target); + /* Propagate any types from this set into target. */ + void addSubset(JSContext *cx, StackTypeSet *target); /* * Constraints for JIT compilation. @@ -679,16 +665,8 @@ class HeapTypeSet : public TypeSet { public: - /* Constraints for type inference. */ - - void addSubset(JSContext *cx, TypeSet *target); - void addGetProperty(JSContext *cx, JSScript *script, jsbytecode *pc, - StackTypeSet *target, jsid id); - void addCallProperty(JSContext *cx, JSScript *script, jsbytecode *pc, jsid id); - void addFilterPrimitives(JSContext *cx, TypeSet *target); - void addSubsetBarrier(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target); - - /* Constraints for JIT compilation. */ + /* Propagate any types from this set into target. */ + void addSubset(JSContext *cx, HeapTypeSet *target); /* Completely freeze the contents of this type set. */ void addFreeze(JSContext *cx); @@ -745,6 +723,13 @@ TypeSet::toHeapTypeSet() return (HeapTypeSet *) this; } +bool +AddClearDefiniteGetterSetterForPrototypeChain(JSContext *cx, TypeObject *type, jsid id); + +void +AddClearDefiniteFunctionUsesInScript(JSContext *cx, TypeObject *type, + JSScript *script, JSScript *calleeScript); + /* * Handler which persists information about dynamic types pushed within a * script which can affect its behavior and are not covered by JOF_TYPESET ops, @@ -932,14 +917,14 @@ struct TypeNewScript : public TypeObjectAddendum * scripted setter is added to one of the object's prototypes while it is * in the middle of being initialized, so we can walk the stack and fixup * any objects which look for in-progress objects which were prematurely - * set with their final shape. Initialization can traverse stack frames, - * in which case FRAME_PUSH/FRAME_POP are used. + * set with their final shape. Property assignments in inner frames are + * preceded by a series of SETPROP_FRAME entries specifying the stack down + * to the frame containing the write. */ struct Initializer { enum Kind { SETPROP, - FRAME_PUSH, - FRAME_POP, + SETPROP_FRAME, DONE } kind; uint32_t offset; @@ -1145,7 +1130,7 @@ struct TypeObject : gc::Cell void clearAddendum(ExclusiveContext *cx); void clearNewScriptAddendum(ExclusiveContext *cx); void clearBinaryDataAddendum(ExclusiveContext *cx); - void getFromPrototypes(JSContext *cx, jsid id, TypeSet *types, bool force = false); + void getFromPrototypes(JSContext *cx, jsid id, HeapTypeSet *types, bool force = false); void print(); @@ -1493,10 +1478,6 @@ struct TypeCompartment void addPendingRecompile(JSContext *cx, const RecompileInfo &info); void addPendingRecompile(JSContext *cx, JSScript *script); - /* Monitor future effects on a bytecode. */ - void monitorBytecode(JSContext *cx, JSScript *script, uint32_t offset, - bool returnOnly = false); - /* Mark any type set containing obj as having a generic object type. */ void markSetsUnknown(JSContext *cx, TypeObject *obj); diff --git a/js/src/jsinferinlines.h b/js/src/jsinferinlines.h index e69f78597b3..99ac4f82a19 100644 --- a/js/src/jsinferinlines.h +++ b/js/src/jsinferinlines.h @@ -1682,18 +1682,6 @@ JSScript::ensureRanAnalysis(JSContext *cx) return true; } -inline bool -JSScript::ensureRanInference(JSContext *cx) -{ - if (!ensureRanAnalysis(cx)) - return false; - if (!analysis()->ranInference()) { - js::types::AutoEnterAnalysis enter(cx); - analysis()->analyzeTypes(cx); - } - return !analysis()->OOM() && !cx->zone()->types.pendingNukeTypes; -} - inline bool JSScript::hasAnalysis() { @@ -1723,14 +1711,6 @@ JSScript::clearPropertyReadTypes() types->propertyReadTypes = NULL; } -inline void -js::analyze::ScriptAnalysis::addPushedType(JSContext *cx, uint32_t offset, uint32_t which, - js::types::Type type) -{ - js::types::TypeSet *pushed = pushedTypes(offset, which); - pushed->addType(cx, type); -} - namespace js { template <> diff --git a/js/src/jsmath.h b/js/src/jsmath.h index 43cdd81e73d..facf7bb307c 100644 --- a/js/src/jsmath.h +++ b/js/src/jsmath.h @@ -9,7 +9,7 @@ #include "mozilla/MemoryReporting.h" -#include "jsapi.h" +#include "NamespaceImports.h" namespace js { diff --git a/js/src/jsnum.h b/js/src/jsnum.h index 20fabea50d1..db77031446a 100644 --- a/js/src/jsnum.h +++ b/js/src/jsnum.h @@ -9,7 +9,7 @@ #include "mozilla/FloatingPoint.h" -#include "jsapi.h" +#include "NamespaceImports.h" #include "vm/NumericConversions.h" diff --git a/js/src/json.h b/js/src/json.h index d1c4f68b6de..fb4f4313ad1 100644 --- a/js/src/json.h +++ b/js/src/json.h @@ -8,6 +8,7 @@ #define json_h #include "jsapi.h" +#include "NamespaceImports.h" #include "js/RootingAPI.h" diff --git a/js/src/jsopcode.h b/js/src/jsopcode.h index f493b83e59b..0dcf0870220 100644 --- a/js/src/jsopcode.h +++ b/js/src/jsopcode.h @@ -13,6 +13,7 @@ #include "jsapi.h" #include "jsbytecode.h" +#include "NamespaceImports.h" #include "frontend/SourceNotes.h" diff --git a/js/src/jspropertytree.cpp b/js/src/jspropertytree.cpp index 657f751c668..ed1beaac66b 100644 --- a/js/src/jspropertytree.cpp +++ b/js/src/jspropertytree.cpp @@ -6,7 +6,6 @@ #include "jspropertytree.h" -#include "jsapi.h" #include "jscntxt.h" #include "jsgc.h" #include "jstypes.h" diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 7ebffce2cde..4a12aaa2d2b 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -2834,11 +2834,6 @@ JSScript::argumentsOptimizationFailed(JSContext *cx, HandleScript script) } } - if (script->hasAnalysis() && script->analysis()->ranInference()) { - types::AutoEnterAnalysis enter(cx); - types::TypeScript::MonitorUnknown(cx, script, script->argumentsBytecode()); - } - return true; } diff --git a/js/src/jsstr.h b/js/src/jsstr.h index bec1312bcce..458c1603fe5 100644 --- a/js/src/jsstr.h +++ b/js/src/jsstr.h @@ -10,12 +10,13 @@ #include "mozilla/HashFunctions.h" #include "mozilla/PodOperations.h" -#include "jsapi.h" #include "jsutil.h" +#include "NamespaceImports.h" #include "js/RootingAPI.h" #include "vm/Unicode.h" +class JSAutoByteString; class JSFlatString; class JSLinearString; class JSStableString; diff --git a/js/src/jswatchpoint.h b/js/src/jswatchpoint.h index 0758cf13380..7452b8909a5 100644 --- a/js/src/jswatchpoint.h +++ b/js/src/jswatchpoint.h @@ -8,7 +8,6 @@ #define jswatchpoint_h #include "jsalloc.h" -#include "jsapi.h" #include "gc/Barrier.h" #include "js/HashTable.h" diff --git a/js/src/jsworkers.cpp b/js/src/jsworkers.cpp index fa9e85a426a..72e832c8d6f 100644 --- a/js/src/jsworkers.cpp +++ b/js/src/jsworkers.cpp @@ -202,6 +202,9 @@ ParseTask::~ParseTask() // ParseTask takes over ownership of its input exclusive context. js_delete(cx); + + for (size_t i = 0; i < errors.length(); i++) + js_delete(errors[i]); } bool @@ -550,7 +553,7 @@ WorkerThreadState::finishParseTask(JSContext *maybecx, JSRuntime *rt, void *toke if (maybecx) { AutoCompartment ac(maybecx, parseTask->scopeChain); for (size_t i = 0; i < parseTask->errors.length(); i++) - parseTask->errors[i].throwError(maybecx); + parseTask->errors[i]->throwError(maybecx); } JSScript *script = parseTask->script; @@ -677,9 +680,12 @@ ExclusiveContext::setWorkerThread(WorkerThread *workerThread) frontend::CompileError & ExclusiveContext::addPendingCompileError() { - if (!workerThread->parseTask->errors.append(frontend::CompileError())) + frontend::CompileError *error = js_new(); + if (!error) MOZ_CRASH(); - return workerThread->parseTask->errors.back(); + if (!workerThread->parseTask->errors.append(error)) + MOZ_CRASH(); + return *error; } void diff --git a/js/src/jsworkers.h b/js/src/jsworkers.h index 52708acd8a7..d7f25850047 100644 --- a/js/src/jsworkers.h +++ b/js/src/jsworkers.h @@ -389,7 +389,7 @@ struct ParseTask // Any errors or warnings produced during compilation. These are reported // when finishing the script. - Vector errors; + Vector errors; ParseTask(ExclusiveContext *cx, const CompileOptions &options, const jschar *chars, size_t length, JSObject *scopeChain, diff --git a/js/src/perf/jsperf.h b/js/src/perf/jsperf.h index 468ce8609c1..da302a7dedc 100644 --- a/js/src/perf/jsperf.h +++ b/js/src/perf/jsperf.h @@ -6,7 +6,10 @@ #ifndef perf_jsperf_h #define perf_jsperf_h -#include "jsapi.h" +#include "jstypes.h" + +#include "js/TypeDecls.h" +#include "js/Utility.h" namespace JS { @@ -118,12 +121,12 @@ extern JS_FRIEND_API(JSObject*) RegisterPerfMeasurement(JSContext *cx, JSObject *global); /* - * Given a jsval which contains an instance of the aforementioned + * Given a Value which contains an instance of the aforementioned * wrapper class, extract the C++ object. Returns NULL if the - * jsval is not an instance of the wrapper. + * Value is not an instance of the wrapper. */ extern JS_FRIEND_API(PerfMeasurement*) - ExtractPerfMeasurement(jsval wrapper); + ExtractPerfMeasurement(Value wrapper); } // namespace JS diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index d745ea88429..89908b55639 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -953,8 +953,8 @@ TryNoteIter::settle() } } -#define PUSH_COPY(v) do { *regs.sp++ = v; assertSameCompartmentDebugOnly(cx, regs.sp[-1]); } while (0) -#define PUSH_COPY_SKIP_CHECK(v) *regs.sp++ = v +#define PUSH_COPY(v) do { *regs.sp++ = (v); assertSameCompartmentDebugOnly(cx, regs.sp[-1]); } while (0) +#define PUSH_COPY_SKIP_CHECK(v) *regs.sp++ = (v) #define PUSH_NULL() regs.sp++->setNull() #define PUSH_UNDEFINED() regs.sp++->setUndefined() #define PUSH_BOOLEAN(b) regs.sp++->setBoolean(b) @@ -964,13 +964,13 @@ TryNoteIter::settle() #define PUSH_OBJECT(obj) do { regs.sp++->setObject(obj); assertSameCompartmentDebugOnly(cx, regs.sp[-1]); } while (0) #define PUSH_OBJECT_OR_NULL(obj) do { regs.sp++->setObjectOrNull(obj); assertSameCompartmentDebugOnly(cx, regs.sp[-1]); } while (0) #define PUSH_HOLE() regs.sp++->setMagic(JS_ELEMENTS_HOLE) -#define POP_COPY_TO(v) v = *--regs.sp +#define POP_COPY_TO(v) (v) = *--regs.sp #define POP_RETURN_VALUE() regs.fp()->setReturnValue(*--regs.sp) #define FETCH_OBJECT(cx, n, obj) \ JS_BEGIN_MACRO \ HandleValue val = regs.stackHandleAt(n); \ - obj = ToObjectFromStack(cx, (val)); \ + obj = ToObjectFromStack((cx), (val)); \ if (!obj) \ goto error; \ JS_END_MACRO @@ -1277,35 +1277,19 @@ Interpret(JSContext *cx, RunState &state) register int switchMask = 0; int switchOp; -# define DO_OP() goto do_op +#define DO_OP() goto do_op -# define BEGIN_CASE(OP) case OP: -# define END_CASE(OP) END_CASE_LEN(OP##_LENGTH) -# define END_CASE_LEN(n) END_CASE_LENX(n) -# define END_CASE_LENX(n) END_CASE_LEN##n +#define BEGIN_CASE(OP) case OP: +#define END_CASE(OP) \ + JS_BEGIN_MACRO \ + len = OP##_LENGTH; \ + goto advanceAndDoOp; \ + JS_END_MACRO; -/* - * To share the code for all len == 1 cases we use the specialized label with - * code that falls through to advanceAndDoOp: . - */ -# define END_CASE_LEN1 goto advance_pc_by_one; -# define END_CASE_LEN2 len = 2; goto advanceAndDoOp; -# define END_CASE_LEN3 len = 3; goto advanceAndDoOp; -# define END_CASE_LEN4 len = 4; goto advanceAndDoOp; -# define END_CASE_LEN5 len = 5; goto advanceAndDoOp; -# define END_CASE_LEN6 len = 6; goto advanceAndDoOp; -# define END_CASE_LEN7 len = 7; goto advanceAndDoOp; -# define END_CASE_LEN8 len = 8; goto advanceAndDoOp; -# define END_CASE_LEN9 len = 9; goto advanceAndDoOp; -# define END_CASE_LEN10 len = 10; goto advanceAndDoOp; -# define END_CASE_LEN11 len = 11; goto advanceAndDoOp; -# define END_CASE_LEN12 len = 12; goto advanceAndDoOp; -# define END_VARLEN_CASE goto advanceAndDoOp; -# define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP) -# define END_EMPTY_CASES goto advance_pc_by_one; +#define END_VARLEN_CASE goto advanceAndDoOp; #define LOAD_DOUBLE(PCOFF, dbl) \ - (dbl = script->getConst(GET_UINT32_INDEX(regs.pc + (PCOFF))).toDouble()) + ((dbl) = script->getConst(GET_UINT32_INDEX(regs.pc + (PCOFF))).toDouble()) /* * Prepare to call a user-supplied branch handler, and abort the script @@ -1431,12 +1415,7 @@ Interpret(JSContext *cx, RunState &state) if (rt->profilingScripts || cx->runtime()->debugHooks.interruptHook) switchMask = -1; /* Enable interrupts. */ - goto advanceAndDoOp; - for (;;) { - advance_pc_by_one: - JS_ASSERT(js_CodeSpec[op].length == 1); - len = 1; advanceAndDoOp: js::gc::MaybeVerifyBarriers(cx); regs.pc += len; @@ -1518,51 +1497,55 @@ Interpret(JSContext *cx, RunState &state) goto do_switch; } -/* No-ops for ease of decompilation. */ -ADD_EMPTY_CASE(JSOP_NOP) -ADD_EMPTY_CASE(JSOP_UNUSED125) -ADD_EMPTY_CASE(JSOP_UNUSED126) -ADD_EMPTY_CASE(JSOP_UNUSED132) -ADD_EMPTY_CASE(JSOP_UNUSED148) -ADD_EMPTY_CASE(JSOP_UNUSED161) -ADD_EMPTY_CASE(JSOP_UNUSED162) -ADD_EMPTY_CASE(JSOP_UNUSED163) -ADD_EMPTY_CASE(JSOP_UNUSED164) -ADD_EMPTY_CASE(JSOP_UNUSED165) -ADD_EMPTY_CASE(JSOP_UNUSED166) -ADD_EMPTY_CASE(JSOP_UNUSED167) -ADD_EMPTY_CASE(JSOP_UNUSED168) -ADD_EMPTY_CASE(JSOP_UNUSED169) -ADD_EMPTY_CASE(JSOP_UNUSED170) -ADD_EMPTY_CASE(JSOP_UNUSED171) -ADD_EMPTY_CASE(JSOP_UNUSED172) -ADD_EMPTY_CASE(JSOP_UNUSED173) -ADD_EMPTY_CASE(JSOP_UNUSED174) -ADD_EMPTY_CASE(JSOP_UNUSED175) -ADD_EMPTY_CASE(JSOP_UNUSED176) -ADD_EMPTY_CASE(JSOP_UNUSED177) -ADD_EMPTY_CASE(JSOP_UNUSED178) -ADD_EMPTY_CASE(JSOP_UNUSED179) -ADD_EMPTY_CASE(JSOP_UNUSED180) -ADD_EMPTY_CASE(JSOP_UNUSED181) -ADD_EMPTY_CASE(JSOP_UNUSED182) -ADD_EMPTY_CASE(JSOP_UNUSED183) -ADD_EMPTY_CASE(JSOP_UNUSED188) -ADD_EMPTY_CASE(JSOP_UNUSED189) -ADD_EMPTY_CASE(JSOP_UNUSED190) -ADD_EMPTY_CASE(JSOP_UNUSED200) -ADD_EMPTY_CASE(JSOP_UNUSED201) -ADD_EMPTY_CASE(JSOP_UNUSED208) -ADD_EMPTY_CASE(JSOP_UNUSED209) -ADD_EMPTY_CASE(JSOP_UNUSED210) -ADD_EMPTY_CASE(JSOP_UNUSED219) -ADD_EMPTY_CASE(JSOP_UNUSED220) -ADD_EMPTY_CASE(JSOP_UNUSED221) -ADD_EMPTY_CASE(JSOP_UNUSED222) -ADD_EMPTY_CASE(JSOP_UNUSED223) -ADD_EMPTY_CASE(JSOP_CONDSWITCH) -ADD_EMPTY_CASE(JSOP_TRY) -END_EMPTY_CASES +/* Various 1-byte no-ops. */ +BEGIN_CASE(JSOP_NOP) +BEGIN_CASE(JSOP_UNUSED125) +BEGIN_CASE(JSOP_UNUSED126) +BEGIN_CASE(JSOP_UNUSED132) +BEGIN_CASE(JSOP_UNUSED148) +BEGIN_CASE(JSOP_UNUSED161) +BEGIN_CASE(JSOP_UNUSED162) +BEGIN_CASE(JSOP_UNUSED163) +BEGIN_CASE(JSOP_UNUSED164) +BEGIN_CASE(JSOP_UNUSED165) +BEGIN_CASE(JSOP_UNUSED166) +BEGIN_CASE(JSOP_UNUSED167) +BEGIN_CASE(JSOP_UNUSED168) +BEGIN_CASE(JSOP_UNUSED169) +BEGIN_CASE(JSOP_UNUSED170) +BEGIN_CASE(JSOP_UNUSED171) +BEGIN_CASE(JSOP_UNUSED172) +BEGIN_CASE(JSOP_UNUSED173) +BEGIN_CASE(JSOP_UNUSED174) +BEGIN_CASE(JSOP_UNUSED175) +BEGIN_CASE(JSOP_UNUSED176) +BEGIN_CASE(JSOP_UNUSED177) +BEGIN_CASE(JSOP_UNUSED178) +BEGIN_CASE(JSOP_UNUSED179) +BEGIN_CASE(JSOP_UNUSED180) +BEGIN_CASE(JSOP_UNUSED181) +BEGIN_CASE(JSOP_UNUSED182) +BEGIN_CASE(JSOP_UNUSED183) +BEGIN_CASE(JSOP_UNUSED188) +BEGIN_CASE(JSOP_UNUSED189) +BEGIN_CASE(JSOP_UNUSED190) +BEGIN_CASE(JSOP_UNUSED200) +BEGIN_CASE(JSOP_UNUSED201) +BEGIN_CASE(JSOP_UNUSED208) +BEGIN_CASE(JSOP_UNUSED209) +BEGIN_CASE(JSOP_UNUSED210) +BEGIN_CASE(JSOP_UNUSED219) +BEGIN_CASE(JSOP_UNUSED220) +BEGIN_CASE(JSOP_UNUSED221) +BEGIN_CASE(JSOP_UNUSED222) +BEGIN_CASE(JSOP_UNUSED223) +BEGIN_CASE(JSOP_CONDSWITCH) +BEGIN_CASE(JSOP_TRY) +{ + JS_ASSERT(js_CodeSpec[op].length == 1); + len = 1; + goto advanceAndDoOp; +} BEGIN_CASE(JSOP_LOOPHEAD) END_CASE(JSOP_LOOPHEAD) @@ -1605,7 +1588,6 @@ END_CASE(JSOP_LOOPENTRY) BEGIN_CASE(JSOP_NOTEARG) END_CASE(JSOP_NOTEARG) -/* ADD_EMPTY_CASE is not used here as JSOP_LINENO_LENGTH == 3. */ BEGIN_CASE(JSOP_LINENO) END_CASE(JSOP_LINENO) @@ -1772,17 +1754,17 @@ END_CASE(JSOP_AND) #define FETCH_ELEMENT_ID(n, id) \ JS_BEGIN_MACRO \ - if (!ValueToId(cx, regs.stackHandleAt(n), &id)) \ + if (!ValueToId(cx, regs.stackHandleAt(n), &(id))) \ goto error; \ JS_END_MACRO #define TRY_BRANCH_AFTER_COND(cond,spdec) \ JS_BEGIN_MACRO \ JS_ASSERT(js_CodeSpec[op].length == 1); \ - unsigned diff_ = (unsigned) GET_UINT8(regs.pc) - (unsigned) JSOP_IFEQ; \ + unsigned diff_ = (unsigned) GET_UINT8(regs.pc) - (unsigned) JSOP_IFEQ;\ if (diff_ <= 1) { \ - regs.sp -= spdec; \ - if (cond == (diff_ != 0)) { \ + regs.sp -= (spdec); \ + if ((cond) == (diff_ != 0)) { \ ++regs.pc; \ len = GET_JUMP_OFFSET(regs.pc); \ BRANCH(len); \ @@ -2004,7 +1986,7 @@ END_CASE(JSOP_NE) bool equal; \ if (!StrictlyEqual(cx, lref, rref, &equal)) \ goto error; \ - COND = equal OP true; \ + (COND) = equal OP true; \ regs.sp--; \ JS_END_MACRO diff --git a/js/src/vm/ObjectImpl.h b/js/src/vm/ObjectImpl.h index 68464c7a1ef..6a81377c0a5 100644 --- a/js/src/vm/ObjectImpl.h +++ b/js/src/vm/ObjectImpl.h @@ -14,6 +14,7 @@ #include "jsfriendapi.h" #include "jsinfer.h" +#include "NamespaceImports.h" #include "gc/Barrier.h" #include "gc/Heap.h" diff --git a/js/src/vm/Xdr.h b/js/src/vm/Xdr.h index f74445e4256..b15af9e89de 100644 --- a/js/src/vm/Xdr.h +++ b/js/src/vm/Xdr.h @@ -9,7 +9,6 @@ #include "mozilla/Endian.h" -#include "jsapi.h" #include "jsatom.h" namespace js { diff --git a/js/xpconnect/src/Sandbox.cpp b/js/xpconnect/src/Sandbox.cpp index 91b0b22517d..0700d723f6d 100644 --- a/js/xpconnect/src/Sandbox.cpp +++ b/js/xpconnect/src/Sandbox.cpp @@ -36,6 +36,7 @@ #include "mozilla/dom/BindingUtils.h" using namespace mozilla; +using namespace JS; using namespace js; using namespace xpc; diff --git a/js/xpconnect/src/XPCComponents.cpp b/js/xpconnect/src/XPCComponents.cpp index d37a5c2e5d2..87a9ea7c17f 100644 --- a/js/xpconnect/src/XPCComponents.cpp +++ b/js/xpconnect/src/XPCComponents.cpp @@ -41,6 +41,7 @@ #include "nsGlobalWindow.h" using namespace mozilla; +using namespace JS; using namespace js; using namespace xpc; @@ -3714,7 +3715,7 @@ nsXPCComponents::AttachComponentsObject(JSContext* aCx, MOZ_ASSERT(js::IsObjectInContextCompartment(global, aCx)); RootedId id(aCx, XPCJSRuntime::Get()->GetStringID(XPCJSRuntime::IDX_COMPONENTS)); - return JS_DefinePropertyById(aCx, global, id, js::ObjectValue(*components), + return JS_DefinePropertyById(aCx, global, id, JS::ObjectValue(*components), nullptr, nullptr, JSPROP_PERMANENT | JSPROP_READONLY); } diff --git a/js/xpconnect/wrappers/AccessCheck.cpp b/js/xpconnect/wrappers/AccessCheck.cpp index affa10b51c7..2460511cd69 100644 --- a/js/xpconnect/wrappers/AccessCheck.cpp +++ b/js/xpconnect/wrappers/AccessCheck.cpp @@ -24,6 +24,7 @@ #include "mozilla/dom/BindingUtils.h" using namespace mozilla; +using namespace JS; using namespace js; namespace xpc { diff --git a/js/xpconnect/wrappers/FilteringWrapper.cpp b/js/xpconnect/wrappers/FilteringWrapper.cpp index 7b35d79d982..3b861d06f36 100644 --- a/js/xpconnect/wrappers/FilteringWrapper.cpp +++ b/js/xpconnect/wrappers/FilteringWrapper.cpp @@ -15,6 +15,7 @@ #include "jsapi.h" +using namespace JS; using namespace js; namespace xpc { diff --git a/js/xpconnect/wrappers/WrapperFactory.cpp b/js/xpconnect/wrappers/WrapperFactory.cpp index e2c48ace044..5cd8cba891b 100644 --- a/js/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/xpconnect/wrappers/WrapperFactory.cpp @@ -20,6 +20,7 @@ #include "mozilla/Likely.h" #include "nsContentUtils.h" +using namespace JS; using namespace js; using namespace mozilla; diff --git a/media/omx-plugin/include/ics/stagefright/DataSource.h b/media/omx-plugin/include/ics/stagefright/DataSource.h index 81ef632415f..870d80571db 100644 --- a/media/omx-plugin/include/ics/stagefright/DataSource.h +++ b/media/omx-plugin/include/ics/stagefright/DataSource.h @@ -66,17 +66,6 @@ public: return 0; } -#if defined(MOZ_ANDROID_SONY_WORKAROUND) -private: - // Sony ICS devices have extra virtual methods in DataSource. - // The dummy methods below fill these vtable positions. - virtual void foo1() { abort(); } - virtual void foo2() { abort(); } - virtual void foo3() { abort(); } - virtual void foo4() { abort(); } -public: -#endif - virtual status_t reconnectAtOffset(off64_t offset) { return ERROR_UNSUPPORTED; } diff --git a/media/omx-plugin/include/ics/update.patch b/media/omx-plugin/include/ics/update.patch index 691c5506de2..fbf699b5542 100644 --- a/media/omx-plugin/include/ics/update.patch +++ b/media/omx-plugin/include/ics/update.patch @@ -20,22 +20,4 @@ index 81ef632..713af92 100644 public: enum Flags { kWantsPrefetching = 1, -@@ -66,17 +62,6 @@ public: - return 0; - } - --#if defined(MOZ_ANDROID_SONY_WORKAROUND) --private: -- // Sony ICS devices have extra virtual methods in DataSource. -- // The dummy methods below fill these vtable positions. -- virtual void foo1() { abort(); } -- virtual void foo2() { abort(); } -- virtual void foo3() { abort(); } -- virtual void foo4() { abort(); } --public: --#endif -- - virtual status_t reconnectAtOffset(off64_t offset) { - return ERROR_UNSUPPORTED; - } diff --git a/media/omx-plugin/jb-htc/Makefile.in b/media/omx-plugin/jb-htc/Makefile.in deleted file mode 100644 index 14c24eb7e6b..00000000000 --- a/media/omx-plugin/jb-htc/Makefile.in +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright 2012 Mozilla Foundation and Mozilla contributors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -DEPTH = @DEPTH@ -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ - -include $(DEPTH)/config/autoconf.mk - -MODULE_NAME = omxpluginjb-htc -FORCE_SHARED_LIB = 1 - -# Don't use STL wrappers; this isn't Gecko code -STL_FLAGS = - -# must link statically with the CRT; this isn't Gecko code -USE_STATIC_LIBS = 1 - -# Need to custom install OMX media plugin -NO_INSTALL = 1 - -include $(topsrcdir)/config/rules.mk - -ifdef GNU_CXX -# Turn off C++ 11 features due to conflicts with Android OS headers and char16_t definition -CXXFLAGS += -std=gnu++98 -endif - -INCLUDES += \ - -I$(srcdir)/../../../content/media/plugins \ - $(NULL) - -EXTRA_DSO_LDOPTS += \ - -L$(DEPTH)/media/omx-plugin/lib/ics/libutils \ - -lutils \ - -L$(DEPTH)/media/omx-plugin/lib/ics/libstagefright \ - -lstagefright \ - $(NULL) - -INCLUDES += \ - -I$(srcdir)/../include/ics \ - -I$(srcdir)/../include/ics/media/stagefright/openmax \ - $(NULL) - -libs:: $(DLL_PREFIX)$(LIBRARY_NAME)$(DLL_SUFFIX) - $(INSTALL) $< $(DEPTH)/dist/bin - -libs:: $(PROGRAMS) diff --git a/media/omx-plugin/jb-htc/OmxPluginJB-HTC.cpp b/media/omx-plugin/jb-htc/OmxPluginJB-HTC.cpp deleted file mode 100644 index 5abc4cbda24..00000000000 --- a/media/omx-plugin/jb-htc/OmxPluginJB-HTC.cpp +++ /dev/null @@ -1,9 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* 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/. */ -#define MOZ_STAGEFRIGHT_OFF_T off64_t -#define MOZ_ANDROID_ICS -#define MOZ_ANDROID_HTC_WORKAROUND -#include "../OmxPlugin.cpp" diff --git a/media/omx-plugin/jb-htc/moz.build b/media/omx-plugin/jb-htc/moz.build deleted file mode 100644 index 31c27eca758..00000000000 --- a/media/omx-plugin/jb-htc/moz.build +++ /dev/null @@ -1,15 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# 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/. - -MODULE = 'omxpluginjb-htc' -NO_DIST_INSTALL = True - -CPP_SOURCES += [ - 'OmxPluginJB-HTC.cpp', -] - -LIBRARY_NAME = 'omxpluginjb-htc' - diff --git a/media/omx-plugin/sony/Makefile.in b/media/omx-plugin/sony/Makefile.in deleted file mode 100644 index 13a4de0cceb..00000000000 --- a/media/omx-plugin/sony/Makefile.in +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright 2012 Mozilla Foundation and Mozilla contributors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -DEPTH = @DEPTH@ -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ - -include $(DEPTH)/config/autoconf.mk - -MODULE_NAME = omxpluginsony -FORCE_SHARED_LIB = 1 - -# Don't use STL wrappers; this isn't Gecko code -STL_FLAGS = - -# must link statically with the CRT; this isn't Gecko code -USE_STATIC_LIBS = 1 - -# Need to custom install OMX media plugin -NO_INSTALL = 1 - -include $(topsrcdir)/config/rules.mk - -ifdef GNU_CXX -# Turn off C++ 11 features due to conflicts with Android OS headers and char16_t definition -CXXFLAGS += -std=gnu++98 -endif - -INCLUDES += \ - -I$(srcdir)/../../../content/media/plugins \ - $(NULL) - -EXTRA_DSO_LDOPTS += \ - -L$(DEPTH)/media/omx-plugin/lib/ics/libutils \ - -lutils \ - -L$(DEPTH)/media/omx-plugin/lib/ics/libstagefright \ - -lstagefright \ - $(NULL) - -INCLUDES += \ - -I$(srcdir)/../include/ics \ - -I$(srcdir)/../include/ics/media/stagefright/openmax \ - $(NULL) - -libs:: $(DLL_PREFIX)$(LIBRARY_NAME)$(DLL_SUFFIX) - $(INSTALL) $< $(DEPTH)/dist/bin - -libs:: $(PROGRAMS) diff --git a/media/omx-plugin/sony/OmxPluginHoneycomb.cpp b/media/omx-plugin/sony/OmxPluginHoneycomb.cpp deleted file mode 100644 index 36e8adefcdc..00000000000 --- a/media/omx-plugin/sony/OmxPluginHoneycomb.cpp +++ /dev/null @@ -1,10 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* 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/. */ -#define MOZ_STAGEFRIGHT_OFF_T off64_t -#define MOZ_ANDROID_GB -#define MOZ_ANDROID_HC -#include -#include "../OmxPlugin.cpp" diff --git a/media/omx-plugin/sony/OmxPluginSony.cpp b/media/omx-plugin/sony/OmxPluginSony.cpp deleted file mode 100644 index 8afbd9d4ed5..00000000000 --- a/media/omx-plugin/sony/OmxPluginSony.cpp +++ /dev/null @@ -1,9 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* 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/. */ -#define MOZ_STAGEFRIGHT_OFF_T off64_t -#define MOZ_ANDROID_ICS -#define MOZ_ANDROID_SONY_WORKAROUND -#include "../OmxPlugin.cpp" diff --git a/media/omx-plugin/sony/moz.build b/media/omx-plugin/sony/moz.build deleted file mode 100644 index e9db358c6eb..00000000000 --- a/media/omx-plugin/sony/moz.build +++ /dev/null @@ -1,15 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# 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/. - -MODULE = 'omxpluginsony' -NO_DIST_INSTALL = True - -CPP_SOURCES += [ - 'OmxPluginSony.cpp', -] - -LIBRARY_NAME = 'omxpluginsony' - diff --git a/mfbt/tests/TestFloatingPoint.cpp b/mfbt/tests/TestFloatingPoint.cpp index 6e16cba18d0..b2798fdc202 100644 --- a/mfbt/tests/TestFloatingPoint.cpp +++ b/mfbt/tests/TestFloatingPoint.cpp @@ -8,6 +8,11 @@ #include using mozilla::DoublesAreIdentical; +using mozilla::IsFinite; +using mozilla::IsInfinite; +using mozilla::IsNaN; +using mozilla::IsNegative; +using mozilla::IsNegativeZero; using mozilla::NegativeInfinity; using mozilla::PositiveInfinity; using mozilla::SpecificNaN; @@ -97,8 +102,54 @@ TestDoublesAreIdentical() ShouldNotBeIdentical(UnspecifiedNaN(), NegativeInfinity()); } +static void +TestPredicates() +{ + MOZ_ASSERT(IsNaN(UnspecifiedNaN())); + MOZ_ASSERT(IsNaN(SpecificNaN(1, 17)));; + MOZ_ASSERT(IsNaN(SpecificNaN(0, 0xfffffffffff0fULL))); + MOZ_ASSERT(!IsNaN(0)); + MOZ_ASSERT(!IsNaN(-0.0)); + MOZ_ASSERT(!IsNaN(1.0)); + MOZ_ASSERT(!IsNaN(PositiveInfinity())); + MOZ_ASSERT(!IsNaN(NegativeInfinity())); + + MOZ_ASSERT(IsInfinite(PositiveInfinity())); + MOZ_ASSERT(IsInfinite(NegativeInfinity())); + MOZ_ASSERT(!IsInfinite(UnspecifiedNaN())); + MOZ_ASSERT(!IsInfinite(0)); + MOZ_ASSERT(!IsInfinite(-0.0)); + MOZ_ASSERT(!IsInfinite(1.0)); + + MOZ_ASSERT(!IsFinite(PositiveInfinity())); + MOZ_ASSERT(!IsFinite(NegativeInfinity())); + MOZ_ASSERT(!IsFinite(UnspecifiedNaN())); + MOZ_ASSERT(IsFinite(0)); + MOZ_ASSERT(IsFinite(-0.0)); + MOZ_ASSERT(IsFinite(1.0)); + + MOZ_ASSERT(!IsNegative(PositiveInfinity())); + MOZ_ASSERT(IsNegative(NegativeInfinity())); + MOZ_ASSERT(IsNegative(-0.0)); + MOZ_ASSERT(!IsNegative(0.0)); + MOZ_ASSERT(IsNegative(-1.0)); + MOZ_ASSERT(!IsNegative(1.0)); + + MOZ_ASSERT(!IsNegativeZero(PositiveInfinity())); + MOZ_ASSERT(!IsNegativeZero(NegativeInfinity())); + MOZ_ASSERT(!IsNegativeZero(SpecificNaN(1, 17)));; + MOZ_ASSERT(!IsNegativeZero(SpecificNaN(1, 0xfffffffffff0fULL))); + MOZ_ASSERT(!IsNegativeZero(SpecificNaN(0, 17)));; + MOZ_ASSERT(!IsNegativeZero(SpecificNaN(0, 0xfffffffffff0fULL))); + MOZ_ASSERT(IsNegativeZero(-0.0)); + MOZ_ASSERT(!IsNegativeZero(0.0)); + MOZ_ASSERT(!IsNegativeZero(-1.0)); + MOZ_ASSERT(!IsNegativeZero(1.0)); +} + int main() { TestDoublesAreIdentical(); + TestPredicates(); } diff --git a/mobile/android/installer/package-manifest.in b/mobile/android/installer/package-manifest.in index 38f58c827c8..a1d49c02bb1 100644 --- a/mobile/android/installer/package-manifest.in +++ b/mobile/android/installer/package-manifest.in @@ -44,8 +44,6 @@ @BINPATH@/@DLL_PREFIX@omxplugingb@DLL_SUFFIX@ @BINPATH@/@DLL_PREFIX@omxplugingb235@DLL_SUFFIX@ @BINPATH@/@DLL_PREFIX@omxpluginhc@DLL_SUFFIX@ -@BINPATH@/@DLL_PREFIX@omxpluginjb-htc@DLL_SUFFIX@ -@BINPATH@/@DLL_PREFIX@omxpluginsony@DLL_SUFFIX@ @BINPATH@/@DLL_PREFIX@omxpluginfroyo@DLL_SUFFIX@ @BINPATH@/@DLL_PREFIX@xul@DLL_SUFFIX@ diff --git a/python/mozbuild/mozbuild/frontend/emitter.py b/python/mozbuild/mozbuild/frontend/emitter.py index 7f172215862..4979e8b02c5 100644 --- a/python/mozbuild/mozbuild/frontend/emitter.py +++ b/python/mozbuild/mozbuild/frontend/emitter.py @@ -118,17 +118,21 @@ class TreeMetadataEmitter(LoggingMixin): CPP_UNIT_TESTS='CPP_UNIT_TESTS', CSRCS='CSRCS', DEFINES='DEFINES', + EXPORT_LIBRARY='EXPORT_LIBRARY', EXTRA_COMPONENTS='EXTRA_COMPONENTS', EXTRA_JS_MODULES='EXTRA_JS_MODULES', EXTRA_PP_COMPONENTS='EXTRA_PP_COMPONENTS', EXTRA_PP_JS_MODULES='EXTRA_PP_JS_MODULES', FAIL_ON_WARNINGS='FAIL_ON_WARNINGS', + FORCE_SHARED_LIB='FORCE_SHARED_LIB', + FORCE_STATIC_LIB='FORCE_STATIC_LIB', GTEST_CMMSRCS='GTEST_CMM_SOURCES', GTEST_CPPSRCS='GTEST_CPP_SOURCES', GTEST_CSRCS='GTEST_C_SOURCES', HOST_CPPSRCS='HOST_CPPSRCS', HOST_CSRCS='HOST_CSRCS', HOST_LIBRARY_NAME='HOST_LIBRARY_NAME', + IS_COMPONENT='IS_COMPONENT', JS_MODULES_PATH='JS_MODULES_PATH', LIBRARY_NAME='LIBRARY_NAME', LIBS='LIBS', diff --git a/python/mozbuild/mozbuild/frontend/sandbox_symbols.py b/python/mozbuild/mozbuild/frontend/sandbox_symbols.py index 32f1fcc4352..70ec4d9d7f2 100644 --- a/python/mozbuild/mozbuild/frontend/sandbox_symbols.py +++ b/python/mozbuild/mozbuild/frontend/sandbox_symbols.py @@ -98,6 +98,10 @@ VARIABLES = { delimiters. """), + 'EXPORT_LIBRARY': (bool, bool, False, + """Install the library to the static libraries folder. + """), + 'EXTRA_COMPONENTS': (StrictOrderingOnAppendList, list, [], """Additional component files to distribute. @@ -138,6 +142,14 @@ VARIABLES = { """Whether to treat warnings as errors. """), + 'FORCE_SHARED_LIB': (bool, bool, False, + """Whether the library in this directory is a shared library. + """), + + 'FORCE_STATIC_LIB': (bool, bool, False, + """Whether the library in this directory is a static library. + """), + 'GTEST_C_SOURCES': (StrictOrderingOnAppendList, list, [], """C code source files for GTest unit tests. @@ -171,6 +183,10 @@ VARIABLES = { This variable contains a list of C source files to compile. """), + 'IS_COMPONENT': (bool, bool, False, + """Whether the library contains a binary XPCOM component manifest. + """), + 'PARALLEL_DIRS': (list, list, [], """A parallel version of DIRS. @@ -212,7 +228,7 @@ VARIABLES = { 'LIBXUL_LIBRARY': (bool, bool, False, """Whether the library in this directory is linked into libxul. - Implies MOZILLA_INTERNAL_API. + Implies MOZILLA_INTERNAL_API and FORCE_STATIC_LIB. """), 'MSVC_ENABLE_PGO': (bool, bool, False, diff --git a/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/moz.build b/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/moz.build index 16a0a48d391..2dd7f0fd5a1 100644 --- a/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/moz.build +++ b/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/moz.build @@ -45,3 +45,8 @@ LIBXUL_LIBRARY = True MSVC_ENABLE_PGO = True NO_DIST_INSTALL = True + +FORCE_SHARED_LIB = True +FORCE_STATIC_LIB = True +EXPORT_LIBRARY = True +IS_COMPONENT = True diff --git a/python/mozbuild/mozbuild/test/frontend/test_emitter.py b/python/mozbuild/mozbuild/test/frontend/test_emitter.py index 3986f492388..85da16795f8 100644 --- a/python/mozbuild/mozbuild/test/frontend/test_emitter.py +++ b/python/mozbuild/mozbuild/test/frontend/test_emitter.py @@ -132,17 +132,21 @@ class TestEmitterBasic(unittest.TestCase): CSRCS=['fans.c', 'tans.c'], CPP_UNIT_TESTS=['foo.cpp'], DEFINES=['-Dfans', '-Dtans'], + EXPORT_LIBRARY=True, EXTRA_COMPONENTS=['fans.js', 'tans.js'], EXTRA_PP_COMPONENTS=['fans.pp.js', 'tans.pp.js'], EXTRA_JS_MODULES=['bar.jsm', 'foo.jsm'], EXTRA_PP_JS_MODULES=['bar.pp.jsm', 'foo.pp.jsm'], FAIL_ON_WARNINGS=True, + FORCE_SHARED_LIB=True, + FORCE_STATIC_LIB=True, GTEST_CSRCS=['test1.c', 'test2.c'], GTEST_CMMSRCS=['test1.mm', 'test2.mm'], GTEST_CPPSRCS=['test1.cpp', 'test2.cpp'], HOST_CPPSRCS=['fans.cpp', 'tans.cpp'], HOST_CSRCS=['fans.c', 'tans.c'], HOST_LIBRARY_NAME='host_fans', + IS_COMPONENT=True, LIBRARY_NAME='lib_name', LIBS=['fans.lib', 'tans.lib'], LIBXUL_LIBRARY=True, diff --git a/testing/xpcshell/head.js b/testing/xpcshell/head.js index 6da236eba5d..356f33fba35 100644 --- a/testing/xpcshell/head.js +++ b/testing/xpcshell/head.js @@ -184,15 +184,15 @@ function _do_quit() { } function _format_exception_stack(stack) { - // frame is of the form "fname(args)@file:line" - let frame_regexp = new RegExp("(.*)\\(.*\\)@(.*):(\\d*)", "g"); + // frame is of the form "fname@file:line" + let frame_regexp = new RegExp("(.*)@(.*):(\\d*)", "g"); return stack.split("\n").reduce(function(stack_msg, frame) { if (frame) { let parts = frame_regexp.exec(frame); if (parts) { - return stack_msg + "JS frame :: " + parts[2] + " :: " + - (parts[1] ? parts[1] : "anonymous") + - " :: line " + parts[3] + "\n"; + let [ _, func, file, line ] = parts; + return stack_msg + "JS frame :: " + file + " :: " + + (func || "anonymous") + " :: line " + line + "\n"; } else { /* Could be a -e (command line string) style location. */ return stack_msg + "JS frame :: " + frame + "\n"; diff --git a/toolkit/mozapps/installer/packager.mk b/toolkit/mozapps/installer/packager.mk index c5073757b84..8057bf716b1 100644 --- a/toolkit/mozapps/installer/packager.mk +++ b/toolkit/mozapps/installer/packager.mk @@ -345,7 +345,7 @@ INNER_ROBOCOP_PACKAGE=echo 'Testing is disabled - No Robocop for you' endif ifdef MOZ_OMX_PLUGIN -DIST_FILES += libomxplugin.so libomxplugingb.so libomxplugingb235.so libomxpluginhc.so libomxpluginsony.so libomxpluginfroyo.so libomxpluginjb-htc.so +DIST_FILES += libomxplugin.so libomxplugingb.so libomxplugingb235.so libomxpluginhc.so libomxpluginfroyo.so endif SO_LIBRARIES := $(filter %.so,$(DIST_FILES)) diff --git a/toolkit/toolkit.mozbuild b/toolkit/toolkit.mozbuild index d29740e1aeb..fc0bf8a1f26 100644 --- a/toolkit/toolkit.mozbuild +++ b/toolkit/toolkit.mozbuild @@ -86,8 +86,6 @@ if CONFIG['MOZ_OMX_PLUGIN']: 'media/omx-plugin/froyo', 'media/omx-plugin/lib/hc/libstagefright', 'media/omx-plugin/hc', - 'media/omx-plugin/sony', - 'media/omx-plugin/jb-htc', ]) if CONFIG['ENABLE_TESTS']: diff --git a/widget/android/GfxInfo.cpp b/widget/android/GfxInfo.cpp index d6527052a52..4e198775b1c 100644 --- a/widget/android/GfxInfo.cpp +++ b/widget/android/GfxInfo.cpp @@ -351,7 +351,6 @@ GfxInfo::GetFeatureStatusImpl(int32_t aFeature, NS_LossyConvertUTF16toASCII cHardware(mHardware); if (cHardware.Equals("antares") || - cHardware.Equals("endeavoru") || cHardware.Equals("harmony") || cHardware.Equals("picasso") || cHardware.Equals("picasso_e") || @@ -381,10 +380,11 @@ GfxInfo::GetFeatureStatusImpl(int32_t aFeature, // Gingerbread HTC devices are whitelisted. // Gingerbread Samsung devices are whitelisted except for: // Samsung devices identified in Bug 847837 - // Samsung SGH-T989 (Bug 818363) + // Gingerbread Sony devices are whitelisted. // All other Gingerbread devices are blacklisted. bool isWhitelisted = cManufacturer.Equals("htc", nsCaseInsensitiveCStringComparator()) || + (cManufacturer.Find("sony", true) != -1) || cManufacturer.Equals("samsung", nsCaseInsensitiveCStringComparator()); if (cModel.Equals("GT-I8160", nsCaseInsensitiveCStringComparator()) || @@ -397,7 +397,6 @@ GfxInfo::GetFeatureStatusImpl(int32_t aFeature, cModel.Equals("GT-S7500T", nsCaseInsensitiveCStringComparator()) || cModel.Equals("GT-S7500L", nsCaseInsensitiveCStringComparator()) || cModel.Equals("GT-S6500T", nsCaseInsensitiveCStringComparator()) || - cModel.Equals("SGH-T989", nsCaseInsensitiveCStringComparator()) || cHardware.Equals("smdkc110", nsCaseInsensitiveCStringComparator()) || cHardware.Equals("smdkc210", nsCaseInsensitiveCStringComparator()) || cHardware.Equals("herring", nsCaseInsensitiveCStringComparator()) || @@ -442,7 +441,6 @@ GfxInfo::GetFeatureStatusImpl(int32_t aFeature, // Samsung SGH-I717 (Bug 845729) // Samsung SGH-I727 (Bug 845729) // Samsung SGH-I757 (Bug 845729) - // Samsung SGH-T989 (Bug 845729) // All Galaxy nexus ICS devices // Sony Xperia Ion (LT28) ICS devices bool isWhitelisted = @@ -452,8 +450,7 @@ GfxInfo::GetFeatureStatusImpl(int32_t aFeature, if (cModel.Find("SGH-I717", true) != -1 || cModel.Find("SGH-I727", true) != -1 || - cModel.Find("SGH-I757", true) != -1 || - cModel.Find("SGH-T989", true) != -1) + cModel.Find("SGH-I757", true) != -1) { isWhitelisted = false; } @@ -470,8 +467,6 @@ GfxInfo::GetFeatureStatusImpl(int32_t aFeature, // Blocklist: // Samsung devices from bug 812881 and 853522. // Motorola XT890 from bug 882342. - // All Sony devices (Bug 845734) - bool isBlocklisted = cModel.Find("GT-P3100", true) != -1 || cModel.Find("GT-P3110", true) != -1 || @@ -479,8 +474,7 @@ GfxInfo::GetFeatureStatusImpl(int32_t aFeature, cModel.Find("GT-P5100", true) != -1 || cModel.Find("GT-P5110", true) != -1 || cModel.Find("GT-P5113", true) != -1 || - cModel.Find("XT890", true) != -1 || - cManufacturer.Find("Sony", true) != -1; + cModel.Find("XT890", true) != -1; if (isBlocklisted) { *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE; diff --git a/xpcom/glue/FileUtils.cpp b/xpcom/glue/FileUtils.cpp index 25f1744426a..bc531308c2c 100644 --- a/xpcom/glue/FileUtils.cpp +++ b/xpcom/glue/FileUtils.cpp @@ -450,7 +450,7 @@ mozilla::ReadAheadLib(mozilla::pathstr_t aFilePath) // information to find the biggest offset from the library that // will be mapped in memory. char *cmd = &base[sizeof(struct cpu_mach_header)]; - off_t end = 0; + uint32_t end = 0; for (uint32_t ncmds = mh->ncmds; ncmds; ncmds--) { struct segment_command *sh = (struct segment_command *)cmd; if (sh->cmd != LC_SEGMENT) {