Bug 929236 - Cache asm.js compiled code in Gecko (r=janv)

--HG--
extra : rebase_source : 1c97962da0044858c583fc45e69dd22e519b8066
This commit is contained in:
Luke Wagner 2013-11-18 15:49:53 -06:00
parent 3bec480e98
commit a1cfb21b89
21 changed files with 1567 additions and 12 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,86 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#ifndef mozilla_dom_asmjscache_asmjscache_h
#define mozilla_dom_asmjscache_asmjscache_h
#include "ipc/IPCMessageUtils.h"
#include "js/TypeDecls.h"
#include "js/Vector.h"
class nsIPrincipal;
namespace mozilla {
namespace dom {
namespace quota {
class Client;
}
namespace asmjscache {
class PAsmJSCacheEntryChild;
class PAsmJSCacheEntryParent;
enum OpenMode
{
eOpenForRead,
eOpenForWrite,
NUM_OPEN_MODES
};
// Implementation of AsmJSCacheOps, installed by nsJSEnvironment:
bool
OpenEntryForRead(JS::Handle<JSObject*> aGlobal, size_t* aSize,
const uint8_t** aMemory, intptr_t *aHandle);
void
CloseEntryForRead(JS::Handle<JSObject*> aGlobal, size_t aSize,
const uint8_t* aMemory, intptr_t aHandle);
bool
OpenEntryForWrite(JS::Handle<JSObject*> aGlobal, size_t aSize,
uint8_t** aMemory, intptr_t* aHandle);
void
CloseEntryForWrite(JS::Handle<JSObject*> aGlobal, size_t aSize,
uint8_t* aMemory, intptr_t aHandle);
bool
GetBuildId(js::Vector<char>* aBuildId);
// Called from QuotaManager.cpp:
quota::Client*
CreateClient();
// Called from ipc/ContentParent.cpp:
PAsmJSCacheEntryParent*
AllocEntryParent(OpenMode aOpenMode, uint32_t aSizeToWrite,
nsIPrincipal* aPrincipal);
void
DeallocEntryParent(PAsmJSCacheEntryParent* aActor);
// Called from ipc/ContentChild.cpp:
void
DeallocEntryChild(PAsmJSCacheEntryChild* aActor);
} // namespace asmjscache
} // namespace dom
} // namespace mozilla
namespace IPC {
template <>
struct ParamTraits<mozilla::dom::asmjscache::OpenMode> :
public EnumSerializer<mozilla::dom::asmjscache::OpenMode,
mozilla::dom::asmjscache::eOpenForRead,
mozilla::dom::asmjscache::NUM_OPEN_MODES>
{ };
} // namespace IPC
#endif // mozilla_dom_asmjscache_asmjscache_h

View File

@ -0,0 +1,24 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
include protocol PContent;
namespace mozilla {
namespace dom {
namespace asmjscache {
protocol PAsmJSCacheEntry
{
manager PContent;
child:
OnOpen(int64_t fileSize, FileDescriptor fileDesc);
both:
__delete__();
};
} // namespace asmjscache
} // namespace dom
} // namespace mozilla

25
dom/asmjscache/moz.build Normal file
View File

@ -0,0 +1,25 @@
# -*- 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/.
EXPORTS.mozilla.dom.asmjscache += [
'AsmJSCache.h'
]
SOURCES += [
'AsmJSCache.cpp'
]
IPDL_SOURCES += [
'PAsmJSCacheEntry.ipdl'
]
FAIL_ON_WARNINGS = True
MSVC_ENABLE_PGO = True
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'gklayout'

View File

@ -79,6 +79,7 @@
#include "mozilla/Telemetry.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/Attributes.h"
#include "mozilla/dom/asmjscache/AsmJSCache.h"
#include "mozilla/dom/CanvasRenderingContext2DBinding.h"
#include "mozilla/CycleCollectedJSRuntime.h"
#include "mozilla/ContentEvents.h"
@ -2878,6 +2879,16 @@ nsJSContext::EnsureStatics()
};
SetDOMCallbacks(sRuntime, &DOMcallbacks);
// Set up the asm.js cache callbacks
static JS::AsmJSCacheOps asmJSCacheOps = {
asmjscache::OpenEntryForRead,
asmjscache::CloseEntryForRead,
asmjscache::OpenEntryForWrite,
asmjscache::CloseEntryForWrite,
asmjscache::GetBuildId
};
JS::SetAsmJSCacheOps(sRuntime, &asmJSCacheOps);
// Set these global xpconnect options...
Preferences::RegisterCallbackAndCall(ReportAllJSExceptionsPrefChangedCallback,
"dom.report_all_js_exceptions");

