зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changeset 58f0722012cd (bug 1475415) for force-cargo-library-build bustages CLOSED TREE
This commit is contained in:
Родитель
8724a9004a
Коммит
87009004b2
|
@ -900,7 +900,6 @@ dependencies = [
|
|||
"encoding_glue 0.1.0",
|
||||
"env_logger 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"geckoservo 0.0.1",
|
||||
"ipdl_bindings 0.1.0",
|
||||
"log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mozurl 0.0.1",
|
||||
"mp4parse_capi 0.10.1",
|
||||
|
@ -1040,23 +1039,6 @@ dependencies = [
|
|||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipdl"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"pipdl 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipdl_bindings"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ipdl 0.1.0",
|
||||
"mfbt-maybe 0.1.0",
|
||||
"nsstring 0.1.0",
|
||||
"thin-vec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.7.6"
|
||||
|
@ -1323,10 +1305,6 @@ name = "memoffset"
|
|||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "mfbt-maybe"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.1.2"
|
||||
|
@ -1677,10 +1655,6 @@ dependencies = [
|
|||
"siphasher 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pipdl"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.9"
|
||||
|
@ -2316,14 +2290,6 @@ name = "thin-slice"
|
|||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "thin-vec"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "0.3.5"
|
||||
|
@ -3069,7 +3035,6 @@ dependencies = [
|
|||
"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
|
||||
"checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693"
|
||||
"checksum thin-slice 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c"
|
||||
"checksum thin-vec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "73fdf4b84c65a85168477b7fb6c498e0716bc9487fba24623389ea7f51708044"
|
||||
"checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963"
|
||||
"checksum thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf947d192a9be60ef5131cc7a4648886ba89d712f16700ebbf80c8a69d05d48f"
|
||||
"checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b"
|
||||
|
|
|
@ -7422,16 +7422,6 @@ nsGlobalWindowInner::MessageManager()
|
|||
return mChromeFields.mMessageManager;
|
||||
}
|
||||
|
||||
IPDL*
|
||||
nsGlobalWindowInner::IPDL()
|
||||
{
|
||||
MOZ_ASSERT(IsChromeWindow());
|
||||
if (!mChromeFields.mIPDL) {
|
||||
mChromeFields.mIPDL = new class IPDL(this);
|
||||
}
|
||||
return mChromeFields.mIPDL;
|
||||
}
|
||||
|
||||
ChromeMessageBroadcaster*
|
||||
nsGlobalWindowInner::GetGroupMessageManager(const nsAString& aGroup)
|
||||
{
|
||||
|
|
|
@ -37,7 +37,6 @@
|
|||
#include "mozilla/dom/DOMPrefs.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "mozilla/dom/ChromeMessageBroadcaster.h"
|
||||
#include "mozilla/dom/IPDL.h"
|
||||
#include "mozilla/dom/NavigatorBinding.h"
|
||||
#include "mozilla/dom/StorageEvent.h"
|
||||
#include "mozilla/dom/StorageEventBinding.h"
|
||||
|
@ -939,7 +938,6 @@ public:
|
|||
void NotifyDefaultButtonLoaded(mozilla::dom::Element& aDefaultButton,
|
||||
mozilla::ErrorResult& aError);
|
||||
mozilla::dom::ChromeMessageBroadcaster* MessageManager();
|
||||
mozilla::dom::IPDL* IPDL();
|
||||
mozilla::dom::ChromeMessageBroadcaster* GetGroupMessageManager(const nsAString& aGroup);
|
||||
void BeginWindowMove(mozilla::dom::Event& aMouseDownEvent,
|
||||
mozilla::ErrorResult& aError);
|
||||
|
@ -1491,8 +1489,6 @@ protected:
|
|||
RefPtr<mozilla::dom::ChromeMessageBroadcaster> mMessageManager;
|
||||
nsRefPtrHashtable<nsStringHashKey,
|
||||
mozilla::dom::ChromeMessageBroadcaster> mGroupMessageManagers;
|
||||
|
||||
RefPtr<mozilla::dom::IPDL> mIPDL;
|
||||
} mChromeFields;
|
||||
|
||||
// These fields are used by the inner and outer windows to prevent
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
include protocol PTest;
|
||||
|
||||
sync protocol PSubTest {
|
||||
manager PTest;
|
||||
|
||||
both:
|
||||
async AsyncMessage(int inNumber) returns(int outNumber);
|
||||
|
||||
parent:
|
||||
sync SyncMessage(int inNumber) returns(int outNumber);
|
||||
|
||||
child:
|
||||
async __delete__();
|
||||
};
|
|
@ -1,14 +0,0 @@
|
|||
include protocol PSubTest;
|
||||
|
||||
sync protocol PTest {
|
||||
manages PSubTest;
|
||||
|
||||
both:
|
||||
async AsyncMessage(int inNumber) returns(int outNumber);
|
||||
|
||||
parent:
|
||||
sync SyncMessage(int inNumber) returns(int outNumber);
|
||||
|
||||
child:
|
||||
async PSubTest();
|
||||
};
|
|
@ -1 +0,0 @@
|
|||
var IPDLGlob = new IPDL();
|
|
@ -1,42 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
IPDLGlob.registerProtocol("PTest", "resource://test/PTest.ipdl");
|
||||
IPDLGlob.registerProtocol("PSubTest", "resource://test/PSubTest.ipdl");
|
||||
|
||||
// Child process tests
|
||||
async function run_test() {
|
||||
IPDLGlob.registerTopLevelClass(TestChild);
|
||||
|
||||
// Note that we stop the test in the parent process test
|
||||
}
|
||||
|
||||
class TestChild extends IPDLGlob.PTestChild {
|
||||
allocPSubTest() {
|
||||
return new SubTestChild();
|
||||
}
|
||||
|
||||
async recvPSubTestConstructor(protocol) {
|
||||
test_ipdlChildSendSyncMessage(protocol);
|
||||
await test_ipdlChildSendAsyncMessage(protocol);
|
||||
}
|
||||
|
||||
recvAsyncMessage(input) {
|
||||
return input + 1;
|
||||
}
|
||||
}
|
||||
|
||||
class SubTestChild extends IPDLGlob.PSubTestChild {
|
||||
recvAsyncMessage(input) {
|
||||
return input + 1;
|
||||
}
|
||||
}
|
||||
|
||||
function test_ipdlChildSendSyncMessage(protocol) {
|
||||
Assert.equal(protocol.sendSyncMessage(42), 43);
|
||||
}
|
||||
|
||||
async function test_ipdlChildSendAsyncMessage(protocol) {
|
||||
await protocol.sendAsyncMessage(42).then(res => {
|
||||
Assert.equal(res, 43);
|
||||
});
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
IPDLGlob.registerProtocol("PTest", "resource://test/PTest.ipdl");
|
||||
IPDLGlob.registerProtocol("PSubTest", "resource://test/PSubTest.ipdl");
|
||||
|
||||
// Parent process test starting
|
||||
function run_test() {
|
||||
do_load_child_test_harness();
|
||||
// Switch test termination to manual
|
||||
do_test_pending();
|
||||
|
||||
do_get_idle();
|
||||
|
||||
IPDLGlob.registerTopLevelClass(TestParent);
|
||||
|
||||
// Load the child process test script
|
||||
sendCommand('load("head_ipdl.js"); load("test_ipdl_child.js");', function() {
|
||||
|
||||
// Setup child protocol and run its tests
|
||||
sendCommand("run_test();", async function() {
|
||||
// Run parent tests
|
||||
var protocol = IPDLGlob.getTopLevelInstances()[0];
|
||||
|
||||
try {
|
||||
await test_ipdlParentSendAsyncMessage(protocol);
|
||||
await test_ipdlParentSendPSubTestConstructor(protocol);
|
||||
} catch (errMsg) {
|
||||
do_test_finished();
|
||||
throw new Error(errMsg);
|
||||
}
|
||||
|
||||
// We're done, bye.
|
||||
do_test_finished();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class TestParent extends IPDLGlob.PTestParent {
|
||||
recvSyncMessage(input) {
|
||||
return input + 1;
|
||||
}
|
||||
|
||||
recvAsyncMessage(input) {
|
||||
return input + 1;
|
||||
}
|
||||
|
||||
allocPSubTest() {
|
||||
return new SubTestParent();
|
||||
}
|
||||
}
|
||||
|
||||
class SubTestParent extends IPDLGlob.PSubTestParent {
|
||||
recvSyncMessage(input) {
|
||||
return input + 1;
|
||||
}
|
||||
|
||||
recvAsyncMessage(input) {
|
||||
return input + 1;
|
||||
}
|
||||
}
|
||||
|
||||
async function test_ipdlParentSendAsyncMessage(protocol) {
|
||||
await protocol.sendAsyncMessage(42).then(res => {
|
||||
equal(res, 43);
|
||||
});
|
||||
}
|
||||
|
||||
async function test_ipdlParentSendPSubTestConstructor(protocol) {
|
||||
await protocol.sendPSubTestConstructor().then(async subprotocol => {
|
||||
await subprotocol.sendAsyncMessage(42).then(res => {
|
||||
equal(res, 43);
|
||||
});
|
||||
|
||||
await subprotocol.send__delete__();
|
||||
});
|
||||
}
|
|
@ -22,8 +22,6 @@ support-files =
|
|||
isequalnode_data.xml
|
||||
nodelist_data_1.xml
|
||||
nodelist_data_2.xul
|
||||
PTest.ipdl
|
||||
PSubTest.ipdl
|
||||
test_delete_range.xml
|
||||
|
||||
[test_bug553888.js]
|
||||
|
@ -54,13 +52,6 @@ head = head_xml.js
|
|||
[test_xmlserializer.js]
|
||||
[test_cancelPrefetch.js]
|
||||
[test_chromeutils_base64.js]
|
||||
[test_ipdl_parent.js]
|
||||
head = head_ipdl.js
|
||||
skip-if = toolkit == 'android'
|
||||
firefox-appdir = browser
|
||||
[test_ipdl_child.js]
|
||||
skip-if = true # Used by test_ipdl_parent only
|
||||
head = head_ipdl.js
|
||||
[test_generate_xpath.js]
|
||||
head = head_xml.js
|
||||
[test_js_dev_error_interceptor.js]
|
||||
|
|
|
@ -490,10 +490,6 @@ DOMInterfaces = {
|
|||
'headerFile': 'DOMIntersectionObserver.h',
|
||||
},
|
||||
|
||||
'IPDL': {
|
||||
'implicitJSContext': [ 'registerProtocol']
|
||||
},
|
||||
|
||||
'KeyEvent': {
|
||||
'concrete': False
|
||||
},
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/.
|
||||
*/
|
||||
|
||||
|
||||
// UPDATE IPDL::sPropertyNames IF YOU ADD METHODS OR ATTRIBUTES HERE.
|
||||
|
||||
[ChromeConstructor, ChromeOnly, Exposed=(Window, System, Worker), NeedResolve]
|
||||
interface IPDL {
|
||||
void registerProtocol(DOMString protocolName, DOMString protocolURI, optional boolean eagerLoad = false);
|
||||
void registerTopLevelClass(object classConstructor);
|
||||
object getTopLevelInstance(optional unsigned long id);
|
||||
sequence<object> getTopLevelInstances();
|
||||
};
|
|
@ -35,7 +35,6 @@ WEBIDL_FILES = [
|
|||
'DominatorTree.webidl',
|
||||
'HeapSnapshot.webidl',
|
||||
'InspectorUtils.webidl',
|
||||
'IPDL.webidl',
|
||||
'IteratorResult.webidl',
|
||||
'MatchGlob.webidl',
|
||||
'MatchPattern.webidl',
|
||||
|
|
|
@ -62,7 +62,6 @@
|
|||
#include "mozilla/ipc/PChildToParentStreamChild.h"
|
||||
#include "mozilla/intl/LocaleService.h"
|
||||
#include "mozilla/ipc/TestShellChild.h"
|
||||
#include "mozilla/ipdl/ipc/PContentChildIPCInterface.h"
|
||||
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
|
||||
#include "mozilla/jsipc/PJavaScript.h"
|
||||
#include "mozilla/layers/APZChild.h"
|
||||
|
@ -2628,26 +2627,6 @@ ContentChild::RecvUpdateSharedData(const FileDescriptor& aMapFile,
|
|||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
ContentChild::RecvAsyncMessageIPDL(const nsCString& aProtocolName,
|
||||
const uint32_t& aChannelId,
|
||||
const nsCString& aMessage,
|
||||
const ClonedMessageData& aData,
|
||||
AsyncMessageIPDLResolver&& aResolve)
|
||||
{
|
||||
AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING(
|
||||
"ContentChild::RecvAsyncMessageIPDL", OTHER, aMessage);
|
||||
|
||||
if (mIPDLIPCInterface) {
|
||||
nsTArray<StructuredCloneData> returnData;
|
||||
auto res = mIPDLIPCInterface->RecvMessage(aProtocolName, aChannelId, aMessage, aData, &returnData);
|
||||
aResolve(std::move(returnData));
|
||||
return res;
|
||||
}
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
ContentChild::RecvGeolocationUpdate(nsIDOMGeoPosition* aPosition)
|
||||
{
|
||||
|
@ -3921,13 +3900,6 @@ ContentChild::GetSpecificMessageEventTarget(const Message& aMsg)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
ContentChild::RegisterIPDLIPCInterface(
|
||||
mozilla::ipdl::ipc::PContentChildIPCInterface* aIPDLIPCInterface)
|
||||
{
|
||||
mIPDLIPCInterface = aIPDLIPCInterface;
|
||||
}
|
||||
|
||||
#ifdef NIGHTLY_BUILD
|
||||
void
|
||||
ContentChild::OnChannelReceivedMessage(const Message& aMsg)
|
||||
|
|
|
@ -64,12 +64,6 @@ class OptionalURIParams;
|
|||
class URIParams;
|
||||
}// namespace ipc
|
||||
|
||||
namespace ipdl {
|
||||
namespace ipc {
|
||||
class PContentChildIPCInterface;
|
||||
} // namespace ipc
|
||||
} // namespace ipdl
|
||||
|
||||
namespace dom {
|
||||
|
||||
namespace ipc {
|
||||
|
@ -406,13 +400,6 @@ public:
|
|||
const IPC::Principal& aPrincipal,
|
||||
const ClonedMessageData& aData) override;
|
||||
|
||||
// See PContent.ipdl for doc.
|
||||
virtual mozilla::ipc::IPCResult RecvAsyncMessageIPDL(const nsCString& aProtocol,
|
||||
const uint32_t& aChannelId,
|
||||
const nsCString& aMessage,
|
||||
const ClonedMessageData& aData,
|
||||
AsyncMessageIPDLResolver&& aResolve) override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvRegisterStringBundles(nsTArray<StringBundleDescriptor>&& stringBundles) override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvUpdateSharedData(const FileDescriptor& aMapFile,
|
||||
|
@ -761,9 +748,6 @@ public:
|
|||
}
|
||||
#endif
|
||||
|
||||
void RegisterIPDLIPCInterface(
|
||||
mozilla::ipdl::ipc::PContentChildIPCInterface* aIPDLIPCInterface);
|
||||
|
||||
private:
|
||||
static void ForceKillTimerCallback(nsITimer* aTimer, void* aClosure);
|
||||
void StartForceKillTimer();
|
||||
|
@ -869,8 +853,6 @@ private:
|
|||
|
||||
mozilla::Atomic<bool> mShuttingDown;
|
||||
|
||||
mozilla::ipdl::ipc::PContentChildIPCInterface* mIPDLIPCInterface;
|
||||
|
||||
#ifdef NIGHTLY_BUILD
|
||||
// NOTE: This member is atomic because it can be accessed from off-main-thread.
|
||||
mozilla::Atomic<uint32_t> mPendingInputEvents;
|
||||
|
|
|
@ -77,7 +77,6 @@
|
|||
#include "mozilla/ipc/PChildToParentStreamParent.h"
|
||||
#include "mozilla/ipc/TestShellParent.h"
|
||||
#include "mozilla/ipc/IPCStreamUtils.h"
|
||||
#include "mozilla/ipdl/ipc/PContentParentIPCInterface.h"
|
||||
#include "mozilla/intl/LocaleService.h"
|
||||
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
|
||||
#include "mozilla/layers/PAPZParent.h"
|
||||
|
@ -2326,7 +2325,6 @@ ContentParent::ContentParent(ContentParent* aOpener,
|
|||
, mIsRemoteInputEventQueueEnabled(false)
|
||||
, mIsInputPriorityEventEnabled(false)
|
||||
, mHangMonitorActor(nullptr)
|
||||
, mIPDLIPCInterface(nullptr)
|
||||
{
|
||||
// Insert ourselves into the global linked list of ContentParent objects.
|
||||
if (!sContentParents) {
|
||||
|
@ -4008,23 +4006,6 @@ ContentParent::RecvSyncMessage(const nsString& aMsg,
|
|||
aPrincipal, aRetvals);
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
ContentParent::RecvSyncMessageIPDL(const nsCString& aProtocolName,
|
||||
const uint32_t& aChannelId,
|
||||
const nsCString& aMessage,
|
||||
const ClonedMessageData& aData,
|
||||
nsTArray<StructuredCloneData>* returnData)
|
||||
{
|
||||
AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING(
|
||||
"ContentParent::RecvSyncMessageIPDL", OTHER, aMessage);
|
||||
|
||||
if (mIPDLIPCInterface) {
|
||||
return mIPDLIPCInterface->RecvMessage(aProtocolName, aChannelId, aMessage, aData, returnData);
|
||||
}
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
ContentParent::RecvRpcMessage(const nsString& aMsg,
|
||||
const ClonedMessageData& aData,
|
||||
|
@ -4046,26 +4027,6 @@ ContentParent::RecvAsyncMessage(const nsString& aMsg,
|
|||
aData);
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
ContentParent::RecvAsyncMessageIPDL(const nsCString& aProtocolName,
|
||||
const uint32_t& aChannelId,
|
||||
const nsCString& aMessage,
|
||||
const ClonedMessageData& aData,
|
||||
AsyncMessageIPDLResolver&& aResolve)
|
||||
{
|
||||
AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING(
|
||||
"ContentParent::RecvAsyncMessageIPDL", OTHER, aMessage);
|
||||
|
||||
if (mIPDLIPCInterface) {
|
||||
nsTArray<StructuredCloneData> returnData;
|
||||
auto res = mIPDLIPCInterface->RecvMessage(aProtocolName, aChannelId, aMessage, aData, &returnData);
|
||||
aResolve(std::move(returnData));
|
||||
return res;
|
||||
}
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
static int32_t
|
||||
AddGeolocationListener(nsIDOMGeoPositionCallback* watcher,
|
||||
nsIDOMGeoPositionErrorCallback* errorCallBack,
|
||||
|
@ -6095,10 +6056,3 @@ ContentParent::RecvDetachBrowsingContext(const BrowsingContextId& aContextId,
|
|||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
void
|
||||
ContentParent::RegisterIPDLIPCInterface(
|
||||
mozilla::ipdl::ipc::PContentParentIPCInterface* aIPDLIPCInterface)
|
||||
{
|
||||
mIPDLIPCInterface = aIPDLIPCInterface;
|
||||
}
|
||||
|
|
|
@ -84,12 +84,6 @@ class ProtocolFuzzerHelper;
|
|||
#endif
|
||||
} // namespace ipc
|
||||
|
||||
namespace ipdl {
|
||||
namespace ipc {
|
||||
class PContentParentIPCInterface;
|
||||
} // namespace ipc
|
||||
} // namespace ipdl
|
||||
|
||||
namespace jsipc {
|
||||
class PJavaScriptParent;
|
||||
} // namespace jsipc
|
||||
|
@ -1079,13 +1073,6 @@ private:
|
|||
const IPC::Principal& aPrincipal,
|
||||
nsTArray<StructuredCloneData>* aRetvals) override;
|
||||
|
||||
// See PContent.ipdl for doc.
|
||||
virtual mozilla::ipc::IPCResult RecvSyncMessageIPDL(const nsCString& aProtocol,
|
||||
const uint32_t& aChannelId,
|
||||
const nsCString& aMessage,
|
||||
const ClonedMessageData& aData,
|
||||
nsTArray<StructuredCloneData>* returnData) override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult RecvRpcMessage(const nsString& aMsg,
|
||||
const ClonedMessageData& aData,
|
||||
InfallibleTArray<CpowEntry>&& aCpows,
|
||||
|
@ -1097,13 +1084,6 @@ private:
|
|||
const IPC::Principal& aPrincipal,
|
||||
const ClonedMessageData& aData) override;
|
||||
|
||||
// See PContent.ipdl for doc.
|
||||
virtual mozilla::ipc::IPCResult RecvAsyncMessageIPDL(const nsCString& aProtocol,
|
||||
const uint32_t& aChannelId,
|
||||
const nsCString& aMessage,
|
||||
const ClonedMessageData& aData,
|
||||
AsyncMessageIPDLResolver&& aResolve) override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult RecvAddGeolocationListener(const IPC::Principal& aPrincipal,
|
||||
const bool& aHighAccuracy) override;
|
||||
virtual mozilla::ipc::IPCResult RecvRemoveGeolocationListener() override;
|
||||
|
@ -1291,10 +1271,6 @@ public:
|
|||
|
||||
bool CanCommunicateWith(ContentParentId aOtherProcess);
|
||||
|
||||
void RegisterIPDLIPCInterface(
|
||||
mozilla::ipdl::ipc::PContentParentIPCInterface* aIPDLIPCInterface);
|
||||
|
||||
|
||||
nsresult SaveRecording(nsIFile* aFile, bool* aRetval);
|
||||
|
||||
bool IsRecordingOrReplaying() const {
|
||||
|
@ -1407,8 +1383,6 @@ private:
|
|||
|
||||
UniquePtr<mozilla::ipc::CrashReporterHost> mCrashReporter;
|
||||
|
||||
mozilla::ipdl::ipc::PContentParentIPCInterface* mIPDLIPCInterface;
|
||||
|
||||
static uint64_t sNextTabParentId;
|
||||
static nsDataHashtable<nsUint64HashKey, TabParent*> sNextTabParents;
|
||||
};
|
||||
|
|
484
dom/ipc/IPDL.cpp
484
dom/ipc/IPDL.cpp
|
@ -1,484 +0,0 @@
|
|||
#include "mozilla/dom/IPDL.h"
|
||||
|
||||
#include "mozilla/dom/IPDLBinding.h"
|
||||
|
||||
#include "mozilla/ipdl/IPDLProtocol.h"
|
||||
#include "mozilla/ipdl/IPDLProtocolInstance.h"
|
||||
#include "mozilla/ipdl/ipc/PContentChildIPCInterface.h"
|
||||
#include "mozilla/ipdl/ipc/PContentParentIPCInterface.h"
|
||||
#include "nsReadableUtils.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "nsWrapperCacheInlines.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(IPDL)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IPDL)
|
||||
tmp->mTopLevelClassConstructor = nullptr;
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mParsedProtocolClassTable)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTopLevelParentInstances)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTopLevelChildInstance)
|
||||
mozilla::DropJSObjects(tmp);
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IPDL)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParsedProtocolClassTable)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTopLevelParentInstances)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTopLevelChildInstance)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IPDL)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mTopLevelClassConstructor)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(IPDL)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(IPDL)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IPDL)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsIObserver)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
IPDL::IPDL(nsIGlobalObject* aParentObject)
|
||||
: mParentObject(aParentObject)
|
||||
, mChildInterface(nullptr)
|
||||
{
|
||||
mozilla::HoldJSObjects(this);
|
||||
|
||||
// Register to listen to new Content notifications.
|
||||
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
|
||||
if (os) {
|
||||
os->AddObserver(this, "ipc:content-created", /* ownsWeak */ true);
|
||||
}
|
||||
}
|
||||
|
||||
/* virtual */
|
||||
IPDL::~IPDL() {}
|
||||
|
||||
void
|
||||
IPDL::RegisterTopLevelClass(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aClassConstructor)
|
||||
{
|
||||
mTopLevelClassConstructor = aClassConstructor;
|
||||
|
||||
Sequence<JS::HandleValue> emptyArray;
|
||||
|
||||
// When we register the top level class, we want to trigger a content creation
|
||||
// event for all existing content protocol instances.
|
||||
for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) {
|
||||
NewTopLevelProtocolParent(aCx, emptyArray, cp);
|
||||
}
|
||||
|
||||
if (ContentChild::GetSingleton()) {
|
||||
NewTopLevelProtocolChild(aCx, emptyArray);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
IPDL::GetTopLevelInstance(JSContext* aCx,
|
||||
const Optional<uint32_t>& aId,
|
||||
JS::MutableHandle<JSObject*> aRetval)
|
||||
{
|
||||
// If we got an ID, then we return a parent instance, otherwise we return the
|
||||
// child instance.
|
||||
if (aId.WasPassed() && !ContentChild::GetSingleton()) {
|
||||
if (auto instance = mTopLevelParentInstances.GetWeak(aId.Value())) {
|
||||
aRetval.set(instance->GetInstanceObject());
|
||||
} else {
|
||||
JS_ReportErrorUTF8(aCx, "Unknown parent instance ID");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (mTopLevelChildInstance) {
|
||||
aRetval.set(mTopLevelChildInstance->GetInstanceObject());
|
||||
} else {
|
||||
JS_ReportErrorUTF8(aCx, "No top level child instance");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
IPDL::GetTopLevelInstances(JSContext* aCx, nsTArray<JSObject*>& aRetval)
|
||||
{
|
||||
// Return all instances depending on the side we're on.
|
||||
if (mTopLevelChildInstance) {
|
||||
aRetval.AppendElement(mTopLevelChildInstance->GetInstanceObject());
|
||||
} else {
|
||||
for (auto i = mTopLevelParentInstances.Iter(); !i.Done(); i.Next()) {
|
||||
aRetval.AppendElement(i.Data()->GetInstanceObject());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
IPDL::RegisterProtocol(JSContext* aCx,
|
||||
const nsAString& aProtocolName,
|
||||
const nsAString& aProtocolURI,
|
||||
bool aEagerLoad)
|
||||
{
|
||||
mProtocolURIs.Put(aProtocolName, nsString(aProtocolURI));
|
||||
if (aEagerLoad) {
|
||||
JS::RootedObject wrapper(aCx, GetWrapper());
|
||||
auto lookup = mParsedProtocolClassTable.LookupForAdd(aProtocolName);
|
||||
if (!lookup) {
|
||||
RefPtr<ipdl::IPDLProtocol> newIPDLProtocol = NewIPDLProtocol(aProtocolName, wrapper, aCx);
|
||||
|
||||
if (!newIPDLProtocol) {
|
||||
lookup.OrRemove();
|
||||
return;
|
||||
}
|
||||
|
||||
lookup.OrInsert([&newIPDLProtocol]() { return std::move(newIPDLProtocol); });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
IPDL::DoResolve(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aObj,
|
||||
JS::Handle<jsid> aId,
|
||||
JS::MutableHandle<JS::PropertyDescriptor> aRv)
|
||||
{
|
||||
// If it's not an ID, skip this.
|
||||
if (!JSID_IS_STRING(aId)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get the property name.
|
||||
nsAutoJSString propertyName;
|
||||
if (!propertyName.init(aCx, JSID_TO_STRING(aId))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check it's not a real existing property.
|
||||
if (IsRealProperty(propertyName)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, resolve the protocol property.
|
||||
return ResolveProtocolProperty(propertyName, aObj, aCx, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
IPDL::GetOwnPropertyNames(JSContext* aCx,
|
||||
JS::AutoIdVector& aNames,
|
||||
bool aEnumerableOnly,
|
||||
mozilla::ErrorResult& aRv)
|
||||
{
|
||||
if (aEnumerableOnly) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We want to return the real properties + the existing protocol names we
|
||||
// already parsed.
|
||||
if (!aNames.initCapacity(mParsedProtocolIDs.Capacity() +
|
||||
sPropertyNamesLength)) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
// We add the protocol names.
|
||||
JS::RootedId id(aCx);
|
||||
for (auto& name : mParsedProtocolIDs) {
|
||||
if (!JS_CharsToId(aCx, JS::TwoByteChars(name.get(), name.Length()), &id)) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
if (!aNames.append(id)) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// We add the real properties.
|
||||
for (auto& propertyName : sPropertyNames) {
|
||||
if (!JS_CharsToId(
|
||||
aCx,
|
||||
JS::TwoByteChars(propertyName.get(), propertyName.Length()),
|
||||
&id)) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
if (!aNames.append(id)) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsISupports*
|
||||
IPDL::GetParentObject()
|
||||
{
|
||||
return mParentObject;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
IPDL::MayResolve(jsid aId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/* virtual */ JSObject*
|
||||
IPDL::WrapObject(JSContext* aCx, JS::HandleObject aGivenProto)
|
||||
{
|
||||
return IPDL_Binding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<IPDL>
|
||||
IPDL::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
RefPtr<IPDL> ipdl = new IPDL(global);
|
||||
return ipdl.forget();
|
||||
}
|
||||
|
||||
bool
|
||||
IPDL::IsRealProperty(const nsAString& aPropertyName)
|
||||
{
|
||||
// Simple loop through the real property names.
|
||||
for (auto& propertyName : sPropertyNames) {
|
||||
if (aPropertyName.Equals(propertyName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
IPDL::ResolveProtocolProperty(const nsString& aProtocolPropertyName,
|
||||
JS::Handle<JSObject*> aObj,
|
||||
JSContext* aCx,
|
||||
JS::MutableHandle<JS::PropertyDescriptor> aRv)
|
||||
{
|
||||
// If we haven't parsed this protocol yet, do it and create a new IPDLProtocol
|
||||
// object.
|
||||
auto ipdlProtocol = mParsedProtocolClassTable.LookupForAdd(aProtocolPropertyName);
|
||||
if (!ipdlProtocol) {
|
||||
RefPtr<ipdl::IPDLProtocol> newIPDLProtocol = NewIPDLProtocol(aProtocolPropertyName, aObj, aCx);
|
||||
|
||||
if (!newIPDLProtocol) {
|
||||
ipdlProtocol.OrRemove();
|
||||
return false;
|
||||
}
|
||||
|
||||
ipdlProtocol.OrInsert([&newIPDLProtocol]() { return std::move(newIPDLProtocol); });
|
||||
}
|
||||
|
||||
aRv.object().set(aObj);
|
||||
aRv.value().setObject(*ipdlProtocol.Data()->GetProtocolClassConstructor());
|
||||
aRv.setAttributes(0);
|
||||
aRv.setGetter(nullptr);
|
||||
aRv.setSetter(nullptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Maybe<ipdl::IPDLSide>
|
||||
IPDL::GetProtocolSide(const nsAString& aProtocolName)
|
||||
{
|
||||
NS_NAMED_LITERAL_STRING(parentLiteral, "Parent");
|
||||
NS_NAMED_LITERAL_STRING(childLiteral, "Child");
|
||||
|
||||
// Find the parent suffix.
|
||||
if (StringEndsWith(aProtocolName, parentLiteral)) {
|
||||
return Some(ipdl::IPDLSide::Parent);
|
||||
}
|
||||
|
||||
// Find the child suffix.
|
||||
if (StringEndsWith(aProtocolName, childLiteral)) {
|
||||
return Some(ipdl::IPDLSide::Child);
|
||||
}
|
||||
|
||||
return Nothing();
|
||||
}
|
||||
|
||||
void
|
||||
IPDL::StripParentSuffix(const nsAString& aProtocolName, nsAString& aRetVal)
|
||||
{
|
||||
NS_NAMED_LITERAL_STRING(parentLiteral, "Parent");
|
||||
aRetVal = Substring(
|
||||
aProtocolName, 0, aProtocolName.Length() - parentLiteral.Length());
|
||||
}
|
||||
|
||||
void
|
||||
IPDL::StripChildSuffix(const nsAString& aProtocolName, nsAString& aRetVal)
|
||||
{
|
||||
NS_NAMED_LITERAL_STRING(childLiteral, "Child");
|
||||
aRetVal =
|
||||
Substring(aProtocolName, 0, aProtocolName.Length() - childLiteral.Length());
|
||||
}
|
||||
|
||||
ipdl::ipc::PContentChildIPCInterface*
|
||||
IPDL::GetOrInitChildInterface()
|
||||
{
|
||||
if (!mChildInterface && ContentChild::GetSingleton()) {
|
||||
mChildInterface.reset(new ipdl::ipc::PContentChildIPCInterface(
|
||||
this, ContentChild::GetSingleton()));
|
||||
}
|
||||
|
||||
return mChildInterface.get();
|
||||
}
|
||||
|
||||
already_AddRefed<ipdl::IPDLProtocol>
|
||||
IPDL::NewIPDLProtocol(const nsAString& aProtocolName,
|
||||
JS::Handle<JSObject*> aObj,
|
||||
JSContext* aCx)
|
||||
{
|
||||
auto ipdlSide = GetProtocolSide(aProtocolName);
|
||||
|
||||
nsAutoString unsidedProtocolName;
|
||||
// Assign a name depending on the protocol side.
|
||||
if (!ipdlSide) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
switch (*ipdlSide) {
|
||||
case ipdl::IPDLSide::Parent:
|
||||
StripParentSuffix(aProtocolName, unsidedProtocolName);
|
||||
break;
|
||||
|
||||
case ipdl::IPDLSide::Child:
|
||||
GetOrInitChildInterface();
|
||||
StripChildSuffix(aProtocolName, unsidedProtocolName);
|
||||
break;
|
||||
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!mParsedProtocolIDs.AppendElement(aProtocolName)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!mProtocolURIs.Contains(unsidedProtocolName)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create our new IPDLProtocol object and return it.
|
||||
auto newIPDLProtocol = MakeRefPtr<ipdl::IPDLProtocol>(
|
||||
this,
|
||||
*ipdlSide,
|
||||
NS_ConvertUTF16toUTF8(mProtocolURIs.Get(unsidedProtocolName)),
|
||||
xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx)),
|
||||
aObj,
|
||||
aCx);
|
||||
|
||||
// Some error during parsing or construction.
|
||||
if (!newIPDLProtocol->GetProtocolClassConstructor()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return newIPDLProtocol.forget();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
IPDL::NewTopLevelProtocolParent(JSContext* aCx,
|
||||
const Sequence<JS::Handle<JS::Value>>& aArgs,
|
||||
ContentParent* aCp)
|
||||
{
|
||||
// If we don't already have an IPCInterface for this content parent, create
|
||||
// it.
|
||||
auto parentInterface = mParentInterfaces.LookupForAdd(aCp->ChildID()).OrInsert([this, aCp]() {
|
||||
return new ipdl::ipc::PContentParentIPCInterface(this, aCp);
|
||||
});
|
||||
|
||||
JS::RootedObject constructor(aCx, mTopLevelClassConstructor.get());
|
||||
|
||||
// Prepare the arguments into a vector.
|
||||
JS::AutoValueVector argVector(aCx);
|
||||
if (!argVector.initCapacity(aArgs.Length())) {
|
||||
JS_ReportErrorUTF8(aCx, "Couldn't initialize argument vector");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (auto& arg : aArgs) {
|
||||
if (!argVector.append(arg.get())) {
|
||||
JS_ReportErrorUTF8(aCx, "Couldn't add argument to arg vector");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Create the protocol instance, add the IPCInterface to the
|
||||
// IPDLProtocolInstance...
|
||||
JS::RootedObject topLevelProtocol(aCx, JS_New(aCx, constructor, argVector));
|
||||
auto* instance =
|
||||
static_cast<ipdl::IPDLProtocolInstance*>(JS_GetPrivate(topLevelProtocol.get()));
|
||||
|
||||
if (!instance) {
|
||||
JS_ReportErrorUTF8(aCx, "Couldn't get protocol instance object from private date field");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
instance->SetIPCInterface(parentInterface);
|
||||
|
||||
mTopLevelParentInstances.Put(aCp->ChildID(), instance);
|
||||
|
||||
return topLevelProtocol;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
IPDL::NewTopLevelProtocolChild(JSContext* aCx,
|
||||
const Sequence<JS::Handle<JS::Value>>& aArgs)
|
||||
{
|
||||
JS::RootedObject constructor(aCx, mTopLevelClassConstructor.get());
|
||||
|
||||
// Prepare the arguments into a vector.
|
||||
JS::AutoValueVector argVector(aCx);
|
||||
if (!argVector.initCapacity(aArgs.Length())) {
|
||||
JS_ReportErrorUTF8(aCx, "Couldn't initialize argument vector");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (auto& arg : aArgs) {
|
||||
if (!argVector.append(arg.get())) {
|
||||
JS_ReportErrorUTF8(aCx, "Couldn't add argument to arg vector");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Create the protocol instance, add the IPCInterface to the
|
||||
// IPDLProtocolInstance...
|
||||
JS::RootedObject topLevelProtocol(aCx, JS_New(aCx, constructor, argVector));
|
||||
auto* instance =
|
||||
static_cast<ipdl::IPDLProtocolInstance*>(JS_GetPrivate(topLevelProtocol.get()));
|
||||
|
||||
if (!instance) {
|
||||
JS_ReportErrorUTF8(aCx, "Couldn't get protocol instance object from private date field");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
instance->SetIPCInterface(GetOrInitChildInterface());
|
||||
|
||||
mTopLevelChildInstance = instance;
|
||||
|
||||
return topLevelProtocol;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
IPDL::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
|
||||
{
|
||||
nsDependentCString topic(aTopic);
|
||||
if (topic.EqualsLiteral("ipc:content-created")) {
|
||||
nsCOMPtr<nsIContentParent> cp = do_QueryInterface(aSubject);
|
||||
AutoEntryScript aes(mParentObject, "New Content Parent");
|
||||
NewTopLevelProtocolParent(
|
||||
aes.cx(), Sequence<JS::HandleValue>(), cp->AsContentParent());
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
constexpr nsLiteralString IPDL::sPropertyNames[];
|
||||
constexpr size_t IPDL::sPropertyNamesLength;
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
154
dom/ipc/IPDL.h
154
dom/ipc/IPDL.h
|
@ -1,154 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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 https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_IPDL__
|
||||
#define mozilla_dom_IPDL__
|
||||
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsRefPtrHashtable.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/HoldDropJSObjects.h"
|
||||
|
||||
class nsIGlobalObject;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace ipdl {
|
||||
class IPDLProtocol;
|
||||
class IPDLProtocolInstance;
|
||||
enum class IPDLSide : bool;
|
||||
|
||||
namespace ipc {
|
||||
class PContentParentIPCInterface;
|
||||
class PContentChildIPCInterface;
|
||||
} // ipc
|
||||
} // ipdl
|
||||
|
||||
namespace dom {
|
||||
|
||||
class ContentParent;
|
||||
|
||||
class IPDL
|
||||
: public nsIObserver
|
||||
, public nsWrapperCache
|
||||
{
|
||||
protected:
|
||||
virtual ~IPDL();
|
||||
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IPDL)
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
explicit IPDL(nsIGlobalObject* aParentObject);
|
||||
|
||||
static already_AddRefed<IPDL> Constructor(const GlobalObject& aGlobal,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void SetupContentParent();
|
||||
|
||||
void RegisterProtocol(JSContext* aCx, const nsAString& aProtocolName, const nsAString& aProtocolURI, bool aEagerLoad);
|
||||
|
||||
void RegisterTopLevelClass(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aClassConstructor);
|
||||
void CreateTopLevelInstance(JSContext* aCx,
|
||||
const Sequence<JS::Handle<JS::Value>>& aArgs);
|
||||
void GetTopLevelInstance(JSContext* aCx,
|
||||
const Optional<uint32_t>& aId,
|
||||
JS::MutableHandle<JSObject*> aRetval);
|
||||
void GetTopLevelInstances(JSContext* aCx,
|
||||
nsTArray<JSObject*>& aRetval);
|
||||
|
||||
bool DoResolve(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aObj,
|
||||
JS::Handle<jsid> aId,
|
||||
JS::MutableHandle<JS::PropertyDescriptor> aRv);
|
||||
|
||||
void GetOwnPropertyNames(JSContext* aCx,
|
||||
JS::AutoIdVector& aNames,
|
||||
bool aEnumerableOnly,
|
||||
ErrorResult& aRv);
|
||||
|
||||
nsISupports* GetParentObject();
|
||||
|
||||
nsIGlobalObject* GetGlobalObject() { return mParentObject; }
|
||||
|
||||
static bool MayResolve(jsid aId);
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::HandleObject aGivenProto) override;
|
||||
|
||||
ipdl::ipc::PContentParentIPCInterface* GetParentInterface(uint32_t aId)
|
||||
{
|
||||
return mParentInterfaces.Get(aId);
|
||||
}
|
||||
|
||||
ipdl::ipc::PContentChildIPCInterface* GetChildInterface()
|
||||
{
|
||||
return mChildInterface.get();
|
||||
}
|
||||
|
||||
JSObject* NewTopLevelProtocolParent(
|
||||
JSContext* aCx,
|
||||
const Sequence<JS::Handle<JS::Value>>& aArgs,
|
||||
ContentParent* aCp);
|
||||
JSObject* NewTopLevelProtocolChild(
|
||||
JSContext* aCx,
|
||||
const Sequence<JS::Handle<JS::Value>>& aArgs);
|
||||
|
||||
protected:
|
||||
bool IsRealProperty(const nsAString& aPropertyName);
|
||||
|
||||
bool ResolveProtocolProperty(const nsString& aProtocolPropertyName,
|
||||
JS::Handle<JSObject*> aObj,
|
||||
JSContext* aCx,
|
||||
JS::MutableHandle<JS::PropertyDescriptor> aRv);
|
||||
|
||||
Maybe<ipdl::IPDLSide> GetProtocolSide(const nsAString& aProtocolName);
|
||||
|
||||
void StripParentSuffix(const nsAString& aProtocolName, nsAString& aRetVal);
|
||||
void StripChildSuffix(const nsAString& aProtocolName, nsAString& aRetVal);
|
||||
|
||||
ipdl::ipc::PContentChildIPCInterface* GetOrInitChildInterface();
|
||||
|
||||
already_AddRefed<ipdl::IPDLProtocol> NewIPDLProtocol(
|
||||
const nsAString& aProtocolName,
|
||||
JS::Handle<JSObject*> aObj,
|
||||
JSContext* aCx);
|
||||
|
||||
typedef nsRefPtrHashtable<nsStringHashKey, ipdl::IPDLProtocol>
|
||||
ProtocolClassTable;
|
||||
|
||||
ProtocolClassTable mParsedProtocolClassTable;
|
||||
nsTArray<nsString> mParsedProtocolIDs;
|
||||
nsIGlobalObject* MOZ_NON_OWNING_REF mParentObject;
|
||||
|
||||
nsClassHashtable<nsUint32HashKey, ipdl::ipc::PContentParentIPCInterface>
|
||||
mParentInterfaces;
|
||||
UniquePtr<ipdl::ipc::PContentChildIPCInterface> mChildInterface;
|
||||
|
||||
nsRefPtrHashtable<nsUint32HashKey, ipdl::IPDLProtocolInstance>
|
||||
mTopLevelParentInstances;
|
||||
RefPtr<ipdl::IPDLProtocolInstance> mTopLevelChildInstance;
|
||||
|
||||
JS::Heap<JSObject*> mTopLevelClassConstructor;
|
||||
|
||||
nsDataHashtable<nsStringHashKey, nsString> mProtocolURIs;
|
||||
|
||||
static constexpr const nsLiteralString sPropertyNames[] = {NS_LITERAL_STRING("registerTopLevelClass"), NS_LITERAL_STRING("getTopLevelInstance"), NS_LITERAL_STRING("getTopLevelInstances"), NS_LITERAL_STRING("registerProtocol")};
|
||||
static constexpr size_t sPropertyNamesLength = ArrayLength(sPropertyNames);
|
||||
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_IPDL__
|
|
@ -826,14 +826,6 @@ parent:
|
|||
CpowEntry[] aCpows, Principal aPrincipal)
|
||||
returns (StructuredCloneData[] retval);
|
||||
|
||||
/**
|
||||
* Send an IPDL sync message with args packed as a JS array in the input
|
||||
* and the output ClonedMessageData.
|
||||
*/
|
||||
sync SyncMessageIPDL(nsCString aProtocolName, uint32_t aChannelId,
|
||||
nsCString aMessage, ClonedMessageData aData)
|
||||
returns (StructuredCloneData[] retval);
|
||||
|
||||
nested(inside_sync) sync RpcMessage(nsString aMessage, ClonedMessageData aData,
|
||||
CpowEntry[] aCpows, Principal aPrincipal)
|
||||
returns (StructuredCloneData[] retval);
|
||||
|
@ -1206,17 +1198,9 @@ parent:
|
|||
async DetachBrowsingContext(BrowsingContextId aContextId,
|
||||
bool aMoveToBFCache);
|
||||
both:
|
||||
async AsyncMessage(nsString aMessage, CpowEntry[] aCpows,
|
||||
async AsyncMessage(nsString aMessage, CpowEntry[] aCpows,
|
||||
Principal aPrincipal, ClonedMessageData aData);
|
||||
|
||||
/**
|
||||
* Send an IPDL async message with args packed as a JS array in the input
|
||||
* and the output ClonedMessageData.
|
||||
*/
|
||||
async AsyncMessageIPDL(nsCString aProtocol, uint32_t aChannelId,
|
||||
nsCString aMessage, ClonedMessageData aData)
|
||||
returns (StructuredCloneData[] retval);
|
||||
|
||||
/**
|
||||
* Notify `push-subscription-modified` observers in the parent and child.
|
||||
*/
|
||||
|
|
|
@ -40,7 +40,6 @@ EXPORTS.mozilla.dom += [
|
|||
'ContentProcessManager.h',
|
||||
'CPOWManagerGetter.h',
|
||||
'FilePickerParent.h',
|
||||
'IPDL.h',
|
||||
'MemoryReportRequest.h',
|
||||
'nsIContentChild.h',
|
||||
'nsIContentParent.h',
|
||||
|
@ -91,7 +90,6 @@ UNIFIED_SOURCES += [
|
|||
# ContentChild.cpp cannot be compiled in unified mode on linux due to Time conflict
|
||||
SOURCES += [
|
||||
'ContentChild.cpp',
|
||||
'IPDL.cpp',
|
||||
'ProcessHangMonitor.cpp',
|
||||
]
|
||||
|
||||
|
|
|
@ -440,9 +440,6 @@ partial interface Window {
|
|||
[Func="nsGlobalWindowInner::IsPrivilegedChromeWindow"]
|
||||
readonly attribute ChromeMessageBroadcaster messageManager;
|
||||
|
||||
[Func="nsGlobalWindowInner::IsPrivilegedChromeWindow"]
|
||||
readonly attribute IPDL IPDL;
|
||||
|
||||
/**
|
||||
* Returns the message manager identified by the given group name that
|
||||
* manages all frame loaders belonging to that group.
|
||||
|
|
|
@ -848,8 +848,6 @@ description = test only
|
|||
description = test only
|
||||
[PContent::SyncMessage]
|
||||
description =
|
||||
[PContent::SyncMessageIPDL]
|
||||
description =
|
||||
[PContent::CreateChildProcess]
|
||||
description =
|
||||
[PContent::BridgeToChildProcess]
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
[package]
|
||||
name = "ipdl"
|
||||
version = "0.1.0"
|
||||
authors = ["Tristan Bourvon <tristanbourvon@gmail.com>", "Andrew McCreight <continuation@gmail.com>"]
|
||||
license = "MPL-2.0"
|
||||
|
||||
[dependencies]
|
||||
pipdl = { path = "../pipdl" }
|
||||
|
||||
[lib]
|
||||
name = "ipdl"
|
||||
path = "src/lib.rs"
|
|
@ -1,5 +0,0 @@
|
|||
# ipdl-rs
|
||||
|
||||
Code taken from https://github.com/mystor/pipdl-rs has been written by Nika Layzell
|
||||
|
||||
Code taken from https://github.com/amccreight/ipdl_parser has been written by Andrew McCreight
|
|
@ -1,58 +0,0 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
# 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/.
|
||||
|
||||
# Test command generator for the IPDL parser.
|
||||
|
||||
# 1. Add the following code to ipc/ipdl/ipdl.py, do a build, and copy
|
||||
# the output from the build, from INCLUDES to DONE, somewhere:
|
||||
#
|
||||
# print "INCLUDES"
|
||||
# for i in includedirs:
|
||||
# print i
|
||||
# print "FILES"
|
||||
# for f in files:
|
||||
# print f
|
||||
# print "DONE"
|
||||
|
||||
# 2. Adjust leading_text_example as necessary, if the log timestamp
|
||||
# stuff has changed.
|
||||
|
||||
# 3. Run this script on the output from step 1. This should produce a
|
||||
# command to run cargo with all of the files from step 1. You can run
|
||||
# it with bash or whatever.
|
||||
|
||||
import sys
|
||||
|
||||
# Used to decide how many characters to chop off the start.
|
||||
leading_text_example = " 0:02.39 "
|
||||
|
||||
in_include = False
|
||||
in_files = False
|
||||
|
||||
start_trim = len(leading_text_example)
|
||||
|
||||
print("cargo run --"),
|
||||
|
||||
for line in sys.stdin:
|
||||
line = line [start_trim:-1]
|
||||
if line.endswith("INCLUDES"):
|
||||
in_include = True
|
||||
continue
|
||||
if line.endswith("FILES"):
|
||||
assert in_include
|
||||
in_include = False
|
||||
in_files = True
|
||||
continue
|
||||
if line.endswith("DONE"):
|
||||
assert in_files
|
||||
exit(0)
|
||||
|
||||
if in_include:
|
||||
print("-I", line),
|
||||
elif in_files:
|
||||
print(line),
|
||||
else:
|
||||
assert False
|
|
@ -1,285 +0,0 @@
|
|||
/* 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/. */
|
||||
|
||||
use std::fmt;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct QualifiedId {
|
||||
pub base_id: Identifier,
|
||||
pub quals: Vec<String>,
|
||||
}
|
||||
|
||||
impl QualifiedId {
|
||||
pub fn new(base: Identifier) -> QualifiedId {
|
||||
QualifiedId { base_id: base, quals: Vec::new() }
|
||||
}
|
||||
|
||||
pub fn qualify(mut self, id: Identifier) -> QualifiedId {
|
||||
self.quals.push(self.base_id.id);
|
||||
self.base_id = id;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn new_from_iter<'a, I> (mut ids: I) -> QualifiedId
|
||||
where I: Iterator<Item=&'a str>
|
||||
{
|
||||
let loc = Location { file_name: String::from("<builtin>"), lineno: 0, colno: 0 };
|
||||
let mut qual_id = QualifiedId::new(Identifier::new(String::from(ids.next().expect("Empty iterator when creating QID")), loc.clone()));
|
||||
for i in ids {
|
||||
qual_id = qual_id.qualify(Identifier::new(String::from(i), loc.clone()));
|
||||
}
|
||||
qual_id
|
||||
}
|
||||
|
||||
pub fn short_name(&self) -> String {
|
||||
self.base_id.to_string()
|
||||
}
|
||||
|
||||
pub fn full_name(&self) -> Option<String> {
|
||||
if self.quals.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(self.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn loc(&self) -> &Location {
|
||||
&self.base_id.loc
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for QualifiedId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
for q in &self.quals {
|
||||
try!(write!(f, "{}::", q));
|
||||
}
|
||||
write!(f, "{}", self.base_id)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TypeSpec {
|
||||
pub spec: QualifiedId,
|
||||
pub array: bool,
|
||||
pub nullable: bool,
|
||||
}
|
||||
|
||||
impl TypeSpec {
|
||||
pub fn new(spec: QualifiedId) -> TypeSpec {
|
||||
TypeSpec { spec: spec, array: false, nullable: false }
|
||||
}
|
||||
|
||||
pub fn loc(&self) -> &Location {
|
||||
self.spec.loc()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Param {
|
||||
pub name: Identifier,
|
||||
pub type_spec: TypeSpec,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct StructField {
|
||||
pub type_spec: TypeSpec,
|
||||
pub name: Identifier,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Namespace {
|
||||
pub name: Identifier,
|
||||
pub namespaces: Vec<String>,
|
||||
}
|
||||
|
||||
impl Namespace {
|
||||
pub fn qname(&self) -> QualifiedId {
|
||||
QualifiedId { base_id: self.name.clone(), quals: self.namespaces.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
pub enum Compress {
|
||||
None,
|
||||
Enabled,
|
||||
All,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
pub enum SendSemantics {
|
||||
Async,
|
||||
Sync,
|
||||
Intr,
|
||||
}
|
||||
|
||||
impl SendSemantics {
|
||||
pub fn is_async(&self) -> bool {
|
||||
self == &SendSemantics::Async
|
||||
}
|
||||
|
||||
pub fn is_sync(&self) -> bool {
|
||||
self == &SendSemantics::Sync
|
||||
}
|
||||
|
||||
pub fn is_intr(&self) -> bool {
|
||||
self == &SendSemantics::Intr
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
|
||||
pub enum Nesting {
|
||||
None,
|
||||
InsideSync,
|
||||
InsideCpow,
|
||||
}
|
||||
|
||||
impl Nesting {
|
||||
pub fn is_none(&self) -> bool {
|
||||
self == &Nesting::None
|
||||
}
|
||||
|
||||
pub fn inside_sync(&self) -> bool {
|
||||
self == &Nesting::InsideSync
|
||||
}
|
||||
|
||||
pub fn inside_cpow(&self) -> bool {
|
||||
self == &Nesting::InsideCpow
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Priority {
|
||||
Normal,
|
||||
High,
|
||||
Input,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum Direction {
|
||||
ToParent,
|
||||
ToChild,
|
||||
ToParentOrChild,
|
||||
}
|
||||
|
||||
impl Direction {
|
||||
pub fn is_to_child(&self) -> bool {
|
||||
self == &Direction::ToChild
|
||||
}
|
||||
|
||||
pub fn is_both(&self) -> bool {
|
||||
self == &Direction::ToParentOrChild
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Location {
|
||||
pub file_name: String,
|
||||
pub lineno: usize,
|
||||
pub colno: usize,
|
||||
}
|
||||
|
||||
impl fmt::Display for Location {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}:{}:{}", self.file_name, self.lineno, self.colno)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Identifier {
|
||||
pub id: String,
|
||||
pub loc: Location,
|
||||
}
|
||||
|
||||
impl Identifier {
|
||||
pub fn new(name: String, loc: Location) -> Identifier {
|
||||
Identifier {
|
||||
id: name,
|
||||
loc: loc,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Identifier {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.id)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MessageDecl {
|
||||
pub name: Identifier,
|
||||
pub send_semantics: SendSemantics,
|
||||
pub nested: Nesting,
|
||||
pub prio: Priority,
|
||||
pub direction: Direction,
|
||||
pub in_params: Vec<Param>,
|
||||
pub out_params: Vec<Param>,
|
||||
pub compress: Compress,
|
||||
pub verify: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Protocol {
|
||||
pub send_semantics: SendSemantics,
|
||||
pub nested: Nesting,
|
||||
pub managers: Vec<Identifier>,
|
||||
pub manages: Vec<Identifier>,
|
||||
pub messages: Vec<MessageDecl>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CxxTypeKind {
|
||||
Struct,
|
||||
Class,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UsingStmt {
|
||||
pub cxx_type: TypeSpec,
|
||||
pub header: String,
|
||||
pub kind: Option<CxxTypeKind>,
|
||||
pub refcounted: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum FileType {
|
||||
Protocol,
|
||||
Header,
|
||||
}
|
||||
|
||||
impl FileType {
|
||||
pub fn from_file_path(file_path: &str) -> Option<FileType> {
|
||||
if let Some(e) = file_path.rsplit('.').next() {
|
||||
if e == "ipdlh" {
|
||||
Some(FileType::Header)
|
||||
} else {
|
||||
Some(FileType::Protocol)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Translation unit identifier.
|
||||
pub type TUId = i32;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TranslationUnit {
|
||||
pub namespace: Namespace,
|
||||
pub file_type: FileType,
|
||||
pub file_name: String,
|
||||
pub cxx_includes: Vec<String>,
|
||||
pub includes: Vec<TUId>,
|
||||
pub using: Vec<UsingStmt>,
|
||||
pub structs: Vec<(Namespace, Vec<StructField>)>,
|
||||
pub unions: Vec<(Namespace, Vec<TypeSpec>)>,
|
||||
pub protocol: Option<(Namespace, Protocol)>,
|
||||
}
|
||||
|
||||
pub struct AST {
|
||||
pub main_tuid: TUId,
|
||||
pub translation_units: HashMap<TUId, TranslationUnit>,
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
/* 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/. */
|
||||
|
||||
use ast::Location;
|
||||
use pipdl;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
|
||||
fn error_msg(loc: &Location, err: &str) -> String {
|
||||
format!("{}: error: {}", loc, err)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub struct Errors {
|
||||
errors: Vec<String>,
|
||||
}
|
||||
|
||||
impl From<pipdl::Error> for Errors {
|
||||
fn from(error: pipdl::Error) -> Self {
|
||||
Errors::one(
|
||||
&Location {
|
||||
file_name: error.span().start.file,
|
||||
lineno: error.span().start.line,
|
||||
colno: error.span().start.col,
|
||||
},
|
||||
error.description(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Errors {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str(&self.errors.join("\n"))
|
||||
}
|
||||
}
|
||||
|
||||
impl Errors {
|
||||
pub fn none() -> Errors {
|
||||
Errors { errors: Vec::new() }
|
||||
}
|
||||
|
||||
pub fn one(loc: &Location, err: &str) -> Errors {
|
||||
Errors {
|
||||
errors: vec![error_msg(&loc, &err)],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn append(&mut self, mut other: Errors) {
|
||||
self.errors.append(&mut other.errors);
|
||||
}
|
||||
|
||||
pub fn append_one(&mut self, loc: &Location, other: &str) {
|
||||
self.errors.push(error_msg(&loc, &other));
|
||||
}
|
||||
|
||||
pub fn to_result(&self) -> Result<(), String> {
|
||||
if self.errors.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(self.errors.join("\n"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.errors.is_empty()
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
/* 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/. */
|
||||
|
||||
#![allow(unknown_lints)]
|
||||
|
||||
extern crate pipdl;
|
||||
|
||||
pub mod ast;
|
||||
mod errors;
|
||||
pub mod parser;
|
||||
mod passes;
|
|
@ -1,114 +0,0 @@
|
|||
/* 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/. */
|
||||
|
||||
use pipdl;
|
||||
|
||||
use passes::include_resolution::IncludeResolver;
|
||||
use passes::parsetree_to_tu::ParseTreeToTU;
|
||||
use passes::type_check;
|
||||
|
||||
use errors;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::hash::Hash;
|
||||
use std::str;
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use ast::{Location, AST};
|
||||
|
||||
pub trait OwnedSourceBuffer: Drop + AsRef<[u8]> {
|
||||
fn to_utf8(&self) -> &str;
|
||||
}
|
||||
impl<T> OwnedSourceBuffer for T
|
||||
where
|
||||
T: Drop + AsRef<[u8]>,
|
||||
{
|
||||
fn to_utf8(&self) -> &str {
|
||||
str::from_utf8(self.as_ref()).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait FileURI: Clone + Eq + Hash {
|
||||
fn resolve_relative_path(&self, relative_path: &str) -> Result<Self, ()>;
|
||||
fn to_utf8(&self) -> &str;
|
||||
}
|
||||
|
||||
impl FileURI for PathBuf {
|
||||
fn resolve_relative_path(&self, relative_path: &str) -> Result<Self, ()> {
|
||||
let mut new_path = self.clone();
|
||||
new_path.push(relative_path);
|
||||
new_path.canonicalize().map_err(|_| ())
|
||||
}
|
||||
|
||||
fn to_utf8(&self) -> &str {
|
||||
self.to_str().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ParseTree<T>
|
||||
where
|
||||
T: FileURI,
|
||||
{
|
||||
pub translation_unit: pipdl::Spanned<pipdl::TranslationUnit>,
|
||||
pub file_path: T,
|
||||
}
|
||||
|
||||
pub fn parse_file<F: Fn(&str) -> Result<Box<OwnedSourceBuffer>, ()>, T: FileURI>(
|
||||
file_path: &T,
|
||||
source_string_loader: &mut F,
|
||||
) -> Result<ParseTree<T>, errors::Errors> {
|
||||
let file_path_str = file_path.to_utf8();
|
||||
let file_text = source_string_loader(file_path_str).map_err(|()| {
|
||||
errors::Errors::one(
|
||||
&Location {
|
||||
file_name: file_path_str.to_owned(),
|
||||
lineno: 0,
|
||||
colno: 0,
|
||||
},
|
||||
"Error loading source string",
|
||||
)
|
||||
})?;
|
||||
|
||||
let parse_tree = ParseTree {
|
||||
translation_unit: pipdl::parse(file_text.to_utf8(), file_path_str)?,
|
||||
file_path: file_path.clone(),
|
||||
};
|
||||
|
||||
Ok(parse_tree)
|
||||
}
|
||||
|
||||
pub fn parse<F: Fn(&str) -> Result<Box<OwnedSourceBuffer>, ()>, T: FileURI>(
|
||||
file_path: &T,
|
||||
include_dirs: &[T],
|
||||
mut source_string_loader: F,
|
||||
) -> Result<AST, errors::Errors> {
|
||||
let parse_tree = parse_file(file_path, &mut source_string_loader)?;
|
||||
|
||||
let mut include_resolver = IncludeResolver::new(include_dirs);
|
||||
|
||||
let (main_tuid, result) = include_resolver.resolve_includes(parse_tree, source_string_loader)?;
|
||||
|
||||
let parsetree_to_translation_unit = ParseTreeToTU::new(&include_resolver);
|
||||
|
||||
let ast = AST {
|
||||
main_tuid,
|
||||
translation_units: {
|
||||
result
|
||||
.into_iter()
|
||||
.map(|(tuid, parse_tree)| {
|
||||
Ok((
|
||||
tuid,
|
||||
parsetree_to_translation_unit.parsetree_to_translation_unit(parse_tree)?,
|
||||
))
|
||||
})
|
||||
.collect::<Result<HashMap<_, _>, errors::Errors>>()?
|
||||
},
|
||||
};
|
||||
|
||||
type_check::check(&ast.translation_units)?;
|
||||
|
||||
Ok(ast)
|
||||
}
|
|
@ -1,205 +0,0 @@
|
|||
/* 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/. */
|
||||
|
||||
use ast::{Location, TUId};
|
||||
use errors::Errors;
|
||||
use parser::{parse_file, FileURI, OwnedSourceBuffer, ParseTree};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
pub struct IncludeResolver<'a, T: 'a>
|
||||
where
|
||||
T: FileURI,
|
||||
{
|
||||
include_dirs: &'a [T],
|
||||
include_files: HashMap<String, T>,
|
||||
id_file_map: TUIdFileMap<T>,
|
||||
}
|
||||
|
||||
impl<'a, T> IncludeResolver<'a, T>
|
||||
where
|
||||
T: FileURI,
|
||||
{
|
||||
pub fn new(include_dirs: &'a [T]) -> IncludeResolver<'a, T> {
|
||||
IncludeResolver {
|
||||
include_dirs,
|
||||
include_files: HashMap::new(),
|
||||
id_file_map: TUIdFileMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_include(&self, include_name: &str) -> Option<TUId> {
|
||||
match self.include_files.get(include_name) {
|
||||
Some(ref path) => self.id_file_map.get_tuid(path),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_include<'b>(&'b mut self, include_name: &str) -> Option<TUId> {
|
||||
if let Some(ref include_file_path) = self.include_files.get(include_name) {
|
||||
return Some(self.id_file_map.resolve_file_name(include_file_path));
|
||||
}
|
||||
|
||||
// XXX The Python parser also checks '' for some reason.
|
||||
for include_dir in self.include_dirs {
|
||||
let mut new_include_path = include_dir.clone();
|
||||
new_include_path = match new_include_path.resolve_relative_path(include_name) {
|
||||
Ok(inc_path) => inc_path,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
let new_id = self.id_file_map.resolve_file_name(&new_include_path);
|
||||
self.include_files
|
||||
.insert(String::from(include_name), new_include_path);
|
||||
return Some(new_id);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn print_include_context(include_context: &[T]) {
|
||||
for path in include_context {
|
||||
println!(" in file included from `{}':", path.to_utf8());
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(needless_pass_by_value)]
|
||||
pub fn resolve_includes<F: Fn(&str) -> Result<Box<OwnedSourceBuffer>, ()>>(
|
||||
&mut self,
|
||||
parse_tree: ParseTree<T>,
|
||||
mut source_string_loader: F,
|
||||
) -> Result<(TUId, HashMap<TUId, ParseTree<T>>), Errors> {
|
||||
let mut work_list: Vec<(T, Vec<T>)> = Vec::new();
|
||||
let mut parsed_files = HashMap::new();
|
||||
let mut visited_files = HashSet::new();
|
||||
|
||||
let resolved_path = parse_tree
|
||||
.file_path
|
||||
.resolve_relative_path("")
|
||||
.map_err(|()| {
|
||||
Errors::one(
|
||||
&Location {
|
||||
file_name: parse_tree.file_path.to_utf8().to_owned(),
|
||||
lineno: 0,
|
||||
colno: 0,
|
||||
},
|
||||
"Could not resolve file path",
|
||||
)
|
||||
})?;
|
||||
|
||||
let file_id = self.id_file_map.resolve_file_name(&resolved_path);
|
||||
visited_files.insert(file_id);
|
||||
work_list.push((resolved_path.clone(), Vec::new()));
|
||||
|
||||
while !work_list.is_empty() {
|
||||
let mut new_work_list = Vec::new();
|
||||
for (curr_file, include_context) in work_list {
|
||||
let curr_parse_tree = if curr_file == resolved_path {
|
||||
parse_tree.clone()
|
||||
} else {
|
||||
match parse_file(&curr_file, &mut source_string_loader) {
|
||||
Ok(tu) => tu,
|
||||
Err(err) => {
|
||||
Self::print_include_context(&include_context);
|
||||
return Err(Errors::from(err));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut include_errors = Errors::none();
|
||||
|
||||
for include in &curr_parse_tree.translation_unit.data.includes {
|
||||
let include_filename = format!(
|
||||
"{}{}{}",
|
||||
include.data.id.data,
|
||||
".ipdl",
|
||||
if include.data.protocol.is_some() {
|
||||
""
|
||||
} else {
|
||||
"h"
|
||||
}
|
||||
);
|
||||
let include_id = match self.resolve_include(&include_filename) {
|
||||
Some(tuid) => tuid,
|
||||
None => {
|
||||
include_errors.append_one(
|
||||
&Location {
|
||||
file_name: include_filename.clone(),
|
||||
lineno: 0,
|
||||
colno: 0,
|
||||
},
|
||||
&format!("Cannot resolve include {}", include_filename),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
if visited_files.contains(&include_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut new_include_context = include_context.clone();
|
||||
new_include_context.push(curr_file.clone());
|
||||
|
||||
visited_files.insert(include_id);
|
||||
new_work_list.push((
|
||||
self.include_files
|
||||
.get(&include_filename)
|
||||
.expect("Resolve include is broken")
|
||||
.clone(),
|
||||
new_include_context,
|
||||
));
|
||||
}
|
||||
|
||||
if !include_errors.is_empty() {
|
||||
return Err(include_errors);
|
||||
}
|
||||
|
||||
let curr_id = self.id_file_map.resolve_file_name(&curr_file);
|
||||
parsed_files.insert(curr_id, curr_parse_tree);
|
||||
}
|
||||
|
||||
work_list = new_work_list;
|
||||
}
|
||||
|
||||
Ok((file_id, parsed_files))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TUIdFileMap<T>
|
||||
where
|
||||
T: FileURI,
|
||||
{
|
||||
next_id: TUId,
|
||||
file_ids: HashMap<T, TUId>,
|
||||
id_files: HashMap<TUId, T>,
|
||||
}
|
||||
|
||||
impl<T> TUIdFileMap<T>
|
||||
where
|
||||
T: FileURI,
|
||||
{
|
||||
fn new() -> TUIdFileMap<T> {
|
||||
TUIdFileMap {
|
||||
next_id: 0,
|
||||
file_ids: HashMap::new(),
|
||||
id_files: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_tuid(&self, path: &T) -> Option<TUId> {
|
||||
self.file_ids.get(path).cloned()
|
||||
}
|
||||
|
||||
fn resolve_file_name(&mut self, path: &T) -> TUId {
|
||||
if let Some(&id) = self.file_ids.get(path) {
|
||||
return id;
|
||||
}
|
||||
|
||||
let id = self.next_id;
|
||||
self.next_id += 1;
|
||||
self.id_files.insert(id, path.clone());
|
||||
self.file_ids.insert(path.clone(), id);
|
||||
id
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
/* 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/. */
|
||||
|
||||
pub mod include_resolution;
|
||||
pub mod parsetree_to_tu;
|
||||
pub mod type_check;
|
|
@ -1,387 +0,0 @@
|
|||
/* 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/. */
|
||||
|
||||
use ast;
|
||||
use errors::Errors;
|
||||
use parser;
|
||||
use passes::include_resolution::IncludeResolver;
|
||||
use pipdl;
|
||||
|
||||
pub struct ParseTreeToTU<'includedirs, T: 'includedirs> where T: parser::FileURI {
|
||||
include_resolver: &'includedirs IncludeResolver<'includedirs, T>,
|
||||
}
|
||||
|
||||
impl<'includedirs, T> ParseTreeToTU<'includedirs, T> where T: parser::FileURI {
|
||||
pub fn new(include_resolver: &'includedirs IncludeResolver<'includedirs, T>) -> Self {
|
||||
ParseTreeToTU { include_resolver }
|
||||
}
|
||||
|
||||
pub fn parsetree_to_translation_unit(
|
||||
&self,
|
||||
parse_tree: parser::ParseTree<T>,
|
||||
) -> Result<ast::TranslationUnit, Errors> {
|
||||
self.convert_translation_unit(parse_tree.translation_unit.data, parse_tree.file_path.to_utf8().to_owned())
|
||||
}
|
||||
|
||||
fn convert_translation_unit(
|
||||
&self,
|
||||
pt_translation_unit: pipdl::TranslationUnit,
|
||||
pt_filename: String,
|
||||
) -> Result<ast::TranslationUnit, Errors> {
|
||||
let mut structs = Vec::new();
|
||||
let mut unions = Vec::new();
|
||||
let mut protocol = None;
|
||||
let mut last_is_struct = false;
|
||||
|
||||
for item in pt_translation_unit.items {
|
||||
match item {
|
||||
pipdl::Item::Struct(struct_item) => {
|
||||
structs.push(self.convert_struct_item(struct_item.data));
|
||||
last_is_struct = true;
|
||||
}
|
||||
pipdl::Item::Union(union_item) => {
|
||||
unions.push(self.convert_union_item(union_item.data));
|
||||
last_is_struct = false;
|
||||
}
|
||||
pipdl::Item::Protocol(protocol_item) => match protocol {
|
||||
Some(_) => {
|
||||
return Err(Errors::one(
|
||||
&ast::Location {
|
||||
file_name: pt_filename,
|
||||
lineno: protocol_item.span.start.line,
|
||||
colno: protocol_item.span.start.col,
|
||||
},
|
||||
"only one protocol definition per file",
|
||||
))
|
||||
}
|
||||
None => protocol = Some(self.convert_protocol_item(protocol_item.data)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ast::TranslationUnit {
|
||||
cxx_includes: pt_translation_unit
|
||||
.cxx_includes
|
||||
.into_iter()
|
||||
.map(|x| x.data.file.data)
|
||||
.collect(),
|
||||
|
||||
includes: pt_translation_unit
|
||||
.includes
|
||||
.into_iter()
|
||||
.map(|x| {
|
||||
let filename = format!(
|
||||
"{}{}{}",
|
||||
x.data.id.data,
|
||||
".ipdl",
|
||||
if x.data.protocol.is_some() { "" } else { "h" }
|
||||
);
|
||||
self.include_resolver
|
||||
.get_include(&filename)
|
||||
.expect("Cannot find include TUId when converting ParseTree to AST")
|
||||
})
|
||||
.collect(),
|
||||
|
||||
using: pt_translation_unit
|
||||
.usings
|
||||
.into_iter()
|
||||
.map(|x| self.convert_using_stmt(x.data))
|
||||
.collect(),
|
||||
|
||||
namespace: match &protocol {
|
||||
Some(p) => p.0.clone(),
|
||||
None => {
|
||||
// There's not really a canonical "thing" in headers. So
|
||||
// somewhat arbitrarily use the namespace of the last
|
||||
// interesting thing that was declared.
|
||||
if last_is_struct {
|
||||
structs.last().expect("last_is_struct is broken").0.clone()
|
||||
} else {
|
||||
match unions.last() {
|
||||
Some(u) => u.0.clone(),
|
||||
None => {
|
||||
return Err(Errors::one(
|
||||
&ast::Location {
|
||||
file_name: pt_filename,
|
||||
lineno: 0,
|
||||
colno: 0,
|
||||
},
|
||||
"file is empty",
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
structs,
|
||||
unions,
|
||||
protocol,
|
||||
file_type: ast::FileType::from_file_path(&pt_filename)
|
||||
.expect("Cannot determine file type when converting parse tree to AST"),
|
||||
file_name: pt_filename,
|
||||
})
|
||||
}
|
||||
|
||||
fn convert_using_stmt(&self, pt_using_stmt: pipdl::Using) -> ast::UsingStmt {
|
||||
ast::UsingStmt {
|
||||
cxx_type: self.convert_cxx_type(pt_using_stmt.ty.data),
|
||||
header: pt_using_stmt.file.data,
|
||||
kind: self.convert_cxx_type_kind(&pt_using_stmt.kind.data),
|
||||
refcounted: pt_using_stmt.refcounted.is_some(),
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_cxx_type_kind(&self, pt_type_kind: &pipdl::CxxTypeKind) -> Option<ast::CxxTypeKind> {
|
||||
match pt_type_kind {
|
||||
pipdl::CxxTypeKind::Class => Some(ast::CxxTypeKind::Class),
|
||||
pipdl::CxxTypeKind::Struct => Some(ast::CxxTypeKind::Struct),
|
||||
pipdl::CxxTypeKind::None => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_cxx_type(&self, pt_cxx_type: pipdl::CxxPath) -> ast::TypeSpec {
|
||||
ast::TypeSpec {
|
||||
spec: self.convert_cxx_path(pt_cxx_type),
|
||||
array: false,
|
||||
nullable: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_cxx_path(&self, mut pt_cxx_path: pipdl::CxxPath) -> ast::QualifiedId {
|
||||
ast::QualifiedId {
|
||||
base_id: self.convert_cxx_path_seg(
|
||||
pt_cxx_path
|
||||
.segs
|
||||
.pop()
|
||||
.expect("Empty path when converting into QualifiedId")
|
||||
.data,
|
||||
),
|
||||
quals: pt_cxx_path
|
||||
.segs
|
||||
.into_iter()
|
||||
.map(|x| self.convert_cxx_path_seg(x.data).id)
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_cxx_path_seg(&self, pt_cxx_path_seg: pipdl::CxxPathSeg) -> ast::Identifier {
|
||||
ast::Identifier {
|
||||
id: match pt_cxx_path_seg.args {
|
||||
Some(args) => format!(
|
||||
"{}<{}>",
|
||||
pt_cxx_path_seg.id.data,
|
||||
args.data
|
||||
.into_iter()
|
||||
.map(|x| x.data)
|
||||
.collect::<Vec<String>>()
|
||||
.concat()
|
||||
),
|
||||
None => pt_cxx_path_seg.id.data,
|
||||
},
|
||||
loc: self.convert_location(pt_cxx_path_seg.id.span.start),
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_location(&self, pt_location: pipdl::Location) -> ast::Location {
|
||||
ast::Location {
|
||||
file_name: pt_location.file,
|
||||
lineno: pt_location.line,
|
||||
colno: pt_location.col,
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_struct_item(
|
||||
&self,
|
||||
pt_struct_item: pipdl::StructItem,
|
||||
) -> (ast::Namespace, Vec<ast::StructField>) {
|
||||
(
|
||||
self.convert_path(pt_struct_item.path),
|
||||
pt_struct_item
|
||||
.fields
|
||||
.into_iter()
|
||||
.map(|x| self.convert_struct_field(x.data))
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
fn convert_path(&self, mut pt_path: Vec<pipdl::Spanned<String>>) -> ast::Namespace {
|
||||
ast::Namespace {
|
||||
name: self.convert_name(
|
||||
pt_path
|
||||
.pop()
|
||||
.expect("Empty path when converting into Namespace"),
|
||||
),
|
||||
namespaces: pt_path.into_iter().map(|x| x.data).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_struct_field(&self, pt_struct_field: pipdl::Field) -> ast::StructField {
|
||||
ast::StructField {
|
||||
type_spec: self.convert_type(pt_struct_field.ty.data),
|
||||
name: self.convert_name(pt_struct_field.name),
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_name(&self, pt_name: pipdl::Spanned<String>) -> ast::Identifier {
|
||||
ast::Identifier {
|
||||
id: pt_name.data,
|
||||
loc: self.convert_location(pt_name.span.start),
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_type(&self, pt_type: pipdl::Type) -> ast::TypeSpec {
|
||||
ast::TypeSpec {
|
||||
spec: ast::QualifiedId::new(self.convert_cxx_path_seg(pt_type.name.data)),
|
||||
array: pt_type.is_array.is_some(),
|
||||
nullable: pt_type.is_nullable.is_some(),
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_union_item(
|
||||
&self,
|
||||
pt_union_item: pipdl::UnionItem,
|
||||
) -> (ast::Namespace, Vec<ast::TypeSpec>) {
|
||||
(
|
||||
self.convert_path(pt_union_item.path),
|
||||
pt_union_item
|
||||
.components
|
||||
.into_iter()
|
||||
.map(|x| self.convert_type(x.data))
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
fn convert_protocol_item(
|
||||
&self,
|
||||
pt_protocol_item: pipdl::ProtocolItem,
|
||||
) -> (ast::Namespace, ast::Protocol) {
|
||||
(
|
||||
self.convert_path(pt_protocol_item.path),
|
||||
ast::Protocol {
|
||||
send_semantics: self.convert_send_semantics(&pt_protocol_item.send_semantics.data),
|
||||
nested: self.convert_nesting(pt_protocol_item.nested),
|
||||
managers: match pt_protocol_item.managers {
|
||||
Some(managers) => managers
|
||||
.data
|
||||
.into_iter()
|
||||
.map(|x| self.convert_name(x))
|
||||
.collect(),
|
||||
None => Vec::new(),
|
||||
},
|
||||
manages: pt_protocol_item
|
||||
.manages
|
||||
.into_iter()
|
||||
.map(|x| self.convert_name(x.data))
|
||||
.collect(),
|
||||
messages: pt_protocol_item
|
||||
.groups
|
||||
.into_iter()
|
||||
.flat_map(|group| {
|
||||
let (decls, direction) = (group.data.decls, group.data.direction.data);
|
||||
decls
|
||||
.into_iter()
|
||||
.map(move |x| (x.data, direction.clone()))
|
||||
.map(|(message_decl, direction)| {
|
||||
self.convert_message_decl(message_decl, &direction)
|
||||
})
|
||||
})
|
||||
.collect(),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn convert_message_decl(
|
||||
&self,
|
||||
pt_message_decl: pipdl::MessageDecl,
|
||||
pt_direction: &pipdl::Direction,
|
||||
) -> ast::MessageDecl {
|
||||
let mut compress = ast::Compress::None;
|
||||
let mut verify = false;
|
||||
|
||||
for modifier in pt_message_decl.modifiers {
|
||||
match modifier.data {
|
||||
pipdl::MessageModifier::Compress => compress = ast::Compress::Enabled,
|
||||
pipdl::MessageModifier::CompressAll => compress = ast::Compress::All,
|
||||
pipdl::MessageModifier::Verify => verify = true,
|
||||
}
|
||||
}
|
||||
|
||||
ast::MessageDecl {
|
||||
name: self.convert_name(pt_message_decl.name),
|
||||
send_semantics: self.convert_send_semantics(&pt_message_decl.send_semantics.data),
|
||||
nested: self.convert_nesting(pt_message_decl.nested),
|
||||
prio: self.convert_priority(pt_message_decl.priority),
|
||||
direction: self.convert_direction(&pt_direction),
|
||||
in_params: pt_message_decl
|
||||
.params
|
||||
.into_iter()
|
||||
.map(|x| self.convert_param(x.data))
|
||||
.collect(),
|
||||
out_params: match pt_message_decl.returns {
|
||||
Some(returns) => returns
|
||||
.data
|
||||
.into_iter()
|
||||
.map(|x| self.convert_param(x.data))
|
||||
.collect(),
|
||||
None => Vec::new(),
|
||||
},
|
||||
compress,
|
||||
verify,
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_param(&self, pt_param: pipdl::Param) -> ast::Param {
|
||||
ast::Param {
|
||||
name: self.convert_name(pt_param.name),
|
||||
type_spec: self.convert_type(pt_param.ty.data),
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_send_semantics(
|
||||
&self,
|
||||
pt_send_semantics: &pipdl::SendSemantics,
|
||||
) -> ast::SendSemantics {
|
||||
match pt_send_semantics {
|
||||
pipdl::SendSemantics::Async => ast::SendSemantics::Async,
|
||||
pipdl::SendSemantics::Sync => ast::SendSemantics::Sync,
|
||||
pipdl::SendSemantics::Intr => ast::SendSemantics::Intr,
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_nesting(
|
||||
&self,
|
||||
pt_nesting: Option<pipdl::Spanned<pipdl::Spanned<pipdl::Nesting>>>,
|
||||
) -> ast::Nesting {
|
||||
match pt_nesting {
|
||||
Some(x) => match x.data.data {
|
||||
pipdl::Nesting::None => ast::Nesting::None,
|
||||
pipdl::Nesting::InsideCpow => ast::Nesting::InsideCpow,
|
||||
pipdl::Nesting::InsideSync => ast::Nesting::InsideSync,
|
||||
},
|
||||
None => ast::Nesting::None,
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_priority(
|
||||
&self,
|
||||
pt_priority: Option<pipdl::Spanned<pipdl::Spanned<pipdl::Priority>>>,
|
||||
) -> ast::Priority {
|
||||
match pt_priority {
|
||||
Some(x) => match x.data.data {
|
||||
pipdl::Priority::Normal => ast::Priority::Normal,
|
||||
pipdl::Priority::High => ast::Priority::High,
|
||||
pipdl::Priority::Input => ast::Priority::Input,
|
||||
},
|
||||
None => ast::Priority::Normal,
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_direction(&self, pt_direction: &pipdl::Direction) -> ast::Direction {
|
||||
match pt_direction {
|
||||
pipdl::Direction::ToChild => ast::Direction::ToChild,
|
||||
pipdl::Direction::ToParent => ast::Direction::ToParent,
|
||||
pipdl::Direction::Both => ast::Direction::ToParentOrChild,
|
||||
}
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,74 +0,0 @@
|
|||
extern crate ipdl;
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::ffi::OsStr;
|
||||
use std::fs;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::path::PathBuf;
|
||||
|
||||
const BASE_PATH: [&str; 5] = ["..", "..", "ipdl", "test", "ipdl"];
|
||||
const OK_PATH: &str = "ok";
|
||||
const ERROR_PATH: &str = "error";
|
||||
|
||||
// Tests in error/ are disabled because the given checking is not
|
||||
// enabled yet.
|
||||
|
||||
const DISABLED_TESTS: &[&str] = &["unknownSyncMessage.ipdl", "unknownIntrMessage.ipdl", "asyncMessageListed.ipdl"];
|
||||
|
||||
// XXX This does not run efficiently. If A includes B, then we end up
|
||||
// testing A and B two times each. At least for the non-error case we
|
||||
// should be able to do them all together.
|
||||
|
||||
fn test_files(test_file_path: &str, should_pass: bool) {
|
||||
let mut path: PathBuf = BASE_PATH.iter().collect();
|
||||
path.push(test_file_path);
|
||||
|
||||
let include_dirs = vec![path.clone()];
|
||||
|
||||
let mut disabled_tests = HashSet::new();
|
||||
for f in DISABLED_TESTS {
|
||||
disabled_tests.insert(OsStr::new(f));
|
||||
}
|
||||
|
||||
let entries = fs::read_dir(&path).expect("Should have the test file directory");
|
||||
for entry in entries {
|
||||
if let Ok(entry) = entry {
|
||||
let expected_result = if !should_pass
|
||||
&& disabled_tests.contains(
|
||||
entry
|
||||
.path()
|
||||
.file_name()
|
||||
.expect("No filename for path in test directory"),
|
||||
) {
|
||||
println!(
|
||||
"Expecting test to pass when it should fail {:?}",
|
||||
entry.file_name()
|
||||
);
|
||||
true
|
||||
} else {
|
||||
println!("Testing {:?}", entry.file_name());
|
||||
should_pass
|
||||
};
|
||||
|
||||
let file_name = entry.path();
|
||||
let ok = ipdl::parser::parse(&file_name, &include_dirs, |src| {
|
||||
let mut buffer = Vec::new();
|
||||
let mut file = File::open(src).map_err(|_| ())?;
|
||||
file.read_to_end(&mut buffer).map_err(|_| ())?;
|
||||
Ok(Box::new(buffer))
|
||||
}).is_ok();
|
||||
assert!(expected_result == ok);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ok_tests() {
|
||||
test_files(OK_PATH, true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_tests() {
|
||||
test_files(ERROR_PATH, false);
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
[package]
|
||||
name = "ipdl_bindings"
|
||||
version = "0.1.0"
|
||||
authors = ["Tristan Bourvon <tristanbourvon@gmail.com>"]
|
||||
license = "MPL-2.0"
|
||||
|
||||
[dependencies]
|
||||
ipdl = { path = "../ipdl" }
|
||||
thin-vec = { version = "*", features = ["gecko-ffi"] }
|
||||
nsstring = { path = "../../../servo/support/gecko/nsstring" }
|
||||
mfbt-maybe = { path = "../../../xpcom/rust/mfbt-maybe" }
|
||||
|
||||
[lib]
|
||||
name = "ipdl_bindings"
|
||||
path = "src/lib.rs"
|
|
@ -1,121 +0,0 @@
|
|||
#include "IPCInterface.h"
|
||||
|
||||
#include "mozilla/ipdl/IPDLProtocolInstance.h"
|
||||
#include "mozilla/dom/IPDL.h"
|
||||
#include "mozilla/CycleCollectedJSContext.h"
|
||||
#include "mozilla/MozPromise.h"
|
||||
#include "mozilla/ipdl/IPDLProtocol.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipdl {
|
||||
namespace ipc {
|
||||
|
||||
/* virtual */ mozilla::ipc::IPCResult
|
||||
IPCInterface::RecvMessageCommon(
|
||||
mozilla::ipc::IProtocol* aActor,
|
||||
IPDLSide aSide,
|
||||
const nsCString& aProtocolName,
|
||||
const uint32_t& aChannelId,
|
||||
const nsCString& aMessage,
|
||||
const dom::ClonedMessageData& aData,
|
||||
nsTArray<dom::ipc::StructuredCloneData>* aReturnData)
|
||||
{
|
||||
// Borrow from the CloneMessageData to the StructuredCloneData.
|
||||
dom::ipc::StructuredCloneData data;
|
||||
switch (aSide) {
|
||||
case IPDLSide::Child:
|
||||
data.BorrowFromClonedMessageDataForChild(aData);
|
||||
break;
|
||||
case IPDLSide::Parent:
|
||||
data.BorrowFromClonedMessageDataForParent(aData);
|
||||
break;
|
||||
}
|
||||
|
||||
// Get the destination instance object to initiate a new context.
|
||||
JS::RootingContext* rcx = CycleCollectedJSContext::Get()->RootingCx();
|
||||
JS::RootedObject object(rcx);
|
||||
|
||||
auto sidedProtocolName = IPDLProtocol::GetSidedProtocolName(
|
||||
aProtocolName, aSide);
|
||||
|
||||
object = GetDestinationObject(sidedProtocolName, aChannelId);
|
||||
dom::AutoEntryScript aes(object, "RecvSyncMessageIPDL");
|
||||
|
||||
JSContext* cx = aes.cx();
|
||||
|
||||
// Read the arguments from the StructuredCloneData.
|
||||
JS::RootedValue argsVal(cx);
|
||||
|
||||
ErrorResult errRes;
|
||||
data.Read(cx, &argsVal, errRes);
|
||||
|
||||
if (NS_WARN_IF(errRes.Failed())) {
|
||||
return IPC_FAIL(aActor,
|
||||
"Could not read arguments from StructuredCloneData");
|
||||
}
|
||||
|
||||
JS::RootedObject argsObj(cx, &argsVal.toObject());
|
||||
|
||||
uint32_t arrayLength = 0;
|
||||
if (!JS_GetArrayLength(cx, argsObj, &arrayLength)) {
|
||||
return IPC_FAIL(aActor, "Could not get argument array length");
|
||||
}
|
||||
|
||||
// Extract all the arguments.
|
||||
JS::AutoValueVector args(cx);
|
||||
for (uint32_t i = 0; i < arrayLength; i++) {
|
||||
JS::RootedValue val(cx);
|
||||
|
||||
if (!JS_GetElement(cx, argsObj, i, &val)) {
|
||||
return IPC_FAIL(aActor,
|
||||
"Could not get argument value from argument array");
|
||||
}
|
||||
|
||||
if (!args.append(val)) {
|
||||
return IPC_FAIL(aActor,
|
||||
"Could not append argument value to argument vector");
|
||||
}
|
||||
}
|
||||
|
||||
JS::HandleValueArray argsHandle(args);
|
||||
|
||||
JS::RootedValue retValue(cx);
|
||||
|
||||
// Call the receive handler in the protocol instance.
|
||||
if (!mProtocolInstances.GetOrInsert(sidedProtocolName)
|
||||
.Get(aChannelId)
|
||||
->RecvMessage(
|
||||
cx, aMessage, argsHandle, &retValue)) {
|
||||
NS_WARNING("Error in the RecvMessage handler");
|
||||
aReturnData->Clear();
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
// Write back the return value.
|
||||
dom::ipc::StructuredCloneData retData;
|
||||
retData.Write(cx, retValue, errRes);
|
||||
|
||||
if (NS_WARN_IF(errRes.Failed())) {
|
||||
return IPC_FAIL(aActor,
|
||||
"Could not write return value to StructuredCloneData");
|
||||
}
|
||||
|
||||
aReturnData->AppendElement(std::move(retData));
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
/* virtual */ JSObject*
|
||||
IPCInterface::GetDestinationObject(const nsCString& aProtocolName,
|
||||
const uint32_t& aChannelId)
|
||||
{
|
||||
// Return the instance object corresponding to the protocol name and channel
|
||||
// ID.
|
||||
return mProtocolInstances.GetOrInsert(aProtocolName)
|
||||
.Get(aChannelId)
|
||||
->GetInstanceObject();
|
||||
}
|
||||
|
||||
} // ipc
|
||||
} // ipdl
|
||||
} // mozilla
|
|
@ -1,117 +0,0 @@
|
|||
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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 https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef dom_base_ipdl_bindings_ipcinterface_h
|
||||
#define dom_base_ipdl_bindings_ipcinterface_h
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "mozilla/ipc/ProtocolUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class IPDL;
|
||||
}
|
||||
|
||||
namespace ipdl {
|
||||
class IPDLProtocolInstance;
|
||||
enum class IPDLSide : bool;
|
||||
|
||||
namespace ipc {
|
||||
|
||||
class IPCInterface
|
||||
{
|
||||
public:
|
||||
// Message call return type.
|
||||
typedef JS::MutableHandleValue OutObject;
|
||||
// Message call argument list type.
|
||||
typedef JS::HandleValueArray InArgs;
|
||||
// Async message call promise type.
|
||||
typedef MozPromise<JS::Value, nsCString, true> AsyncMessagePromise;
|
||||
|
||||
explicit IPCInterface(dom::IPDL* aIPDL)
|
||||
: mIPDL(aIPDL)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~IPCInterface() = default;
|
||||
|
||||
// Send an async message through IPC.
|
||||
virtual RefPtr<AsyncMessagePromise> SendAsyncMessage(
|
||||
JSContext* aCx,
|
||||
const nsCString& aProtocolName,
|
||||
const uint32_t& aChannelId,
|
||||
const nsCString& aMessageName,
|
||||
const InArgs& aArgs) = 0;
|
||||
|
||||
// Send a sync message through IPC.
|
||||
virtual bool SendSyncMessage(JSContext* aCx,
|
||||
const nsCString& aProtocolName,
|
||||
const uint32_t& aChannelId,
|
||||
const nsCString& aMessageName,
|
||||
const InArgs& aArgs,
|
||||
OutObject aRet) = 0;
|
||||
|
||||
// Send an intr message through IPC.
|
||||
virtual bool SendIntrMessage(JSContext* aCx,
|
||||
const nsCString& aProtocolName,
|
||||
const uint32_t& aChannelId,
|
||||
const nsCString& aMessageName,
|
||||
const InArgs& aArgs,
|
||||
OutObject aRet) = 0;
|
||||
|
||||
// Set the recv handler to call when we receive a message.
|
||||
virtual void SetIPDLInstance(const uint32_t& aChannelId,
|
||||
const nsCString& aProtocolName,
|
||||
IPDLProtocolInstance* aInstance)
|
||||
{
|
||||
mProtocolInstances.GetOrInsert(aProtocolName).GetOrInsert(aChannelId) =
|
||||
aInstance;
|
||||
}
|
||||
|
||||
virtual void RemoveIPDLInstance(const uint32_t& aChannelId,
|
||||
const nsCString& aProtocolName)
|
||||
{
|
||||
mProtocolInstances.GetOrInsert(aProtocolName).Remove(aChannelId);
|
||||
}
|
||||
|
||||
// Receive a message from IPC. Empty return array means error.
|
||||
virtual mozilla::ipc::IPCResult RecvMessage(
|
||||
const nsCString& aProtocolName,
|
||||
const uint32_t& aChannelId,
|
||||
const nsCString& aMessage,
|
||||
const dom::ClonedMessageData& aData,
|
||||
nsTArray<dom::ipc::StructuredCloneData>* aReturnData) = 0;
|
||||
|
||||
protected:
|
||||
// Inner common function for receiving a message from either the parent or the
|
||||
// child. Empty return array means error.
|
||||
virtual mozilla::ipc::IPCResult RecvMessageCommon(
|
||||
mozilla::ipc::IProtocol* aActor,
|
||||
IPDLSide aSide,
|
||||
const nsCString& aProtocolName,
|
||||
const uint32_t& aChannelId,
|
||||
const nsCString& aMessage,
|
||||
const dom::ClonedMessageData& aData,
|
||||
nsTArray<dom::ipc::StructuredCloneData>* aReturnData);
|
||||
|
||||
// Retrieves the destination instance object for a protocol/channel pair.
|
||||
virtual JSObject* GetDestinationObject(const nsCString& aProtocolName,
|
||||
const uint32_t& aChannelId);
|
||||
|
||||
dom::IPDL* MOZ_NON_OWNING_REF mIPDL;
|
||||
nsDataHashtable<nsCStringHashKey,
|
||||
nsDataHashtable<nsUint32HashKey, IPDLProtocolInstance*>>
|
||||
mProtocolInstances;
|
||||
};
|
||||
|
||||
} // ipc
|
||||
} // ipdl
|
||||
} // mozilla
|
||||
|
||||
#endif // dom_base_ipdl_bindings_ipcinterface_h
|
|
@ -1,778 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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 https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "IPDLProtocol.h"
|
||||
|
||||
#include <cfloat>
|
||||
|
||||
#include "ipdl_ffi_generated.h"
|
||||
#include "mozilla/dom/DOMMozPromiseRequestHolder.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "wrapper.h"
|
||||
#include "mozilla/dom/IPDL.h"
|
||||
#include "IPCInterface.h"
|
||||
#include "IPDLProtocolInstance.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipdl {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(IPDLProtocol)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IPDLProtocol)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mInstances)
|
||||
tmp->mProtoObj = nullptr;
|
||||
tmp->mConstructorObj = nullptr;
|
||||
mozilla::DropJSObjects(tmp);
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IPDLProtocol)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInstances)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IPDLProtocol)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mProtoObj)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mConstructorObj)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(IPDLProtocol)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(IPDLProtocol)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IPDLProtocol)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
void
|
||||
IPDLProtocol::ASTDeletePolicy::operator()(const mozilla::ipdl::ffi::AST* aASTPtr)
|
||||
{
|
||||
if (!aASTPtr) {
|
||||
return;
|
||||
}
|
||||
wrapper::FreeAST(aASTPtr);
|
||||
}
|
||||
|
||||
IPDLProtocol::IPDLProtocol(dom::IPDL* aIPDL,
|
||||
IPDLSide aSide,
|
||||
const nsACString& aIPDLFile,
|
||||
nsIGlobalObject* aGlobal,
|
||||
JS::HandleObject aParent,
|
||||
JSContext* aCx)
|
||||
: mSide(aSide)
|
||||
, mGlobal(aGlobal)
|
||||
, mIPDL(aIPDL)
|
||||
, mNextProtocolInstanceChannelId(0)
|
||||
{
|
||||
mozilla::HoldJSObjects(this);
|
||||
|
||||
nsCString errorString;
|
||||
mAST.reset(wrapper::Parse(aIPDLFile, errorString));
|
||||
|
||||
if (!mAST) {
|
||||
JS_ReportErrorUTF8(aCx, "IPDL: %s", errorString.get());
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the main translation unit.
|
||||
auto mainTU = GetMainTU();
|
||||
|
||||
MOZ_ASSERT(mainTU->protocol, "should have a protocol in protocol file");
|
||||
|
||||
mProtocolName = JoinNamespace(mainTU->protocol->ns);
|
||||
|
||||
auto& manages = mainTU->protocol->protocol.manages;
|
||||
auto& messages = mainTU->protocol->protocol.messages;
|
||||
|
||||
NS_NAMED_LITERAL_CSTRING(sendPrefix, "send");
|
||||
NS_NAMED_LITERAL_CSTRING(recvPrefix, "recv");
|
||||
NS_NAMED_LITERAL_CSTRING(allocPrefix, "alloc");
|
||||
NS_NAMED_LITERAL_CSTRING(constructorSuffix, "Constructor");
|
||||
|
||||
// Store the managed protocols in a set for fast retrieval and access.
|
||||
nsTHashtable<nsCStringHashKey> managedProtocols;
|
||||
for (auto& managedProtocol : manages) {
|
||||
managedProtocols.PutEntry(managedProtocol.id);
|
||||
}
|
||||
|
||||
nsTArray<JSFunctionSpec> funcs;
|
||||
nsTArray<nsCString> functionNames;
|
||||
// We loop through every message of the protocol and add them to our message
|
||||
// table, taking into account our protocol side and the message direction.
|
||||
for (auto& message : messages) {
|
||||
// The message is gonna be sendXXX.
|
||||
if ((message.direction == ffi::Direction::ToChild &&
|
||||
mSide == IPDLSide::Parent) ||
|
||||
(message.direction == ffi::Direction::ToParent &&
|
||||
mSide == IPDLSide::Child) ||
|
||||
message.direction == ffi::Direction::ToParentOrChild) {
|
||||
|
||||
// If this is a managed protocol name...
|
||||
if (managedProtocols.Contains(message.name.id)) {
|
||||
// Append the sendXXXConstructor message.
|
||||
const nsAutoCString& constructorName =
|
||||
sendPrefix + message.name.id + constructorSuffix;
|
||||
|
||||
functionNames.AppendElement(constructorName);
|
||||
funcs.AppendElement<JSFunctionSpec>(JS_FN(
|
||||
functionNames.LastElement().get(),
|
||||
SendConstructorDispatch,
|
||||
static_cast<uint16_t>(message.in_params.Length()),
|
||||
0));
|
||||
mMessageTable.Put(constructorName, &message);
|
||||
|
||||
// And the allocXXX message.
|
||||
const nsAutoCString& allocName = allocPrefix + message.name.id;
|
||||
functionNames.AppendElement(allocName);
|
||||
funcs.AppendElement<JSFunctionSpec>(JS_FN(
|
||||
functionNames.LastElement().get(),
|
||||
AbstractAlloc,
|
||||
static_cast<uint16_t>(message.in_params.Length()),
|
||||
0));
|
||||
mMessageTable.Put(allocName, &message);
|
||||
} else {
|
||||
// Otherwise for the regular sendXXX messages, we define the associated JS function.
|
||||
const nsAutoCString& funcName = sendPrefix + message.name.id;
|
||||
functionNames.AppendElement(funcName);
|
||||
funcs.AppendElement<JSFunctionSpec>(JS_FN(
|
||||
functionNames.LastElement().get(),
|
||||
message.name.id.Equals("__delete__") ? SendDeleteDispatch
|
||||
: SendMessageDispatch,
|
||||
static_cast<uint16_t>(message.in_params.Length()),
|
||||
0));
|
||||
mMessageTable.Put(funcName, &message);
|
||||
}
|
||||
}
|
||||
|
||||
// The message is gonna be recvXXX;
|
||||
if ((message.direction == ffi::Direction::ToChild &&
|
||||
mSide == IPDLSide::Child) ||
|
||||
(message.direction == ffi::Direction::ToParent &&
|
||||
mSide == IPDLSide::Parent) ||
|
||||
message.direction == ffi::Direction::ToParentOrChild) {
|
||||
|
||||
// If this is a managed protocol...
|
||||
if (managedProtocols.Contains(message.name.id)) {
|
||||
// Append the recvXXXConstructor message.
|
||||
const nsAutoCString& constructorName =
|
||||
recvPrefix + message.name.id + constructorSuffix;
|
||||
functionNames.AppendElement(constructorName);
|
||||
funcs.AppendElement<JSFunctionSpec>(JS_FN(
|
||||
functionNames.LastElement().get(),
|
||||
RecvConstructor,
|
||||
static_cast<uint16_t>(message.in_params.Length()),
|
||||
0));
|
||||
mMessageTable.Put(constructorName, &message);
|
||||
|
||||
// And the allocXXX message.
|
||||
const nsAutoCString& allocName = allocPrefix + message.name.id;
|
||||
functionNames.AppendElement(allocName);
|
||||
funcs.AppendElement<JSFunctionSpec>(JS_FN(
|
||||
functionNames.LastElement().get(),
|
||||
AbstractAlloc,
|
||||
static_cast<uint16_t>(message.in_params.Length()),
|
||||
0));
|
||||
mMessageTable.Put(allocName, &message);
|
||||
} else {
|
||||
// For the regular recvXXX callbacks, we define the associated "abstract"
|
||||
// functions or the delete default function.
|
||||
const nsAutoCString& funcName = recvPrefix + message.name.id;
|
||||
functionNames.AppendElement(funcName);
|
||||
funcs.AppendElement<JSFunctionSpec>(JS_FN(
|
||||
functionNames.LastElement().get(),
|
||||
message.name.id.Equals("__delete__") ? RecvDelete
|
||||
: AbstractRecvMessage,
|
||||
static_cast<uint16_t>(message.in_params.Length()),
|
||||
0));
|
||||
|
||||
mMessageTable.Put(funcName, &message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
funcs.AppendElement<JSFunctionSpec>(JS_FS_END);
|
||||
|
||||
// Create our new protocol JSClass.
|
||||
mSidedProtocolName = GetSidedProtocolName(mProtocolName, mSide);
|
||||
mProtocolClass =
|
||||
{ mSidedProtocolName.get(),
|
||||
JSCLASS_HAS_PRIVATE,
|
||||
&sIPDLJSClassOps };
|
||||
|
||||
JS::RootedObject parentProto(aCx);
|
||||
JS_GetClassPrototype(aCx, JSProto_Object, &parentProto);
|
||||
|
||||
// Initialize it with the constructor and the functions.
|
||||
mProtoObj = JS_InitClass(aCx,
|
||||
aParent,
|
||||
parentProto,
|
||||
&mProtocolClass,
|
||||
Constructor,
|
||||
0,
|
||||
nullptr,
|
||||
funcs.Elements(),
|
||||
nullptr,
|
||||
nullptr);
|
||||
|
||||
JS::RootedObject protoObj(aCx, mProtoObj);
|
||||
|
||||
// Add this object to the private field.
|
||||
JS_SetPrivate(protoObj, this);
|
||||
mConstructorObj = JS_GetConstructor(aCx, protoObj);
|
||||
|
||||
// We build the name->AST lookup tables for later typechecking.
|
||||
BuildNameLookupTables();
|
||||
}
|
||||
|
||||
nsCString
|
||||
IPDLProtocol::GetProtocolName()
|
||||
{
|
||||
return mProtocolName;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
IPDLProtocol::GetProtocolClassConstructor()
|
||||
{
|
||||
return mConstructorObj.get();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
IPDLProtocol::RegisterExternalInstance()
|
||||
{
|
||||
auto childID = mNextProtocolInstanceChannelId++;
|
||||
return childID;
|
||||
}
|
||||
|
||||
JSClass&
|
||||
IPDLProtocol::GetProtocolClass()
|
||||
{
|
||||
return mProtocolClass;
|
||||
}
|
||||
|
||||
const ffi::TranslationUnit*
|
||||
IPDLProtocol::GetMainTU()
|
||||
{
|
||||
return wrapper::GetTU(mAST.get(), wrapper::GetMainTUId(mAST.get()));
|
||||
}
|
||||
|
||||
void
|
||||
IPDLProtocol::BuildNameLookupTables()
|
||||
{
|
||||
// We don't have an Int32 hashkey, but it's ok since we can just perform
|
||||
// conversions in and out.
|
||||
nsTHashtable<nsUint32HashKey> visitedTUId;
|
||||
nsTArray<ffi::TUId> workList;
|
||||
|
||||
// Our current loop element.
|
||||
const ffi::TranslationUnit* currentTU = nullptr;
|
||||
// Our current loop index.
|
||||
ffi::TUId currentTUId = -1;
|
||||
|
||||
// We start with only the main tu in the worklist, and then unroll all the
|
||||
// includes.
|
||||
ffi::TUId mainTUId = wrapper::GetMainTUId(mAST.get());
|
||||
workList.AppendElement(mainTUId);
|
||||
while (!workList.IsEmpty()) {
|
||||
// Get an index and anelement from the work list.
|
||||
currentTUId = workList.PopLastElement();
|
||||
currentTU = wrapper::GetTU(mAST.get(), currentTUId);
|
||||
|
||||
// If we are in a protocol file, just add the protocol and stop here (we)
|
||||
// shouldn't keep going through the includes recursively, and we should
|
||||
// ignore the local structs and unions.
|
||||
if (currentTU->file_type == ffi::FileType::Protocol) {
|
||||
// If there is a protocol, add it.
|
||||
if (currentTU->protocol) {
|
||||
mProtocolTable.Put(JoinNamespace(currentTU->protocol->ns),
|
||||
currentTU->protocol.ptr());
|
||||
}
|
||||
}
|
||||
|
||||
// If we are in a header file, add the structs and the unions it contains,
|
||||
// and then recurse through all of its includes.
|
||||
if (currentTU->file_type == ffi::FileType::Header ||
|
||||
currentTUId == mainTUId) {
|
||||
for (size_t i = 0; i < currentTU->structs.Length(); i++) {
|
||||
// Dirty, but this is the only way to have a pointer to an element
|
||||
const auto* s = currentTU->structs.Elements() + i;
|
||||
mStructTable.Put(JoinNamespace(s->ns), s);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < currentTU->unions.Length(); i++) {
|
||||
// Dirty, but this is the only way to have a pointer to an element
|
||||
const auto* u = currentTU->unions.Elements() + i;
|
||||
mUnionTable.Put(JoinNamespace(u->ns), u);
|
||||
}
|
||||
|
||||
workList.AppendElements(currentTU->includes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
IPDLProtocol::SendMessageDispatch(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
// Unwrap the JS args.
|
||||
auto args = JS::CallArgsFromVp(aArgc, aVp);
|
||||
|
||||
// Get the IPDLProtocol object from the private field of the method `this`.
|
||||
JS::RootedObject thisObj(aCx);
|
||||
args.computeThis(aCx, &thisObj);
|
||||
|
||||
auto* instance = static_cast<IPDLProtocolInstance*>(JS_GetPrivate(thisObj));
|
||||
|
||||
if (!instance) {
|
||||
JS_ReportErrorUTF8(aCx, "Cannot use deleted protocol");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the method name that we are calling.
|
||||
auto function = JS_GetObjectFunction(&args.callee());
|
||||
nsAutoJSString functionName;
|
||||
if (!functionName.init(aCx, JS_GetFunctionId(function))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Call our internal SendMessage function.
|
||||
return instance->SendMessage(aCx, NS_ConvertUTF16toUTF8(functionName), args);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
IPDLProtocol::SendConstructorDispatch(JSContext* aCx,
|
||||
unsigned aArgc,
|
||||
JS::Value* aVp)
|
||||
{
|
||||
// Unwrap the JS args.
|
||||
auto args = JS::CallArgsFromVp(aArgc, aVp);
|
||||
|
||||
// Get the IPDLProtocol object from the private field of the method `this`.
|
||||
JS::RootedObject thisObj(aCx);
|
||||
args.computeThis(aCx, &thisObj);
|
||||
|
||||
auto* instance = static_cast<IPDLProtocolInstance*>(JS_GetPrivate(thisObj));
|
||||
|
||||
if (!instance) {
|
||||
JS_ReportErrorUTF8(aCx, "Cannot use deleted protocol");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the method name that we are calling.
|
||||
auto function = JS_GetObjectFunction(&args.callee());
|
||||
nsAutoJSString functionName;
|
||||
if (!functionName.init(aCx, JS_GetFunctionId(function))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Call our internal SendConstructor function.
|
||||
return instance->SendConstructor(aCx, thisObj, NS_ConvertUTF16toUTF8(functionName), args);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
IPDLProtocol::SendDeleteDispatch(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
// Unwrap the JS args.
|
||||
auto args = JS::CallArgsFromVp(aArgc, aVp);
|
||||
|
||||
// Get the IPDLProtocol object from the private field of the method `this`.
|
||||
JS::RootedObject thisObj(aCx);
|
||||
args.computeThis(aCx, &thisObj);
|
||||
|
||||
auto* instance = static_cast<IPDLProtocolInstance*>(JS_GetPrivate(thisObj));
|
||||
|
||||
if (!instance) {
|
||||
JS_ReportErrorUTF8(aCx, "Cannot use deleted protocol");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the method name that we are calling.
|
||||
auto function = JS_GetObjectFunction(&args.callee());
|
||||
nsAutoJSString functionName;
|
||||
if (!functionName.init(aCx, JS_GetFunctionId(function))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Call our internal SendConstructor function.
|
||||
return instance->SendDelete(aCx, NS_ConvertUTF16toUTF8(functionName), args);
|
||||
}
|
||||
|
||||
void
|
||||
IPDLProtocol::RemoveInstance(IPDLProtocolInstance *instance)
|
||||
{
|
||||
mInstances.RemoveEntry(instance);
|
||||
}
|
||||
|
||||
bool
|
||||
IPDLProtocol::CheckParamTypeSpec(JSContext* aCx,
|
||||
JS::HandleValue aJSVal,
|
||||
ffi::Param aParam)
|
||||
{
|
||||
// Just unwrap the type spec.
|
||||
return CheckTypeSpec(aCx, aJSVal, aParam.type_spec);
|
||||
}
|
||||
|
||||
bool
|
||||
IPDLProtocol::CheckTypeSpec(JSContext* aCx,
|
||||
JS::HandleValue aJSVal,
|
||||
ffi::TypeSpec aTypeSpec)
|
||||
{
|
||||
// If the type is nullable and we get a null, we return now
|
||||
if (aTypeSpec.nullable && aJSVal.isNull()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the type is an array, we check the type for all the array elements
|
||||
if (aTypeSpec.array) {
|
||||
// Check if jsVal is an array.
|
||||
bool isArray = false;
|
||||
JS_IsArrayObject(aCx, aJSVal, &isArray);
|
||||
if (!isArray) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the JS array.
|
||||
JS::RootedObject jsArray(aCx, &aJSVal.toObject());
|
||||
|
||||
uint32_t arrayLength = 0;
|
||||
JS_GetArrayLength(aCx, jsArray, &arrayLength);
|
||||
|
||||
// For each element of the array, we check its type.
|
||||
for (size_t i = 0; i < arrayLength; i++) {
|
||||
JS::RootedValue arrayVal(aCx);
|
||||
JS_GetElement(aCx, jsArray, i, &arrayVal);
|
||||
|
||||
if (!CheckType(aCx, arrayVal, aTypeSpec.spec)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// We check the actual type if not an array.
|
||||
return CheckType(aCx, aJSVal, aTypeSpec.spec);
|
||||
}
|
||||
|
||||
bool
|
||||
IPDLProtocol::CheckType(JSContext* aCx,
|
||||
JS::HandleValue aJSVal,
|
||||
ffi::QualifiedId aType)
|
||||
{
|
||||
// First check the builtin types
|
||||
if (CheckBuiltinType(aCx, aJSVal, aType)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Then the protocol types
|
||||
if (CheckProtocolType(aCx, aJSVal, aType)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Then the struct types
|
||||
if (CheckStructType(aCx, aJSVal, aType)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Then the union types.
|
||||
if (CheckUnionType(aCx, aJSVal, aType)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise we have a type mismatch.
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
IPDLProtocol::CheckProtocolType(JSContext* aCx,
|
||||
JS::HandleValue aJSVal,
|
||||
ffi::QualifiedId aType)
|
||||
{
|
||||
// If we don't know that protocol name, return false.
|
||||
auto typeString = JoinQualifiedId(aType);
|
||||
if (!mProtocolTable.Contains(typeString)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we don't have an object, return false.
|
||||
if (!aJSVal.isObject()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get our JS object.
|
||||
JS::RootedObject jsObj(aCx, &aJSVal.toObject());
|
||||
|
||||
// If the object is not an IPDL protocol class instance, return false.
|
||||
if (typeString.Equals(JS_GetClass(jsObj)->name) != 0) { // not equal
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* protocolObj = static_cast<IPDLProtocol*>(JS_GetPrivate(jsObj));
|
||||
|
||||
if (!protocolObj) {
|
||||
JS_ReportErrorUTF8(aCx, "Couldn't get protocol object from private date field");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the protocol name against the one we require.
|
||||
return protocolObj->GetProtocolName().Equals(typeString);
|
||||
}
|
||||
|
||||
bool
|
||||
IPDLProtocol::CheckStructType(JSContext* aCx,
|
||||
JS::HandleValue aJSVal,
|
||||
ffi::QualifiedId aType)
|
||||
{
|
||||
// If we don't know that struct name, return false.
|
||||
auto* ipdlStruct = mStructTable.Get(JoinQualifiedId(aType));
|
||||
if (!ipdlStruct) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we don't have an object, return false.
|
||||
if (!aJSVal.isObject()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get our JS object.
|
||||
JS::RootedObject jsObj(aCx, &aJSVal.toObject());
|
||||
|
||||
// Go through every field of the struct.
|
||||
for (auto& field : ipdlStruct->fields) {
|
||||
JS::RootedValue propertyValue(aCx);
|
||||
|
||||
// Check that the field exists in the object we got.
|
||||
if (!JS_GetProperty(aCx, jsObj, field.name.id.get(), &propertyValue)) {
|
||||
return false;
|
||||
}
|
||||
if (propertyValue.isUndefined()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that the field type matches the element we got.
|
||||
if (!CheckTypeSpec(aCx, propertyValue, field.type_spec)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
IPDLProtocol::CheckUnionType(JSContext* aCx,
|
||||
JS::HandleValue aJSVal,
|
||||
ffi::QualifiedId aType)
|
||||
{
|
||||
auto* ipdlUnion = mUnionTable.Get(JoinQualifiedId(aType));
|
||||
if (!ipdlUnion) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Go through every type of the union
|
||||
for (auto& type : ipdlUnion->types) {
|
||||
// If our input value matches any of these types, return true.
|
||||
if (CheckTypeSpec(aCx, aJSVal, type)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise return false.
|
||||
return false;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
IPDLProtocol::CheckBuiltinType(JSContext* aCx, JS::HandleValue aJSVal, ffi::QualifiedId type)
|
||||
{
|
||||
// Check all the builtin types using their type name, and custom constraints
|
||||
// such as bounds checking and length.
|
||||
auto typeString = JoinQualifiedId(type);
|
||||
|
||||
if (typeString.Equals("bool")) {
|
||||
return aJSVal.isBoolean();
|
||||
}
|
||||
if (typeString.Equals("char")) {
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
size_t length;
|
||||
if (!aJSVal.isString()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JS_GetLatin1StringCharsAndLength(aCx, nogc, aJSVal.toString(), &length);
|
||||
return length == 1;
|
||||
}
|
||||
if (typeString.Equals("nsString") || typeString.Equals("nsCString")) {
|
||||
return aJSVal.isString();
|
||||
}
|
||||
if (typeString.Equals("short")) {
|
||||
return aJSVal.isNumber() && (trunc(aJSVal.toNumber()) == aJSVal.toNumber()) &&
|
||||
(aJSVal.toNumber() <= SHRT_MAX) && (aJSVal.toNumber() >= SHRT_MIN);
|
||||
}
|
||||
if (typeString.Equals("int")) {
|
||||
return aJSVal.isNumber() && (trunc(aJSVal.toNumber()) == aJSVal.toNumber()) &&
|
||||
(aJSVal.toNumber() <= INT_MAX) && (aJSVal.toNumber() >= INT_MIN);
|
||||
}
|
||||
if (typeString.Equals("long")) {
|
||||
return aJSVal.isNumber() && (trunc(aJSVal.toNumber()) == aJSVal.toNumber()) &&
|
||||
(aJSVal.toNumber() <= LONG_MAX) && (aJSVal.toNumber() >= LONG_MIN);
|
||||
}
|
||||
if (typeString.Equals("float")) {
|
||||
return aJSVal.isNumber() && (aJSVal.toNumber() <= FLT_MAX) &&
|
||||
(aJSVal.toNumber() >= FLT_MIN);
|
||||
}
|
||||
if (typeString.Equals("double")) {
|
||||
return aJSVal.isNumber();
|
||||
}
|
||||
if (typeString.Equals("int8_t")) {
|
||||
return aJSVal.isNumber() && (trunc(aJSVal.toNumber()) == aJSVal.toNumber()) &&
|
||||
(aJSVal.toNumber() <= INT8_MAX) && (aJSVal.toNumber() >= INT8_MIN);
|
||||
}
|
||||
if (typeString.Equals("uint8_t")) {
|
||||
return aJSVal.isNumber() && (trunc(aJSVal.toNumber()) == aJSVal.toNumber()) &&
|
||||
(aJSVal.toNumber() <= UINT8_MAX) && (aJSVal.toNumber() >= 0);
|
||||
}
|
||||
if (typeString.Equals("int16_t")) {
|
||||
return aJSVal.isNumber() && (trunc(aJSVal.toNumber()) == aJSVal.toNumber()) &&
|
||||
(aJSVal.toNumber() <= INT16_MAX) && (aJSVal.toNumber() >= INT16_MIN);
|
||||
}
|
||||
if (typeString.Equals("uint16_t")) {
|
||||
return aJSVal.isNumber() && (trunc(aJSVal.toNumber()) == aJSVal.toNumber()) &&
|
||||
(aJSVal.toNumber() <= UINT16_MAX) && (aJSVal.toNumber() >= 0);
|
||||
}
|
||||
if (typeString.Equals("int32_t")) {
|
||||
return aJSVal.isNumber() && (trunc(aJSVal.toNumber()) == aJSVal.toNumber()) &&
|
||||
(aJSVal.toNumber() <= INT32_MAX) && (aJSVal.toNumber() >= INT32_MIN);
|
||||
}
|
||||
if (typeString.Equals("uint32_t")) {
|
||||
return aJSVal.isNumber() && (trunc(aJSVal.toNumber()) == aJSVal.toNumber()) &&
|
||||
(aJSVal.toNumber() <= UINT32_MAX) && (aJSVal.toNumber() >= 0);
|
||||
}
|
||||
|
||||
// These IPDL builtin types are not allowed from the JS API because they are
|
||||
// not easily representable.
|
||||
if (typeString.Equals("int64_t") || typeString.Equals("uint64_t") ||
|
||||
typeString.Equals("size_t") || typeString.Equals("ssize_t") ||
|
||||
typeString.Equals("nsresult") ||
|
||||
typeString.Equals("mozilla::ipc::Shmem") ||
|
||||
typeString.Equals("mozilla::ipc::ByteBuf") ||
|
||||
typeString.Equals("mozilla::ipc::FileDescriptor")) {
|
||||
JS_ReportErrorUTF8(
|
||||
aCx, "IPDL: cannot use type `%s` from JS", typeString.get());
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
IPDLProtocol::~IPDLProtocol()
|
||||
{
|
||||
mProtoObj = nullptr;
|
||||
mConstructorObj = nullptr;
|
||||
mozilla::DropJSObjects(this);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
IPDLProtocol::Constructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
|
||||
|
||||
// This function is a bit tricky. Basically we need to construct an object with
|
||||
// the protocol's C++ JSClass, but with the prototype of the inherited JS class.
|
||||
|
||||
// First we get the prototype of the inherited class.
|
||||
JS::RootedValue newTargetObjProtov(aCx);
|
||||
JS::RootedObject newTargetObj(aCx, &args.newTarget().toObject());
|
||||
if (!JS_GetProperty(aCx, newTargetObj, "prototype", &newTargetObjProtov)) {
|
||||
return false;
|
||||
}
|
||||
JS::RootedObject newTargetObjProto(aCx, &newTargetObjProtov.toObject());
|
||||
|
||||
// Now we get the prototype of abstract protocol class, and get the IPDLProtocol
|
||||
// object from it.
|
||||
JS::RootedObject callee(aCx, &args.callee());
|
||||
JS::RootedValue prototypev(aCx);
|
||||
if (!JS_GetProperty(aCx, callee, "prototype", &prototypev)) {
|
||||
return false;
|
||||
}
|
||||
JS::RootedObject prototype(aCx, &prototypev.toObject());
|
||||
auto protocol = static_cast<IPDLProtocol*>(JS_GetPrivate(prototype));
|
||||
|
||||
if (!protocol) {
|
||||
JS_ReportErrorUTF8(aCx, "Couldn't get protocol object from private date field");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now we construct our new object.
|
||||
JS::RootedObject newClassObject(
|
||||
aCx,
|
||||
JS_NewObjectWithGivenProto(
|
||||
aCx, &protocol->GetProtocolClass(), newTargetObjProto));
|
||||
|
||||
// We add it to our list of protocol instances and set its private field.
|
||||
|
||||
auto newInstance = MakeRefPtr<IPDLProtocolInstance>(
|
||||
nullptr, protocol->RegisterExternalInstance(), protocol, newClassObject);
|
||||
|
||||
protocol->mInstances.PutEntry(newInstance);
|
||||
JS_SetPrivate(newClassObject, newInstance);
|
||||
|
||||
args.rval().setObject(*newClassObject);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
IPDLProtocol::RecvDelete(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
IPDLProtocol::RecvConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
IPDLProtocol::AbstractRecvMessage(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
JS_ReportErrorUTF8(
|
||||
aCx, "Received message but recv method not overriden!");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
IPDLProtocol::AbstractAlloc(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
JS_ReportErrorUTF8(
|
||||
aCx, "Cannot alloc from abstract IPDL protocol class!");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* static */ nsCString
|
||||
IPDLProtocol::JoinQualifiedId(const ffi::QualifiedId aQid)
|
||||
{
|
||||
nsAutoCString ret;
|
||||
|
||||
for (auto& qual : aQid.quals) {
|
||||
ret.Append(qual);
|
||||
ret.Append("::");
|
||||
}
|
||||
ret.Append(aQid.base_id.id);
|
||||
|
||||
return std::move(ret);
|
||||
}
|
||||
|
||||
/* static */ nsCString
|
||||
IPDLProtocol::JoinNamespace(const ffi::Namespace aNs)
|
||||
{
|
||||
nsAutoCString ret;
|
||||
|
||||
for (auto& name : aNs.namespaces) {
|
||||
ret.Append(name);
|
||||
ret.Append("::");
|
||||
}
|
||||
ret.Append(aNs.name.id);
|
||||
|
||||
return std::move(ret);
|
||||
}
|
||||
|
||||
constexpr JSClassOps IPDLProtocol::sIPDLJSClassOps;
|
||||
|
||||
} // ipdl
|
||||
} // mozilla
|
|
@ -1,269 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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 https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef dom_base_ipdl_bindings_IPDLProtocol_h
|
||||
#define dom_base_ipdl_bindings_IPDLProtocol_h
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsStringFwd.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
|
||||
class nsIGlobalObject;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class IPDL;
|
||||
} // dom
|
||||
|
||||
namespace ipdl {
|
||||
class IPDLProtocolInstance;
|
||||
|
||||
namespace ipc {
|
||||
class IPCInterface;
|
||||
} // ipc
|
||||
|
||||
namespace ffi {
|
||||
struct AST;
|
||||
struct Union;
|
||||
struct Struct;
|
||||
struct NamedProtocol;
|
||||
struct MessageDecl;
|
||||
struct TranslationUnit;
|
||||
struct Param;
|
||||
struct TypeSpec;
|
||||
struct QualifiedId;
|
||||
struct Namespace;
|
||||
} // ffi
|
||||
|
||||
// Represents the side of the protocol.
|
||||
// This is un-nested to be forward-declarable.
|
||||
enum class IPDLSide : bool
|
||||
{
|
||||
Parent,
|
||||
Child
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents an IPDLProtocol to be used under the cover from JS.
|
||||
* This is owned by the IPDL class, and represents an abstract IPDL protocol
|
||||
* class which can be extended from JS.
|
||||
*
|
||||
* Example usage from JS:
|
||||
*
|
||||
* IPDL.registerTopLevelClass(TestParent);
|
||||
*
|
||||
* class TestParent extends IPDL.PTestParent {
|
||||
* // We add a listener for the message `SomeMessageFromChild` defined in
|
||||
* // the IPDL protocol
|
||||
* recvSomeMessageFromChild(arg1, arg2) {
|
||||
* // In IPDL, out params are named. This is how we return them.
|
||||
* return {out1: "test", out2: "test2" };
|
||||
* };
|
||||
*
|
||||
* // We define the allocation function
|
||||
* allocPSubTest() {
|
||||
* return new SubTestParent();
|
||||
* }
|
||||
*
|
||||
* // We need to pass the content parent ID to the super constructor
|
||||
* constructor(id) {
|
||||
* super(id);
|
||||
* // We send the sync message `SomeSyncMessageToChild` to the child
|
||||
* var syncResult = this.sendSomeSyncMessageToChild(123, [4, 5, 6]);
|
||||
* console.log(syncResult);
|
||||
*
|
||||
* // We send the async message `SomeAsyncMessageToChild` to the child
|
||||
* // Async calls return a promise
|
||||
* protocol.sendSomeAsyncMessageToChild("hi").then(function(asyncResult)
|
||||
* { console.log(asyncResult);
|
||||
* });
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
class IPDLProtocol : public nsISupports
|
||||
{
|
||||
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IPDLProtocol)
|
||||
|
||||
// Custom deleter for our AST smart pointer.
|
||||
struct ASTDeletePolicy
|
||||
{
|
||||
void operator()(const mozilla::ipdl::ffi::AST* aASTPtr);
|
||||
};
|
||||
|
||||
IPDLProtocol(dom::IPDL* aIPDL,
|
||||
IPDLSide aSide,
|
||||
const nsACString& aIPDLFile,
|
||||
nsIGlobalObject* aGlobal,
|
||||
JS::HandleObject aParent,
|
||||
JSContext* aCx);
|
||||
|
||||
// Get the JSObject corresponding to the constructor of the JS class
|
||||
// of our protocol.
|
||||
JSObject* GetProtocolClassConstructor();
|
||||
|
||||
// Returns a new channel ID and updates the internal channel ID counter.
|
||||
uint32_t RegisterExternalInstance();
|
||||
|
||||
// Returns the protocol name.
|
||||
nsCString GetProtocolName();
|
||||
|
||||
// Returns the protocol side.
|
||||
IPDLSide GetSide() { return mSide; }
|
||||
|
||||
// Returns a message decl for a given name.
|
||||
const ffi::MessageDecl* GetMessageDecl(const nsACString& aName)
|
||||
{
|
||||
return mMessageTable.Get(aName);
|
||||
}
|
||||
|
||||
// Type checks a parameter against a JS Value argument.
|
||||
// This is just a slim wrapper around CheckTypeSpec.
|
||||
bool CheckParamTypeSpec(JSContext* aCx,
|
||||
JS::HandleValue aJSVal,
|
||||
ffi::Param aParam);
|
||||
|
||||
// Returns the global object associated to that protocol.
|
||||
nsIGlobalObject* GetGlobal() { return mGlobal; }
|
||||
|
||||
void RemoveInstance(IPDLProtocolInstance* instance);
|
||||
|
||||
// Returns the protocol name suffixed with the protocol side.
|
||||
static nsCString GetSidedProtocolName(const nsCString& aProtocolName,
|
||||
IPDLSide aSide)
|
||||
{
|
||||
return aProtocolName + (aSide == IPDLSide::Parent
|
||||
? NS_LITERAL_CSTRING("Parent")
|
||||
: NS_LITERAL_CSTRING("Child"));
|
||||
}
|
||||
|
||||
// Small helper function to join a qualified id into a string.
|
||||
static nsCString JoinQualifiedId(const ffi::QualifiedId qid);
|
||||
// Small helper function to join a namespace into a string.
|
||||
static nsCString JoinNamespace(const ffi::Namespace ns);
|
||||
|
||||
protected:
|
||||
// The IPC interface object that we use for the actual IPC stuff.
|
||||
// Which side of the protocol we're on.
|
||||
IPDLSide mSide;
|
||||
// Our Rust-owned AST.
|
||||
UniquePtr<const ffi::AST, ASTDeletePolicy> mAST;
|
||||
// The global JS object interface needed for promises.
|
||||
nsCOMPtr<nsIGlobalObject> mGlobal;
|
||||
// The JS constructor we expose to the API user.
|
||||
JS::Heap<JSObject*> mConstructorObj;
|
||||
// The JS prototype we expose to the API user.
|
||||
JS::Heap<JSObject*> mProtoObj;
|
||||
// The C++ JSClass corresponding to the protocol.
|
||||
JSClass mProtocolClass;
|
||||
// The unsided protocol name.
|
||||
nsCString mProtocolName;
|
||||
// The sided protocol name.
|
||||
nsCString mSidedProtocolName;
|
||||
// The IPDL global object managing this protocol.
|
||||
dom::IPDL* MOZ_NON_OWNING_REF mIPDL;
|
||||
|
||||
// Typedefs for the member variables defined below.
|
||||
typedef nsDataHashtable<nsCStringHashKey, const ffi::MessageDecl*>
|
||||
MessageTable;
|
||||
|
||||
typedef nsDataHashtable<nsCStringHashKey, const ffi::Struct*> StructTable;
|
||||
|
||||
typedef nsDataHashtable<nsCStringHashKey, const ffi::Union*> UnionTable;
|
||||
|
||||
typedef nsDataHashtable<nsCStringHashKey, const ffi::NamedProtocol*>
|
||||
ProtocolTable;
|
||||
|
||||
typedef nsTHashtable<nsRefPtrHashKey<IPDLProtocolInstance>> InstanceList;
|
||||
|
||||
// List of current IPDL protocol instances living in JS.
|
||||
InstanceList mInstances;
|
||||
uint32_t mNextProtocolInstanceChannelId;
|
||||
|
||||
// Maps message names to the AST struct describing them.
|
||||
MessageTable mMessageTable;
|
||||
// Maps struct names to the AST struct describing them.
|
||||
StructTable mStructTable;
|
||||
// Maps union names to the AST struct describing them.
|
||||
UnionTable mUnionTable;
|
||||
// Maps protocol names to the AST struct describing them.
|
||||
ProtocolTable mProtocolTable;
|
||||
|
||||
virtual ~IPDLProtocol();
|
||||
|
||||
// Builds the struct, union and protocol name->AST lookup tables so that we
|
||||
// can easily access name from the TypeSpecs given by message parameters.
|
||||
void BuildNameLookupTables();
|
||||
|
||||
// Used internally to get the protocol JSClass.
|
||||
JSClass& GetProtocolClass();
|
||||
|
||||
// Returns the main translation unit.
|
||||
const ffi::TranslationUnit* GetMainTU();
|
||||
|
||||
// Type checks a TypeSpec against a JS Value.
|
||||
// This takes care of checking the nullable and array extras, and then calls
|
||||
// CheckType, possibly on all elements if dealing with an array.
|
||||
bool CheckTypeSpec(JSContext* cx,
|
||||
JS::HandleValue jsVal,
|
||||
ffi::TypeSpec typeSpec);
|
||||
|
||||
// Type checks a type name against a JS Value.
|
||||
bool CheckType(JSContext* cx, JS::HandleValue jsVal, ffi::QualifiedId type);
|
||||
|
||||
// Type checks a protocol type name against a JS Value.
|
||||
bool CheckProtocolType(JSContext* cx,
|
||||
JS::HandleValue jsVal,
|
||||
ffi::QualifiedId type);
|
||||
|
||||
// Type checks a struct type name against a JS Value.
|
||||
bool CheckStructType(JSContext* cx,
|
||||
JS::HandleValue jsVal,
|
||||
ffi::QualifiedId type);
|
||||
|
||||
// Type checks a union type name against a JS Value.
|
||||
bool CheckUnionType(JSContext* cx,
|
||||
JS::HandleValue jsVal,
|
||||
ffi::QualifiedId type);
|
||||
|
||||
// Type checks a builtin type name against a JS Value.
|
||||
// Note that some usual IPDL C++-inspired types are not available to use
|
||||
// with this JS API (for instance, int64_t).
|
||||
static bool CheckBuiltinType(JSContext* cx,
|
||||
JS::HandleValue jsVal,
|
||||
ffi::QualifiedId type);
|
||||
|
||||
// These are wrappers unpacking the IPDLProtocolInstance stored in the private
|
||||
// data field of the callee, then dispatching the call to that instance.
|
||||
static bool SendMessageDispatch(JSContext* cx, unsigned argc, JS::Value* vp);
|
||||
|
||||
static bool SendConstructorDispatch(JSContext* cx,
|
||||
unsigned argc,
|
||||
JS::Value* vp);
|
||||
static bool SendDeleteDispatch(JSContext* cx, unsigned argc, JS::Value* vp);
|
||||
|
||||
// Constructor for the protocol class, which is called from the inherited
|
||||
// classes.
|
||||
static bool Constructor(JSContext* cx, unsigned argc, JS::Value* vp);
|
||||
|
||||
// Default implementations for various IPDL stuff. Abstract methods throw on
|
||||
// call because they MUST be reimplemented.
|
||||
static bool RecvDelete(JSContext* cx, unsigned argc, JS::Value* vp);
|
||||
static bool RecvConstructor(JSContext* cx, unsigned argc, JS::Value* vp);
|
||||
static bool AbstractRecvMessage(JSContext* cx, unsigned argc, JS::Value* vp);
|
||||
static bool AbstractAlloc(JSContext* cx, unsigned argc, JS::Value* vp);
|
||||
|
||||
// Our IPDLProtocol JS class custom operations.
|
||||
static constexpr JSClassOps sIPDLJSClassOps = {};
|
||||
};
|
||||
|
||||
} // ipdl
|
||||
} // mozilla
|
||||
|
||||
#endif // dom_base_ipdl_bindings_IPDLProtocol_h
|
|
@ -1,460 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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 https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "IPDLProtocolInstance.h"
|
||||
|
||||
#include <cfloat>
|
||||
|
||||
#include "IPCInterface.h"
|
||||
#include "ipdl_ffi_generated.h"
|
||||
#include "mozilla/dom/DOMMozPromiseRequestHolder.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "wrapper.h"
|
||||
#include "mozilla/dom/IPDL.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipdl {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(IPDLProtocolInstance)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IPDLProtocolInstance)
|
||||
tmp->mInstanceObject = nullptr;
|
||||
tmp->mAsyncReturnOverride = nullptr;
|
||||
mozilla::DropJSObjects(tmp);
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IPDLProtocolInstance)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IPDLProtocolInstance)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mInstanceObject)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mAsyncReturnOverride)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(IPDLProtocolInstance)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(IPDLProtocolInstance)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IPDLProtocolInstance)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
IPDLProtocolInstance::IPDLProtocolInstance(ipc::IPCInterface* aIPCInterface,
|
||||
uint32_t aChannelId,
|
||||
IPDLProtocol* aIPDLProtocol,
|
||||
JS::HandleObject aInstanceObject)
|
||||
: mChannelId(aChannelId)
|
||||
, mIPDLProtocol(aIPDLProtocol)
|
||||
, mInstanceObject(aInstanceObject)
|
||||
, mAsyncReturnOverride(nullptr)
|
||||
, mConstructing(true)
|
||||
{
|
||||
mozilla::HoldJSObjects(this);
|
||||
|
||||
SetIPCInterface(aIPCInterface);
|
||||
}
|
||||
|
||||
IPDLProtocolInstance::~IPDLProtocolInstance()
|
||||
{
|
||||
mInstanceObject = nullptr;
|
||||
mAsyncReturnOverride = nullptr;
|
||||
mozilla::DropJSObjects(this);
|
||||
}
|
||||
|
||||
void
|
||||
IPDLProtocolInstance::SetIPCInterface(ipc::IPCInterface* aIPCInterface)
|
||||
{
|
||||
mIPCInterface = aIPCInterface;
|
||||
|
||||
if (mIPCInterface) {
|
||||
// If we're setting the interface, it means that we're done with the
|
||||
// constructor.
|
||||
mConstructing = false;
|
||||
|
||||
NS_NAMED_LITERAL_CSTRING(parentSuffix, "Parent");
|
||||
NS_NAMED_LITERAL_CSTRING(childSuffix, "Child");
|
||||
|
||||
// Hook up this instance to the IPC interface for receiving messages.
|
||||
mIPCInterface->SetIPDLInstance(
|
||||
mChannelId,
|
||||
mIPDLProtocol->GetProtocolName() +
|
||||
(mIPDLProtocol->GetSide() == IPDLSide::Child ? childSuffix
|
||||
: parentSuffix),
|
||||
this);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
IPDLProtocolInstance::SendConstructor(JSContext* aCx,
|
||||
JS::HandleObject aThisObj,
|
||||
const nsCString& aFuncName,
|
||||
JS::CallArgs aArgs)
|
||||
{
|
||||
// Make sure the instance can send stuff.
|
||||
if (!CheckIsAvailable(aCx)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NS_NAMED_LITERAL_CSTRING(sendPrefix, "send");
|
||||
NS_NAMED_LITERAL_CSTRING(constructorSuffix, "Constructor");
|
||||
|
||||
nsAutoCString allocName(NS_LITERAL_CSTRING("alloc"));
|
||||
allocName.Append(Substring(aFuncName,
|
||||
sendPrefix.Length(),
|
||||
aFuncName.Length() - sendPrefix.Length() -
|
||||
constructorSuffix.Length()));
|
||||
|
||||
JS::HandleValueArray argsArray(aArgs);
|
||||
|
||||
// Construct the sub protocol by calling our alloc function.
|
||||
JS::RootedValue constructedValue(aCx);
|
||||
if (!JS_CallFunctionName(
|
||||
aCx, aThisObj, allocName.get(), argsArray, &constructedValue)) {
|
||||
aArgs.rval().setUndefined();
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::RootedObject constructedObject(aCx, &constructedValue.toObject());
|
||||
|
||||
// Add the IPC interface to the newly constructed object.
|
||||
auto instance = static_cast<IPDLProtocolInstance*>(JS_GetPrivate(constructedObject.get()));
|
||||
|
||||
if (!instance) {
|
||||
JS_ReportErrorUTF8(aCx, "Couldn't get protocol instance object from private date field");
|
||||
return false;
|
||||
}
|
||||
|
||||
instance->SetIPCInterface(mIPCInterface);
|
||||
|
||||
// Create new call arguments from the current call arguments to be passed to
|
||||
// SendMessage.
|
||||
JS::AutoValueVector newArgsVec(aCx);
|
||||
if (!newArgsVec.initCapacity(aArgs.length() +
|
||||
3)) { // this + callee + channelID
|
||||
JS_ReportErrorUTF8(
|
||||
aCx, "Could not initialize new argument vector for constructor");
|
||||
aArgs.rval().setUndefined();
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::CallArgs newArgs =
|
||||
JS::CallArgsFromVp(aArgs.length() + 1, newArgsVec.begin());
|
||||
newArgs.setThis(aArgs.thisv().get());
|
||||
newArgs.setCallee(aArgs.calleev().get());
|
||||
|
||||
// We add the channel ID as the first argument.
|
||||
JS::RootedValue channelID(aCx, JS::NumberValue(instance->ChannelId()));
|
||||
newArgs[0].set(channelID);
|
||||
// We copy all the other arguments.
|
||||
for (unsigned int i = 0; i < aArgs.length(); i++) {
|
||||
newArgs[i + 1].set(aArgs[i]);
|
||||
}
|
||||
|
||||
// We send the construction message, overriding the return result
|
||||
// with the constructed object.
|
||||
bool res = SendMessage(aCx, aFuncName, newArgs, constructedObject);
|
||||
|
||||
aArgs.rval().set(newArgs.rval());
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool
|
||||
IPDLProtocolInstance::SendDelete(JSContext* aCx,
|
||||
const nsCString& aFuncName,
|
||||
JS::CallArgs aArgs)
|
||||
{
|
||||
// Make sure the instance can send stuff.
|
||||
if (!CheckIsAvailable(aCx)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool res = SendMessage(aCx, aFuncName, aArgs);
|
||||
return res;
|
||||
}
|
||||
|
||||
bool
|
||||
IPDLProtocolInstance::SendMessage(JSContext* aCx,
|
||||
const nsCString& aFuncName,
|
||||
JS::CallArgs aArgs,
|
||||
JS::HandleObject aReturnOverride)
|
||||
{
|
||||
// Make sure the instance can send stuff.
|
||||
if (!CheckIsAvailable(aCx)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the corresponding message AST struct from our lookup table.
|
||||
auto message = mIPDLProtocol->GetMessageDecl(aFuncName);
|
||||
// Sanity check. Usually the JS engine will trigger a type error saying it
|
||||
// doesn't find the function before we reach this stage. If this assertion
|
||||
// is triggered, it means there is a flaw in the way we add messages.
|
||||
MOZ_ASSERT(message);
|
||||
|
||||
auto& inParams = message->in_params;
|
||||
|
||||
auto expectedArgCount = inParams.Length();
|
||||
// Since our arg count reflects a well defined protocol, should we
|
||||
// require strict arg count?
|
||||
if (!aArgs.requireAtLeast(aCx, aFuncName.get(), expectedArgCount)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Check that every param and args have matching types.
|
||||
for (size_t i = 0; i < inParams.Length(); i++) {
|
||||
if (!mIPDLProtocol->CheckParamTypeSpec(
|
||||
aCx, aArgs.get(i), inParams.ElementAt(i))) {
|
||||
JS_ReportErrorUTF8(
|
||||
aCx,
|
||||
"Type mismatch on %zuth argument of `%s`: expected %s, got %s",
|
||||
i,
|
||||
aFuncName.get(),
|
||||
IPDLProtocol::JoinQualifiedId(inParams.ElementAt(i).type_spec.spec)
|
||||
.get(),
|
||||
InformalValueTypeName(aArgs.get(i)));
|
||||
aArgs.rval().setUndefined();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
NS_NAMED_LITERAL_CSTRING(sendPrefix, "send");
|
||||
|
||||
nsAutoCString messageName(
|
||||
Substring(aFuncName,
|
||||
sendPrefix.Length(),
|
||||
aFuncName.Length() - sendPrefix.Length())); // trim leading `send`
|
||||
|
||||
// Prepare the argument array for sending to the IPC interface.
|
||||
auto argArray = JS::HandleValueArray(aArgs);
|
||||
|
||||
// Send the message to the underlying IPC interface depending on the send
|
||||
// semantics.
|
||||
JS::RootedValue retVal(aCx);
|
||||
bool ret;
|
||||
switch (message->send_semantics) {
|
||||
case ffi::SendSemantics::Async:
|
||||
retVal = SendAsyncMessage(aCx, messageName, argArray, aReturnOverride);
|
||||
ret = true;
|
||||
break;
|
||||
|
||||
case ffi::SendSemantics::Sync:
|
||||
ret = SendSyncMessage(aCx, messageName, argArray, &retVal);
|
||||
if (aReturnOverride.get()) {
|
||||
retVal.setObject(*aReturnOverride);
|
||||
}
|
||||
break;
|
||||
|
||||
case ffi::SendSemantics::Intr:
|
||||
ret = SendIntrMessage(aCx, messageName, argArray, &retVal);
|
||||
if (aReturnOverride.get()) {
|
||||
retVal.setObject(*aReturnOverride);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
aArgs.rval().set(retVal);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
JS::Value
|
||||
IPDLProtocolInstance::SendAsyncMessage(JSContext* aCx,
|
||||
const nsCString& aFuncName,
|
||||
const JS::HandleValueArray& aArgArray,
|
||||
JS::HandleObject aReturnOverride)
|
||||
{
|
||||
// Create a JS/DOM promise to return.
|
||||
// It will resolve to the result of the message call.
|
||||
ErrorResult aRv;
|
||||
RefPtr<dom::Promise> outer =
|
||||
dom::Promise::Create(mIPDLProtocol->GetGlobal(), aRv);
|
||||
if (aRv.Failed()) {
|
||||
return JS::UndefinedValue();
|
||||
}
|
||||
|
||||
// This wraps the AsyncMessagePromise that we use for the actual IPC/C++
|
||||
// message so that if the global object gets disconnected, that promise also
|
||||
// gets disconnected and is not left dangling.
|
||||
auto promiseHolder = MakeRefPtr<
|
||||
dom::DOMMozPromiseRequestHolder<ipc::IPCInterface::AsyncMessagePromise>>(
|
||||
mIPDLProtocol->GetGlobal());
|
||||
|
||||
bool hasReturnOverride = (aReturnOverride.get());
|
||||
mAsyncReturnOverride = aReturnOverride;
|
||||
|
||||
// Actually send the message to the IPC interface.
|
||||
// If the C++ promise succeeds, then we resolve the JS promise.
|
||||
// If it fails, we reject the JS promise.
|
||||
// We also make sure that the promise holder tracks our promise.
|
||||
mIPCInterface
|
||||
->SendAsyncMessage(
|
||||
aCx, mIPDLProtocol->GetProtocolName(), mChannelId, aFuncName, aArgArray)
|
||||
->Then(mIPDLProtocol->GetGlobal()->EventTargetFor(TaskCategory::Other),
|
||||
__func__,
|
||||
[&asyncReturnOverride = mAsyncReturnOverride, promiseHolder, outer, hasReturnOverride](
|
||||
const JS::Value& aResult) {
|
||||
// Tell the promise holder that our promise is resolved.
|
||||
promiseHolder->Complete();
|
||||
|
||||
// Note, you can access the holder's bound global in
|
||||
// your reaction handler. Its mostly likely set if
|
||||
// the handler fires, but you still must check for
|
||||
// its existence since something could disconnect
|
||||
// the global between when the MozPromise reaction
|
||||
// runnable is queued and when it actually runs.
|
||||
nsIGlobalObject* global = promiseHolder->GetParentObject();
|
||||
NS_ENSURE_TRUE_VOID(global);
|
||||
|
||||
// Resolve the JS promise with our result.
|
||||
// If we have an override return value, return it instead.
|
||||
if (hasReturnOverride) {
|
||||
outer->MaybeResolve(JS::ObjectValue(*asyncReturnOverride));
|
||||
asyncReturnOverride = nullptr;
|
||||
} else {
|
||||
outer->MaybeResolve(aResult);
|
||||
}
|
||||
},
|
||||
[promiseHolder, outer, aCx](nsCString aRv) {
|
||||
// Tell the promise holder that our promise is resolved.
|
||||
promiseHolder->Complete();
|
||||
|
||||
// Reject the JS promise with our error message.
|
||||
JS::RootedValue retVal(
|
||||
aCx, JS::StringValue(JS_NewStringCopyZ(aCx, aRv.get())));
|
||||
outer->MaybeReject(aCx, retVal);
|
||||
})
|
||||
->Track(*promiseHolder);
|
||||
|
||||
// Get the JSObject corresponding to the JS promise.
|
||||
JS::RootedObject retObj(aCx, outer->PromiseObj());
|
||||
|
||||
return JS::ObjectValue(*retObj);
|
||||
}
|
||||
|
||||
bool
|
||||
IPDLProtocolInstance::SendSyncMessage(JSContext* aCx,
|
||||
const nsCString& aFuncName,
|
||||
const JS::HandleValueArray& aArgArray,
|
||||
JS::MutableHandleValue aRet)
|
||||
{
|
||||
// Simply call the underlying IPC interface directly.
|
||||
return mIPCInterface->SendSyncMessage(aCx,
|
||||
mIPDLProtocol->GetProtocolName(),
|
||||
mChannelId,
|
||||
aFuncName,
|
||||
aArgArray,
|
||||
aRet);
|
||||
}
|
||||
|
||||
bool
|
||||
IPDLProtocolInstance::SendIntrMessage(JSContext* aCx,
|
||||
const nsCString& aFuncName,
|
||||
const JS::HandleValueArray& aArgArray,
|
||||
JS::MutableHandleValue aRet)
|
||||
{
|
||||
// Simply call the underlying IPC interface directly.
|
||||
return mIPCInterface->SendIntrMessage(aCx,
|
||||
mIPDLProtocol->GetProtocolName(),
|
||||
mChannelId,
|
||||
aFuncName,
|
||||
aArgArray,
|
||||
aRet);
|
||||
}
|
||||
|
||||
bool
|
||||
IPDLProtocolInstance::RecvMessage(JSContext* aCx,
|
||||
const nsCString& aMessageName,
|
||||
const JS::HandleValueArray& aArgArray,
|
||||
JS::MutableHandleValue aRet)
|
||||
{
|
||||
// Make sure the instance can receive stuff.
|
||||
if (!CheckIsAvailable(aCx)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const nsAutoCString& funcName = NS_LITERAL_CSTRING("recv") + aMessageName;
|
||||
JS::RootedValue prop(aCx);
|
||||
|
||||
NS_NAMED_LITERAL_CSTRING(constructorSuffix, "Constructor");
|
||||
|
||||
// Check if the message name ends with the constructor suffix.
|
||||
if (aMessageName.RFind(
|
||||
constructorSuffix.get(), false, -1, constructorSuffix.Length()) != kNotFound) {
|
||||
nsAutoCString protocolName(Substring(
|
||||
aMessageName, 0, aMessageName.Length() - constructorSuffix.Length()));
|
||||
nsAutoCString allocName(protocolName);
|
||||
allocName.InsertLiteral("alloc", 0);
|
||||
|
||||
// Allocate the new sub protocol instance by calling the alloc method.
|
||||
JS::RootedValue allocatedProtocolValue(aCx);
|
||||
JS::RootedObject objectInstance(aCx, mInstanceObject);
|
||||
JS_CallFunctionName(
|
||||
aCx,
|
||||
objectInstance,
|
||||
allocName.get(),
|
||||
// Ignore the leading channel ID.
|
||||
JS::HandleValueArray::subarray(aArgArray, 1, aArgArray.length() - 1),
|
||||
&allocatedProtocolValue);
|
||||
|
||||
JS::RootedObject allocatedProtocol(aCx, &allocatedProtocolValue.toObject());
|
||||
|
||||
auto instance = static_cast<IPDLProtocolInstance*>(JS_GetPrivate(allocatedProtocol.get()));
|
||||
|
||||
if (!instance) {
|
||||
JS_ReportErrorUTF8(aCx, "Couldn't get protocol instance object from private date field");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set the IPC interface of the constructed protocol object.
|
||||
instance->SetIPCInterface(mIPCInterface);
|
||||
|
||||
nsAutoCString constructorName(protocolName);
|
||||
constructorName.InsertLiteral("recv", 0);
|
||||
constructorName.AppendLiteral("Constructor");
|
||||
|
||||
// Call the recvConstructor function
|
||||
JS::RootedValue unusedResult(aCx);
|
||||
JS_CallFunctionName(aCx,
|
||||
objectInstance,
|
||||
constructorName.get(),
|
||||
JS::HandleValueArray(allocatedProtocolValue),
|
||||
&unusedResult);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise if we have a regular message, just call it.
|
||||
JS::RootedObject objInstance(aCx, mInstanceObject);
|
||||
if (!JS_GetProperty(aCx, objInstance, funcName.get(), &prop)) {
|
||||
aRet.setUndefined();
|
||||
return false;
|
||||
}
|
||||
if (!JS_CallFunctionValue(aCx, objInstance, prop, aArgArray, aRet)) {
|
||||
aRet.setUndefined();
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we receive a delete, mark our protocol instance as deleted.
|
||||
if (aMessageName.Equals("__delete__")) {
|
||||
aRet.setUndefined();
|
||||
|
||||
JS_SetPrivate(mInstanceObject, nullptr);
|
||||
mIPCInterface->RemoveIPDLInstance(mChannelId,
|
||||
mIPDLProtocol->GetProtocolName());
|
||||
// ALWAYS PUT THIS LAST, DO NOT ACCESS `this` AFTERWARDS
|
||||
mIPDLProtocol->RemoveInstance(this);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
IPDLProtocolInstance::GetInstanceObject()
|
||||
{
|
||||
return mInstanceObject;
|
||||
}
|
||||
|
||||
} // ipdl
|
||||
} // mozilla
|
|
@ -1,134 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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 https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef dom_base_ipdl_bindings_IPDLProtocolInstance_h
|
||||
#define dom_base_ipdl_bindings_IPDLProtocolInstance_h
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "nsStringFwd.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class IPDL;
|
||||
} // dom
|
||||
|
||||
namespace ipdl {
|
||||
class IPDLProtocol;
|
||||
|
||||
namespace ipc {
|
||||
class IPCInterface;
|
||||
} // ipc
|
||||
|
||||
namespace ffi {
|
||||
struct AST;
|
||||
struct Union;
|
||||
struct Struct;
|
||||
struct NamedProtocol;
|
||||
struct MessageDecl;
|
||||
struct TranslationUnit;
|
||||
struct Param;
|
||||
struct TypeSpec;
|
||||
struct QualifiedId;
|
||||
struct Namespace;
|
||||
} // ffi
|
||||
|
||||
/**
|
||||
* Represents an IPDL protocol object instance, either created automatically if the protocol is top level,
|
||||
* or allocated from the parent protocol.
|
||||
*/
|
||||
class IPDLProtocolInstance : public nsISupports
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IPDLProtocolInstance)
|
||||
|
||||
IPDLProtocolInstance(ipc::IPCInterface* aIPCInterface,
|
||||
uint32_t aChannelId,
|
||||
IPDLProtocol* aIPDLProtocol,
|
||||
JS::HandleObject aInstanceObject);
|
||||
|
||||
// Returns the JSObject corresponding to this instance.
|
||||
JSObject* GetInstanceObject();
|
||||
|
||||
// Returns the channel ID of this instance.
|
||||
uint32_t ChannelId() { return mChannelId; };
|
||||
|
||||
// Represents the IPDL sendSubProtocolConstructor functions.
|
||||
bool SendConstructor(JSContext* aCx,
|
||||
JS::HandleObject aThisObj,
|
||||
const nsCString& aFuncName,
|
||||
JS::CallArgs aArgs);
|
||||
// Represents the IPDL send__delete__ function.
|
||||
bool SendDelete(JSContext* aCx, const nsCString& aFuncName, JS::CallArgs aArgs);
|
||||
|
||||
// Performs param/arg type checking and calls a sub-function depending on
|
||||
// the message send semantics.
|
||||
bool SendMessage(JSContext* aCx,
|
||||
const nsCString& aFuncName,
|
||||
JS::CallArgs aArgs,
|
||||
JS::HandleObject aReturnOverride = nullptr);
|
||||
|
||||
// Sends an async message by calling the underlying IPC interface, returning
|
||||
// a promise to the return value object.
|
||||
JS::Value SendAsyncMessage(JSContext* aCx,
|
||||
const nsCString& aFuncName,
|
||||
const JS::HandleValueArray& aArgArray,
|
||||
JS::HandleObject aReturnOverride = nullptr);
|
||||
|
||||
// Sends a sync message by calling the underlying IPC interface, returning
|
||||
// the return value object.
|
||||
bool SendSyncMessage(JSContext* aCx,
|
||||
const nsCString& aFuncName,
|
||||
const JS::HandleValueArray& aArgArray,
|
||||
JS::MutableHandleValue aRet);
|
||||
|
||||
// Sends an intr message by calling the underlying IPC interface, returning
|
||||
// the return value object.
|
||||
bool SendIntrMessage(JSContext* aCx,
|
||||
const nsCString& aFuncName,
|
||||
const JS::HandleValueArray& aArgArray,
|
||||
JS::MutableHandleValue aRet);
|
||||
|
||||
// Receives a message from the underlying IPC interface, returning the result
|
||||
// if any.
|
||||
bool RecvMessage(JSContext* aCx,
|
||||
const nsCString& aMessageName,
|
||||
const JS::HandleValueArray& aArgArray,
|
||||
JS::MutableHandleValue aRet);
|
||||
|
||||
// Sets the IPC interface of the IPDL instance.
|
||||
void SetIPCInterface(ipc::IPCInterface* aIPCInterface);
|
||||
|
||||
protected:
|
||||
virtual ~IPDLProtocolInstance();
|
||||
|
||||
// Whether this protocol is being constructed (we're in the constructor of the instance).
|
||||
bool IsConstructing() { return mConstructing; }
|
||||
|
||||
bool CheckIsAvailable(JSContext* aCx)
|
||||
{
|
||||
if (IsConstructing()) {
|
||||
JS_ReportErrorUTF8(aCx,
|
||||
"Protocol is constructing, cannot be used yet.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ipc::IPCInterface* MOZ_NON_OWNING_REF mIPCInterface;
|
||||
uint32_t mChannelId;
|
||||
IPDLProtocol* MOZ_NON_OWNING_REF mIPDLProtocol;
|
||||
JS::Heap<JSObject*> mInstanceObject;
|
||||
JS::Heap<JSObject*> mAsyncReturnOverride;
|
||||
bool mConstructing;
|
||||
};
|
||||
|
||||
} // ipdl
|
||||
} // mozilla
|
||||
|
||||
#endif // dom_base_ipdl_bindings_IPDLProtocolInstance_h
|
|
@ -1,198 +0,0 @@
|
|||
#include "PContentChildIPCInterface.h"
|
||||
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipdl {
|
||||
namespace ipc {
|
||||
|
||||
PContentChildIPCInterface::PContentChildIPCInterface(dom::IPDL* aIPDL,
|
||||
dom::ContentChild* aCc)
|
||||
: IPCInterface(aIPDL)
|
||||
, mCc(aCc)
|
||||
{
|
||||
mCc->RegisterIPDLIPCInterface(this);
|
||||
}
|
||||
|
||||
/* virtual */ mozilla::ipc::IPCResult
|
||||
PContentChildIPCInterface::RecvMessage(
|
||||
const nsCString& aProtocolName,
|
||||
const uint32_t& aChannelId,
|
||||
const nsCString& aMessage,
|
||||
const dom::ClonedMessageData& aData,
|
||||
nsTArray<dom::ipc::StructuredCloneData>* aReturnData)
|
||||
{
|
||||
return RecvMessageCommon(mCc,
|
||||
IPDLSide::Child,
|
||||
aProtocolName,
|
||||
aChannelId,
|
||||
aMessage,
|
||||
aData,
|
||||
aReturnData);
|
||||
}
|
||||
|
||||
/* virtual */ RefPtr<IPCInterface::AsyncMessagePromise>
|
||||
PContentChildIPCInterface::SendAsyncMessage(JSContext* aCx,
|
||||
const nsCString& aProtocolName,
|
||||
const uint32_t& aChannelId,
|
||||
const nsCString& aMessageName,
|
||||
const InArgs& aArgs)
|
||||
{
|
||||
// Create a new Promise to resolve the async call.
|
||||
auto promise = MakeRefPtr<AsyncMessagePromise::Private>(__func__);
|
||||
|
||||
auto promiseHolder = MakeRefPtr<
|
||||
dom::DOMMozPromiseRequestHolder<dom::ContentChild::AsyncMessageIPDLPromise>>(
|
||||
xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx)));
|
||||
|
||||
// Write arguments to the StructuredCloneData.
|
||||
dom::ipc::StructuredCloneData data;
|
||||
ErrorResult errRes;
|
||||
|
||||
auto* argsObjPtr = JS_NewArrayObject(aCx, aArgs);
|
||||
|
||||
if (!argsObjPtr) {
|
||||
promise->Reject(
|
||||
NS_LITERAL_CSTRING("Failed to allocate new array object"),
|
||||
__func__);
|
||||
return promise;
|
||||
}
|
||||
|
||||
JS::RootedObject argsObj(aCx, argsObjPtr);
|
||||
JS::RootedValue argsVal(aCx, JS::ObjectValue(*argsObj));
|
||||
|
||||
data.Write(aCx, argsVal, errRes);
|
||||
|
||||
if (NS_WARN_IF(errRes.Failed())) {
|
||||
promise->Reject(
|
||||
NS_LITERAL_CSTRING("Failed to write arguments to StructuredCloneData"),
|
||||
__func__);
|
||||
return promise;
|
||||
}
|
||||
|
||||
// Wrap the StructuredCloneData in a ClonedMessageData.
|
||||
dom::ClonedMessageData cmd;
|
||||
if (!data.BuildClonedMessageDataForChild((dom::nsIContentChild*)mCc, cmd)) {
|
||||
promise->Reject(
|
||||
NS_LITERAL_CSTRING("Failed to write arguments to StructuredCloneData"),
|
||||
__func__);
|
||||
return promise;
|
||||
}
|
||||
|
||||
// Send the actual message through the ContentChild
|
||||
mCc->SendAsyncMessageIPDL(aProtocolName, aChannelId, aMessageName, cmd)
|
||||
->Then(promiseHolder->GetParentObject()->EventTargetFor(TaskCategory::Other),
|
||||
__func__,
|
||||
[promiseHolder, promise](nsTArray<dom::ipc::StructuredCloneData> aRes) {
|
||||
promiseHolder->Complete();
|
||||
|
||||
// If the array is empty, it means we got an error.
|
||||
if (aRes.IsEmpty()) {
|
||||
promise->Reject(NS_LITERAL_CSTRING(
|
||||
"Error in RecvAsyncMessage in parent process"),
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
|
||||
nsIGlobalObject* global = promiseHolder->GetParentObject();
|
||||
NS_ENSURE_TRUE_VOID(global);
|
||||
|
||||
dom::AutoEntryScript aes(global, "SendAsyncMessageIPDL");
|
||||
|
||||
JSContext* cx = aes.cx();
|
||||
|
||||
JS::RootedValue ret(cx);
|
||||
ErrorResult rv;
|
||||
aRes.ElementAt(0).Read(cx, &ret, rv);
|
||||
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
promise->Reject(
|
||||
NS_LITERAL_CSTRING(
|
||||
"Failed to read return value from StructuredCloneData"),
|
||||
__func__);
|
||||
} else {
|
||||
promise->Resolve(ret, __func__);
|
||||
}
|
||||
},
|
||||
[promiseHolder, promise](::mozilla::ipc::ResponseRejectReason aReason) {
|
||||
promiseHolder->Complete();
|
||||
promise->Reject(nsPrintfCString(
|
||||
"IPC error when calling SendAsyncMessageIPDL, "
|
||||
"see error message above. Reason: %d",
|
||||
(unsigned int)aReason),
|
||||
__func__);
|
||||
})->Track(*promiseHolder);
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
/* virtual */ bool
|
||||
PContentChildIPCInterface::SendSyncMessage(JSContext* aCx,
|
||||
const nsCString& aProtocolName,
|
||||
const uint32_t& aChannelId,
|
||||
const nsCString& aMessageName,
|
||||
const InArgs& aArgs,
|
||||
OutObject aRet)
|
||||
{
|
||||
// Write arguments to the StructuredCloneData.
|
||||
dom::ipc::StructuredCloneData data;
|
||||
ErrorResult errRes;
|
||||
|
||||
auto* argsObjPtr = JS_NewArrayObject(aCx, aArgs);
|
||||
|
||||
if (!argsObjPtr) {
|
||||
aRet.setUndefined();
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::RootedObject argsObj(aCx, argsObjPtr);
|
||||
JS::RootedValue argsVal(aCx, JS::ObjectValue(*argsObj));
|
||||
|
||||
data.Write(aCx, argsVal, errRes);
|
||||
|
||||
if (NS_WARN_IF(errRes.Failed())) {
|
||||
JS_ReportErrorUTF8(aCx, "Failed to write arguments to StructuredCloneData");
|
||||
aRet.setUndefined();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Wrap the StructuredCloneData in a ClonedMessageData.
|
||||
dom::ClonedMessageData cmd;
|
||||
if (!data.BuildClonedMessageDataForChild((dom::nsIContentChild*)mCc, cmd)) {
|
||||
JS_ReportErrorUTF8(aCx, "Failed to build cloned message data for arguments");
|
||||
aRet.setUndefined();
|
||||
return false;
|
||||
}
|
||||
|
||||
nsTArray<dom::ipc::StructuredCloneData> retData;
|
||||
|
||||
// Send the actual message through the ContentChild
|
||||
if (!mCc->SendSyncMessageIPDL(
|
||||
aProtocolName, aChannelId, aMessageName, cmd, &retData)) {
|
||||
JS_ReportErrorUTF8(aCx, "IPC error when calling SendSyncMessageIPDL");
|
||||
aRet.setUndefined();
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the array is empty, it means we got an error.
|
||||
if (retData.IsEmpty()) {
|
||||
JS_ReportErrorUTF8(aCx, "Error in RecvSyncMessageIPDL in parent process");
|
||||
aRet.setUndefined();
|
||||
return false;
|
||||
}
|
||||
|
||||
retData.ElementAt(0).Read(aCx, aRet, errRes);
|
||||
|
||||
if (NS_WARN_IF(errRes.Failed())) {
|
||||
JS_ReportErrorUTF8(aCx,
|
||||
"Failed to read return value from StructuredCloneData");
|
||||
aRet.setUndefined();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // ipc
|
||||
} // ipdl
|
||||
} // mozilla
|
|
@ -1,77 +0,0 @@
|
|||
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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 https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef dom_base_ipdl_bindings_pcontentchildipcinterface_h
|
||||
#define dom_base_ipdl_bindings_pcontentchildipcinterface_h
|
||||
|
||||
#include "IPCInterface.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class IPDL;
|
||||
class ContentChild;
|
||||
namespace ipc {
|
||||
class StructuredCloneData;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ipdl {
|
||||
namespace ipc {
|
||||
|
||||
class PContentChildIPCInterface : public IPCInterface
|
||||
{
|
||||
public:
|
||||
PContentChildIPCInterface(dom::IPDL* aIPDL, dom::ContentChild* aCc);
|
||||
|
||||
virtual ~PContentChildIPCInterface() = default;
|
||||
|
||||
// Receive a message from IPC.
|
||||
virtual mozilla::ipc::IPCResult RecvMessage(
|
||||
const nsCString& aProtocolName,
|
||||
const uint32_t& aChannelId,
|
||||
const nsCString& aMessage,
|
||||
const dom::ClonedMessageData& aData,
|
||||
nsTArray<dom::ipc::StructuredCloneData>* aReturnData) override;
|
||||
|
||||
// Send an async message through IPC.
|
||||
virtual RefPtr<AsyncMessagePromise> SendAsyncMessage(
|
||||
JSContext* aCx,
|
||||
const nsCString& aProtocolName,
|
||||
const uint32_t& aChannelId,
|
||||
const nsCString& aMessageName,
|
||||
const InArgs& aArgs) override;
|
||||
|
||||
// Send a sync message through IPC.
|
||||
virtual bool SendSyncMessage(JSContext* aCx,
|
||||
const nsCString& aProtocolName,
|
||||
const uint32_t& aChannelId,
|
||||
const nsCString& aMessageName,
|
||||
const InArgs& aArgs,
|
||||
OutObject aRet) override;
|
||||
|
||||
// Send an intr message through IPC.
|
||||
virtual bool SendIntrMessage(JSContext* aCx,
|
||||
const nsCString& aProtocolName,
|
||||
const uint32_t& aChannelId,
|
||||
const nsCString& aMessageName,
|
||||
const InArgs& aArgs,
|
||||
OutObject aRet) override
|
||||
{
|
||||
MOZ_CRASH("Unimplemented");
|
||||
aRet.setUndefined();
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
dom::ContentChild* mCc;
|
||||
};
|
||||
|
||||
} // ipc
|
||||
} // ipdl
|
||||
} // mozilla
|
||||
|
||||
#endif // dom_base_ipdl_bindings_pcontentchildipcinterface_h
|
|
@ -1,131 +0,0 @@
|
|||
#include "PContentParentIPCInterface.h"
|
||||
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipdl {
|
||||
namespace ipc {
|
||||
|
||||
PContentParentIPCInterface::PContentParentIPCInterface(dom::IPDL* aIPDL,
|
||||
dom::ContentParent* aCp)
|
||||
: IPCInterface(aIPDL)
|
||||
, mCp(aCp)
|
||||
{
|
||||
mCp->RegisterIPDLIPCInterface(this);
|
||||
}
|
||||
|
||||
/* virtual */ mozilla::ipc::IPCResult
|
||||
PContentParentIPCInterface::RecvMessage(
|
||||
const nsCString& aProtocolName,
|
||||
const uint32_t& aChannelId,
|
||||
const nsCString& aMessage,
|
||||
const dom::ClonedMessageData& aData,
|
||||
nsTArray<dom::ipc::StructuredCloneData>* aReturnData)
|
||||
{
|
||||
return RecvMessageCommon(mCp,
|
||||
IPDLSide::Parent,
|
||||
aProtocolName,
|
||||
aChannelId,
|
||||
aMessage,
|
||||
aData,
|
||||
aReturnData);
|
||||
}
|
||||
|
||||
/* virtual */ RefPtr<IPCInterface::AsyncMessagePromise>
|
||||
PContentParentIPCInterface::SendAsyncMessage(JSContext* aCx,
|
||||
const nsCString& aProtocolName,
|
||||
const uint32_t& aChannelId,
|
||||
const nsCString& aMessageName,
|
||||
const InArgs& aArgs)
|
||||
{
|
||||
auto promise = MakeRefPtr<AsyncMessagePromise::Private>(__func__);
|
||||
|
||||
auto promiseHolder = MakeRefPtr<
|
||||
dom::DOMMozPromiseRequestHolder<dom::ContentParent::AsyncMessageIPDLPromise>>(
|
||||
xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx)));
|
||||
|
||||
// Write arguments to the StructuredCloneData.
|
||||
dom::ipc::StructuredCloneData data;
|
||||
ErrorResult errRes;
|
||||
|
||||
auto* argsObjPtr = JS_NewArrayObject(aCx, aArgs);
|
||||
|
||||
if (!argsObjPtr) {
|
||||
promise->Reject(
|
||||
NS_LITERAL_CSTRING("Failed to allocate new array object"),
|
||||
__func__);
|
||||
return promise;
|
||||
}
|
||||
|
||||
JS::RootedObject argsObj(aCx, argsObjPtr);
|
||||
JS::RootedValue argsVal(aCx, JS::ObjectValue(*argsObj));
|
||||
|
||||
data.Write(aCx, argsVal, errRes);
|
||||
|
||||
if (NS_WARN_IF(errRes.Failed())) {
|
||||
promise->Reject(
|
||||
NS_LITERAL_CSTRING("Failed to write arguments to StructuredCloneData"),
|
||||
__func__);
|
||||
return promise;
|
||||
}
|
||||
|
||||
// Wrap the StructuredCloneData in a ClonedMessageData.
|
||||
dom::ClonedMessageData cmd;
|
||||
if (!data.BuildClonedMessageDataForParent((dom::nsIContentParent*)mCp, cmd)) {
|
||||
promise->Reject(
|
||||
NS_LITERAL_CSTRING("Failed to write arguments to StructuredCloneData"),
|
||||
__func__);
|
||||
return promise;
|
||||
}
|
||||
|
||||
// Send the actual message through the ContentParent
|
||||
mCp->SendAsyncMessageIPDL(aProtocolName, aChannelId, aMessageName, cmd)
|
||||
->Then(promiseHolder->GetParentObject()->EventTargetFor(TaskCategory::Other),
|
||||
__func__,
|
||||
[promiseHolder, promise](nsTArray<dom::ipc::StructuredCloneData> aRes) {
|
||||
promiseHolder->Complete();
|
||||
|
||||
// If the array is empty, it means we got an error.
|
||||
if (aRes.IsEmpty()) {
|
||||
promise->Reject(NS_LITERAL_CSTRING(
|
||||
"Error in RecvAsyncMessage in parent process"),
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
|
||||
nsIGlobalObject* global = promiseHolder->GetParentObject();
|
||||
NS_ENSURE_TRUE_VOID(global);
|
||||
|
||||
dom::AutoEntryScript aes(global, "SendAsyncMessageIPDL");
|
||||
|
||||
JSContext* cx = aes.cx();
|
||||
|
||||
JS::RootedValue ret(cx);
|
||||
ErrorResult rv;
|
||||
aRes.ElementAt(0).Read(cx, &ret, rv);
|
||||
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
promise->Reject(
|
||||
NS_LITERAL_CSTRING(
|
||||
"Failed to read return value from StructuredCloneData"),
|
||||
__func__);
|
||||
} else {
|
||||
promise->Resolve(ret, __func__);
|
||||
}
|
||||
},
|
||||
[promiseHolder, promise](::mozilla::ipc::ResponseRejectReason aReason) {
|
||||
promiseHolder->Complete();
|
||||
|
||||
promise->Reject(nsPrintfCString(
|
||||
"IPC error when calling SendAsyncMessageIPDL, "
|
||||
"see error message above. Reason: %d",
|
||||
(unsigned int)aReason),
|
||||
__func__);
|
||||
})->Track(*promiseHolder);
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
} // ipc
|
||||
} // ipdl
|
||||
} // mozilla
|
|
@ -1,81 +0,0 @@
|
|||
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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 https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef dom_base_ipdl_bindings_pcontentparentipcinterface_h
|
||||
#define dom_base_ipdl_bindings_pcontentparentipcinterface_h
|
||||
|
||||
#include "IPCInterface.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class IPDL;
|
||||
class ContentParent;
|
||||
namespace ipc {
|
||||
class StructuredCloneData;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ipdl {
|
||||
namespace ipc {
|
||||
|
||||
class PContentParentIPCInterface : public IPCInterface
|
||||
{
|
||||
public:
|
||||
explicit PContentParentIPCInterface(dom::IPDL* aIPDL, dom::ContentParent* aCp);
|
||||
virtual ~PContentParentIPCInterface() = default;
|
||||
|
||||
// Receive a message from IPC.
|
||||
virtual mozilla::ipc::IPCResult RecvMessage(
|
||||
const nsCString& aProtocolName,
|
||||
const uint32_t& aChannelId,
|
||||
const nsCString& aMessage,
|
||||
const dom::ClonedMessageData& aData,
|
||||
nsTArray<dom::ipc::StructuredCloneData>* aReturnData) override;
|
||||
|
||||
// Send an async message through IPC.
|
||||
virtual RefPtr<AsyncMessagePromise> SendAsyncMessage(
|
||||
JSContext* aCx,
|
||||
const nsCString& aProtocolName,
|
||||
const uint32_t& aChannelId,
|
||||
const nsCString& aMessageName,
|
||||
const InArgs& aArgs) override;
|
||||
|
||||
// Send a sync message through IPC.
|
||||
virtual bool SendSyncMessage(JSContext* aCx,
|
||||
const nsCString& aProtocolName,
|
||||
const uint32_t& aChannelId,
|
||||
const nsCString& aMessageName,
|
||||
const InArgs& aArgs,
|
||||
OutObject aRet) override
|
||||
{
|
||||
MOZ_CRASH("Unimplemented");
|
||||
aRet.setUndefined();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Send an intr message through IPC.
|
||||
virtual bool SendIntrMessage(JSContext* aCx,
|
||||
const nsCString& aProtocolName,
|
||||
const uint32_t& aChannelId,
|
||||
const nsCString& aMessageName,
|
||||
const InArgs& aArgs,
|
||||
OutObject aRet) override
|
||||
{
|
||||
MOZ_CRASH("Unimplemented");
|
||||
aRet.setUndefined();
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
dom::ContentParent* mCp;
|
||||
};
|
||||
|
||||
} // ipc
|
||||
} // ipdl
|
||||
} // mozilla
|
||||
|
||||
#endif // dom_base_ipdl_bindings_pcontentparentipcinterface_h
|
|
@ -1,24 +0,0 @@
|
|||
# rustup run nightly cbindgen toolkit/library/rust/ --crate ipdl --lockfile Cargo.lock -o dom/base/ipdl_bindings/ipdl_ffi_generated.h
|
||||
|
||||
header="""
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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 https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef ipdl_ffi_generated_h
|
||||
#define ipdl_ffi_generated_h
|
||||
|
||||
#include <nsTArray.h>
|
||||
#include <nsString.h>"""
|
||||
|
||||
trailer="""
|
||||
#endif // ipdl_ffi_generated_h"""
|
||||
|
||||
autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */"
|
||||
namespaces = ["mozilla", "ipdl", "ffi"]
|
||||
language="C++"
|
||||
|
||||
[export.rename]
|
||||
"ThinVec" = "nsTArray"
|
|
@ -1,181 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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 https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef ipdl_ffi_generated_h
|
||||
#define ipdl_ffi_generated_h
|
||||
|
||||
#include <nsTArray.h>
|
||||
#include <nsString.h>
|
||||
|
||||
/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipdl {
|
||||
namespace ffi {
|
||||
|
||||
enum class Compress {
|
||||
None,
|
||||
Enabled,
|
||||
All,
|
||||
};
|
||||
|
||||
enum class CxxTypeKind {
|
||||
Struct,
|
||||
Class,
|
||||
};
|
||||
|
||||
enum class Direction {
|
||||
ToParent,
|
||||
ToChild,
|
||||
ToParentOrChild,
|
||||
};
|
||||
|
||||
enum class FileType {
|
||||
Protocol,
|
||||
Header,
|
||||
};
|
||||
|
||||
enum class Nesting {
|
||||
None,
|
||||
InsideSync,
|
||||
InsideCpow,
|
||||
};
|
||||
|
||||
enum class Priority {
|
||||
Normal,
|
||||
High,
|
||||
Input,
|
||||
};
|
||||
|
||||
enum class SendSemantics {
|
||||
Async,
|
||||
Sync,
|
||||
Intr,
|
||||
};
|
||||
|
||||
struct AST;
|
||||
|
||||
struct Location {
|
||||
nsCString file_name;
|
||||
uintptr_t lineno;
|
||||
uintptr_t colno;
|
||||
};
|
||||
|
||||
struct Identifier {
|
||||
nsCString id;
|
||||
Location loc;
|
||||
};
|
||||
|
||||
struct Namespace {
|
||||
Identifier name;
|
||||
nsTArray<nsCString> namespaces;
|
||||
};
|
||||
|
||||
using TUId = int32_t;
|
||||
|
||||
struct QualifiedId {
|
||||
Identifier base_id;
|
||||
nsTArray<nsCString> quals;
|
||||
};
|
||||
|
||||
struct TypeSpec {
|
||||
QualifiedId spec;
|
||||
bool array;
|
||||
bool nullable;
|
||||
};
|
||||
|
||||
struct UsingStmt {
|
||||
TypeSpec cxx_type;
|
||||
nsCString header;
|
||||
Maybe<CxxTypeKind> kind;
|
||||
bool refcounted;
|
||||
};
|
||||
|
||||
struct StructField {
|
||||
TypeSpec type_spec;
|
||||
Identifier name;
|
||||
};
|
||||
|
||||
struct Struct {
|
||||
Namespace ns;
|
||||
nsTArray<StructField> fields;
|
||||
};
|
||||
|
||||
struct Union {
|
||||
Namespace ns;
|
||||
nsTArray<TypeSpec> types;
|
||||
};
|
||||
|
||||
struct Param {
|
||||
Identifier name;
|
||||
TypeSpec type_spec;
|
||||
};
|
||||
|
||||
struct MessageDecl {
|
||||
Identifier name;
|
||||
SendSemantics send_semantics;
|
||||
Nesting nested;
|
||||
Priority prio;
|
||||
Direction direction;
|
||||
nsTArray<Param> in_params;
|
||||
nsTArray<Param> out_params;
|
||||
Compress compress;
|
||||
bool verify;
|
||||
};
|
||||
|
||||
struct Protocol {
|
||||
SendSemantics send_semantics;
|
||||
Nesting nested;
|
||||
nsTArray<Identifier> managers;
|
||||
nsTArray<Identifier> manages;
|
||||
nsTArray<MessageDecl> messages;
|
||||
};
|
||||
|
||||
struct NamedProtocol {
|
||||
Namespace ns;
|
||||
Protocol protocol;
|
||||
};
|
||||
|
||||
struct TranslationUnit {
|
||||
Namespace ns;
|
||||
FileType file_type;
|
||||
nsCString file_name;
|
||||
nsTArray<nsCString> cxx_includes;
|
||||
nsTArray<TUId> includes;
|
||||
nsTArray<UsingStmt> using_stmt;
|
||||
nsTArray<Struct> structs;
|
||||
nsTArray<Union> unions;
|
||||
Maybe<NamedProtocol> protocol;
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
|
||||
/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */
|
||||
|
||||
const TranslationUnit *ipdl_ast_get_tu(const AST *ast, TUId tuid);
|
||||
|
||||
TUId ipdl_ast_main_tuid(const AST *ast);
|
||||
|
||||
void ipdl_free_ast(const AST *ast);
|
||||
|
||||
const AST *ipdl_parse_file(const nsACString *ipdl_file,
|
||||
nsACString *error_string,
|
||||
uint8_t (*source_string_loader)(const nsACString*, nsACString*),
|
||||
uint8_t (*resolve_relative_path)(const nsACString*, const nsACString*, nsACString*),
|
||||
uint8_t (*equals)(const nsACString*, const nsACString*));
|
||||
|
||||
} // extern "C"
|
||||
|
||||
} // namespace ffi
|
||||
} // namespace ipdl
|
||||
} // namespace mozilla
|
||||
|
||||
/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */
|
||||
|
||||
#endif // ipdl_ffi_generated_h
|
|
@ -1,29 +0,0 @@
|
|||
# -*- Mode: python; 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/.
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
EXPORTS.mozilla.ipdl = [
|
||||
'IPDLProtocol.h',
|
||||
'IPDLProtocolInstance.h'
|
||||
]
|
||||
|
||||
EXPORTS.mozilla.ipdl.ipc = [
|
||||
'IPCInterface.h',
|
||||
'PContentChildIPCInterface.h',
|
||||
'PContentParentIPCInterface.h'
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'IPCInterface.cpp',
|
||||
'IPDLProtocol.cpp',
|
||||
'IPDLProtocolInstance.cpp',
|
||||
'PContentChildIPCInterface.cpp',
|
||||
'PContentParentIPCInterface.cpp',
|
||||
'wrapper.cpp',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
|
@ -1,529 +0,0 @@
|
|||
/* 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/. */
|
||||
|
||||
extern crate ipdl;
|
||||
extern crate nsstring;
|
||||
extern crate thin_vec;
|
||||
#[macro_use]
|
||||
extern crate mfbt_maybe;
|
||||
|
||||
use ipdl::ast;
|
||||
use ipdl::parser;
|
||||
use ipdl::parser::FileURI;
|
||||
use mfbt_maybe::{Maybe, MaybeTrait};
|
||||
use nsstring::{nsACString, nsCStr, nsCString};
|
||||
use std::collections::HashMap;
|
||||
use std::iter::FromIterator;
|
||||
use std::str;
|
||||
use thin_vec::ThinVec;
|
||||
use std::hash::Hash;
|
||||
use std::hash::Hasher;
|
||||
|
||||
maybe!(CxxTypeKind);
|
||||
maybe!(NamedProtocol);
|
||||
|
||||
fn vec_to_thinvec<T, F: Into<T>>(vec: Vec<F>) -> ThinVec<T> {
|
||||
ThinVec::from_iter(vec.into_iter().map(|x| x.into()))
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct QualifiedId {
|
||||
pub base_id: Identifier,
|
||||
pub quals: ThinVec<nsCString>,
|
||||
}
|
||||
|
||||
impl From<ast::QualifiedId> for QualifiedId {
|
||||
fn from(qi: ast::QualifiedId) -> Self {
|
||||
QualifiedId {
|
||||
base_id: qi.base_id.into(),
|
||||
quals: ThinVec::from_iter(qi.quals.into_iter().map(|x| x.into())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct TypeSpec {
|
||||
pub spec: QualifiedId,
|
||||
pub array: bool,
|
||||
pub nullable: bool,
|
||||
}
|
||||
|
||||
impl From<ast::TypeSpec> for TypeSpec {
|
||||
fn from(ts: ast::TypeSpec) -> Self {
|
||||
TypeSpec {
|
||||
spec: ts.spec.into(),
|
||||
array: ts.array,
|
||||
nullable: ts.nullable,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Param {
|
||||
pub name: Identifier,
|
||||
pub type_spec: TypeSpec,
|
||||
}
|
||||
|
||||
impl From<ast::Param> for Param {
|
||||
fn from(p: ast::Param) -> Self {
|
||||
Param {
|
||||
name: p.name.into(),
|
||||
type_spec: p.type_spec.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct StructField {
|
||||
pub type_spec: TypeSpec,
|
||||
pub name: Identifier,
|
||||
}
|
||||
|
||||
impl From<ast::StructField> for StructField {
|
||||
fn from(sf: ast::StructField) -> Self {
|
||||
StructField {
|
||||
type_spec: sf.type_spec.into(),
|
||||
name: sf.name.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Namespace {
|
||||
pub name: Identifier,
|
||||
pub namespaces: ThinVec<nsCString>,
|
||||
}
|
||||
|
||||
impl From<ast::Namespace> for Namespace {
|
||||
fn from(ns: ast::Namespace) -> Self {
|
||||
Namespace {
|
||||
name: ns.name.into(),
|
||||
namespaces: vec_to_thinvec(ns.namespaces),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub enum Compress {
|
||||
None,
|
||||
Enabled,
|
||||
All,
|
||||
}
|
||||
|
||||
impl From<ast::Compress> for Compress {
|
||||
fn from(c: ast::Compress) -> Self {
|
||||
match c {
|
||||
ast::Compress::None => Compress::None,
|
||||
ast::Compress::Enabled => Compress::Enabled,
|
||||
ast::Compress::All => Compress::All,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub enum SendSemantics {
|
||||
Async,
|
||||
Sync,
|
||||
Intr,
|
||||
}
|
||||
|
||||
impl From<ast::SendSemantics> for SendSemantics {
|
||||
fn from(ss: ast::SendSemantics) -> Self {
|
||||
match ss {
|
||||
ast::SendSemantics::Async => SendSemantics::Async,
|
||||
ast::SendSemantics::Sync => SendSemantics::Sync,
|
||||
ast::SendSemantics::Intr => SendSemantics::Intr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub enum Nesting {
|
||||
None,
|
||||
InsideSync,
|
||||
InsideCpow,
|
||||
}
|
||||
|
||||
impl From<ast::Nesting> for Nesting {
|
||||
fn from(n: ast::Nesting) -> Self {
|
||||
match n {
|
||||
ast::Nesting::InsideCpow => Nesting::InsideCpow,
|
||||
ast::Nesting::InsideSync => Nesting::InsideSync,
|
||||
ast::Nesting::None => Nesting::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub enum Priority {
|
||||
Normal,
|
||||
High,
|
||||
Input,
|
||||
}
|
||||
|
||||
impl From<ast::Priority> for Priority {
|
||||
fn from(p: ast::Priority) -> Self {
|
||||
match p {
|
||||
ast::Priority::High => Priority::High,
|
||||
ast::Priority::Input => Priority::Input,
|
||||
ast::Priority::Normal => Priority::Normal,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub enum Direction {
|
||||
ToParent,
|
||||
ToChild,
|
||||
ToParentOrChild,
|
||||
}
|
||||
|
||||
impl From<ast::Direction> for Direction {
|
||||
fn from(d: ast::Direction) -> Self {
|
||||
match d {
|
||||
ast::Direction::ToChild => Direction::ToChild,
|
||||
ast::Direction::ToParent => Direction::ToParent,
|
||||
ast::Direction::ToParentOrChild => Direction::ToParentOrChild,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Location {
|
||||
pub file_name: nsCString,
|
||||
pub lineno: usize,
|
||||
pub colno: usize,
|
||||
}
|
||||
|
||||
impl From<ast::Location> for Location {
|
||||
fn from(l: ast::Location) -> Self {
|
||||
Location {
|
||||
file_name: l.file_name.into(),
|
||||
lineno: l.lineno,
|
||||
colno: l.colno,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Identifier {
|
||||
pub id: nsCString,
|
||||
pub loc: Location,
|
||||
}
|
||||
|
||||
impl From<ast::Identifier> for Identifier {
|
||||
fn from(id: ast::Identifier) -> Self {
|
||||
Identifier {
|
||||
id: id.id.into(),
|
||||
loc: id.loc.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct MessageDecl {
|
||||
pub name: Identifier,
|
||||
pub send_semantics: SendSemantics,
|
||||
pub nested: Nesting,
|
||||
pub prio: Priority,
|
||||
pub direction: Direction,
|
||||
pub in_params: ThinVec<Param>,
|
||||
pub out_params: ThinVec<Param>,
|
||||
pub compress: Compress,
|
||||
pub verify: bool,
|
||||
}
|
||||
|
||||
impl From<ast::MessageDecl> for MessageDecl {
|
||||
fn from(md: ast::MessageDecl) -> Self {
|
||||
MessageDecl {
|
||||
name: md.name.into(),
|
||||
send_semantics: md.send_semantics.into(),
|
||||
nested: md.nested.into(),
|
||||
prio: md.prio.into(),
|
||||
direction: md.direction.into(),
|
||||
in_params: vec_to_thinvec(md.in_params),
|
||||
out_params: vec_to_thinvec(md.out_params),
|
||||
compress: md.compress.into(),
|
||||
verify: md.verify,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Protocol {
|
||||
pub send_semantics: SendSemantics,
|
||||
pub nested: Nesting,
|
||||
pub managers: ThinVec<Identifier>,
|
||||
pub manages: ThinVec<Identifier>,
|
||||
pub messages: ThinVec<MessageDecl>,
|
||||
}
|
||||
|
||||
impl From<ast::Protocol> for Protocol {
|
||||
fn from(p: ast::Protocol) -> Self {
|
||||
Protocol {
|
||||
send_semantics: p.send_semantics.into(),
|
||||
nested: p.nested.into(),
|
||||
managers: vec_to_thinvec(p.managers),
|
||||
manages: vec_to_thinvec(p.manages),
|
||||
messages: vec_to_thinvec(p.messages),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub enum CxxTypeKind {
|
||||
Struct,
|
||||
Class,
|
||||
}
|
||||
|
||||
impl From<ast::CxxTypeKind> for CxxTypeKind {
|
||||
fn from(cxxtk: ast::CxxTypeKind) -> Self {
|
||||
match cxxtk {
|
||||
ast::CxxTypeKind::Class => CxxTypeKind::Class,
|
||||
ast::CxxTypeKind::Struct => CxxTypeKind::Struct,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct UsingStmt {
|
||||
pub cxx_type: TypeSpec,
|
||||
pub header: nsCString,
|
||||
pub kind: Maybe<CxxTypeKind>,
|
||||
pub refcounted: bool,
|
||||
}
|
||||
|
||||
impl From<ast::UsingStmt> for UsingStmt {
|
||||
fn from(using: ast::UsingStmt) -> Self {
|
||||
UsingStmt {
|
||||
cxx_type: using.cxx_type.into(),
|
||||
header: using.header.into(),
|
||||
kind: using.kind.into(),
|
||||
refcounted: using.refcounted,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub enum FileType {
|
||||
Protocol,
|
||||
Header,
|
||||
}
|
||||
|
||||
impl From<ast::FileType> for FileType {
|
||||
fn from(ft: ast::FileType) -> Self {
|
||||
match ft {
|
||||
ast::FileType::Protocol => FileType::Protocol,
|
||||
ast::FileType::Header => FileType::Header,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Translation unit identifier.
|
||||
pub type TUId = i32;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Struct {
|
||||
pub ns: Namespace,
|
||||
pub fields: ThinVec<StructField>,
|
||||
}
|
||||
|
||||
impl From<(ast::Namespace, Vec<ast::StructField>)> for Struct {
|
||||
fn from(s: (ast::Namespace, Vec<ast::StructField>)) -> Self {
|
||||
Struct {
|
||||
ns: s.0.into(),
|
||||
fields: vec_to_thinvec(s.1),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Union {
|
||||
pub ns: Namespace,
|
||||
pub types: ThinVec<TypeSpec>,
|
||||
}
|
||||
|
||||
impl From<(ast::Namespace, Vec<ast::TypeSpec>)> for Union {
|
||||
fn from(u: (ast::Namespace, Vec<ast::TypeSpec>)) -> Self {
|
||||
Union {
|
||||
ns: u.0.into(),
|
||||
types: vec_to_thinvec(u.1),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct NamedProtocol {
|
||||
pub ns: Namespace,
|
||||
pub protocol: Protocol,
|
||||
}
|
||||
|
||||
impl From<(ast::Namespace, ast::Protocol)> for NamedProtocol {
|
||||
fn from(p: (ast::Namespace, ast::Protocol)) -> Self {
|
||||
NamedProtocol {
|
||||
ns: p.0.into(),
|
||||
protocol: p.1.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct TranslationUnit {
|
||||
pub ns: Namespace,
|
||||
pub file_type: FileType,
|
||||
pub file_name: nsCString,
|
||||
pub cxx_includes: ThinVec<nsCString>,
|
||||
pub includes: ThinVec<TUId>,
|
||||
pub using_stmt: ThinVec<UsingStmt>,
|
||||
pub structs: ThinVec<Struct>,
|
||||
pub unions: ThinVec<Union>,
|
||||
pub protocol: Maybe<NamedProtocol>,
|
||||
}
|
||||
|
||||
impl From<ast::TranslationUnit> for TranslationUnit {
|
||||
fn from(tu: ast::TranslationUnit) -> Self {
|
||||
TranslationUnit {
|
||||
ns: tu.namespace.into(),
|
||||
file_type: tu.file_type.into(),
|
||||
file_name: tu.file_name.into(),
|
||||
cxx_includes: vec_to_thinvec(tu.cxx_includes),
|
||||
includes: vec_to_thinvec(tu.includes),
|
||||
using_stmt: vec_to_thinvec(tu.using),
|
||||
structs: vec_to_thinvec(tu.structs),
|
||||
unions: vec_to_thinvec(tu.unions),
|
||||
protocol: tu.protocol.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AST {
|
||||
pub main_tuid: TUId,
|
||||
pub translation_units: HashMap<TUId, TranslationUnit>,
|
||||
}
|
||||
|
||||
impl From<ast::AST> for AST {
|
||||
fn from(ast: ast::AST) -> Self {
|
||||
AST {
|
||||
main_tuid: ast.main_tuid,
|
||||
translation_units: ast
|
||||
.translation_units
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, v.into()))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn ipdl_ast_get_tu(ast: &AST, tuid: TUId) -> *const TranslationUnit {
|
||||
ast.translation_units
|
||||
.get(&tuid)
|
||||
.map(|x| x as *const TranslationUnit)
|
||||
.unwrap_or(::std::ptr::null())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn ipdl_ast_main_tuid(ast: &AST) -> TUId {
|
||||
ast.main_tuid
|
||||
}
|
||||
|
||||
struct FFIURI {
|
||||
data: nsCString,
|
||||
resolve_relative_path_fn: fn(&nsACString, &nsACString, &mut nsACString) -> u8,
|
||||
equals_fn: fn(&nsACString, &nsACString) -> u8,
|
||||
}
|
||||
|
||||
impl parser::FileURI for FFIURI {
|
||||
fn resolve_relative_path(&self, relative_path: &str) -> Result<Self, ()> {
|
||||
let mut result = nsCString::new();
|
||||
let ok = (self.resolve_relative_path_fn)(&self.data, &nsCStr::from(relative_path), &mut result);
|
||||
if ok != 0 {
|
||||
Ok(FFIURI {
|
||||
data: result,
|
||||
resolve_relative_path_fn: self.resolve_relative_path_fn,
|
||||
equals_fn: self.equals_fn,
|
||||
})
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
fn to_utf8(&self) -> &str {
|
||||
str::from_utf8(&self.data).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for FFIURI {
|
||||
fn eq(&self, other: &FFIURI) -> bool {
|
||||
(self.equals_fn)(&self.data, &other.data) != 0
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for FFIURI {}
|
||||
|
||||
impl Hash for FFIURI {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.data.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for FFIURI {
|
||||
fn clone(&self) -> FFIURI {
|
||||
let new_data = nsCString::from(self.data.as_ref());
|
||||
FFIURI {
|
||||
data: new_data,
|
||||
resolve_relative_path_fn: self.resolve_relative_path_fn,
|
||||
equals_fn: self.equals_fn,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn ipdl_parse_file(
|
||||
ipdl_file: &nsACString,
|
||||
error_string: &mut nsACString,
|
||||
source_string_loader: fn(&nsACString, &mut nsACString) -> u8,
|
||||
resolve_relative_path: fn(&nsACString, &nsACString, &mut nsACString) -> u8,
|
||||
equals: fn(&nsACString, &nsACString) -> u8,
|
||||
) -> *const AST {
|
||||
let uri = FFIURI {
|
||||
data: nsCString::from(ipdl_file),
|
||||
resolve_relative_path_fn: resolve_relative_path,
|
||||
equals_fn: equals,
|
||||
};
|
||||
|
||||
let parent_uri = match uri.resolve_relative_path("./") {
|
||||
Ok(pu) => pu,
|
||||
Err(()) => {
|
||||
error_string.assign("Could not get IPDL file parent directory");
|
||||
return ::std::ptr::null()
|
||||
},
|
||||
};
|
||||
|
||||
Box::into_raw(Box::new(match parser::parse(
|
||||
&uri,
|
||||
&[parent_uri],
|
||||
|uri: &str| -> Result<Box<parser::OwnedSourceBuffer>, ()> {
|
||||
let ns_uri = nsCStr::from(uri);
|
||||
let mut result = nsCString::new();
|
||||
let ok = source_string_loader(&ns_uri, &mut result);
|
||||
if ok != 0 {
|
||||
Ok(Box::new(result))
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
},
|
||||
) {
|
||||
Ok(ast) => ast.into(),
|
||||
Err(error) => {
|
||||
error_string.assign(&format!("{}", error));
|
||||
return ::std::ptr::null()
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn ipdl_free_ast(ast: *const AST) {
|
||||
drop(Box::from_raw(ast as *mut AST))
|
||||
}
|
|
@ -1,155 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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 https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "wrapper.h"
|
||||
#include "nsAString.h"
|
||||
#include "nsString.h"
|
||||
#include "nsNetUtil.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipdl {
|
||||
namespace wrapper {
|
||||
|
||||
static uint8_t
|
||||
source_string_loader(const nsACString* aFileName, nsACString* aRet)
|
||||
{
|
||||
nsCOMPtr<nsIChannel> chan;
|
||||
nsCOMPtr<nsIInputStream> instream;
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsCOMPtr<nsIIOService> serv = do_GetService(NS_IOSERVICE_CONTRACTID);
|
||||
nsresult rv;
|
||||
|
||||
rv = NS_NewURI(getter_AddRefs(uri), *aFileName);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("FAILED TO GET URI");
|
||||
return 0;
|
||||
}
|
||||
|
||||
rv = NS_NewChannel(getter_AddRefs(chan),
|
||||
uri,
|
||||
nsContentUtils::GetSystemPrincipal(),
|
||||
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
nsIContentPolicy::TYPE_OTHER,
|
||||
nullptr, // PerformanceStorage
|
||||
nullptr, // aLoadGroup
|
||||
nullptr, // aCallbacks
|
||||
nsIRequest::LOAD_NORMAL,
|
||||
serv);
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
chan->SetContentType(NS_LITERAL_CSTRING("application/x-ipdl"));
|
||||
rv = chan->Open2(getter_AddRefs(instream));
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("LOAD_ERROR_NOSTREAM");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int64_t len = -1;
|
||||
|
||||
rv = chan->GetContentLength(&len);
|
||||
if (NS_FAILED(rv) || len == -1) {
|
||||
NS_WARNING("LOAD_ERROR_NOCONTENT");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (len > INT32_MAX) {
|
||||
NS_WARNING("LOAD_ERROR_CONTENTTOOBIG");
|
||||
return 0;
|
||||
}
|
||||
|
||||
rv = NS_ReadInputStreamToString(instream, *aRet, len);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("FAILED TO READ");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
resolve_relative_path(const nsACString* aFileName, const nsACString* aRelativePath, nsACString* aRet)
|
||||
{
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsresult rv;
|
||||
|
||||
rv = NS_NewURI(getter_AddRefs(uri), *aFileName);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("FAILED TO GET URI");
|
||||
return 0;
|
||||
}
|
||||
|
||||
rv = uri->Resolve(*aRelativePath, *aRet);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("FAILED TO RESOLVE URI");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
uri_equals(const nsACString* aFileName1, const nsACString* aFileName2)
|
||||
{
|
||||
nsCOMPtr<nsIURI> uri1;
|
||||
nsCOMPtr<nsIURI> uri2;
|
||||
nsresult rv;
|
||||
|
||||
rv = NS_NewURI(getter_AddRefs(uri1), *aFileName1);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("FAILED TO GET URI 1");
|
||||
}
|
||||
|
||||
rv = NS_NewURI(getter_AddRefs(uri2), *aFileName2);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("FAILED TO GET URI 2");
|
||||
}
|
||||
|
||||
bool retval;
|
||||
|
||||
rv = uri1->Equals(uri2, &retval);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("FAILED TO COMPARE URIs");
|
||||
}
|
||||
|
||||
return retval ? 1 : 0;
|
||||
}
|
||||
|
||||
const ffi::AST*
|
||||
Parse(const nsACString& aIPDLFile, nsACString& errorString)
|
||||
{
|
||||
return ffi::ipdl_parse_file(&aIPDLFile, &errorString, source_string_loader, resolve_relative_path, uri_equals);
|
||||
}
|
||||
|
||||
void
|
||||
FreeAST(const ffi::AST* aAST)
|
||||
{
|
||||
ffi::ipdl_free_ast(aAST);
|
||||
}
|
||||
|
||||
const ffi::TranslationUnit*
|
||||
GetTU(const ffi::AST* aAST, ffi::TUId aTUID)
|
||||
{
|
||||
return ffi::ipdl_ast_get_tu(aAST, aTUID);
|
||||
}
|
||||
|
||||
ffi::TUId
|
||||
GetMainTUId(const ffi::AST* aAST)
|
||||
{
|
||||
return ffi::ipdl_ast_main_tuid(aAST);
|
||||
}
|
||||
|
||||
} // wrapper
|
||||
} // ipdl
|
||||
} // mozilla
|
|
@ -1,37 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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 https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef dom_base_ipdl_bindings_wrapper_h
|
||||
#define dom_base_ipdl_bindings_wrapper_h
|
||||
|
||||
#include "ipdl_ffi_generated.h"
|
||||
#include "nsStringFwd.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipdl {
|
||||
namespace wrapper {
|
||||
|
||||
// Parse the given ipdl file and return a Rust-owned AST.
|
||||
const ffi::AST*
|
||||
Parse(const nsACString& aIPDLFile, nsACString& errorString);
|
||||
|
||||
// Free the AST from Rust code.
|
||||
void
|
||||
FreeAST(const ffi::AST* aAST);
|
||||
|
||||
// Get the translation unit with the given tuid from the given AST.
|
||||
const ffi::TranslationUnit*
|
||||
GetTU(const ffi::AST* aAST, ffi::TUId aTUID);
|
||||
|
||||
// Get the tuid of the main translation unit of the AST.
|
||||
ffi::TUId
|
||||
GetMainTUId(const ffi::AST* aAST);
|
||||
|
||||
} // wrapper
|
||||
} // ipdl
|
||||
} // mozilla
|
||||
|
||||
#endif // dom_base_ipdl_bindings_wrapper_h
|
|
@ -1,9 +0,0 @@
|
|||
# -*- Mode: python; 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/.
|
||||
|
||||
DIRS += [
|
||||
'ipdl_bindings',
|
||||
]
|
|
@ -1,2 +0,0 @@
|
|||
/target/
|
||||
**/*.rs.bk
|
|
@ -1,7 +0,0 @@
|
|||
[package]
|
||||
name = "pipdl"
|
||||
version = "0.1.0"
|
||||
authors = ["Nika Layzell <nika@thelayzells.com>"]
|
||||
license = "MPL-2.0"
|
||||
|
||||
[dependencies]
|
|
@ -1,5 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Run the parser on every error ipdl file in the passed-in directory.
|
||||
find $1 -name "*.ipdl" -print0 | sort -z | xargs -0 -n 1 cargo run --release --example try_parse --
|
||||
echo status $? >&2
|
|
@ -1,30 +0,0 @@
|
|||
/* 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/. */
|
||||
|
||||
extern crate pipdl;
|
||||
|
||||
use std::env::args;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
|
||||
fn run() -> Result<(), Box<std::error::Error>> {
|
||||
let filename = args().nth(1).ok_or("Filename expected")?;
|
||||
let mut f = File::open(&filename)?;
|
||||
let mut s = String::new();
|
||||
f.read_to_string(&mut s)?;
|
||||
if let Err(e) = pipdl::parse(&s, &filename) {
|
||||
return Err(Box::new(e));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
::std::process::exit(match run() {
|
||||
Ok(_) => 0,
|
||||
Err(e) => {
|
||||
eprintln!("{}", e);
|
||||
1
|
||||
}
|
||||
})
|
||||
}
|
|
@ -1,720 +0,0 @@
|
|||
/* 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 is a basic recursive-descent parser for ipdl. It intentionally doesn't
|
||||
//! have some featres which most languages would have, such as unicode support
|
||||
//! in identifiers, as it is unnecessary for our usecase.
|
||||
|
||||
// Our code is Clippy aware, so we disable warnings for unknown lints
|
||||
// which are triggered when we silence a clippy lint
|
||||
#![allow(unknown_lints)]
|
||||
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
|
||||
#[macro_use]
|
||||
pub mod util;
|
||||
pub use util::*;
|
||||
|
||||
/// Public error type for messages with resolved type information.
|
||||
pub struct Error(Box<ParserError>);
|
||||
|
||||
impl Error {
|
||||
pub fn span(&self) -> Span {
|
||||
self.0.span()
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for Error {
|
||||
fn description(&self) -> &str {
|
||||
&self.0.message()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str(&format!(
|
||||
"{}:{}:{}: error: {}",
|
||||
self.0.span().start.file,
|
||||
self.0.span().start.line,
|
||||
self.0.span().start.col,
|
||||
self.0.message()
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CxxInclude {
|
||||
pub file: Spanned<String>,
|
||||
}
|
||||
|
||||
fn cxx_include(i: In) -> PResult<Spanned<CxxInclude>> {
|
||||
let start = i.loc();
|
||||
let (i, _) = kw(i, "include")?;
|
||||
let (i, file) = string(i)?;
|
||||
commit! {
|
||||
let (i, _) = punct(i, ";")?;
|
||||
let end = i.loc();
|
||||
Ok((i.clone(), Spanned::new(Span { start, end }, CxxInclude { file })))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Include {
|
||||
pub protocol: Option<Spanned<()>>,
|
||||
pub id: Spanned<String>,
|
||||
}
|
||||
|
||||
fn include(i: In) -> PResult<Spanned<Include>> {
|
||||
let start = i.loc();
|
||||
let (i, _) = kw(i, "include")?;
|
||||
let (i, pcol) = maybe(i.clone(), kw(i, "protocol"))?;
|
||||
|
||||
let pcol_is_some = pcol.is_some();
|
||||
|
||||
let common_end_code = |i, id| {
|
||||
let (i, _) = punct(i, ";")?;
|
||||
let end = i.loc();
|
||||
Ok((
|
||||
i,
|
||||
Spanned::new(Span { start, end }, Include { protocol: pcol, id }),
|
||||
))
|
||||
};
|
||||
|
||||
if pcol_is_some {
|
||||
// If we have the protocol keyword, then it can't be a C++ include anymore, and we start committing
|
||||
commit! {
|
||||
let (i, id) = ident(i)?;
|
||||
common_end_code(i, id)
|
||||
}
|
||||
} else {
|
||||
// Otherwise we first parse the ident
|
||||
let (i, id) = ident(i)?;
|
||||
commit! {
|
||||
common_end_code(i, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum CxxTypeKind {
|
||||
Class,
|
||||
Struct,
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CxxPathSeg {
|
||||
pub id: Spanned<String>,
|
||||
pub args: Option<Spanned<Vec<Spanned<String>>>>,
|
||||
}
|
||||
|
||||
fn template_args(i: In) -> PResult<Spanned<Vec<Spanned<String>>>> {
|
||||
let start = i.loc();
|
||||
let (i, _) = punct(i, "<")?;
|
||||
commit! {
|
||||
let (i, args) = sep(i, ident, ",")?;
|
||||
let (i, _) = punct(i, ">")?;
|
||||
|
||||
let end = i.loc();
|
||||
Ok((i, Spanned::new(Span { start, end }, args)))
|
||||
}
|
||||
}
|
||||
|
||||
fn cxx_path_seg(i: In) -> PResult<Spanned<CxxPathSeg>> {
|
||||
let start = i.loc();
|
||||
let (i, id) = ident(i)?;
|
||||
let (i, args) = maybe(i.clone(), template_args(i))?;
|
||||
let end = i.loc();
|
||||
Ok((
|
||||
i,
|
||||
Spanned::new(Span { start, end }, CxxPathSeg { id, args }),
|
||||
))
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CxxPath {
|
||||
pub segs: Vec<Spanned<CxxPathSeg>>,
|
||||
}
|
||||
|
||||
fn cxx_path(i: In) -> PResult<Spanned<CxxPath>> {
|
||||
let start = i.loc();
|
||||
let (i, segs) = sep(i, cxx_path_seg, "::")?;
|
||||
let end = i.loc();
|
||||
Ok((i, Spanned::new(Span { start, end }, CxxPath { segs })))
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Using {
|
||||
pub refcounted: Option<Spanned<()>>,
|
||||
pub kind: Spanned<CxxTypeKind>,
|
||||
pub ty: Spanned<CxxPath>,
|
||||
pub file: Spanned<String>,
|
||||
}
|
||||
|
||||
fn using(i: In) -> PResult<Spanned<Using>> {
|
||||
let start = i.loc();
|
||||
let (i, _) = kw(i, "using")?;
|
||||
commit! {
|
||||
let (i, refcounted) = maybe(i.clone(), kw(i, "refcounted"))?;
|
||||
let kw_start = i.loc();
|
||||
let (i, kind) = any!(
|
||||
i, "struct or class keyword",
|
||||
kw(i.clone(), "struct") => CxxTypeKind::Struct,
|
||||
kw(i.clone(), "class") => CxxTypeKind::Class,
|
||||
Ok((i.clone(), ())) => CxxTypeKind::None,
|
||||
)?;
|
||||
let kw_end = i.loc();
|
||||
|
||||
let (i, ty) = cxx_path(i)?;
|
||||
let (i, _) = kw(i, "from")?;
|
||||
let (i, file) = string(i)?;
|
||||
let (i, _) = punct(i, ";")?;
|
||||
|
||||
let end = i.loc();
|
||||
|
||||
Ok((i, Spanned::new(Span { start, end }, Using { refcounted, kind: Spanned::new(Span { start: kw_start, end: kw_end }, kind), ty, file })))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Type {
|
||||
pub is_nullable: Option<Spanned<()>>,
|
||||
pub name: Spanned<CxxPathSeg>,
|
||||
pub is_array: Option<Spanned<()>>,
|
||||
}
|
||||
|
||||
fn ty(i: In) -> PResult<Spanned<Type>> {
|
||||
fn array_brackets(i: In) -> PResult<Spanned<()>> {
|
||||
let (i, _) = punct(i, "[")?;
|
||||
commit! { punct(i, "]") }
|
||||
}
|
||||
|
||||
let start = i.loc();
|
||||
let (i, nullable) = maybe(i.clone(), kw(i, "nullable"))?;
|
||||
let (i, name) = cxx_path_seg(i)?;
|
||||
let (i, array) = maybe(i.clone(), array_brackets(i))?;
|
||||
|
||||
let end = i.loc();
|
||||
|
||||
Ok((
|
||||
i,
|
||||
Spanned::new(
|
||||
Span { start, end },
|
||||
Type {
|
||||
is_nullable: nullable,
|
||||
name,
|
||||
is_array: array,
|
||||
},
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
fn component(i: In) -> PResult<Spanned<Type>> {
|
||||
let (i, ty) = ty(i)?;
|
||||
commit! {
|
||||
let (i, _) = punct(i, ";")?;
|
||||
Ok((i, ty))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct UnionItem {
|
||||
pub path: Vec<Spanned<String>>,
|
||||
pub components: Vec<Spanned<Type>>,
|
||||
}
|
||||
|
||||
fn union_item(i: In) -> PResult<Spanned<UnionItem>> {
|
||||
let start = i.loc();
|
||||
let (i, _) = kw(i, "union")?;
|
||||
commit! {
|
||||
let (i, name) = ident(i)?;
|
||||
let (i, _) = punct(i, "{")?;
|
||||
let (i, components) = many(i, component)?;
|
||||
let (i, _) = punct(i, "}")?;
|
||||
let (i, _) = punct(i, ";")?;
|
||||
|
||||
let end = i.loc();
|
||||
|
||||
Ok((i, Spanned::new(Span { start, end }, UnionItem {
|
||||
path: vec![name],
|
||||
components,
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Field {
|
||||
pub ty: Spanned<Type>,
|
||||
pub name: Spanned<String>,
|
||||
}
|
||||
|
||||
fn field(i: In) -> PResult<Spanned<Field>> {
|
||||
let start = i.loc();
|
||||
let (i, ty) = ty(i)?;
|
||||
commit! {
|
||||
let (i, name) = ident(i)?;
|
||||
let (i, _) = punct(i, ";")?;
|
||||
let end = i.loc();
|
||||
Ok((i, Spanned::new(Span {start, end}, Field { ty, name })))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StructItem {
|
||||
pub path: Vec<Spanned<String>>,
|
||||
pub fields: Vec<Spanned<Field>>,
|
||||
}
|
||||
|
||||
fn struct_item(i: In) -> PResult<Spanned<StructItem>> {
|
||||
let start = i.loc();
|
||||
let (i, _) = kw(i, "struct")?;
|
||||
commit! {
|
||||
let (i, name) = ident(i)?;
|
||||
let (i, _) = punct(i, "{")?;
|
||||
let (i, fields) = many(i, field)?;
|
||||
let (i, _) = punct(i, "}")?;
|
||||
let (i, _) = punct(i, ";")?;
|
||||
|
||||
let end = i.loc();
|
||||
|
||||
Ok((i, Spanned::new(Span { start, end }, StructItem {
|
||||
path: vec![name],
|
||||
fields,
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Nesting {
|
||||
None,
|
||||
InsideSync,
|
||||
InsideCpow,
|
||||
}
|
||||
|
||||
#[allow(needless_pass_by_value)]
|
||||
fn nesting(i: In) -> PResult<Spanned<Nesting>> {
|
||||
let start = i.loc();
|
||||
let (i, nesting) = any!(
|
||||
i, "nesting specifier (not, inside_sync, or inside_cpow)",
|
||||
kw(i.clone(), "not") => Nesting::None,
|
||||
kw(i.clone(), "inside_sync") => Nesting::InsideSync,
|
||||
kw(i.clone(), "inside_cpow") => Nesting::InsideCpow,
|
||||
)?;
|
||||
let end = i.loc();
|
||||
Ok((i, Spanned::new(Span { start, end }, nesting)))
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Priority {
|
||||
Normal,
|
||||
High,
|
||||
Input,
|
||||
}
|
||||
|
||||
#[allow(needless_pass_by_value)]
|
||||
fn priority(i: In) -> PResult<Spanned<Priority>> {
|
||||
let start = i.loc();
|
||||
let (i, priority) = any!(
|
||||
i, "priority specifier (normal, high, or input)",
|
||||
kw(i.clone(), "normal") => Priority::Normal,
|
||||
kw(i.clone(), "high") => Priority::High,
|
||||
kw(i.clone(), "input") => Priority::Input,
|
||||
)?;
|
||||
let end = i.loc();
|
||||
Ok((i, Spanned::new(Span { start, end }, priority)))
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum SendSemantics {
|
||||
Async,
|
||||
Sync,
|
||||
Intr,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum MessageModifier {
|
||||
Verify,
|
||||
Compress,
|
||||
CompressAll,
|
||||
}
|
||||
|
||||
#[allow(needless_pass_by_value)]
|
||||
fn message_modifier(i: In) -> PResult<Spanned<MessageModifier>> {
|
||||
let start = i.loc();
|
||||
let (i, message_modifier) = any!(
|
||||
i, "message modifier (verify, compress, or compressall)",
|
||||
kw(i.clone(), "verify") => MessageModifier::Verify,
|
||||
kw(i.clone(), "compress") => MessageModifier::Compress,
|
||||
kw(i.clone(), "compressall") => MessageModifier::CompressAll,
|
||||
)?;
|
||||
let end = i.loc();
|
||||
Ok((i, Spanned::new(Span { start, end }, message_modifier)))
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Param {
|
||||
pub ty: Spanned<Type>,
|
||||
pub name: Spanned<String>,
|
||||
}
|
||||
|
||||
fn param(i: In) -> PResult<Spanned<Param>> {
|
||||
let start = i.loc();
|
||||
let (i, ty) = ty(i)?;
|
||||
commit! {
|
||||
let (i, name) = ident(i)?;
|
||||
let end = i.loc();
|
||||
Ok((i, Spanned::new(Span { start, end }, Param { ty, name })))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MessageDecl {
|
||||
pub nested: Option<Spanned<Spanned<Nesting>>>,
|
||||
pub priority: Option<Spanned<Spanned<Priority>>>,
|
||||
pub send_semantics: Spanned<SendSemantics>,
|
||||
pub name: Spanned<String>,
|
||||
pub params: Vec<Spanned<Param>>,
|
||||
pub returns: Option<Spanned<Vec<Spanned<Param>>>>,
|
||||
pub modifiers: Vec<Spanned<MessageModifier>>,
|
||||
}
|
||||
|
||||
fn returns(i: In) -> PResult<Spanned<Vec<Spanned<Param>>>> {
|
||||
let start = i.loc();
|
||||
let (i, _) = kw(i, "returns")?;
|
||||
commit! {
|
||||
let (i, _) = punct(i, "(")?;
|
||||
let (i, p) = sep(i, param, ",")?;
|
||||
let (i, _) = punct(i, ")")?;
|
||||
let end = i.loc();
|
||||
Ok((i, Spanned::new(Span { start, end }, p)))
|
||||
}
|
||||
}
|
||||
|
||||
fn message_nested(i: In) -> PResult<Spanned<Spanned<Nesting>>> {
|
||||
let start = i.loc();
|
||||
let (i, _) = kw(i, "nested")?;
|
||||
commit! {
|
||||
let (i, _) = punct(i, "(")?;
|
||||
let (i, nested) = nesting(i)?;
|
||||
let (i, _) = punct(i, ")")?;
|
||||
|
||||
let end = i.loc();
|
||||
|
||||
Ok((i, Spanned::new(Span { start, end }, nested)))
|
||||
}
|
||||
}
|
||||
|
||||
fn message_prio(i: In) -> PResult<Spanned<Spanned<Priority>>> {
|
||||
let start = i.loc();
|
||||
let (i, _) = kw(i, "prio")?;
|
||||
commit! {
|
||||
let (i, _) = punct(i, "(")?;
|
||||
let (i, prio) = priority(i)?;
|
||||
let (i, _) = punct(i, ")")?;
|
||||
|
||||
let end = i.loc();
|
||||
|
||||
Ok((i, Spanned::new(Span { start, end }, prio)))
|
||||
}
|
||||
}
|
||||
|
||||
fn message_decl(i: In) -> PResult<Spanned<MessageDecl>> {
|
||||
let start = i.loc();
|
||||
|
||||
// XXX(nika): This is really gross, maybe clean it up?
|
||||
let mut nested = None;
|
||||
let mut priority = None;
|
||||
drive!(
|
||||
i,
|
||||
any!(
|
||||
i, "message prefix",
|
||||
message_prio(i.clone()) => |p| priority = Some(p),
|
||||
message_nested(i.clone()) => |n| nested = Some(n),
|
||||
)
|
||||
);
|
||||
|
||||
let send_semantics_start = i.loc();
|
||||
let (i, send_semantics) = any!(
|
||||
i, "send semantics (async, sync, or intr)",
|
||||
kw(i.clone(), "async") => SendSemantics::Async,
|
||||
kw(i.clone(), "sync") => SendSemantics::Sync,
|
||||
kw(i.clone(), "intr") => SendSemantics::Intr,
|
||||
)?;
|
||||
let send_semantics_end = i.loc();
|
||||
|
||||
commit! {
|
||||
let (i, name) = ident(i)?;
|
||||
let (i, _) = punct(i, "(")?;
|
||||
let (i, params) = sep(i, param, ",")?;
|
||||
let (i, _) = punct(i, ")")?;
|
||||
let (i, returns) = maybe(i.clone(), returns(i))?;
|
||||
let (i, modifiers) = many(i, message_modifier)?;
|
||||
let (i, _) = punct(i, ";")?;
|
||||
|
||||
let end = i.loc();
|
||||
|
||||
Ok((i, Spanned::new(Span { start, end }, MessageDecl {
|
||||
nested,
|
||||
priority,
|
||||
send_semantics: Spanned::new(Span { start: send_semantics_start, end: send_semantics_end }, send_semantics),
|
||||
name,
|
||||
params,
|
||||
returns,
|
||||
modifiers,
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Direction {
|
||||
ToChild,
|
||||
ToParent,
|
||||
Both,
|
||||
}
|
||||
|
||||
#[allow(needless_pass_by_value)]
|
||||
fn direction(i: In) -> PResult<Spanned<Direction>> {
|
||||
let start = i.loc();
|
||||
let (i, direction) = any!(
|
||||
i, "direction (child, parent, or both)",
|
||||
kw(i.clone(), "child") => Direction::ToChild,
|
||||
kw(i.clone(), "parent") => Direction::ToParent,
|
||||
kw(i.clone(), "both") => Direction::Both,
|
||||
)?;
|
||||
let end = i.loc();
|
||||
Ok((i, Spanned::new(Span { start, end }, direction)))
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MessageGroup {
|
||||
pub direction: Spanned<Direction>,
|
||||
pub decls: Vec<Spanned<MessageDecl>>,
|
||||
}
|
||||
|
||||
fn message_group(i: In) -> PResult<Spanned<MessageGroup>> {
|
||||
let start = i.loc();
|
||||
let (i, direction) = direction(i)?;
|
||||
commit! {
|
||||
let (i, _) = punct(i, ":")?;
|
||||
let (i, decls) = many(i, message_decl)?;
|
||||
|
||||
let end = i.loc();
|
||||
|
||||
Ok((i, Spanned::new(Span { start, end }, MessageGroup {
|
||||
direction,
|
||||
decls,
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ProtocolItem {
|
||||
pub path: Vec<Spanned<String>>,
|
||||
pub nested: Option<Spanned<Spanned<Nesting>>>,
|
||||
pub send_semantics: Spanned<SendSemantics>,
|
||||
pub managers: Option<Spanned<Vec<Spanned<String>>>>,
|
||||
pub manages: Vec<Spanned<Spanned<String>>>,
|
||||
pub groups: Vec<Spanned<MessageGroup>>,
|
||||
}
|
||||
|
||||
fn protocol_nested(i: In) -> PResult<(Spanned<Spanned<Nesting>>, Spanned<SendSemantics>)> {
|
||||
let nested_start = i.loc();
|
||||
let (i, _) = kw(i, "nested")?;
|
||||
commit! {
|
||||
let (i, _) = punct(i, "(")?;
|
||||
let (i, _) = kw(i, "upto")?;
|
||||
let (i, n) = nesting(i)?;
|
||||
let (i, _) = punct(i, ")")?;
|
||||
let nested_end = i.loc();
|
||||
let ss_start = i.loc();
|
||||
let (i, ss) = any!(
|
||||
i, "send semantics (async or sync)",
|
||||
kw(i.clone(), "async") => SendSemantics::Async,
|
||||
kw(i.clone(), "sync") => SendSemantics::Sync,
|
||||
)?;
|
||||
let ss_end = i.loc();
|
||||
Ok((i, (Spanned::new(Span { start: nested_start, end: nested_end }, n), Spanned::new(Span { start: ss_start, end: ss_end}, ss))))
|
||||
}
|
||||
}
|
||||
|
||||
fn managers(i: In) -> PResult<Spanned<Vec<Spanned<String>>>> {
|
||||
let start = i.loc();
|
||||
let (i, _) = kw(i, "manager")?;
|
||||
commit! {
|
||||
let (i, managers) = sep(i, ident, "or")?;
|
||||
let (i, _) = punct(i, ";")?;
|
||||
let end = i.loc();
|
||||
Ok((i, Spanned::new(Span {start, end}, managers)))
|
||||
}
|
||||
}
|
||||
|
||||
fn manages(i: In) -> PResult<Spanned<Spanned<String>>> {
|
||||
let start = i.loc();
|
||||
let (i, _) = kw(i, "manages")?;
|
||||
commit! {
|
||||
let (i, name) = ident(i)?;
|
||||
let (i, _) = punct(i, ";")?;
|
||||
let end = i.loc();
|
||||
Ok((i, Spanned::new(Span { start, end }, name)))
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(needless_pass_by_value)]
|
||||
fn protocol_item(i: In) -> PResult<Spanned<ProtocolItem>> {
|
||||
let start = i.loc();
|
||||
let mut ss_span = Span::new(i.loc().file);
|
||||
|
||||
let ss_start = i.loc();
|
||||
let (i, (nested, send_semantics)) = any!(
|
||||
i, "protocol item prefixes",
|
||||
kw(i.clone(), "async") => |_x| (None, SendSemantics::Async),
|
||||
kw(i.clone(), "sync") => |_x| (None, SendSemantics::Sync),
|
||||
kw(i.clone(), "intr") => |_x| (None, SendSemantics::Intr),
|
||||
protocol_nested(i.clone()) => |x| {ss_span = x.1.span; (Some(x.0), x.1.data)},
|
||||
Ok((i.clone(), ())) => |_x| (None, SendSemantics::Async),
|
||||
)?;
|
||||
let ss_end = i.loc();
|
||||
|
||||
if ss_span.is_null() {
|
||||
ss_span = Span {
|
||||
start: ss_start,
|
||||
end: ss_end,
|
||||
};
|
||||
}
|
||||
|
||||
let (i, _) = kw(i, "protocol")?;
|
||||
commit! {
|
||||
let (i, name) = ident(i)?;
|
||||
let (i, _) = punct(i, "{")?;
|
||||
let (i, managers) = maybe(i.clone(), managers(i))?;
|
||||
let (i, manages) = many(i, manages)?;
|
||||
let (i, groups) = many(i, message_group)?;
|
||||
let (i, _) = punct(i, "}")?;
|
||||
let (i, _) = punct(i, ";")?;
|
||||
|
||||
let end = i.loc();
|
||||
Ok((i, Spanned::new(Span { start, end }, ProtocolItem {
|
||||
send_semantics: Spanned::new(ss_span, send_semantics),
|
||||
nested,
|
||||
path: vec![name],
|
||||
managers,
|
||||
manages,
|
||||
groups,
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(large_enum_variant)]
|
||||
pub enum Item {
|
||||
Struct(Spanned<StructItem>),
|
||||
Union(Spanned<UnionItem>),
|
||||
Protocol(Spanned<ProtocolItem>),
|
||||
}
|
||||
|
||||
fn namespace(i: In) -> PResult<Vec<Item>> {
|
||||
let (i, _) = kw(i, "namespace")?;
|
||||
|
||||
commit! {
|
||||
let (i, name) = ident(i)?;
|
||||
let (i, _) = punct(i, "{")?;
|
||||
let (i, mut items) = items(i)?;
|
||||
let (i, _) = punct(i, "}")?;
|
||||
for it in &mut items {
|
||||
match *it {
|
||||
Item::Struct(ref mut i) =>
|
||||
i.data.path.insert(0, name.clone()),
|
||||
Item::Union(ref mut i) =>
|
||||
i.data.path.insert(0, name.clone()),
|
||||
Item::Protocol(ref mut i) =>
|
||||
i.data.path.insert(0, name.clone()),
|
||||
}
|
||||
}
|
||||
Ok((i, items))
|
||||
}
|
||||
}
|
||||
|
||||
fn items(i: In) -> PResult<Vec<Item>> {
|
||||
let mut v = Vec::new();
|
||||
drive!(
|
||||
i,
|
||||
any!(
|
||||
i, "item (struct, union, protocol, or namespace)",
|
||||
struct_item(i.clone()) => |x| v.push(Item::Struct(x)),
|
||||
union_item(i.clone()) => |x| v.push(Item::Union(x)),
|
||||
protocol_item(i.clone()) => |x| v.push(Item::Protocol(x)),
|
||||
namespace(i.clone()) => |x| v.extend(x),
|
||||
)
|
||||
);
|
||||
Ok((i, v))
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TranslationUnit {
|
||||
pub cxx_includes: Vec<Spanned<CxxInclude>>,
|
||||
pub includes: Vec<Spanned<Include>>,
|
||||
pub usings: Vec<Spanned<Using>>,
|
||||
pub items: Vec<Item>,
|
||||
}
|
||||
|
||||
fn translation_unit(i: In) -> PResult<Spanned<TranslationUnit>> {
|
||||
// Prelude.
|
||||
let mut usings = Vec::new();
|
||||
let mut includes = Vec::new();
|
||||
let mut cxx_includes = Vec::new();
|
||||
|
||||
let start = i.loc();
|
||||
|
||||
drive!(
|
||||
i,
|
||||
any!(
|
||||
i, "include or using declaration",
|
||||
using(i.clone()) => |u| usings.push(u),
|
||||
include(i.clone()) => |u| includes.push(u),
|
||||
cxx_include(i.clone()) => |u| cxx_includes.push(u),
|
||||
)
|
||||
);
|
||||
|
||||
// Body.
|
||||
let (i, items) = items(i)?;
|
||||
|
||||
// Make sure we're at EOF
|
||||
let i = skip_ws(i)?;
|
||||
if !i.rest().is_empty() {
|
||||
return i.expected("item (struct, union, protocol, or namespace)");
|
||||
}
|
||||
|
||||
let end = i.loc();
|
||||
|
||||
Ok((
|
||||
i,
|
||||
Spanned::new(
|
||||
Span { start, end },
|
||||
TranslationUnit {
|
||||
cxx_includes,
|
||||
includes,
|
||||
usings,
|
||||
items,
|
||||
},
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
/// Entry point - parses a whole translation unit.
|
||||
pub fn parse<'filepath>(
|
||||
src: &str,
|
||||
file: &'filepath str,
|
||||
) -> Result<Spanned<TranslationUnit>, Error> {
|
||||
match translation_unit(In::new(src, file.to_owned())) {
|
||||
Ok((_, v)) => Ok(v),
|
||||
Err(err) => Err(Error(Box::new(err))),
|
||||
}
|
||||
}
|
|
@ -1,450 +0,0 @@
|
|||
/* 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/. */
|
||||
|
||||
//! Fairly minimal recursive-descent parser helper functions and types.
|
||||
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Location {
|
||||
pub line: usize,
|
||||
pub col: usize,
|
||||
pub file: String,
|
||||
}
|
||||
|
||||
impl Location {
|
||||
fn new(file: String) -> Self {
|
||||
Location {
|
||||
line: 0,
|
||||
col: 0,
|
||||
file,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_null(&self) -> bool {
|
||||
self.line == 0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Span {
|
||||
pub start: Location,
|
||||
pub end: Location,
|
||||
}
|
||||
|
||||
impl Span {
|
||||
pub fn new(file: String) -> Self {
|
||||
Span {
|
||||
start: Location::new(file.clone()),
|
||||
end: Location::new(file),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_null(&self) -> bool {
|
||||
self.start.is_null() && self.end.is_null()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Spanned<T> {
|
||||
pub data: T,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl<T> Spanned<T> {
|
||||
pub fn new(span: Span, data: T) -> Self {
|
||||
Spanned { data, span }
|
||||
}
|
||||
}
|
||||
|
||||
/// Every bit set but the high bit in usize.
|
||||
const OFF_MASK: usize = <usize>::max_value() / 2;
|
||||
|
||||
/// Only the high bit in usize.
|
||||
const FATAL_MASK: usize = !OFF_MASK;
|
||||
|
||||
/// An error produced by pipdl
|
||||
pub struct ParserError {
|
||||
message: String,
|
||||
fatal: bool,
|
||||
span: Span,
|
||||
}
|
||||
|
||||
impl ParserError {
|
||||
pub(crate) fn is_fatal(&self) -> bool {
|
||||
self.fatal
|
||||
}
|
||||
|
||||
pub(crate) fn make_fatal(mut self) -> Self {
|
||||
self.fatal = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn span(&self) -> Span {
|
||||
self.span.clone()
|
||||
}
|
||||
|
||||
pub(crate) fn message(&self) -> &str {
|
||||
&self.message
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ParserError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("Error")
|
||||
.field("file", &self.span.start.file)
|
||||
.field("start_line", &self.span.start.line)
|
||||
.field("start_column", &self.span.start.col)
|
||||
.field("end_line", &self.span.end.line)
|
||||
.field("end_column", &self.span.end.col)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to run each expression in order, recovering from any non-fatal
|
||||
/// errors and attempting the next option.
|
||||
macro_rules! any {
|
||||
($i:ident, $expected:expr, $($e:expr => |$x:ident| $f:expr),+ $(,)*) => {
|
||||
// NOTE: We have to do this sorcery for early returns. Using a loop with breaks makes clippy
|
||||
// mad because the loop never loops and we can't desable the lint because we're not in a function.
|
||||
// Also, we don't directly call the closure otherwise clippy would also complain about that. Yeah.
|
||||
{
|
||||
let mut my_closure = || {
|
||||
$(match $e {
|
||||
Ok((i, $x)) => return Ok((i, $f)),
|
||||
Err(e) => {
|
||||
// This assignment is used to help out with type inference.
|
||||
let e: $crate::util::ParserError = e;
|
||||
if e.is_fatal() {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
})+
|
||||
return $i.expected($expected);
|
||||
};
|
||||
|
||||
my_closure()
|
||||
}
|
||||
};
|
||||
|
||||
($i:ident, $expected:expr, $($e:expr => $f:expr),+ $(,)*) => {
|
||||
any!($i, $expected, $($e => |_x| $f),+);
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to repeatedly run the expression, stopping on a non-fatal error,
|
||||
/// and directly returning any fatal error.
|
||||
macro_rules! drive {
|
||||
($i:ident, $e:expr) => {
|
||||
let mut $i = $i;
|
||||
loop {
|
||||
match $e {
|
||||
Ok((j, _)) => $i = j,
|
||||
Err(e) => if e.is_fatal() {
|
||||
return Err(e);
|
||||
} else {
|
||||
break;
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// The type of error used by internal parsers
|
||||
pub(crate) type PResult<'src, T> = Result<(In<'src>, T), ParserError>;
|
||||
|
||||
/// Specify that after this point, errors produced while parsing which are not
|
||||
/// handled should instead be treated as fatal parsing errors.
|
||||
macro_rules! commit {
|
||||
($($e:tt)*) => {
|
||||
// Evaluate the inner expression, transforming errors into fatal errors.
|
||||
let eval = || { $($e)* };
|
||||
eval().map_err($crate::util::ParserError::make_fatal)
|
||||
}
|
||||
}
|
||||
|
||||
/// This datastructure is used as the cursor type into the input source data. It
|
||||
/// holds the full source string, and the current offset.
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct In<'src> {
|
||||
src: &'src str,
|
||||
byte_offset: usize,
|
||||
loc: Location,
|
||||
}
|
||||
impl<'src> In<'src> {
|
||||
pub(crate) fn new(s: &'src str, file: String) -> Self {
|
||||
In {
|
||||
src: s,
|
||||
byte_offset: 0,
|
||||
loc: Location {
|
||||
line: 1,
|
||||
col: 0,
|
||||
file,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// The remaining string in the source file.
|
||||
pub(crate) fn rest(&self) -> &'src str {
|
||||
&self.src[self.byte_offset..]
|
||||
}
|
||||
|
||||
/// Move the cursor forward by `n` characters.
|
||||
pub(crate) fn advance(&self, n_chars: usize) -> Self {
|
||||
let mut loc = self.loc.clone();
|
||||
|
||||
let (n_bytes, last_c) = self
|
||||
.rest()
|
||||
.char_indices()
|
||||
.take(n_chars)
|
||||
.inspect(|&(_, character)| {
|
||||
if character == '\n' {
|
||||
loc.line += 1;
|
||||
loc.col = 0;
|
||||
} else {
|
||||
loc.col += 1;
|
||||
}
|
||||
})
|
||||
.last()
|
||||
.expect("No characters remaining in advance");
|
||||
|
||||
let byte_offset = self
|
||||
.byte_offset
|
||||
.checked_add(n_bytes + last_c.len_utf8())
|
||||
.expect("Failed checked add");
|
||||
|
||||
assert!(byte_offset <= self.src.len());
|
||||
|
||||
In {
|
||||
src: self.src,
|
||||
byte_offset,
|
||||
loc,
|
||||
}
|
||||
}
|
||||
|
||||
/// Produce a new non-fatal error result with the given expected value.
|
||||
pub(crate) fn expected<T>(&self, expected: &'static str) -> Result<T, ParserError> {
|
||||
assert!((self.byte_offset & FATAL_MASK) == 0, "Offset is too large!");
|
||||
|
||||
// Get the line where the error occurred.
|
||||
let text = self.src.lines().nth(self.loc.line - 1).unwrap_or(""); // Usually happens when the error occurs on the last, empty line
|
||||
|
||||
// Format the final error message.
|
||||
let message = format!(
|
||||
"{}\n\
|
||||
| {}\n{:~>off$}^\n",
|
||||
format!("Expected {}", expected),
|
||||
text,
|
||||
"",
|
||||
off = self.loc.col + 2
|
||||
);
|
||||
|
||||
Err(ParserError {
|
||||
message,
|
||||
fatal: false,
|
||||
span: Span {
|
||||
start: self.loc.clone(),
|
||||
end: self.loc.clone(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn loc(&self) -> Location {
|
||||
self.loc.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// Repeatedly run f, collecting results into a vec. Returns an error if a fatal
|
||||
/// error is produced while parsing.
|
||||
pub(crate) fn many<'src, F, R>(i: In<'src>, mut f: F) -> PResult<'src, Vec<R>>
|
||||
where
|
||||
F: FnMut(In<'src>) -> PResult<'src, R>,
|
||||
{
|
||||
let mut v = Vec::new();
|
||||
drive!(
|
||||
i,
|
||||
match f(i.clone()) {
|
||||
Ok((i, x)) => {
|
||||
v.push(x);
|
||||
Ok((i, ()))
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
);
|
||||
Ok((i, v))
|
||||
}
|
||||
|
||||
/// Repeatedly run f, followed by parsing the seperator sep. Returns an error if
|
||||
/// a fatal error is produced while parsing.
|
||||
pub(crate) fn sep<'src, Parser, Ret>(
|
||||
i: In<'src>,
|
||||
mut parser: Parser,
|
||||
sep: &'static str,
|
||||
) -> PResult<'src, Vec<Ret>>
|
||||
where
|
||||
Parser: FnMut(In<'src>) -> PResult<'src, Ret>,
|
||||
{
|
||||
let mut return_vector = Vec::new();
|
||||
drive!(
|
||||
i,
|
||||
match parser(i.clone()) {
|
||||
Ok((i, result)) => {
|
||||
return_vector.push(result);
|
||||
match punct(i.clone(), sep) {
|
||||
Ok(o) => Ok(o),
|
||||
Err(_) => return Ok((i, return_vector)),
|
||||
}
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
);
|
||||
Ok((i, return_vector))
|
||||
}
|
||||
|
||||
/// Skip any leading whitespace, including comments
|
||||
pub(crate) fn skip_ws(mut i: In) -> Result<In, ParserError> {
|
||||
loop {
|
||||
if i.rest().is_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
let c = i
|
||||
.rest()
|
||||
.chars()
|
||||
.next()
|
||||
.expect("No characters remaining when skipping ws");
|
||||
if c.is_whitespace() {
|
||||
i = i.advance(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Line comments
|
||||
if i.rest().starts_with("//") {
|
||||
while !i.rest().starts_with('\n') {
|
||||
i = i.advance(1);
|
||||
if i.rest().is_empty() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Block comments
|
||||
if i.rest().starts_with("/*") {
|
||||
while !i.rest().starts_with("*/") {
|
||||
i = i.advance(1);
|
||||
if i.rest().is_empty() {
|
||||
return i.expected("end of block comment (`*/`)");
|
||||
}
|
||||
}
|
||||
|
||||
i = i.advance(2);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
Ok(i)
|
||||
}
|
||||
|
||||
/// Read an identifier as a string.
|
||||
pub(crate) fn ident(i: In) -> PResult<Spanned<String>> {
|
||||
let i = skip_ws(i)?;
|
||||
let start = i.loc();
|
||||
let (end_char, end_byte) = i
|
||||
.rest()
|
||||
.char_indices()
|
||||
.enumerate()
|
||||
.skip_while(|&(_, (b_idx, c))| match c {
|
||||
'_' | 'a'...'z' | 'A'...'Z' => true,
|
||||
'0'...'9' if b_idx != 0 => true,
|
||||
_ => false,
|
||||
})
|
||||
.next()
|
||||
.map(|(c_idx, (b_idx, _))| (c_idx, b_idx))
|
||||
.unwrap_or((i.rest().chars().count(), i.rest().len()));
|
||||
|
||||
if end_byte == 0 {
|
||||
return i.expected("identifier");
|
||||
}
|
||||
|
||||
let j = i.advance(end_char);
|
||||
let end = j.loc();
|
||||
|
||||
Ok((
|
||||
j,
|
||||
Spanned::new(Span { start, end }, i.rest()[..end_byte].to_owned()),
|
||||
))
|
||||
}
|
||||
|
||||
/// Parse a specific keyword.
|
||||
pub(crate) fn kw<'src>(i: In<'src>, kw: &'static str) -> PResult<'src, Spanned<()>> {
|
||||
let error_message = i.expected(kw);
|
||||
|
||||
let (i, id) = ident(i)?;
|
||||
if id.data == kw {
|
||||
Ok((i, Spanned::new(id.span, ())))
|
||||
} else {
|
||||
error_message
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse punctuation.
|
||||
pub(crate) fn punct<'src>(i: In<'src>, p: &'static str) -> PResult<'src, Spanned<()>> {
|
||||
let i = skip_ws(i)?;
|
||||
let start = i.loc();
|
||||
if i.rest().starts_with(p) {
|
||||
let i = i.advance(p.chars().count());
|
||||
let end = i.loc();
|
||||
Ok((i, Spanned::new(Span { start, end }, ())))
|
||||
} else {
|
||||
i.expected(p)
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to parse the inner value, and return Some() if it succeeded, None if it
|
||||
/// failed non-fatally, and an error if it failed fatally.
|
||||
pub(crate) fn maybe<'src, T>(i: In<'src>, r: PResult<'src, T>) -> PResult<'src, Option<T>> {
|
||||
match r {
|
||||
Ok((i, x)) => Ok((i, Some(x))),
|
||||
Err(e) => if e.is_fatal() {
|
||||
Err(e)
|
||||
} else {
|
||||
Ok((i, None))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a string literal.
|
||||
pub(crate) fn string(i: In) -> PResult<Spanned<String>> {
|
||||
let mut s = String::new();
|
||||
let start = i.loc();
|
||||
let (i, _) = punct(i, "\"")?;
|
||||
let mut chars = i.rest().chars().enumerate().peekable();
|
||||
while let Some((char_offset, ch)) = chars.next() {
|
||||
match ch {
|
||||
'"' => {
|
||||
let i = i.advance(char_offset + 1);
|
||||
let end = i.loc();
|
||||
return Ok((i, Spanned::new(Span { start, end }, s)));
|
||||
}
|
||||
'\\' => match chars.next() {
|
||||
Some((_, 'n')) => s.push('\n'),
|
||||
Some((_, 'r')) => s.push('\r'),
|
||||
Some((_, 't')) => s.push('\t'),
|
||||
Some((_, '\\')) => s.push('\\'),
|
||||
Some((_, '\'')) => s.push('\''),
|
||||
Some((_, '"')) => s.push('"'),
|
||||
Some((_, '0')) => s.push('\0'),
|
||||
_ => {
|
||||
return i
|
||||
.advance(char_offset)
|
||||
.expected("valid escape (\\n, \\r, \\t, \\\\, \\', \\\", or \\0)")
|
||||
}
|
||||
},
|
||||
x => s.push(x),
|
||||
}
|
||||
}
|
||||
i.expected("end of string literal (\")")
|
||||
}
|
|
@ -8,7 +8,6 @@ DIRS += [
|
|||
'chromium',
|
||||
'glue',
|
||||
'ipdl',
|
||||
'ipdl_new',
|
||||
'testshell',
|
||||
]
|
||||
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
{"files":{"Cargo.toml":"fb96cad605ae48215811808c1cc1b9a50248f2b14542058094b23983e2f8d8a0","README.md":"c26d7101e3031e7dd8890ce938e50cad7a1e6adf7fc2f2b0d3c36b03afe68c0b","src/heap.rs":"fe84a4ff433568d5713685456d87597ac5dcdb9d5190061a3da8074240ba1bc3","src/lib.rs":"ce36db8e3464dddade7c1ddbe3ee1f5e525af5be492ea51a0d8a0776c1adfc28","src/range.rs":"bac59bcb6230367a39c7e28ac15263e4526f966cd8c72015873017f17c115aaa"},"package":"73fdf4b84c65a85168477b7fb6c498e0716bc9487fba24623389ea7f51708044"}
|
|
@ -1,28 +0,0 @@
|
|||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g. crates.io) dependencies
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
[package]
|
||||
name = "thin-vec"
|
||||
version = "0.1.0"
|
||||
authors = ["Alexis Beingessner <a.beingessner@gmail.com>"]
|
||||
description = "a vec that takes up less space on the stack"
|
||||
homepage = "https://github.com/gankro/thin-vec"
|
||||
readme = "README.md"
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/gankro/thin-vec"
|
||||
[dependencies.libc]
|
||||
version = "0.2"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
gecko-ffi = []
|
||||
unstable = []
|
|
@ -1,4 +0,0 @@
|
|||
ThinVec is a Vec that stores its length and capacity inline, making it take up
|
||||
less space. Currently this crate mostly exists to facilitate gecko ffi. The
|
||||
crate isn't quite ready for use elsewhere, as it currently unconditionally
|
||||
uses the libc allocator.
|
|
@ -1,15 +0,0 @@
|
|||
extern crate libc;
|
||||
|
||||
pub unsafe fn allocate(size: usize, align: usize) -> *mut u8 {
|
||||
assert!(align <= 16);
|
||||
libc::malloc(size) as *mut _
|
||||
}
|
||||
|
||||
pub unsafe fn deallocate(ptr: *mut u8, _size: usize, _align: usize) {
|
||||
libc::free(ptr as *mut _);
|
||||
}
|
||||
|
||||
pub unsafe fn reallocate(ptr: *mut u8, _old_size: usize, size: usize, align: usize) -> *mut u8 {
|
||||
assert!(align <= 16);
|
||||
libc::realloc(ptr as *mut _, size) as *mut _
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,102 +0,0 @@
|
|||
use std::ops::{RangeFull, Range, RangeTo, RangeFrom};
|
||||
use std::collections::Bound::{self, Excluded, Included, Unbounded};
|
||||
|
||||
/// `RangeArgument` is implemented by Rust's built-in range types, produced
|
||||
/// by range syntax like `..`, `a..`, `..b` or `c..d`.
|
||||
pub trait RangeArgument<T: ?Sized> {
|
||||
/// Start index bound.
|
||||
///
|
||||
/// Returns the start value as a `Bound`.
|
||||
fn start(&self) -> Bound<&T>;
|
||||
|
||||
/// End index bound.
|
||||
///
|
||||
/// Returns the end value as a `Bound`.
|
||||
fn end(&self) -> Bound<&T>;
|
||||
}
|
||||
|
||||
// FIXME add inclusive ranges to RangeArgument
|
||||
|
||||
impl<T: ?Sized> RangeArgument<T> for RangeFull {
|
||||
fn start(&self) -> Bound<&T> {
|
||||
Unbounded
|
||||
}
|
||||
fn end(&self) -> Bound<&T> {
|
||||
Unbounded
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> RangeArgument<T> for RangeFrom<T> {
|
||||
fn start(&self) -> Bound<&T> {
|
||||
Included(&self.start)
|
||||
}
|
||||
fn end(&self) -> Bound<&T> {
|
||||
Unbounded
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> RangeArgument<T> for RangeTo<T> {
|
||||
fn start(&self) -> Bound<&T> {
|
||||
Unbounded
|
||||
}
|
||||
fn end(&self) -> Bound<&T> {
|
||||
Excluded(&self.end)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> RangeArgument<T> for Range<T> {
|
||||
fn start(&self) -> Bound<&T> {
|
||||
Included(&self.start)
|
||||
}
|
||||
fn end(&self) -> Bound<&T> {
|
||||
Excluded(&self.end)
|
||||
}
|
||||
}
|
||||
|
||||
/* ~one day~
|
||||
impl<T> RangeArgument<T> for RangeToInclusive<T> {
|
||||
fn start(&self) -> Bound<&T> {
|
||||
Unbounded
|
||||
}
|
||||
fn end(&self) -> Bound<&T> {
|
||||
Included(&self.end)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> RangeArgument<T> for RangeInclusive<T> {
|
||||
fn start(&self) -> Bound<&T> {
|
||||
Included(&self.start)
|
||||
}
|
||||
fn end(&self) -> Bound<&T> {
|
||||
Included(&self.end)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
impl<T> RangeArgument<T> for (Bound<T>, Bound<T>) {
|
||||
fn start(&self) -> Bound<&T> {
|
||||
match *self {
|
||||
(Included(ref start), _) => Included(start),
|
||||
(Excluded(ref start), _) => Excluded(start),
|
||||
(Unbounded, _) => Unbounded,
|
||||
}
|
||||
}
|
||||
|
||||
fn end(&self) -> Bound<&T> {
|
||||
match *self {
|
||||
(_, Included(ref end)) => Included(end),
|
||||
(_, Excluded(ref end)) => Excluded(end),
|
||||
(_, Unbounded) => Unbounded,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized + 'a> RangeArgument<T> for (Bound<&'a T>, Bound<&'a T>) {
|
||||
fn start(&self) -> Bound<&T> {
|
||||
self.0
|
||||
}
|
||||
|
||||
fn end(&self) -> Bound<&T> {
|
||||
self.1
|
||||
}
|
||||
}
|
|
@ -27,7 +27,6 @@ rsdparsa_capi = { path = "../../../../media/webrtc/signaling/src/sdp/rsdparsa_ca
|
|||
log = {version = "0.4", features = ["release_max_level_info"]}
|
||||
env_logger = {version = "0.5", default-features = false} # disable `regex` to reduce code size
|
||||
cose-c = { version = "0.1.5" }
|
||||
ipdl_bindings = {path = "../../../../ipc/ipdl_new/ipdl_bindings"}
|
||||
rkv = "0.4"
|
||||
|
||||
[build-dependencies]
|
||||
|
|
|
@ -31,7 +31,6 @@ extern crate u2fhid;
|
|||
extern crate log;
|
||||
extern crate cosec;
|
||||
extern crate rsdparsa_capi;
|
||||
extern crate ipdl_bindings;
|
||||
|
||||
use std::boxed::Box;
|
||||
use std::env;
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
[package]
|
||||
name = "mfbt-maybe"
|
||||
version = "0.1.0"
|
||||
authors = ["Tristan Bourvon <tristanbourvon@gmail.com>"]
|
||||
|
||||
[lib]
|
||||
name = "mfbt_maybe"
|
|
@ -1,43 +0,0 @@
|
|||
// You know this is black magic, I know this is black magic, the code knows
|
||||
// it's actually black magic - but let's just leave it alone.
|
||||
|
||||
// Thank you Manisheart for the trait type part.
|
||||
|
||||
pub struct Maybe<T: MaybeTrait> {
|
||||
pub data: T::Data,
|
||||
pub is_some: ::std::os::raw::c_char,
|
||||
}
|
||||
|
||||
impl<T: MaybeTrait, U: Into<T>> From<Option<U>> for Maybe<T> {
|
||||
fn from(o: Option<U>) -> Self {
|
||||
T::from_option(o)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait MaybeTrait {
|
||||
type Data;
|
||||
fn from_option<U: Into<Self>>(x: Option<U>) -> Maybe<Self>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! maybe {
|
||||
($t:ident) => {
|
||||
impl MaybeTrait for $t {
|
||||
type Data = [::std::os::raw::c_char; ::std::mem::size_of::<$t>()];
|
||||
fn from_option<U: Into<Self>>(o: Option<U>) -> Maybe<Self> {
|
||||
match o {
|
||||
Some(x) => Maybe {
|
||||
data: unsafe { ::std::mem::transmute(x.into()) },
|
||||
is_some: 1,
|
||||
},
|
||||
None => Maybe {
|
||||
data: unsafe { ::std::mem::zeroed() },
|
||||
is_some: 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
Загрузка…
Ссылка в новой задаче