Bug 1311466 - Part 6: Core changes for WebAssembly module deserialization (works only in xpcshell); r=asuth

This commit is contained in:
Jan Varga 2016-10-25 21:18:58 +02:00
Родитель 2962fac045
Коммит 8fef5e4a97
10 изменённых файлов: 475 добавлений и 47 удалений

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

@ -686,6 +686,71 @@ DeserializeStructuredCloneFiles(
break;
}
case StructuredCloneFile::eWasmBytecode: {
MOZ_ASSERT(blobOrMutableFile.type() ==
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::eWasmBytecode;
file->mBlob.swap(blob);
break;
}
case StructuredCloneFile::eWasmCompiled: {
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!");
}

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

@ -60,6 +60,7 @@
#include "mozilla/ipc/InputStreamParams.h"
#include "mozilla/ipc/InputStreamUtils.h"
#include "mozilla/ipc/PBackground.h"
#include "mozilla/Scoped.h"
#include "mozilla/storage/Variant.h"
#include "nsAutoPtr.h"
#include "nsCharSeparatedTokenizer.h"
@ -124,6 +125,11 @@
{0x6b505c84, 0x2c60, 0x4ffb, {0x8b, 0x91, 0xfe, 0x22, 0xb1, 0xec, 0x75, 0xe2}}
namespace mozilla {
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPRFileDesc,
PRFileDesc,
PR_Close);
namespace dom {
namespace indexedDB {
@ -9592,32 +9598,41 @@ DeserializeStructuredCloneFile(FileManager* aFileManager,
MOZ_ASSERT(!aText.IsEmpty());
MOZ_ASSERT(aFile);
nsresult rv;
int32_t id;
StructuredCloneFile::FileType type;
bool isDot = false;
if ((isDot = aText.First() == '.') ||
aText.First() == '-') {
switch (aText.First()) {
case char16_t('-'):
type = StructuredCloneFile::eMutableFile;
break;
case char16_t('.'):
type = StructuredCloneFile::eStructuredClone;
break;
case char16_t('/'):
type = StructuredCloneFile::eWasmBytecode;
break;
case char16_t('\\'):
type = StructuredCloneFile::eWasmCompiled;
break;
default:
type = StructuredCloneFile::eBlob;
}
nsresult rv;
int32_t id;
if (type == StructuredCloneFile::eBlob) {
id = aText.ToInteger(&rv);
} else {
nsString text(Substring(aText, 1));
id = text.ToInteger(&rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (isDot) {
type = StructuredCloneFile::eStructuredClone;
} else {
type = StructuredCloneFile::eMutableFile;
}
} else {
id = aText.ToInteger(&rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
type = StructuredCloneFile::eBlob;
}
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
RefPtr<FileInfo> fileInfo = aFileManager->GetFileInfo(id);
@ -9634,11 +9649,14 @@ DeserializeStructuredCloneFiles(FileManager* aFileManager,
const nsAString& aText,
nsTArray<StructuredCloneFile>& aResult)
{
MOZ_ASSERT(!IsOnBackgroundThread());
nsCharSeparatedTokenizerTemplate<TokenizerIgnoreNothing>
tokenizer(aText, ' ');
nsAutoString token;
nsresult rv;
nsCOMPtr<nsIFile> directory;
while (tokenizer.hasMoreTokens()) {
token = tokenizer.nextToken();
@ -9649,6 +9667,40 @@ DeserializeStructuredCloneFiles(FileManager* aFileManager,
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (file->mType == StructuredCloneFile::eWasmCompiled) {
if (!directory) {
directory = aFileManager->GetCheckedDirectory();
if (NS_WARN_IF(!directory)) {
return NS_ERROR_FAILURE;
}
}
const int64_t fileId = file->mFileInfo->Id();
MOZ_ASSERT(fileId > 0);
nsCOMPtr<nsIFile> nativeFile =
aFileManager->GetCheckedFileForId(directory, fileId);
if (NS_WARN_IF(!nativeFile)) {
return NS_ERROR_FAILURE;
}
ScopedPRFileDesc fileDesc;
rv = nativeFile->OpenNSPRFileDesc(PR_RDONLY, 0644, &fileDesc.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;
}
MOZ_ASSERT(file->mValid);
file->mValid = JS::CompiledWasmModuleAssumptionsMatch(fileDesc,
Move(buildId));
}
}
return NS_OK;
@ -9694,20 +9746,12 @@ SerializeStructuredCloneFiles(
FileManager* fileManager = aDatabase->GetFileManager();
nsCOMPtr<nsIFile> directory = fileManager->GetDirectory();
nsCOMPtr<nsIFile> directory = fileManager->GetCheckedDirectory();
if (NS_WARN_IF(!directory)) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
DebugOnly<bool> exists;
MOZ_ASSERT(NS_SUCCEEDED(directory->Exists(&exists)));
MOZ_ASSERT(exists);
DebugOnly<bool> isDirectory;
MOZ_ASSERT(NS_SUCCEEDED(directory->IsDirectory(&isDirectory)));
MOZ_ASSERT(isDirectory);
const uint32_t count = aFiles.Length();
if (NS_WARN_IF(!aResult.SetCapacity(count, fallible))) {
@ -9721,19 +9765,12 @@ SerializeStructuredCloneFiles(
MOZ_ASSERT(fileId > 0);
nsCOMPtr<nsIFile> nativeFile =
fileManager->GetFileForId(directory, fileId);
fileManager->GetCheckedFileForId(directory, fileId);
if (NS_WARN_IF(!nativeFile)) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
MOZ_ASSERT(NS_SUCCEEDED(nativeFile->Exists(&exists)));
MOZ_ASSERT(exists);
DebugOnly<bool> isFile;
MOZ_ASSERT(NS_SUCCEEDED(nativeFile->IsFile(&isFile)));
MOZ_ASSERT(isFile);
switch (file.mType) {
case StructuredCloneFile::eBlob: {
RefPtr<BlobImpl> impl = new BlobImplStoredFile(nativeFile,
@ -9803,6 +9840,40 @@ SerializeStructuredCloneFiles(
break;
}
case StructuredCloneFile::eWasmBytecode:
case StructuredCloneFile::eWasmCompiled: {
if (file.mType == StructuredCloneFile::eWasmCompiled && !file.mValid) {
SerializedStructuredCloneFile* serializedFile =
aResult.AppendElement(fallible);
MOZ_ASSERT(serializedFile);
serializedFile->file() = null_t();
serializedFile->type() = StructuredCloneFile::eWasmCompiled;
} else {
RefPtr<BlobImpl> impl = new BlobImplStoredFile(nativeFile,
file.mFileInfo,
/* aSnapshot */ false);
PBlobParent* actor =
BackgroundParent::GetOrCreateActorForBlobImpl(aBackgroundActor,
impl);
if (!actor) {
// This can only fail if the child has crashed.
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
SerializedStructuredCloneFile* serializedFile =
aResult.AppendElement(fallible);
MOZ_ASSERT(serializedFile);
serializedFile->file() = actor;
serializedFile->type() = file.mType;
}
break;
}
default:
MOZ_CRASH("Should never get here!");
}
@ -16804,6 +16875,25 @@ FileManager::GetDirectory()
return GetFileForPath(mDirectoryPath);
}
already_AddRefed<nsIFile>
FileManager::GetCheckedDirectory()
{
nsCOMPtr<nsIFile> directory = GetDirectory();
if (NS_WARN_IF(!directory)) {
return nullptr;
}
DebugOnly<bool> exists;
MOZ_ASSERT(NS_SUCCEEDED(directory->Exists(&exists)));
MOZ_ASSERT(exists);
DebugOnly<bool> isDirectory;
MOZ_ASSERT(NS_SUCCEEDED(directory->IsDirectory(&isDirectory)));
MOZ_ASSERT(isDirectory);
return directory.forget();
}
already_AddRefed<nsIFile>
FileManager::GetJournalDirectory()
{
@ -16911,6 +17001,26 @@ FileManager::GetFileForId(nsIFile* aDirectory, int64_t aId)
return file.forget();
}
// static
already_AddRefed<nsIFile>
FileManager::GetCheckedFileForId(nsIFile* aDirectory, int64_t aId)
{
nsCOMPtr<nsIFile> file = GetFileForId(aDirectory, aId);
if (NS_WARN_IF(!file)) {
return nullptr;
}
DebugOnly<bool> exists;
MOZ_ASSERT(NS_SUCCEEDED(file->Exists(&exists)));
MOZ_ASSERT(exists);
DebugOnly<bool> isFile;
MOZ_ASSERT(NS_SUCCEEDED(file->IsFile(&isFile)));
MOZ_ASSERT(isFile);
return file.forget();
}
// static
nsresult
FileManager::InitDirectory(nsIFile* aDirectory,

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

@ -50,6 +50,9 @@ public:
static already_AddRefed<nsIFile>
GetFileForId(nsIFile* aDirectory, int64_t aId);
static already_AddRefed<nsIFile>
GetCheckedFileForId(nsIFile* aDirectory, int64_t aId);
static nsresult
InitDirectory(nsIFile* aDirectory,
nsIFile* aDatabaseFile,
@ -119,6 +122,9 @@ public:
already_AddRefed<nsIFile>
GetDirectory();
already_AddRefed<nsIFile>
GetCheckedDirectory();
already_AddRefed<nsIFile>
GetJournalDirectory();

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

@ -136,6 +136,26 @@ struct MOZ_STACK_CLASS BlobOrFileData final
}
};
struct MOZ_STACK_CLASS WasmModuleData final
{
uint32_t bytecodeIndex;
uint32_t compiledIndex;
uint32_t flags;
explicit WasmModuleData(uint32_t aFlags)
: bytecodeIndex(0)
, compiledIndex(0)
, flags(aFlags)
{
MOZ_COUNT_CTOR(WasmModuleData);
}
~WasmModuleData()
{
MOZ_COUNT_DTOR(WasmModuleData);
}
};
struct MOZ_STACK_CLASS GetAddInfoClosure final
{
IDBObjectStore::StructuredCloneWriteInfo& mCloneWriteInfo;
@ -170,6 +190,25 @@ GenerateRequest(JSContext* aCx, IDBObjectStore* aObjectStore)
return request.forget();
}
PRFileDesc*
GetFileDescriptorFromStream(nsIInputStream* aStream)
{
nsCOMPtr<nsIFileMetadata> fileMetadata = do_QueryInterface(aStream);
if (NS_WARN_IF(!fileMetadata)) {
return nullptr;
}
PRFileDesc* fileDesc;
nsresult rv = fileMetadata->GetFileDescriptor(&fileDesc);
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
MOZ_ASSERT(fileDesc);
return fileDesc;
}
bool
StructuredCloneWriteCallback(JSContext* aCx,
JSStructuredCloneWriter* aWriter,
@ -578,6 +617,27 @@ ReadBlobOrFile(JSStructuredCloneReader* aReader,
return true;
}
bool
ReadWasmModule(JSStructuredCloneReader* aReader,
WasmModuleData* aRetval)
{
static_assert(SCTAG_DOM_WASM == 0xFFFF8006, "Update me!");
MOZ_ASSERT(aReader && aRetval);
uint32_t bytecodeIndex;
uint32_t compiledIndex;
if (NS_WARN_IF(!JS_ReadUint32Pair(aReader,
&bytecodeIndex,
&compiledIndex))) {
return false;
}
aRetval->bytecodeIndex = bytecodeIndex;
aRetval->compiledIndex = compiledIndex;
return true;
}
class ValueDeserializationHelper
{
public:
@ -682,6 +742,76 @@ public:
aResult.set(&wrappedFile.toObject());
return true;
}
static bool
CreateAndWrapWasmModule(JSContext* aCx,
StructuredCloneFile& aBytecodeFile,
StructuredCloneFile& aCompiledFile,
const WasmModuleData& aData,
JS::MutableHandle<JSObject*> aResult)
{
MOZ_ASSERT(aCx);
MOZ_ASSERT(aBytecodeFile.mType == StructuredCloneFile::eWasmBytecode);
MOZ_ASSERT(aBytecodeFile.mBlob);
MOZ_ASSERT(aCompiledFile.mType == StructuredCloneFile::eWasmCompiled);
ErrorResult errorResult;
nsCOMPtr<nsIInputStream> bytecodeStream;
aBytecodeFile.mBlob->GetInternalStream(getter_AddRefs(bytecodeStream),
errorResult);
if (NS_WARN_IF(errorResult.Failed())) {
return false;
}
PRFileDesc* bytecodeFileDesc = GetFileDescriptorFromStream(bytecodeStream);
if (NS_WARN_IF(!bytecodeFileDesc)) {
return false;
}
// The compiled stream must scoped here!
nsCOMPtr<nsIInputStream> compiledStream;
PRFileDesc* compiledFileDesc;
if (aCompiledFile.mBlob) {
aCompiledFile.mBlob->GetInternalStream(getter_AddRefs(compiledStream),
errorResult);
if (NS_WARN_IF(errorResult.Failed())) {
return false;
}
compiledFileDesc = GetFileDescriptorFromStream(compiledStream);
if (NS_WARN_IF(!compiledFileDesc)) {
return false;
}
} else {
compiledFileDesc = nullptr;
}
JS::BuildIdCharVector buildId;
bool ok = GetBuildId(&buildId);
if (NS_WARN_IF(!ok)) {
return false;
}
RefPtr<JS::WasmModule> module = JS::DeserializeWasmModule(bytecodeFileDesc,
compiledFileDesc,
Move(buildId),
nullptr,
0,
0);
if (NS_WARN_IF(!module)) {
return false;
}
JS::Rooted<JSObject*> moduleObj(aCx, module->createObject(aCx));
if (NS_WARN_IF(!moduleObj)) {
return false;
}
aResult.set(moduleObj);
return true;
}
};
class IndexDeserializationHelper
@ -774,6 +904,23 @@ public:
aResult.set(obj);
return true;
}
static bool
CreateAndWrapWasmModule(JSContext* aCx,
StructuredCloneFile& aBytecodeFile,
StructuredCloneFile& aCompiledFile,
const WasmModuleData& aData,
JS::MutableHandle<JSObject*> aResult)
{
// Wasm module can't be used in index creation, so just make a dummy object.
JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
if (NS_WARN_IF(!obj)) {
return false;
}
aResult.set(obj);
return true;
}
};
// We don't need to upgrade database on B2G. See the comment in ActorsParent.cpp,
@ -830,6 +977,22 @@ public:
aResult.set(obj);
return true;
}
static bool
CreateAndWrapWasmModule(JSContext* aCx,
StructuredCloneFile& aBytecodeFile,
StructuredCloneFile& aCompiledFile,
const WasmModuleData& aData,
JS::MutableHandle<JSObject*> aResult)
{
MOZ_ASSERT(aCx);
MOZ_ASSERT(aBytecodeFile.mType == StructuredCloneFile::eBlob);
MOZ_ASSERT(aCompiledFile.mType == StructuredCloneFile::eBlob);
MOZ_ASSERT(false, "This should never be possible!");
return false;
}
};
#endif // MOZ_B2G
@ -855,9 +1018,43 @@ CommonStructuredCloneReadCallback(JSContext* aCx,
if (aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE ||
aTag == SCTAG_DOM_BLOB ||
aTag == SCTAG_DOM_FILE ||
aTag == SCTAG_DOM_MUTABLEFILE) {
aTag == SCTAG_DOM_MUTABLEFILE ||
aTag == SCTAG_DOM_WASM) {
auto* cloneReadInfo = static_cast<StructuredCloneReadInfo*>(aClosure);
JS::Rooted<JSObject*> result(aCx);
if (aTag == SCTAG_DOM_WASM) {
WasmModuleData data(aData);
if (NS_WARN_IF(!ReadWasmModule(aReader, &data))) {
return nullptr;
}
MOZ_ASSERT(data.compiledIndex == data.bytecodeIndex + 1);
MOZ_ASSERT(!data.flags);
if (data.bytecodeIndex >= cloneReadInfo->mFiles.Length() ||
data.compiledIndex >= cloneReadInfo->mFiles.Length()) {
MOZ_ASSERT(false, "Bad index value!");
return nullptr;
}
StructuredCloneFile& bytecodeFile =
cloneReadInfo->mFiles[data.bytecodeIndex];
StructuredCloneFile& compiledFile =
cloneReadInfo->mFiles[data.compiledIndex];
if (NS_WARN_IF(!Traits::CreateAndWrapWasmModule(aCx,
bytecodeFile,
compiledFile,
data,
&result))) {
return nullptr;
}
return result;
}
if (aData >= cloneReadInfo->mFiles.Length()) {
MOZ_ASSERT(false, "Bad index value!");
return nullptr;
@ -865,8 +1062,6 @@ CommonStructuredCloneReadCallback(JSContext* aCx,
StructuredCloneFile& file = cloneReadInfo->mFiles[aData];
JS::Rooted<JSObject*> result(aCx);
if (aTag == SCTAG_DOM_MUTABLEFILE) {
MutableFileData data;
if (NS_WARN_IF(!ReadFileHandle(aReader, &data))) {

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

@ -38,6 +38,8 @@ struct StructuredCloneFile
RefPtr<IDBMutableFile> mMutableFile;
RefPtr<FileInfo> mFileInfo;
FileType mType;
// This is currently specific to eWasmCompiled files.
bool mValid;
// In IndexedDatabaseInlines.h
inline

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

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

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

@ -45,8 +45,8 @@ function compareBuffers(buffer1, buffer2)
if (buffer1.byteLength != buffer2.byteLength) {
return false;
}
let view1 = new Uint8Array(buffer1);
let view2 = new Uint8Array(buffer2);
let view1 = buffer1 instanceof Uint8Array ? buffer1 : new Uint8Array(buffer1);
let view2 = buffer2 instanceof Uint8Array ? buffer2 : new Uint8Array(buffer2);
for (let i = 0; i < buffer1.byteLength; i++) {
if (view1[i] != view2[i]) {
return false;
@ -202,6 +202,20 @@ function verifyView(view1, view2)
continueToNextStep();
}
function verifyWasmModule(module1, module2)
{
let testingFunctions = SpecialPowers.Cu.getJSTestingFunctions();
let wasmExtractCode = SpecialPowers.unwrap(testingFunctions.wasmExtractCode);
let exp1 = wasmExtractCode(module1);
let exp2 = wasmExtractCode(module2);
let code1 = exp1.code;
let code2 = exp2.code;
ok(code1 instanceof Uint8Array, "Instance of Uint8Array");
ok(code1.length == code2.length, "Correct length");
verifyBuffers(code1, code2);
continueToNextStep();
}
function grabFileUsageAndContinueHandler(request)
{
testGenerator.send(request.fileUsage);

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

@ -392,6 +392,7 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_view_put_get_values.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_wasm_put_get_values.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
#skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
skip-if = true
[test_serviceworker.html]
skip-if = buildapp == 'b2g'

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

@ -63,6 +63,25 @@ function testSteps()
is(request.result, wasmData.key, "Got correct key");
info("Getting wasm");
request = objectStore.get(wasmData.key);
request.onsuccess = continueToNextStepSync;
yield undefined;
verifyWasmModule(request.result, wasmData.wasm);
yield undefined;
info("Getting wasm in new transaction");
request = db.transaction([objectStoreName])
.objectStore(objectStoreName).get(wasmData.key);
request.onsuccess = continueToNextStepSync;
yield undefined;
verifyWasmModule(request.result, wasmData.wasm);
yield undefined;
finishTest();
yield undefined;
}

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

@ -387,8 +387,9 @@ function compareBuffers(buffer1, buffer2)
if (buffer1.byteLength != buffer2.byteLength) {
return false;
}
let view1 = new Uint8Array(buffer1);
let view2 = new Uint8Array(buffer2);
let view1 = buffer1 instanceof Uint8Array ? buffer1 : new Uint8Array(buffer1);
let view2 = buffer2 instanceof Uint8Array ? buffer2 : new Uint8Array(buffer2);
for (let i = 0; i < buffer1.byteLength; i++) {
if (view1[i] != view2[i]) {
return false;
@ -464,6 +465,19 @@ function verifyView(view1, view2)
continueToNextStep();
}
function verifyWasmModule(module1, module2)
{
let testingFunctions = Cu.getJSTestingFunctions();
let exp1 = testingFunctions.wasmExtractCode(module1);
let exp2 = testingFunctions.wasmExtractCode(module2);
let code1 = exp1.code;
let code2 = exp2.code;
ok(code1 instanceof Uint8Array, "Instance of Uint8Array");
ok(code1.length == code2.length, "Correct length");
verifyBuffers(code1, code2);
continueToNextStep();
}
function grabFileUsageAndContinueHandler(request)
{
testGenerator.send(request.fileUsage);