View File

@ -11,7 +11,6 @@
#include "mozilla/dom/quota/Client.h"
#define IDB_DIRECTORY_NAME "idb"
#define JOURNAL_DIRECTORY_NAME "journals"
BEGIN_INDEXEDDB_NAMESPACE

View File

@ -648,8 +648,8 @@ IDBFactory::OpenInternal(const nsAString& aName,
}
else if (aDeleting) {
nsCString databaseId;
QuotaManager::GetStorageId(aPersistenceType, aASCIIOrigin, aName,
databaseId);
QuotaManager::GetStorageId(aPersistenceType, aASCIIOrigin, Client::IDB,
aName, databaseId);
MOZ_ASSERT(!databaseId.IsEmpty());
IndexedDBDeleteDatabaseRequestChild* actor =

View File

@ -1717,8 +1717,8 @@ NS_IMPL_ISUPPORTS1(OpenDatabaseHelper, nsIRunnable)
nsresult
OpenDatabaseHelper::Init()
{
QuotaManager::GetStorageId(mPersistenceType, mASCIIOrigin, mName,
mDatabaseId);
QuotaManager::GetStorageId(mPersistenceType, mASCIIOrigin, Client::IDB,
mName, mDatabaseId);
MOZ_ASSERT(!mDatabaseId.IsEmpty());
return NS_OK;

View File

@ -10,6 +10,7 @@
#include "mozilla/Assertions.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/quota/Client.h"
#include "mozilla/dom/quota/QuotaManager.h"
#include "AsyncConnectionHelper.h"
@ -23,6 +24,7 @@
USING_INDEXEDDB_NAMESPACE
using namespace mozilla::dom;
using mozilla::dom::quota::Client;
using mozilla::dom::quota::QuotaManager;
namespace {
@ -288,8 +290,8 @@ IndexedDBDatabaseChild::EnsureDatabase(
databaseId = mDatabase->Id();
}
else {
QuotaManager::GetStorageId(aDBInfo.persistenceType,
aDBInfo.origin, aDBInfo.name, databaseId);
QuotaManager::GetStorageId(aDBInfo.persistenceType, aDBInfo.origin,
Client::IDB, aDBInfo.name, databaseId);
}
MOZ_ASSERT(!databaseId.IsEmpty());

View File

@ -17,6 +17,8 @@
#include "TabChild.h"
#include "mozilla/Attributes.h"
#include "mozilla/dom/asmjscache/AsmJSCache.h"
#include "mozilla/dom/asmjscache/PAsmJSCacheEntryChild.h"
#include "mozilla/dom/ExternalHelperAppChild.h"
#include "mozilla/dom/PCrashReporterChild.h"
#include "mozilla/dom/DOMStorageIPC.h"
@ -867,6 +869,22 @@ ContentChild::DeallocPIndexedDBChild(PIndexedDBChild* aActor)
return true;
}
asmjscache::PAsmJSCacheEntryChild*
ContentChild::AllocPAsmJSCacheEntryChild(const asmjscache::OpenMode& aOpenMode,
const int64_t& aSizeToWrite,
const IPC::Principal& aPrincipal)
{
NS_NOTREACHED("Should never get here!");
return nullptr;
}
bool
ContentChild::DeallocPAsmJSCacheEntryChild(PAsmJSCacheEntryChild* aActor)
{
asmjscache::DeallocEntryChild(aActor);
return true;
}
PTestShellChild*
ContentChild::AllocPTestShellChild()
{

View File

@ -165,6 +165,13 @@ public:
virtual PFMRadioChild* AllocPFMRadioChild();
virtual bool DeallocPFMRadioChild(PFMRadioChild* aActor);
virtual PAsmJSCacheEntryChild* AllocPAsmJSCacheEntryChild(
const asmjscache::OpenMode& aOpenMode,
const int64_t& aSizeToWrite,
const IPC::Principal& aPrincipal) MOZ_OVERRIDE;
virtual bool DeallocPAsmJSCacheEntryChild(
PAsmJSCacheEntryChild* aActor) MOZ_OVERRIDE;
virtual PSpeechSynthesisChild* AllocPSpeechSynthesisChild();
virtual bool DeallocPSpeechSynthesisChild(PSpeechSynthesisChild* aActor);

View File

@ -26,6 +26,7 @@
#include "IndexedDatabaseManager.h"
#include "mozIApplication.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/dom/asmjscache/AsmJSCache.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/ExternalHelperAppParent.h"
#include "mozilla/dom/PMemoryReportRequestParent.h"
@ -2448,6 +2449,22 @@ ContentParent::DeallocPFMRadioParent(PFMRadioParent* aActor)
#endif
}
asmjscache::PAsmJSCacheEntryParent*
ContentParent::AllocPAsmJSCacheEntryParent(
const asmjscache::OpenMode& aOpenMode,
const int64_t& aSizeToWrite,
const IPC::Principal& aPrincipal)
{
return asmjscache::AllocEntryParent(aOpenMode, aSizeToWrite, aPrincipal);
}
bool
ContentParent::DeallocPAsmJSCacheEntryParent(PAsmJSCacheEntryParent* aActor)
{
asmjscache::DeallocEntryParent(aActor);
return true;
}
PSpeechSynthesisParent*
ContentParent::AllocPSpeechSynthesisParent()
{

View File

@ -368,6 +368,13 @@ private:
virtual PFMRadioParent* AllocPFMRadioParent();
virtual bool DeallocPFMRadioParent(PFMRadioParent* aActor);
virtual PAsmJSCacheEntryParent* AllocPAsmJSCacheEntryParent(
const asmjscache::OpenMode& aOpenMode,
const int64_t& aSizeToWrite,
const IPC::Principal& aPrincipal) MOZ_OVERRIDE;
virtual bool DeallocPAsmJSCacheEntryParent(
PAsmJSCacheEntryParent* aActor) MOZ_OVERRIDE;
virtual PSpeechSynthesisParent* AllocPSpeechSynthesisParent();
virtual bool DeallocPSpeechSynthesisParent(PSpeechSynthesisParent* aActor);
virtual bool RecvPSpeechSynthesisConstructor(PSpeechSynthesisParent* aActor);

View File

@ -4,6 +4,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
include protocol PAsmJSCacheEntry;
include protocol PBlob;
include protocol PBluetooth;
include protocol PBrowser;
@ -40,6 +41,7 @@ using struct IPC::Permission from "mozilla/net/NeckoMessageUtils.h";
using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h";
using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
using mozilla::dom::asmjscache::OpenMode from "mozilla/dom/asmjscache/AsmJSCache.h";
using mozilla::dom::AudioChannelType from "AudioChannelCommon.h";
using mozilla::dom::AudioChannelState from "AudioChannelCommon.h";
using mozilla::dom::NativeThreadId from "mozilla/dom/TabMessageUtils.h";
@ -175,6 +177,7 @@ intr protocol PContent
parent opens PCompositor;
parent opens PImageBridge;
manages PAsmJSCacheEntry;
manages PBlob;
manages PBluetooth;
manages PBrowser;
@ -357,6 +360,9 @@ parent:
PFMRadio();
PAsmJSCacheEntry(OpenMode openMode, int64_t sizeToWrite,
Principal principal);
// Services remoting
async StartVisitedQuery(URIParams uri);

View File

@ -50,6 +50,7 @@ PARALLEL_DIRS += [
'encoding',
'file',
'fmradio',
'asmjscache',
'media',
'messages',
'power',

View File

@ -14,6 +14,9 @@
class nsIOfflineStorage;
class nsIRunnable;
#define IDB_DIRECTORY_NAME "idb"
#define ASMJSCACHE_DIRECTORY_NAME "asmjs"
BEGIN_QUOTA_NAMESPACE
class OriginOrPatternString;
@ -35,6 +38,7 @@ public:
IDB = 0,
//LS,
//APPCACHE,
ASMJS,
TYPE_MAX
};
@ -46,7 +50,11 @@ public:
{
switch (aType) {
case IDB:
aText.AssignLiteral("idb");
aText.AssignLiteral(IDB_DIRECTORY_NAME);
break;
case ASMJS:
aText.AssignLiteral(ASMJSCACHE_DIRECTORY_NAME);
break;
case TYPE_MAX:
@ -61,9 +69,12 @@ public:
static nsresult
TypeFromText(const nsAString& aText, Type& aType)
{
if (aText.EqualsLiteral("idb")) {
if (aText.EqualsLiteral(IDB_DIRECTORY_NAME)) {
aType = IDB;
}
else if (aText.EqualsLiteral(ASMJSCACHE_DIRECTORY_NAME)) {
aType = ASMJS;
}
else {
return NS_ERROR_FAILURE;
}

View File

@ -26,6 +26,7 @@
#include "GeckoProfiler.h"
#include "mozilla/Atomics.h"
#include "mozilla/CondVar.h"
#include "mozilla/dom/asmjscache/AsmJSCache.h"
#include "mozilla/dom/file/FileService.h"
#include "mozilla/dom/indexedDB/Client.h"
#include "mozilla/Mutex.h"
@ -1098,7 +1099,7 @@ QuotaManager::Init()
NS_WARNING("Unable to respond to testing pref changes!");
}
static_assert(Client::IDB == 0 && Client::TYPE_MAX == 1,
static_assert(Client::IDB == 0 && Client::ASMJS == 1 && Client::TYPE_MAX == 2,
"Fix the registration!");
NS_ASSERTION(mClients.Capacity() == Client::TYPE_MAX,
@ -1106,6 +1107,7 @@ QuotaManager::Init()
// Register IndexedDB
mClients.AppendElement(new indexedDB::Client());
mClients.AppendElement(asmjscache::CreateClient());
return NS_OK;
}
@ -2006,6 +2008,7 @@ QuotaManager::GetStorageQuotaMB()
void
QuotaManager::GetStorageId(PersistenceType aPersistenceType,
const nsACString& aOrigin,
Client::Type aClientType,
const nsAString& aName,
nsACString& aDatabaseId)
{
@ -2014,6 +2017,8 @@ QuotaManager::GetStorageId(PersistenceType aPersistenceType,
str.Append('*');
str.Append(aOrigin);
str.Append('*');
str.AppendInt(aClientType);
str.Append('*');
str.Append(NS_ConvertUTF16toUTF8(aName));
aDatabaseId = str;

View File

@ -288,6 +288,7 @@ public:
static void
GetStorageId(PersistenceType aPersistenceType,
const nsACString& aOrigin,
Client::Type aClientType,
const nsAString& aName,
nsACString& aDatabaseId);

View File

@ -1009,8 +1009,10 @@ js::LookupAsmJSModuleInCache(ExclusiveContext *cx,
if (!cursor)
return false;
if (cursor != entry.memory + entry.serializedSize)
MOZ_CRASH("Corrupt serialized module");
bool atEnd = cursor == entry.memory + entry.serializedSize;
MOZ_ASSERT(atEnd, "Corrupt cache file");
if (!atEnd)
return true;
module->staticallyLink(linkData, cx);

View File

@ -39,6 +39,7 @@ support-files =
[test_asmjs.html]
# bug 929498
skip-if = os == 'android'
[test_asmjs2.html]
[test_bug384632.html]
[test_bug390488.html]
[test_bug393269.html]

View File

@ -0,0 +1,63 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=929236
-->
<head>
<meta charset="utf-8">
<title>asm.js browser tests</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=929236">asm.js browser tests</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test"></pre>
<script>
var jsFuns = SpecialPowers.Cu.getJSTestingFunctions();
ok(jsFuns.isAsmJSCompilationAvailable());
// generate a big ol asm.js module and compile it async so that we can hit
// the asm.js cache.
var code = "function f() { 'use asm';\n";
for (var i = 0; i < 1000; i++)
code += "function g" + i + "() { return " + i + "}\n";
code += "return g42 }\n";
code += "ok(jsFuns.isAsmJSModule(f), 'f is an asm.js module')\n";
code += "var g42 = f();\n";
code += "ok(jsFuns.isAsmJSFunction(g42), 'g42 is an asm.js function')\n";
code += "ok(g42() === 42, 'g42 returns the correct result')\n";
code += "finishedEvalAsync();";
ok(code.length > 10000);
function evalAsync(code) {
var blob = new Blob([code], {type:"application/javascript"});
var script = document.createElement('script');
script.src = URL.createObjectURL(blob);
document.body.appendChild(script);
}
var state = 0;
function finishedEvalAsync() {
switch (state) {
case 0:
state++;
evalAsync(code);
break;
case 1:
SimpleTest.finish();
break;
default:
throw "huh?";
}
}
SimpleTest.waitForExplicitFinish();
evalAsync(code);
</script>
</body>
</html>