Bug 1312808 - rewrite stored WebAssembly.Module when build-id changes; r=asuth,luke

This commit is contained in:
Jan Varga 2016-12-08 15:37:13 -10:00
Родитель b2e95c2ac2
Коммит d80cf26c97
12 изменённых файлов: 873 добавлений и 361 удалений

Просмотреть файл

@ -689,7 +689,8 @@ DeserializeStructuredCloneFiles(
break;
}
case StructuredCloneFile::eWasmBytecode: {
case StructuredCloneFile::eWasmBytecode:
case StructuredCloneFile::eWasmCompiled: {
if (aModuleSet) {
MOZ_ASSERT(blobOrMutableFile.type() == BlobOrMutableFile::Tnull_t);
@ -701,6 +702,10 @@ DeserializeStructuredCloneFiles(
MOZ_ASSERT(moduleIndex < aModuleSet->Length());
file->mWasmModule = aModuleSet->ElementAt(moduleIndex);
if (serializedFile.type() == StructuredCloneFile::eWasmCompiled) {
moduleIndex++;
}
break;
}
@ -720,68 +725,12 @@ DeserializeStructuredCloneFiles(
StructuredCloneFile* file = aFiles.AppendElement();
MOZ_ASSERT(file);
file->mType = StructuredCloneFile::eWasmBytecode;
file->mType = serializedFile.type();
file->mBlob.swap(blob);
break;
}
case StructuredCloneFile::eWasmCompiled: {
if (aModuleSet) {
MOZ_ASSERT(blobOrMutableFile.type() == BlobOrMutableFile::Tnull_t);
StructuredCloneFile* file = aFiles.AppendElement();
MOZ_ASSERT(file);
file->mType = serializedFile.type();
MOZ_ASSERT(moduleIndex < aModuleSet->Length());
file->mWasmModule = aModuleSet->ElementAt(moduleIndex++);
break;
}
MOZ_ASSERT(blobOrMutableFile.type() == BlobOrMutableFile::Tnull_t ||
blobOrMutableFile.type() ==
BlobOrMutableFile::TPBlobChild);
switch (blobOrMutableFile.type()) {
case BlobOrMutableFile::Tnull_t: {
StructuredCloneFile* file = aFiles.AppendElement();
MOZ_ASSERT(file);
file->mType = StructuredCloneFile::eWasmCompiled;
break;
}
case BlobOrMutableFile::TPBlobChild: {
auto* actor =
static_cast<BlobChild*>(blobOrMutableFile.get_PBlobChild());
RefPtr<BlobImpl> blobImpl = actor->GetBlobImpl();
MOZ_ASSERT(blobImpl);
RefPtr<Blob> blob = Blob::Create(aDatabase->GetOwner(), blobImpl);
aDatabase->NoteReceivedBlob(blob);
StructuredCloneFile* file = aFiles.AppendElement();
MOZ_ASSERT(file);
file->mType = StructuredCloneFile::eWasmCompiled;
file->mBlob.swap(blob);
break;
}
default:
MOZ_CRASH("Should never get here!");
}
break;
}
default:
MOZ_CRASH("Should never get here!");
}
@ -3015,6 +2964,7 @@ PreprocessHelper::Init(const nsTArray<StructuredCloneFile>& aFiles)
MOZ_ASSERT(bytecodeFile.mType == StructuredCloneFile::eWasmBytecode);
MOZ_ASSERT(bytecodeFile.mBlob);
MOZ_ASSERT(compiledFile.mType == StructuredCloneFile::eWasmCompiled);
MOZ_ASSERT(compiledFile.mBlob);
ErrorResult errorResult;
@ -3026,12 +2976,10 @@ PreprocessHelper::Init(const nsTArray<StructuredCloneFile>& aFiles)
}
nsCOMPtr<nsIInputStream> compiledStream;
if (compiledFile.mBlob) {
compiledFile.mBlob->GetInternalStream(getter_AddRefs(compiledStream),
errorResult);
if (NS_WARN_IF(errorResult.Failed())) {
return errorResult.StealNSResult();
}
compiledFile.mBlob->GetInternalStream(getter_AddRefs(compiledStream),
errorResult);
if (NS_WARN_IF(errorResult.Failed())) {
return errorResult.StealNSResult();
}
streamPairs.AppendElement(StreamPair(bytecodeStream, compiledStream));
@ -3104,14 +3052,11 @@ PreprocessHelper::RunOnStreamTransportThread()
const nsCOMPtr<nsIInputStream>& compiledStream = streamPair.second;
PRFileDesc* compiledFileDesc;
if (compiledStream) {
compiledFileDesc = GetFileDescriptorFromStream(compiledStream);
if (NS_WARN_IF(!bytecodeFileDesc)) {
return NS_ERROR_FAILURE;
}
} else {
compiledFileDesc = nullptr;
MOZ_ASSERT(compiledStream);
PRFileDesc* compiledFileDesc = GetFileDescriptorFromStream(compiledStream);
if (NS_WARN_IF(!compiledFileDesc)) {
return NS_ERROR_FAILURE;
}
JS::BuildIdCharVector buildId;

Просмотреть файл

@ -92,6 +92,7 @@
#include "nsRefPtrHashtable.h"
#include "nsStreamUtils.h"
#include "nsString.h"
#include "nsStringStream.h"
#include "nsThreadPool.h"
#include "nsThreadUtils.h"
#include "nsXPCOMCID.h"
@ -8196,12 +8197,6 @@ private:
nsresult
RemoveOldIndexDataValues(DatabaseConnection* aConnection);
nsresult
CopyFileData(nsIInputStream* aInputStream,
nsIOutputStream* aOutputStream,
char* aBuffer,
uint32_t aBufferSize);
bool
Init(TransactionBase* aTransaction) override;
@ -9587,6 +9582,60 @@ private:
#endif // DEBUG
/*******************************************************************************
* Helper classes
******************************************************************************/
class MOZ_STACK_CLASS FileHelper final
{
RefPtr<FileManager> mFileManager;
nsCOMPtr<nsIFile> mFileDirectory;
nsCOMPtr<nsIFile> mJournalDirectory;
public:
explicit FileHelper(FileManager* aFileManager)
: mFileManager(aFileManager)
{ }
nsresult
Init();
already_AddRefed<nsIFile>
GetFile(FileInfo* aFileInfo);
already_AddRefed<nsIFile>
GetCheckedFile(FileInfo* aFileInfo);
already_AddRefed<nsIFile>
GetJournalFile(FileInfo* aFileInfo);
nsresult
CreateFileFromStream(nsIFile* aFile,
nsIFile* aJournalFile,
nsIInputStream* aInputStream,
bool aCompress);
nsresult
ReplaceFile(nsIFile* aFile,
nsIFile* aNewFile,
nsIFile* aNewJournalFile);
nsresult
RemoveFile(nsIFile* aFile,
nsIFile* aJournalFile);
already_AddRefed<FileInfo>
GetNewFileInfo();
private:
nsresult
SyncCopy(nsIInputStream* aInputStream,
nsIOutputStream* aOutputStream,
char* aBuffer,
uint32_t aBufferSize);
};
/*******************************************************************************
* Helper Functions
******************************************************************************/
@ -9651,6 +9700,139 @@ DeserializeStructuredCloneFile(FileManager* aFileManager,
return NS_OK;
}
nsresult
CheckWasmModule(FileHelper* aFileHelper,
StructuredCloneFile* aBytecodeFile,
StructuredCloneFile* aCompiledFile)
{
MOZ_ASSERT(!IsOnBackgroundThread());
MOZ_ASSERT(aFileHelper);
MOZ_ASSERT(aBytecodeFile);
MOZ_ASSERT(aCompiledFile);
MOZ_ASSERT(aBytecodeFile->mType == StructuredCloneFile::eWasmBytecode);
MOZ_ASSERT(aCompiledFile->mType == StructuredCloneFile::eWasmCompiled);
nsCOMPtr<nsIFile> compiledFile =
aFileHelper->GetCheckedFile(aCompiledFile->mFileInfo);
if (NS_WARN_IF(!compiledFile)) {
return NS_ERROR_FAILURE;
}
nsresult rv;
bool match;
{
ScopedPRFileDesc compiledFileDesc;
rv = compiledFile->OpenNSPRFileDesc(PR_RDONLY,
0644,
&compiledFileDesc.rwget());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
JS::BuildIdCharVector buildId;
bool ok = GetBuildId(&buildId);
if (NS_WARN_IF(!ok)) {
return NS_ERROR_FAILURE;
}
match = JS::CompiledWasmModuleAssumptionsMatch(compiledFileDesc,
Move(buildId));
}
if (match) {
return NS_OK;
}
// Re-compile the module. It would be preferable to do this in the child
// (content process) instead of here in the parent, but that would be way more
// complex and without significant memory allocation or security benefits.
// See the discussion starting from
// https://bugzilla.mozilla.org/show_bug.cgi?id=1312808#c9 for more details.
nsCOMPtr<nsIFile> bytecodeFile =
aFileHelper->GetCheckedFile(aBytecodeFile->mFileInfo);
if (NS_WARN_IF(!bytecodeFile)) {
return NS_ERROR_FAILURE;
}
ScopedPRFileDesc bytecodeFileDesc;
rv = bytecodeFile->OpenNSPRFileDesc(PR_RDONLY,
0644,
&bytecodeFileDesc.rwget());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
JS::BuildIdCharVector buildId;
bool ok = GetBuildId(&buildId);
if (NS_WARN_IF(!ok)) {
return NS_ERROR_FAILURE;
}
RefPtr<JS::WasmModule> module = JS::DeserializeWasmModule(bytecodeFileDesc,
nullptr,
Move(buildId),
nullptr,
0,
0);
if (NS_WARN_IF(!module)) {
return NS_ERROR_FAILURE;
}
size_t compiledSize;
module->serializedSize(nullptr, &compiledSize);
UniquePtr<uint8_t[]> compiled(new (fallible) uint8_t[compiledSize]);
if (NS_WARN_IF(!compiled)) {
return NS_ERROR_OUT_OF_MEMORY;
}
module->serialize(nullptr, 0, compiled.get(), compiledSize);
nsCOMPtr<nsIInputStream> inputStream;
rv = NS_NewByteInputStream(getter_AddRefs(inputStream),
reinterpret_cast<const char*>(compiled.get()),
compiledSize);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
RefPtr<FileInfo> newFileInfo = aFileHelper->GetNewFileInfo();
nsCOMPtr<nsIFile> newFile = aFileHelper->GetFile(newFileInfo);
if (NS_WARN_IF(!newFile)) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIFile> newJournalFile =
aFileHelper->GetJournalFile(newFileInfo);
if (NS_WARN_IF(!newJournalFile)) {
return NS_ERROR_FAILURE;
}
rv = aFileHelper->CreateFileFromStream(newFile,
newJournalFile,
inputStream,
/* aCompress */ false);
if (NS_WARN_IF(NS_FAILED(rv))) {
nsresult rv2 = aFileHelper->RemoveFile(newFile, newJournalFile);
if (NS_WARN_IF(NS_FAILED(rv2))) {
return rv;
}
return rv;
}
rv = aFileHelper->ReplaceFile(compiledFile, newFile, newJournalFile);
if (NS_WARN_IF(NS_FAILED(rv))) {
nsresult rv2 = aFileHelper->RemoveFile(newFile, newJournalFile);
if (NS_WARN_IF(NS_FAILED(rv2))) {
return rv;
}
return rv;
}
return NS_OK;
}
nsresult
DeserializeStructuredCloneFiles(FileManager* aFileManager,
const nsAString& aText,
@ -9658,14 +9840,13 @@ DeserializeStructuredCloneFiles(FileManager* aFileManager,
bool* aHasPreprocessInfo)
{
MOZ_ASSERT(!IsOnBackgroundThread());
MOZ_ASSERT(aHasPreprocessInfo);
nsCharSeparatedTokenizerTemplate<TokenizerIgnoreNothing>
tokenizer(aText, ' ');
nsAutoString token;
nsresult rv;
nsCOMPtr<nsIFile> directory;
Maybe<FileHelper> fileHelper;
while (tokenizer.hasMoreTokens()) {
token = tokenizer.nextToken();
@ -9677,44 +9858,34 @@ DeserializeStructuredCloneFiles(FileManager* aFileManager,
return rv;
}
if (file->mType == StructuredCloneFile::eWasmBytecode) {
MOZ_ASSERT(file->mValid);
if (!aHasPreprocessInfo) {
continue;
}
if (file->mType == StructuredCloneFile::eWasmBytecode) {
*aHasPreprocessInfo = true;
}
else if (file->mType == StructuredCloneFile::eWasmCompiled) {
if (!directory) {
directory = aFileManager->GetCheckedDirectory();
if (NS_WARN_IF(!directory)) {
return NS_ERROR_FAILURE;
if (fileHelper.isNothing()) {
fileHelper.emplace(aFileManager);
rv = fileHelper->Init();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
const int64_t fileId = file->mFileInfo->Id();
MOZ_ASSERT(fileId > 0);
MOZ_ASSERT(aResult.Length() > 1);
MOZ_ASSERT(aResult[aResult.Length() - 2].mType ==
StructuredCloneFile::eWasmBytecode);
nsCOMPtr<nsIFile> nativeFile =
aFileManager->GetCheckedFileForId(directory, fileId);
if (NS_WARN_IF(!nativeFile)) {
return NS_ERROR_FAILURE;
}
StructuredCloneFile* previousFile = &aResult[aResult.Length() - 2];
ScopedPRFileDesc fileDesc;
rv = nativeFile->OpenNSPRFileDesc(PR_RDONLY, 0644, &fileDesc.rwget());
rv = CheckWasmModule(fileHelper.ptr(), previousFile, file);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
JS::BuildIdCharVector buildId;
bool ok = GetBuildId(&buildId);
if (NS_WARN_IF(!ok)) {
return NS_ERROR_FAILURE;
}
MOZ_ASSERT(file->mValid);
file->mValid = JS::CompiledWasmModuleAssumptionsMatch(fileDesc,
Move(buildId));
*aHasPreprocessInfo = true;
}
}
@ -9865,7 +10036,7 @@ SerializeStructuredCloneFiles(
case StructuredCloneFile::eWasmBytecode:
case StructuredCloneFile::eWasmCompiled: {
if (!aForPreprocess || !file.mValid) {
if (!aForPreprocess) {
SerializedStructuredCloneFile* serializedFile =
aResult.AppendElement(fallible);
MOZ_ASSERT(serializedFile);
@ -11480,8 +11651,7 @@ UpdateRefcountFunction::ProcessValue(mozIStorageValueArray* aValues,
}
nsTArray<StructuredCloneFile> files;
bool dummy;
rv = DeserializeStructuredCloneFiles(mFileManager, ids, files, &dummy);
rv = DeserializeStructuredCloneFiles(mFileManager, ids, files, nullptr);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -25800,63 +25970,6 @@ ObjectStoreAddOrPutRequestOp::RemoveOldIndexDataValues(
return NS_OK;
}
nsresult
ObjectStoreAddOrPutRequestOp::CopyFileData(nsIInputStream* aInputStream,
nsIOutputStream* aOutputStream,
char* aBuffer,
uint32_t aBufferSize)
{
AssertIsOnConnectionThread();
MOZ_ASSERT(aInputStream);
MOZ_ASSERT(aOutputStream);
PROFILER_LABEL("IndexedDB",
"ObjectStoreAddOrPutRequestOp::CopyFileData",
js::ProfileEntry::Category::STORAGE);
nsresult rv;
do {
uint32_t numRead;
rv = aInputStream->Read(aBuffer, aBufferSize, &numRead);
if (NS_WARN_IF(NS_FAILED(rv))) {
break;
}
if (!numRead) {
break;
}
uint32_t numWrite;
rv = aOutputStream->Write(aBuffer, numRead, &numWrite);
if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) {
rv = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
}
if (NS_WARN_IF(NS_FAILED(rv))) {
break;
}
if (NS_WARN_IF(numWrite != numRead)) {
rv = NS_ERROR_FAILURE;
break;
}
} while (true);
if (NS_SUCCEEDED(rv)) {
rv = aOutputStream->Flush();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
nsresult rv2 = aOutputStream->Close();
if (NS_WARN_IF(NS_FAILED(rv2))) {
return NS_SUCCEEDED(rv) ? rv2 : rv;
}
return rv;
}
bool
ObjectStoreAddOrPutRequestOp::Init(TransactionBase* aTransaction)
{
@ -26197,35 +26310,16 @@ ObjectStoreAddOrPutRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
}
}
nsCOMPtr<nsIFile> fileDirectory;
nsCOMPtr<nsIFile> journalDirectory;
Maybe<FileHelper> fileHelper;
if (mFileManager) {
fileDirectory = mFileManager->GetDirectory();
if (NS_WARN_IF(!fileDirectory)) {
fileHelper.emplace(mFileManager);
rv = fileHelper->Init();
if (NS_WARN_IF(NS_FAILED(rv))) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
journalDirectory = mFileManager->EnsureJournalDirectory();
if (NS_WARN_IF(!journalDirectory)) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
DebugOnly<bool> exists;
MOZ_ASSERT(NS_SUCCEEDED(fileDirectory->Exists(&exists)));
MOZ_ASSERT(exists);
DebugOnly<bool> isDirectory;
MOZ_ASSERT(NS_SUCCEEDED(fileDirectory->IsDirectory(&isDirectory)));
MOZ_ASSERT(isDirectory);
MOZ_ASSERT(NS_SUCCEEDED(journalDirectory->Exists(&exists)));
MOZ_ASSERT(exists);
MOZ_ASSERT(NS_SUCCEEDED(journalDirectory->IsDirectory(&isDirectory)));
MOZ_ASSERT(isDirectory);
}
if (!mStoredFileInfos.IsEmpty()) {
@ -26237,151 +26331,47 @@ ObjectStoreAddOrPutRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
StoredFileInfo& storedFileInfo = mStoredFileInfos[index];
MOZ_ASSERT(storedFileInfo.mFileInfo);
const int64_t id = storedFileInfo.mFileInfo->Id();
nsCOMPtr<nsIInputStream> inputStream;
storedFileInfo.mInputStream.swap(inputStream);
if (inputStream) {
MOZ_ASSERT(fileDirectory);
MOZ_ASSERT(journalDirectory);
RefPtr<FileInfo>& fileInfo = storedFileInfo.mFileInfo;
nsCOMPtr<nsIFile> diskFile =
mFileManager->GetFileForId(fileDirectory, id);
if (NS_WARN_IF(!diskFile)) {
nsCOMPtr<nsIFile> file = fileHelper->GetFile(fileInfo);
if (NS_WARN_IF(!file)) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
bool exists;
rv = diskFile->Exists(&exists);
nsCOMPtr<nsIFile> journalFile =
fileHelper->GetJournalFile(fileInfo);
if (NS_WARN_IF(!journalFile)) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
bool compress =
storedFileInfo.mType == StructuredCloneFile::eStructuredClone;
rv = fileHelper->CreateFileFromStream(file,
journalFile,
inputStream,
compress);
if (NS_FAILED(rv) &&
NS_ERROR_GET_MODULE(rv) != NS_ERROR_MODULE_DOM_INDEXEDDB) {
IDB_REPORT_INTERNAL_ERR();
rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
if (NS_WARN_IF(NS_FAILED(rv))) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
if (exists) {
bool isFile;
rv = diskFile->IsFile(&isFile);
if (NS_WARN_IF(NS_FAILED(rv))) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
if (NS_WARN_IF(!isFile)) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
uint64_t inputStreamSize;
rv = inputStream->Available(&inputStreamSize);
if (NS_WARN_IF(NS_FAILED(rv))) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
int64_t fileSize;
rv = diskFile->GetFileSize(&fileSize);
if (NS_WARN_IF(NS_FAILED(rv))) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
if (NS_WARN_IF(fileSize < 0)) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
if (NS_WARN_IF(uint64_t(fileSize) != inputStreamSize)) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
} else {
// Create a journal file first.
nsCOMPtr<nsIFile> journalFile =
mFileManager->GetFileForId(journalDirectory, id);
if (NS_WARN_IF(!journalFile)) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
rv = journalFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
if (NS_WARN_IF(NS_FAILED(rv))) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
// Now try to copy the stream.
RefPtr<FileOutputStream> fileOutputStream =
FileOutputStream::Create(mPersistenceType,
mGroup,
mOrigin,
diskFile);
if (NS_WARN_IF(!fileOutputStream)) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
if (storedFileInfo.mType == StructuredCloneFile::eStructuredClone) {
RefPtr<SnappyCompressOutputStream> snappyOutputStream =
new SnappyCompressOutputStream(fileOutputStream);
UniquePtr<char[]> buffer(new char[snappyOutputStream->BlockSize()]);
rv = CopyFileData(inputStream,
snappyOutputStream,
buffer.get(),
snappyOutputStream->BlockSize());
} else {
char buffer[kFileCopyBufferSize];
rv = CopyFileData(inputStream,
fileOutputStream,
buffer,
kFileCopyBufferSize);
}
if (NS_FAILED(rv) &&
NS_ERROR_GET_MODULE(rv) != NS_ERROR_MODULE_DOM_INDEXEDDB) {
IDB_REPORT_INTERNAL_ERR();
rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
if (NS_WARN_IF(NS_FAILED(rv))) {
// Try to remove the file if the copy failed.
nsresult rv2;
int64_t fileSize;
if (mFileManager->EnforcingQuota()) {
rv2 = diskFile->GetFileSize(&fileSize);
if (NS_WARN_IF(NS_FAILED(rv2))) {
return rv;
}
}
rv2 = diskFile->Remove(false);
if (NS_WARN_IF(NS_FAILED(rv2))) {
return rv;
}
rv2 = journalFile->Remove(false);
if (NS_WARN_IF(NS_FAILED(rv2))) {
return rv;
}
if (mFileManager->EnforcingQuota()) {
QuotaManager* quotaManager = QuotaManager::Get();
MOZ_ASSERT(quotaManager);
quotaManager->DecreaseUsageForOrigin(mFileManager->Type(),
mFileManager->Group(),
mFileManager->Origin(),
fileSize);
}
// Try to remove the file if the copy failed.
nsresult rv2 = fileHelper->RemoveFile(file, journalFile);
if (NS_WARN_IF(NS_FAILED(rv2))) {
return rv;
}
storedFileInfo.mCopiedSuccessfully = true;
return rv;
}
storedFileInfo.mCopiedSuccessfully = true;
}
if (index) {
@ -29144,6 +29134,350 @@ DEBUGThreadSlower::AfterProcessNextEvent(nsIThreadInternal* /* aThread */,
#endif // DEBUG
nsresult
FileHelper::Init()
{
MOZ_ASSERT(!IsOnBackgroundThread());
MOZ_ASSERT(mFileManager);
nsCOMPtr<nsIFile> fileDirectory = mFileManager->GetCheckedDirectory();
if (NS_WARN_IF(!fileDirectory)) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIFile> journalDirectory = mFileManager->EnsureJournalDirectory();
if (NS_WARN_IF(!journalDirectory)) {
return NS_ERROR_FAILURE;
}
DebugOnly<bool> exists;
MOZ_ASSERT(NS_SUCCEEDED(journalDirectory->Exists(&exists)));
MOZ_ASSERT(exists);
DebugOnly<bool> isDirectory;
MOZ_ASSERT(NS_SUCCEEDED(journalDirectory->IsDirectory(&isDirectory)));
MOZ_ASSERT(isDirectory);
mFileDirectory = Move(fileDirectory);
mJournalDirectory= Move(journalDirectory);
return NS_OK;
}
already_AddRefed<nsIFile>
FileHelper::GetFile(FileInfo* aFileInfo)
{
MOZ_ASSERT(!IsOnBackgroundThread());
MOZ_ASSERT(aFileInfo);
MOZ_ASSERT(mFileManager);
MOZ_ASSERT(mFileDirectory);
const int64_t fileId = aFileInfo->Id();
MOZ_ASSERT(fileId > 0);
nsCOMPtr<nsIFile> file =
mFileManager->GetFileForId(mFileDirectory, fileId);
return file.forget();
}
already_AddRefed<nsIFile>
FileHelper::GetCheckedFile(FileInfo* aFileInfo)
{
MOZ_ASSERT(!IsOnBackgroundThread());
MOZ_ASSERT(aFileInfo);
MOZ_ASSERT(mFileManager);
MOZ_ASSERT(mFileDirectory);
const int64_t fileId = aFileInfo->Id();
MOZ_ASSERT(fileId > 0);
nsCOMPtr<nsIFile> file =
mFileManager->GetCheckedFileForId(mFileDirectory, fileId);
return file.forget();
}
already_AddRefed<nsIFile>
FileHelper::GetJournalFile(FileInfo* aFileInfo)
{
MOZ_ASSERT(!IsOnBackgroundThread());
MOZ_ASSERT(aFileInfo);
MOZ_ASSERT(mFileManager);
MOZ_ASSERT(mJournalDirectory);
const int64_t fileId = aFileInfo->Id();
MOZ_ASSERT(fileId > 0);
nsCOMPtr<nsIFile> file =
mFileManager->GetFileForId(mJournalDirectory, fileId);
return file.forget();
}
nsresult
FileHelper::CreateFileFromStream(nsIFile* aFile,
nsIFile* aJournalFile,
nsIInputStream* aInputStream,
bool aCompress)
{
MOZ_ASSERT(!IsOnBackgroundThread());
MOZ_ASSERT(aFile);
MOZ_ASSERT(aJournalFile);
MOZ_ASSERT(aInputStream);
MOZ_ASSERT(mFileManager);
MOZ_ASSERT(mFileDirectory);
MOZ_ASSERT(mJournalDirectory);
bool exists;
nsresult rv = aFile->Exists(&exists);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// DOM blobs that are being stored in IDB are cached by calling
// IDBDatabase::GetOrCreateFileActorForBlob. So if the same DOM blob is stored
// again under a different key or in a different object store, we just add
// a new reference instead of creating a new copy (all such stored blobs share
// the same id).
// However, it can happen that CreateFileFromStream failed due to quota
// exceeded error and for some reason the orphaned file couldn't be deleted
// immediately. Now, if the operation is being repeated, the DOM blob is
// already cached, so it has the same file id which clashes with the orphaned
// file. We could do some tricks to restore previous copy loop, but it's safer
// to just delete the orphaned file and start from scratch.
// This corner case is partially simulated in test_file_copy_failure.js
if (exists) {
bool isFile;
rv = aFile->IsFile(&isFile);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (NS_WARN_IF(!isFile)) {
return NS_ERROR_FAILURE;
}
rv = aJournalFile->Exists(&exists);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (NS_WARN_IF(!exists)) {
return NS_ERROR_FAILURE;
}
rv = aJournalFile->IsFile(&isFile);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (NS_WARN_IF(!isFile)) {
return NS_ERROR_FAILURE;
}
IDB_WARNING("Deleting orphaned file!");
rv = RemoveFile(aFile, aJournalFile);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
// Create a journal file first.
rv = aJournalFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// Now try to copy the stream.
RefPtr<FileOutputStream> fileOutputStream =
FileOutputStream::Create(mFileManager->Type(),
mFileManager->Group(),
mFileManager->Origin(),
aFile);
if (NS_WARN_IF(!fileOutputStream)) {
return NS_ERROR_FAILURE;
}
if (aCompress) {
RefPtr<SnappyCompressOutputStream> snappyOutputStream =
new SnappyCompressOutputStream(fileOutputStream);
UniquePtr<char[]> buffer(new char[snappyOutputStream->BlockSize()]);
rv = SyncCopy(aInputStream,
snappyOutputStream,
buffer.get(),
snappyOutputStream->BlockSize());
} else {
char buffer[kFileCopyBufferSize];
rv = SyncCopy(aInputStream,
fileOutputStream,
buffer,
kFileCopyBufferSize);
}
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
nsresult
FileHelper::ReplaceFile(nsIFile* aFile,
nsIFile* aNewFile,
nsIFile* aNewJournalFile)
{
MOZ_ASSERT(!IsOnBackgroundThread());
MOZ_ASSERT(aFile);
MOZ_ASSERT(aNewFile);
MOZ_ASSERT(aNewJournalFile);
MOZ_ASSERT(mFileManager);
MOZ_ASSERT(mFileDirectory);
MOZ_ASSERT(mJournalDirectory);
nsresult rv;
int64_t fileSize;
if (mFileManager->EnforcingQuota()) {
rv = aFile->GetFileSize(&fileSize);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
nsAutoString fileName;
rv = aFile->GetLeafName(fileName);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = aNewFile->RenameTo(nullptr, fileName);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (mFileManager->EnforcingQuota()) {
QuotaManager* quotaManager = QuotaManager::Get();
MOZ_ASSERT(quotaManager);
quotaManager->DecreaseUsageForOrigin(mFileManager->Type(),
mFileManager->Group(),
mFileManager->Origin(),
fileSize);
}
rv = aNewJournalFile->Remove(false);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
nsresult
FileHelper::RemoveFile(nsIFile* aFile,
nsIFile* aJournalFile)
{
nsresult rv;
int64_t fileSize;
if (mFileManager->EnforcingQuota()) {
rv = aFile->GetFileSize(&fileSize);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
rv = aFile->Remove(false);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (mFileManager->EnforcingQuota()) {
QuotaManager* quotaManager = QuotaManager::Get();
MOZ_ASSERT(quotaManager);
quotaManager->DecreaseUsageForOrigin(mFileManager->Type(),
mFileManager->Group(),
mFileManager->Origin(),
fileSize);
}
rv = aJournalFile->Remove(false);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
already_AddRefed<FileInfo>
FileHelper::GetNewFileInfo()
{
MOZ_ASSERT(mFileManager);
return mFileManager->GetNewFileInfo();
}
nsresult
FileHelper::SyncCopy(nsIInputStream* aInputStream,
nsIOutputStream* aOutputStream,
char* aBuffer,
uint32_t aBufferSize)
{
MOZ_ASSERT(!IsOnBackgroundThread());
MOZ_ASSERT(aInputStream);
MOZ_ASSERT(aOutputStream);
PROFILER_LABEL("IndexedDB",
"FileHelper::SyncCopy",
js::ProfileEntry::Category::STORAGE);
nsresult rv;
do {
uint32_t numRead;
rv = aInputStream->Read(aBuffer, aBufferSize, &numRead);
if (NS_WARN_IF(NS_FAILED(rv))) {
break;
}
if (!numRead) {
break;
}
uint32_t numWrite;
rv = aOutputStream->Write(aBuffer, numRead, &numWrite);
if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) {
rv = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
}
if (NS_WARN_IF(NS_FAILED(rv))) {
break;
}
if (NS_WARN_IF(numWrite != numRead)) {
rv = NS_ERROR_FAILURE;
break;
}
} while (true);
if (NS_SUCCEEDED(rv)) {
rv = aOutputStream->Flush();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
nsresult rv2 = aOutputStream->Close();
if (NS_WARN_IF(NS_FAILED(rv2))) {
return NS_SUCCEEDED(rv) ? rv2 : rv;
}
return rv;
}
} // namespace indexedDB
} // namespace dom
} // namespace mozilla

Просмотреть файл

@ -43,8 +43,6 @@ struct StructuredCloneFile
RefPtr<JS::WasmModule> mWasmModule;
RefPtr<FileInfo> mFileInfo;
FileType mType;
// This is currently specific to eWasmCompiled files.
bool mValid;
// In IndexedDatabaseInlines.h
inline

Просмотреть файл

@ -24,7 +24,6 @@ namespace indexedDB {
inline
StructuredCloneFile::StructuredCloneFile()
: mType(eBlob)
, mValid(true)
{
MOZ_COUNT_CTOR(StructuredCloneFile);
}
@ -42,8 +41,7 @@ StructuredCloneFile::operator==(const StructuredCloneFile& aOther) const
return this->mBlob == aOther.mBlob &&
this->mMutableFile == aOther.mMutableFile &&
this->mFileInfo == aOther.mFileInfo &&
this->mType == aOther.mType &&
this->mValid == aOther.mValid;
this->mType == aOther.mType;
}
inline

Просмотреть файл

@ -0,0 +1,75 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
var testGenerator = testSteps();
function testSteps()
{
const name = "test_file_copy_failure.js";
const objectStoreName = "Blobs";
const blob = getBlob(getView(1024));
info("Opening database");
let request = indexedDB.open(name);
request.onerror = errorHandler;
request.onupgradeneeded = continueToNextStepSync;
request.onsuccess = unexpectedSuccessHandler;
yield undefined;
// upgradeneeded
request.onupgradeneeded = unexpectedSuccessHandler;
request.onsuccess = continueToNextStepSync;
info("Creating objectStore");
request.result.createObjectStore(objectStoreName);
yield undefined;
// success
let db = request.result;
db.onerror = errorHandler;
info("Creating orphaned file");
let filesDir = getChromeFilesDir();
let journalFile = filesDir.clone();
journalFile.append("journals");
journalFile.append("1");
let exists = journalFile.exists();
ok(!exists, "Journal file doesn't exist");
journalFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("0644", 8));
let file = filesDir.clone();
file.append("1");
exists = file.exists();
ok(!exists, "File doesn't exist");
file.create(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("0644", 8));
info("Storing blob");
let trans = db.transaction(objectStoreName, "readwrite");
request = trans.objectStore(objectStoreName).add(blob, 1);
request.onsuccess = continueToNextStepSync;
yield undefined;
trans.oncomplete = continueToNextStepSync;
yield undefined;
exists = journalFile.exists();
ok(!exists, "Journal file doesn't exist");
finishTest();
yield undefined;
}

Просмотреть файл

@ -0,0 +1,124 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
var testGenerator = testSteps();
function testSteps()
{
const name = "test_wasm_recompile.js";
const objectStoreName = "Wasm";
const wasmData = { key: 1, wasm: null };
// The goal of this test is to prove that wasm is recompiled and the on-disk
// copy updated.
if (!isWasmSupported()) {
finishTest();
yield undefined;
}
getWasmBinary('(module (func (nop)))');
let binary = yield undefined;
wasmData.wasm = getWasmModule(binary);
info("Installing profile");
clearAllDatabases(continueToNextStepSync);
yield undefined;
// The profile was created by an older build (buildId: 20161116145318,
// cpuId: X64=0x2). It contains one stored wasm module (file id 1 - bytecode
// and file id 2 - compiled/machine code). The file create_db.js in the
// package was run locally (specifically it was temporarily added to
// xpcshell-parent-process.ini and then executed:
// mach xpcshell-test dom/indexedDB/test/unit/create_db.js
installPackagedProfile("wasm_recompile_profile");
let filesDir = getChromeFilesDir();
let file = filesDir.clone();
file.append("2");
info("Reading out contents of compiled blob");
let fileReader = new FileReader();
fileReader.onload = continueToNextStepSync;
fileReader.readAsArrayBuffer(File.createFromNsIFile(file));
yield undefined;
let compiledBuffer = fileReader.result;
info("Opening database");
let request = indexedDB.open(name);
request.onerror = errorHandler;
request.onupgradeneeded = unexpectedSuccessHandler;
request.onsuccess = continueToNextStepSync;
yield undefined;
// success
let db = request.result;
db.onerror = errorHandler;
info("Getting wasm");
request = db.transaction([objectStoreName])
.objectStore(objectStoreName).get(wasmData.key);
request.onsuccess = continueToNextStepSync;
yield undefined;
info("Verifying wasm module");
verifyWasmModule(request.result, wasmData.wasm);
yield undefined;
info("Reading out contents of new compiled blob");
fileReader = new FileReader();
fileReader.onload = continueToNextStepSync;
fileReader.readAsArrayBuffer(File.createFromNsIFile(file));
yield undefined;
let newCompiledBuffer = fileReader.result;
info("Verifying blobs differ");
ok(!compareBuffers(newCompiledBuffer, compiledBuffer), "Blobs differ");
info("Getting wasm again");
request = db.transaction([objectStoreName])
.objectStore(objectStoreName).get(wasmData.key);
request.onsuccess = continueToNextStepSync;
yield undefined;
info("Verifying wasm module");
verifyWasmModule(request.result, wasmData.wasm);
yield undefined;
info("Reading contents of new compiled blob again");
fileReader = new FileReader();
fileReader.onload = continueToNextStepSync;
fileReader.readAsArrayBuffer(File.createFromNsIFile(file));
yield undefined;
let newCompiledBuffer2 = fileReader.result;
info("Verifying blob didn't change");
ok(compareBuffers(newCompiledBuffer2, newCompiledBuffer),
"Blob didn't change");
finishTest();
yield undefined;
}

Двоичные данные
dom/indexedDB/test/unit/wasm_recompile_profile.zip Normal file

Двоичный файл не отображается.

Просмотреть файл

@ -347,6 +347,31 @@ function installPackagedProfile(packageName)
zipReader.close();
}
function getChromeFilesDir()
{
let dirService = Cc["@mozilla.org/file/directory_service;1"]
.getService(Ci.nsIProperties);
let profileDir = dirService.get("ProfD", Ci.nsIFile);
let idbDir = profileDir.clone();
idbDir.append("storage");
idbDir.append("permanent");
idbDir.append("chrome");
idbDir.append("idb");
let idbEntries = idbDir.directoryEntries;
while (idbEntries.hasMoreElements()) {
let entry = idbEntries.getNext();
let file = entry.QueryInterface(Ci.nsIFile);
if (file.isDirectory()) {
return file;
}
}
throw new Error("files directory doesn't exist!");
}
function getView(size)
{
let buffer = new ArrayBuffer(size);

Просмотреть файл

@ -26,6 +26,7 @@ support-files =
schema23upgrade_profile.zip
snappyUpgrade_profile.zip
storagePersistentUpgrade_profile.zip
wasm_recompile_profile.zip
xpcshell-shared.ini
[include:xpcshell-shared.ini]
@ -36,6 +37,7 @@ support-files =
[test_database_close_without_onclose.js]
[test_database_onclose.js]
[test_defaultStorageUpgrade.js]
[test_file_copy_failure.js]
[test_idbSubdirUpgrade.js]
[test_globalObjects_ipc.js]
skip-if = toolkit == 'android'
@ -62,3 +64,4 @@ skip-if = os == "android" && processor == "x86"
[test_view_put_get_values.js]
[test_wasm_getAll.js]
[test_wasm_put_get_values.js]
[test_wasm_recompile.js]

Просмотреть файл

@ -6092,9 +6092,9 @@ struct WasmModule : js::AtomicRefCounted<WasmModule>
MOZ_DECLARE_REFCOUNTED_TYPENAME(WasmModule)
virtual ~WasmModule() {}
virtual void serializedSize(size_t* bytecodeSize, size_t* compiledSize) const = 0;
virtual void serialize(uint8_t* bytecodeBegin, size_t bytecodeSize,
uint8_t* compiledBegin, size_t compiledSize) const = 0;
virtual void serializedSize(size_t* maybeBytecodeSize, size_t* maybeCompiledSize) const = 0;
virtual void serialize(uint8_t* maybeBytecodeBegin, size_t maybeBytecodeSize,
uint8_t* maybeCompiledBegin, size_t maybeCompiledSize) const = 0;
virtual JSObject* createObject(JSContext* cx) = 0;
};

Просмотреть файл

@ -138,44 +138,54 @@ LinkData::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
}
/* virtual */ void
Module::serializedSize(size_t* bytecodeSize, size_t* compiledSize) const
Module::serializedSize(size_t* maybeBytecodeSize, size_t* maybeCompiledSize) const
{
*bytecodeSize = SerializedPodVectorSize(bytecode_->bytes);
if (maybeBytecodeSize)
*maybeBytecodeSize = SerializedPodVectorSize(bytecode_->bytes);
*compiledSize = assumptions_.serializedSize() +
SerializedPodVectorSize(code_) +
linkData_.serializedSize() +
SerializedVectorSize(imports_) +
SerializedVectorSize(exports_) +
SerializedPodVectorSize(dataSegments_) +
SerializedVectorSize(elemSegments_) +
metadata_->serializedSize();
if (maybeCompiledSize) {
*maybeCompiledSize = assumptions_.serializedSize() +
SerializedPodVectorSize(code_) +
linkData_.serializedSize() +
SerializedVectorSize(imports_) +
SerializedVectorSize(exports_) +
SerializedPodVectorSize(dataSegments_) +
SerializedVectorSize(elemSegments_) +
metadata_->serializedSize();
}
}
/* virtual */ void
Module::serialize(uint8_t* bytecodeBegin, size_t bytecodeSize,
uint8_t* compiledBegin, size_t compiledSize) const
Module::serialize(uint8_t* maybeBytecodeBegin, size_t maybeBytecodeSize,
uint8_t* maybeCompiledBegin, size_t maybeCompiledSize) const
{
// Bytecode deserialization is not guarded by Assumptions and thus must not
// change incompatibly between builds.
MOZ_ASSERT(!!maybeBytecodeBegin == !!maybeBytecodeSize);
MOZ_ASSERT(!!maybeCompiledBegin == !!maybeCompiledSize);
uint8_t* bytecodeEnd = SerializePodVector(bytecodeBegin, bytecode_->bytes);
MOZ_RELEASE_ASSERT(bytecodeEnd == bytecodeBegin + bytecodeSize);
if (maybeBytecodeBegin) {
// Bytecode deserialization is not guarded by Assumptions and thus must not
// change incompatibly between builds.
// Assumption must be serialized at the beginning of the compiled bytes so
// that compiledAssumptionsMatch can detect a build-id mismatch before any
// other decoding occurs.
uint8_t* bytecodeEnd = SerializePodVector(maybeBytecodeBegin, bytecode_->bytes);
MOZ_RELEASE_ASSERT(bytecodeEnd == maybeBytecodeBegin + maybeBytecodeSize);
}
uint8_t* cursor = compiledBegin;
cursor = assumptions_.serialize(cursor);
cursor = SerializePodVector(cursor, code_);
cursor = linkData_.serialize(cursor);
cursor = SerializeVector(cursor, imports_);
cursor = SerializeVector(cursor, exports_);
cursor = SerializePodVector(cursor, dataSegments_);
cursor = SerializeVector(cursor, elemSegments_);
cursor = metadata_->serialize(cursor);
MOZ_RELEASE_ASSERT(cursor == compiledBegin + compiledSize);
if (maybeCompiledBegin) {
// Assumption must be serialized at the beginning of the compiled bytes so
// that compiledAssumptionsMatch can detect a build-id mismatch before any
// other decoding occurs.
uint8_t* cursor = maybeCompiledBegin;
cursor = assumptions_.serialize(cursor);
cursor = SerializePodVector(cursor, code_);
cursor = linkData_.serialize(cursor);
cursor = SerializeVector(cursor, imports_);
cursor = SerializeVector(cursor, exports_);
cursor = SerializePodVector(cursor, dataSegments_);
cursor = SerializeVector(cursor, elemSegments_);
cursor = metadata_->serialize(cursor);
MOZ_RELEASE_ASSERT(cursor == maybeCompiledBegin + maybeCompiledSize);
}
}
/* static */ bool

Просмотреть файл

@ -150,9 +150,9 @@ class Module : public JS::WasmModule
// Structured clone support:
void serializedSize(size_t* bytecodeSize, size_t* compiledSize) const override;
void serialize(uint8_t* bytecodeBegin, size_t bytecodeSize,
uint8_t* compiledBegin, size_t compiledSize) const override;
void serializedSize(size_t* maybeBytecodeSize, size_t* maybeCompiledSize) const override;
void serialize(uint8_t* maybeBytecodeBegin, size_t maybeBytecodeSize,
uint8_t* maybeCompiledBegin, size_t maybeCompiledSize) const override;
static bool assumptionsMatch(const Assumptions& current, const uint8_t* compiledBegin,
size_t remain);
static RefPtr<Module> deserialize(const uint8_t* bytecodeBegin, size_t bytecodeSize,