summaryrefslogtreecommitdiffstats
path: root/ipc/ipdl
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /ipc/ipdl
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'ipc/ipdl')
-rw-r--r--ipc/ipdl/Makefile.in27
-rwxr-xr-xipc/ipdl/ipdl.py232
-rw-r--r--ipc/ipdl/ipdl/__init__.py77
-rw-r--r--ipc/ipdl/ipdl/ast.py454
-rw-r--r--ipc/ipdl/ipdl/builtin.py59
-rw-r--r--ipc/ipdl/ipdl/cgen.py101
-rw-r--r--ipc/ipdl/ipdl/cxx/__init__.py6
-rw-r--r--ipc/ipdl/ipdl/cxx/ast.py809
-rw-r--r--ipc/ipdl/ipdl/cxx/cgen.py520
-rw-r--r--ipc/ipdl/ipdl/lower.py4822
-rw-r--r--ipc/ipdl/ipdl/parser.py807
-rw-r--r--ipc/ipdl/ipdl/type.py2200
-rw-r--r--ipc/ipdl/moz.build19
-rw-r--r--ipc/ipdl/msgtype-components13
-rw-r--r--ipc/ipdl/test/README.txt10
-rw-r--r--ipc/ipdl/test/cxx/IPDLUnitTestProcessChild.cpp32
-rw-r--r--ipc/ipdl/test/cxx/IPDLUnitTestProcessChild.h32
-rw-r--r--ipc/ipdl/test/cxx/IPDLUnitTestSubprocess.cpp23
-rw-r--r--ipc/ipdl/test/cxx/IPDLUnitTestSubprocess.h37
-rw-r--r--ipc/ipdl/test/cxx/IPDLUnitTestTypes.h44
-rw-r--r--ipc/ipdl/test/cxx/IPDLUnitTestUtils.h27
-rw-r--r--ipc/ipdl/test/cxx/IPDLUnitTests.h93
-rw-r--r--ipc/ipdl/test/cxx/IPDLUnitTests.template.cpp390
-rw-r--r--ipc/ipdl/test/cxx/Makefile.in46
-rw-r--r--ipc/ipdl/test/cxx/PTestActorPunning.ipdl39
-rw-r--r--ipc/ipdl/test/cxx/PTestActorPunningPunned.ipdl15
-rw-r--r--ipc/ipdl/test/cxx/PTestActorPunningSub.ipdl16
-rw-r--r--ipc/ipdl/test/cxx/PTestBadActor.ipdl18
-rw-r--r--ipc/ipdl/test/cxx/PTestBadActorSub.ipdl17
-rw-r--r--ipc/ipdl/test/cxx/PTestBridgeMain.ipdl26
-rw-r--r--ipc/ipdl/test/cxx/PTestBridgeMainSub.ipdl33
-rw-r--r--ipc/ipdl/test/cxx/PTestBridgeSub.ipdl25
-rw-r--r--ipc/ipdl/test/cxx/PTestCancel.ipdl36
-rw-r--r--ipc/ipdl/test/cxx/PTestCrashCleanup.ipdl22
-rw-r--r--ipc/ipdl/test/cxx/PTestDataStructures.ipdl132
-rw-r--r--ipc/ipdl/test/cxx/PTestDataStructuresCommon.ipdlh107
-rw-r--r--ipc/ipdl/test/cxx/PTestDataStructuresSub.ipdl15
-rw-r--r--ipc/ipdl/test/cxx/PTestDemon.ipdl21
-rw-r--r--ipc/ipdl/test/cxx/PTestDesc.ipdl31
-rw-r--r--ipc/ipdl/test/cxx/PTestDescSub.ipdl18
-rw-r--r--ipc/ipdl/test/cxx/PTestDescSubsub.ipdl15
-rw-r--r--ipc/ipdl/test/cxx/PTestEndpointBridgeMain.ipdl23
-rw-r--r--ipc/ipdl/test/cxx/PTestEndpointBridgeMainSub.ipdl25
-rw-r--r--ipc/ipdl/test/cxx/PTestEndpointBridgeSub.ipdl22
-rw-r--r--ipc/ipdl/test/cxx/PTestEndpointOpens.ipdl19
-rw-r--r--ipc/ipdl/test/cxx/PTestEndpointOpensOpened.ipdl30
-rw-r--r--ipc/ipdl/test/cxx/PTestFailedCtor.ipdl19
-rw-r--r--ipc/ipdl/test/cxx/PTestFailedCtorSub.ipdl18
-rw-r--r--ipc/ipdl/test/cxx/PTestFailedCtorSubsub.ipdl15
-rw-r--r--ipc/ipdl/test/cxx/PTestHandle.ipdl14
-rw-r--r--ipc/ipdl/test/cxx/PTestHangs.ipdl40
-rw-r--r--ipc/ipdl/test/cxx/PTestHighestPrio.ipdl18
-rw-r--r--ipc/ipdl/test/cxx/PTestIndirectProtocolParam.ipdlh15
-rw-r--r--ipc/ipdl/test/cxx/PTestIndirectProtocolParamFirst.ipdl19
-rw-r--r--ipc/ipdl/test/cxx/PTestIndirectProtocolParamManage.ipdl17
-rw-r--r--ipc/ipdl/test/cxx/PTestIndirectProtocolParamSecond.ipdl13
-rw-r--r--ipc/ipdl/test/cxx/PTestInterruptErrorCleanup.ipdl11
-rw-r--r--ipc/ipdl/test/cxx/PTestInterruptRaces.ipdl82
-rw-r--r--ipc/ipdl/test/cxx/PTestInterruptShutdownRace.ipdl34
-rw-r--r--ipc/ipdl/test/cxx/PTestJSON.ipdl54
-rw-r--r--ipc/ipdl/test/cxx/PTestLatency.ipdl75
-rw-r--r--ipc/ipdl/test/cxx/PTestManyChildAllocs.ipdl19
-rw-r--r--ipc/ipdl/test/cxx/PTestManyChildAllocsSub.ipdl19
-rw-r--r--ipc/ipdl/test/cxx/PTestMultiMgrs.ipdl34
-rw-r--r--ipc/ipdl/test/cxx/PTestMultiMgrsBottom.ipdl18
-rw-r--r--ipc/ipdl/test/cxx/PTestMultiMgrsLeft.ipdl24
-rw-r--r--ipc/ipdl/test/cxx/PTestMultiMgrsRight.ipdl24
-rw-r--r--ipc/ipdl/test/cxx/PTestNestedLoops.ipdl34
-rw-r--r--ipc/ipdl/test/cxx/PTestOpens.ipdl25
-rw-r--r--ipc/ipdl/test/cxx/PTestOpensOpened.ipdl28
-rw-r--r--ipc/ipdl/test/cxx/PTestPriority.ipdl14
-rw-r--r--ipc/ipdl/test/cxx/PTestRPC.ipdl21
-rw-r--r--ipc/ipdl/test/cxx/PTestRaceDeadlock.ipdl20
-rw-r--r--ipc/ipdl/test/cxx/PTestRaceDeferral.ipdl45
-rw-r--r--ipc/ipdl/test/cxx/PTestRacyInterruptReplies.ipdl41
-rw-r--r--ipc/ipdl/test/cxx/PTestRacyReentry.ipdl21
-rw-r--r--ipc/ipdl/test/cxx/PTestRacyUndefer.ipdl28
-rw-r--r--ipc/ipdl/test/cxx/PTestSanity.ipdl28
-rw-r--r--ipc/ipdl/test/cxx/PTestSelfManage.ipdl22
-rw-r--r--ipc/ipdl/test/cxx/PTestSelfManageRoot.ipdl23
-rw-r--r--ipc/ipdl/test/cxx/PTestShmem.ipdl22
-rw-r--r--ipc/ipdl/test/cxx/PTestShutdown.ipdl37
-rw-r--r--ipc/ipdl/test/cxx/PTestShutdownSub.ipdl30
-rw-r--r--ipc/ipdl/test/cxx/PTestShutdownSubsub.ipdl17
-rw-r--r--ipc/ipdl/test/cxx/PTestStackHooks.ipdl56
-rw-r--r--ipc/ipdl/test/cxx/PTestSyncError.ipdl28
-rw-r--r--ipc/ipdl/test/cxx/PTestSyncHang.ipdl15
-rw-r--r--ipc/ipdl/test/cxx/PTestSyncWakeup.ipdl41
-rw-r--r--ipc/ipdl/test/cxx/PTestUrgency.ipdl19
-rw-r--r--ipc/ipdl/test/cxx/PTestUrgentHangs.ipdl28
-rw-r--r--ipc/ipdl/test/cxx/README.txt61
-rw-r--r--ipc/ipdl/test/cxx/TestActorPunning.cpp128
-rw-r--r--ipc/ipdl/test/cxx/TestActorPunning.h110
-rw-r--r--ipc/ipdl/test/cxx/TestBadActor.cpp51
-rw-r--r--ipc/ipdl/test/cxx/TestBadActor.h91
-rw-r--r--ipc/ipdl/test/cxx/TestBridgeMain.cpp227
-rw-r--r--ipc/ipdl/test/cxx/TestBridgeMain.h149
-rw-r--r--ipc/ipdl/test/cxx/TestCancel.cpp168
-rw-r--r--ipc/ipdl/test/cxx/TestCancel.h66
-rw-r--r--ipc/ipdl/test/cxx/TestCrashCleanup.cpp116
-rw-r--r--ipc/ipdl/test/cxx/TestCrashCleanup.h58
-rw-r--r--ipc/ipdl/test/cxx/TestDataStructures.cpp984
-rw-r--r--ipc/ipdl/test/cxx/TestDataStructures.h234
-rw-r--r--ipc/ipdl/test/cxx/TestDemon.cpp417
-rw-r--r--ipc/ipdl/test/cxx/TestDemon.h106
-rw-r--r--ipc/ipdl/test/cxx/TestDesc.cpp105
-rw-r--r--ipc/ipdl/test/cxx/TestDesc.h128
-rw-r--r--ipc/ipdl/test/cxx/TestEndpointBridgeMain.cpp257
-rw-r--r--ipc/ipdl/test/cxx/TestEndpointBridgeMain.h133
-rw-r--r--ipc/ipdl/test/cxx/TestEndpointOpens.cpp271
-rw-r--r--ipc/ipdl/test/cxx/TestEndpointOpens.h101
-rw-r--r--ipc/ipdl/test/cxx/TestFailedCtor.cpp137
-rw-r--r--ipc/ipdl/test/cxx/TestFailedCtor.h136
-rw-r--r--ipc/ipdl/test/cxx/TestHangs.cpp146
-rw-r--r--ipc/ipdl/test/cxx/TestHangs.h87
-rw-r--r--ipc/ipdl/test/cxx/TestHighestPrio.cpp118
-rw-r--r--ipc/ipdl/test/cxx/TestHighestPrio.h68
-rw-r--r--ipc/ipdl/test/cxx/TestInterruptErrorCleanup.cpp155
-rw-r--r--ipc/ipdl/test/cxx/TestInterruptErrorCleanup.h60
-rw-r--r--ipc/ipdl/test/cxx/TestInterruptRaces.cpp220
-rw-r--r--ipc/ipdl/test/cxx/TestInterruptRaces.h131
-rw-r--r--ipc/ipdl/test/cxx/TestInterruptShutdownRace.cpp134
-rw-r--r--ipc/ipdl/test/cxx/TestInterruptShutdownRace.h64
-rw-r--r--ipc/ipdl/test/cxx/TestJSON.cpp129
-rw-r--r--ipc/ipdl/test/cxx/TestJSON.h111
-rw-r--r--ipc/ipdl/test/cxx/TestLatency.cpp258
-rw-r--r--ipc/ipdl/test/cxx/TestLatency.h111
-rw-r--r--ipc/ipdl/test/cxx/TestManyChildAllocs.cpp104
-rw-r--r--ipc/ipdl/test/cxx/TestManyChildAllocs.h94
-rw-r--r--ipc/ipdl/test/cxx/TestMultiMgrs.cpp104
-rw-r--r--ipc/ipdl/test/cxx/TestMultiMgrs.h250
-rw-r--r--ipc/ipdl/test/cxx/TestNestedLoops.cpp98
-rw-r--r--ipc/ipdl/test/cxx/TestNestedLoops.h67
-rw-r--r--ipc/ipdl/test/cxx/TestOpens.cpp251
-rw-r--r--ipc/ipdl/test/cxx/TestOpens.h107
-rw-r--r--ipc/ipdl/test/cxx/TestRPC.cpp152
-rw-r--r--ipc/ipdl/test/cxx/TestRPC.h74
-rw-r--r--ipc/ipdl/test/cxx/TestRaceDeadlock.cpp131
-rw-r--r--ipc/ipdl/test/cxx/TestRaceDeadlock.h77
-rw-r--r--ipc/ipdl/test/cxx/TestRaceDeferral.cpp118
-rw-r--r--ipc/ipdl/test/cxx/TestRaceDeferral.h76
-rw-r--r--ipc/ipdl/test/cxx/TestRacyInterruptReplies.cpp119
-rw-r--r--ipc/ipdl/test/cxx/TestRacyInterruptReplies.h73
-rw-r--r--ipc/ipdl/test/cxx/TestRacyReentry.cpp84
-rw-r--r--ipc/ipdl/test/cxx/TestRacyReentry.h67
-rw-r--r--ipc/ipdl/test/cxx/TestRacyUndefer.cpp115
-rw-r--r--ipc/ipdl/test/cxx/TestRacyUndefer.h70
-rw-r--r--ipc/ipdl/test/cxx/TestSanity.cpp75
-rw-r--r--ipc/ipdl/test/cxx/TestSanity.h63
-rw-r--r--ipc/ipdl/test/cxx/TestSelfManageRoot.cpp63
-rw-r--r--ipc/ipdl/test/cxx/TestSelfManageRoot.h141
-rw-r--r--ipc/ipdl/test/cxx/TestShmem.cpp115
-rw-r--r--ipc/ipdl/test/cxx/TestShmem.h66
-rw-r--r--ipc/ipdl/test/cxx/TestShutdown.cpp235
-rw-r--r--ipc/ipdl/test/cxx/TestShutdown.h225
-rw-r--r--ipc/ipdl/test/cxx/TestStackHooks.cpp168
-rw-r--r--ipc/ipdl/test/cxx/TestStackHooks.h130
-rw-r--r--ipc/ipdl/test/cxx/TestSyncError.cpp61
-rw-r--r--ipc/ipdl/test/cxx/TestSyncError.h71
-rw-r--r--ipc/ipdl/test/cxx/TestSyncHang.cpp70
-rw-r--r--ipc/ipdl/test/cxx/TestSyncHang.h58
-rw-r--r--ipc/ipdl/test/cxx/TestSyncWakeup.cpp134
-rw-r--r--ipc/ipdl/test/cxx/TestSyncWakeup.h74
-rw-r--r--ipc/ipdl/test/cxx/TestUrgency.cpp162
-rw-r--r--ipc/ipdl/test/cxx/TestUrgency.h72
-rw-r--r--ipc/ipdl/test/cxx/TestUrgentHangs.cpp217
-rw-r--r--ipc/ipdl/test/cxx/TestUrgentHangs.h79
-rw-r--r--ipc/ipdl/test/cxx/app/Makefile.in5
-rw-r--r--ipc/ipdl/test/cxx/app/TestIPDL.cpp20
-rw-r--r--ipc/ipdl/test/cxx/app/moz.build20
-rw-r--r--ipc/ipdl/test/cxx/genIPDLUnitTests.py141
-rw-r--r--ipc/ipdl/test/cxx/moz.build134
-rw-r--r--ipc/ipdl/test/ipdl/IPDLCompile.py75
-rw-r--r--ipc/ipdl/test/ipdl/Makefile.in17
-rw-r--r--ipc/ipdl/test/ipdl/error/DeleteRace.ipdl14
-rw-r--r--ipc/ipdl/test/ipdl/error/Nullable.ipdl4
-rw-r--r--ipc/ipdl/test/ipdl/error/Nullable2.ipdl8
-rw-r--r--ipc/ipdl/test/ipdl/error/actorparam_badState.ipdl10
-rw-r--r--ipc/ipdl/test/ipdl/error/array_Recursive.ipdl3
-rw-r--r--ipc/ipdl/test/ipdl/error/badProtocolInclude.ipdl7
-rw-r--r--ipc/ipdl/test/ipdl/error/bridgesNonexistent.ipdl6
-rw-r--r--ipc/ipdl/test/ipdl/error/bridgesSubprotocol.ipdl13
-rw-r--r--ipc/ipdl/test/ipdl/error/compressCtor.ipdl8
-rw-r--r--ipc/ipdl/test/ipdl/error/compressCtorManagee.ipdl8
-rw-r--r--ipc/ipdl/test/ipdl/error/conflictProtocolMsg.ipdl7
-rw-r--r--ipc/ipdl/test/ipdl/error/cyclecheck_Child.ipdl12
-rw-r--r--ipc/ipdl/test/ipdl/error/cyclecheck_Grandchild.ipdl12
-rw-r--r--ipc/ipdl/test/ipdl/error/cyclecheck_Parent.ipdl10
-rw-r--r--ipc/ipdl/test/ipdl/error/dtorReserved.ipdl8
-rw-r--r--ipc/ipdl/test/ipdl/error/empty.ipdl1
-rw-r--r--ipc/ipdl/test/ipdl/error/intrMessageCompress.ipdl6
-rw-r--r--ipc/ipdl/test/ipdl/error/lex1.ipdl1
-rw-r--r--ipc/ipdl/test/ipdl/error/manageSelfToplevel.ipdl8
-rw-r--r--ipc/ipdl/test/ipdl/error/managedNoCtor.ipdl7
-rw-r--r--ipc/ipdl/test/ipdl/error/managedNoDtor.ipdl6
-rw-r--r--ipc/ipdl/test/ipdl/error/managerNoCtor.ipdl8
-rw-r--r--ipc/ipdl/test/ipdl/error/managerNoDtor.ipdl9
-rw-r--r--ipc/ipdl/test/ipdl/error/messageNoDirection.ipdl3
-rw-r--r--ipc/ipdl/test/ipdl/error/multimanDupMgrs.ipdl8
-rw-r--r--ipc/ipdl/test/ipdl/error/multimanDupMgrsMgr.ipdl9
-rw-r--r--ipc/ipdl/test/ipdl/error/multimanNonexistentMgrs.ipdl7
-rw-r--r--ipc/ipdl/test/ipdl/error/mutualRecStruct.ipdl20
-rw-r--r--ipc/ipdl/test/ipdl/error/mutualRecStructUnion.ipdl20
-rw-r--r--ipc/ipdl/test/ipdl/error/noEmptyToplevel.ipdl5
-rw-r--r--ipc/ipdl/test/ipdl/error/noProtocolInHeader.ipdlh4
-rw-r--r--ipc/ipdl/test/ipdl/error/oldIncludeSyntax.ipdl5
-rw-r--r--ipc/ipdl/test/ipdl/error/opensNonexistent.ipdl6
-rw-r--r--ipc/ipdl/test/ipdl/error/opensSubprotocol.ipdl13
-rw-r--r--ipc/ipdl/test/ipdl/error/parser.ipdl1
-rw-r--r--ipc/ipdl/test/ipdl/error/race_MultiOut.ipdl20
-rw-r--r--ipc/ipdl/test/ipdl/error/race_OverlappingMultiOut.ipdl35
-rw-r--r--ipc/ipdl/test/ipdl/error/race_ToDiffStates.ipdl20
-rw-r--r--ipc/ipdl/test/ipdl/error/race_ToError.ipdl14
-rw-r--r--ipc/ipdl/test/ipdl/error/race_ViolateSameDirection.ipdl30
-rw-r--r--ipc/ipdl/test/ipdl/error/redeclMessage.ipdl9
-rw-r--r--ipc/ipdl/test/ipdl/error/redeclParamReturn.ipdl7
-rw-r--r--ipc/ipdl/test/ipdl/error/redefState.ipdl14
-rw-r--r--ipc/ipdl/test/ipdl/error/repeatedOutState.ipdl12
-rw-r--r--ipc/ipdl/test/ipdl/error/rpcParentToChild.ipdl6
-rw-r--r--ipc/ipdl/test/ipdl/error/shmem.ipdl5
-rw-r--r--ipc/ipdl/test/ipdl/error/shmem_access_union.ipdl8
-rw-r--r--ipc/ipdl/test/ipdl/error/spawnsNonexistent.ipdl6
-rw-r--r--ipc/ipdl/test/ipdl/error/spawnsSubprotocol.ipdl13
-rw-r--r--ipc/ipdl/test/ipdl/error/structRedecl.ipdl8
-rw-r--r--ipc/ipdl/test/ipdl/error/structUnknownField.ipdl7
-rw-r--r--ipc/ipdl/test/ipdl/error/subprotocolBridges.ipdl10
-rw-r--r--ipc/ipdl/test/ipdl/error/subprotocolOpens.ipdl10
-rw-r--r--ipc/ipdl/test/ipdl/error/subprotocolSpawns.ipdl10
-rw-r--r--ipc/ipdl/test/ipdl/error/syncMessageCompress.ipdl4
-rw-r--r--ipc/ipdl/test/ipdl/error/syncParentToChild.ipdl6
-rw-r--r--ipc/ipdl/test/ipdl/error/tooWeakInterruptAsync.ipdl7
-rw-r--r--ipc/ipdl/test/ipdl/error/tooWeakRpcSync.ipdl6
-rw-r--r--ipc/ipdl/test/ipdl/error/tooWeakSyncAsync.ipdl7
-rw-r--r--ipc/ipdl/test/ipdl/error/trans_WrongDirection.ipdl12
-rw-r--r--ipc/ipdl/test/ipdl/error/trans_WrongDirection2.ipdl12
-rw-r--r--ipc/ipdl/test/ipdl/error/trans_WrongDirection3.ipdl12
-rw-r--r--ipc/ipdl/test/ipdl/error/trans_WrongDirection4.ipdl12
-rw-r--r--ipc/ipdl/test/ipdl/error/trans_WrongDirection5.ipdl12
-rw-r--r--ipc/ipdl/test/ipdl/error/trans_WrongName.ipdl10
-rw-r--r--ipc/ipdl/test/ipdl/error/trans_WrongName2.ipdl10
-rw-r--r--ipc/ipdl/test/ipdl/error/trans_WrongName3.ipdl10
-rw-r--r--ipc/ipdl/test/ipdl/error/trans_WrongName4.ipdl10
-rw-r--r--ipc/ipdl/test/ipdl/error/trans_WrongName5.ipdl10
-rw-r--r--ipc/ipdl/test/ipdl/error/twoprotocols.ipdl9
-rw-r--r--ipc/ipdl/test/ipdl/error/undeclParamType.ipdl5
-rw-r--r--ipc/ipdl/test/ipdl/error/undeclProtocol.ipdl6
-rw-r--r--ipc/ipdl/test/ipdl/error/undeclReturnType.ipdl5
-rw-r--r--ipc/ipdl/test/ipdl/error/undefMutualRecStruct.ipdl7
-rw-r--r--ipc/ipdl/test/ipdl/error/undefMutualRecStructUnion.ipdl7
-rw-r--r--ipc/ipdl/test/ipdl/error/undefMutualRecUnion.ipdl7
-rw-r--r--ipc/ipdl/test/ipdl/error/undefSelfRecStruct.ipdl5
-rw-r--r--ipc/ipdl/test/ipdl/error/undefSelfRecUnion.ipdl5
-rw-r--r--ipc/ipdl/test/ipdl/error/unreachedDelete.ipdl5
-rw-r--r--ipc/ipdl/test/ipdl/error/unreachedDeleteMultiStart.ipdl10
-rw-r--r--ipc/ipdl/test/ipdl/moz.build6
-rw-r--r--ipc/ipdl/test/ipdl/ok/Delete.ipdl8
-rw-r--r--ipc/ipdl/test/ipdl/ok/DeleteSub.ipdl12
-rw-r--r--ipc/ipdl/test/ipdl/ok/Nullable.ipdl11
-rw-r--r--ipc/ipdl/test/ipdl/ok/Struct.ipdl10
-rw-r--r--ipc/ipdl/test/ipdl/ok/actorparam.ipdl5
-rw-r--r--ipc/ipdl/test/ipdl/ok/actorparam_state.ipdl10
-rw-r--r--ipc/ipdl/test/ipdl/ok/actorreturn.ipdl6
-rw-r--r--ipc/ipdl/test/ipdl/ok/array_Basic.ipdl4
-rw-r--r--ipc/ipdl/test/ipdl/ok/array_OfActors.ipdl10
-rw-r--r--ipc/ipdl/test/ipdl/ok/array_OfActorsSub.ipdl7
-rw-r--r--ipc/ipdl/test/ipdl/ok/array_Union.ipdl10
-rw-r--r--ipc/ipdl/test/ipdl/ok/builtins.ipdl21
-rw-r--r--ipc/ipdl/test/ipdl/ok/compositor.ipdl11
-rw-r--r--ipc/ipdl/test/ipdl/ok/content.ipdl17
-rw-r--r--ipc/ipdl/test/ipdl/ok/empty.ipdl3
-rw-r--r--ipc/ipdl/test/ipdl/ok/emptyStruct.ipdl3
-rw-r--r--ipc/ipdl/test/ipdl/ok/header.ipdlh15
-rw-r--r--ipc/ipdl/test/ipdl/ok/headerProto.ipdl10
-rw-r--r--ipc/ipdl/test/ipdl/ok/intrProtocol.ipdl13
-rw-r--r--ipc/ipdl/test/ipdl/ok/jetpack.ipdl7
-rw-r--r--ipc/ipdl/test/ipdl/ok/jetpackContent.ipdl12
-rw-r--r--ipc/ipdl/test/ipdl/ok/manageSelf.ipdl10
-rw-r--r--ipc/ipdl/test/ipdl/ok/manageSelf_Toplevel.ipdl9
-rw-r--r--ipc/ipdl/test/ipdl/ok/managedProtocol.ipdl8
-rw-r--r--ipc/ipdl/test/ipdl/ok/managerProtocol.ipdl13
-rw-r--r--ipc/ipdl/test/ipdl/ok/media.ipdl7
-rw-r--r--ipc/ipdl/test/ipdl/ok/messageCompress.ipdl4
-rw-r--r--ipc/ipdl/test/ipdl/ok/messageVerify.ipdl14
-rw-r--r--ipc/ipdl/test/ipdl/ok/messageVerifyTopLevel.ipdl7
-rw-r--r--ipc/ipdl/test/ipdl/ok/multiManaged.ipdl9
-rw-r--r--ipc/ipdl/test/ipdl/ok/multiManager1.ipdl8
-rw-r--r--ipc/ipdl/test/ipdl/ok/multiManager2.ipdl8
-rw-r--r--ipc/ipdl/test/ipdl/ok/multiOutStates.ipdl12
-rw-r--r--ipc/ipdl/test/ipdl/ok/multiStartState.ipdl11
-rw-r--r--ipc/ipdl/test/ipdl/ok/multipleUsingCxxTypes.ipdl7
-rw-r--r--ipc/ipdl/test/ipdl/ok/mutualRecStructUnion.ipdl21
-rw-r--r--ipc/ipdl/test/ipdl/ok/mutualRecUnion.ipdl20
-rw-r--r--ipc/ipdl/test/ipdl/ok/namespace_Basic.ipdl12
-rw-r--r--ipc/ipdl/test/ipdl/ok/noRedeclCrossMessage.ipdl9
-rw-r--r--ipc/ipdl/test/ipdl/ok/plugin.ipdl7
-rw-r--r--ipc/ipdl/test/ipdl/ok/race_DiamondRule1.ipdl27
-rw-r--r--ipc/ipdl/test/ipdl/ok/race_KitchenSink.ipdl61
-rw-r--r--ipc/ipdl/test/ipdl/ok/race_MultiOut.ipdl35
-rw-r--r--ipc/ipdl/test/ipdl/ok/race_Stateless.ipdl16
-rw-r--r--ipc/ipdl/test/ipdl/ok/selfRecUnion.ipdl11
-rw-r--r--ipc/ipdl/test/ipdl/ok/shmem.ipdl13
-rw-r--r--ipc/ipdl/test/ipdl/ok/syncProtocol.ipdl11
-rw-r--r--ipc/ipdl/test/ipdl/ok/threeDirections.ipdl13
-rw-r--r--ipc/ipdl/test/ipdl/ok/union_Basic.ipdl11
-rw-r--r--ipc/ipdl/test/ipdl/ok/union_Namespaced.ipdl18
-rw-r--r--ipc/ipdl/test/ipdl/runtests.py75
-rw-r--r--ipc/ipdl/test/moz.build12
307 files changed, 25200 insertions, 0 deletions
diff --git a/ipc/ipdl/Makefile.in b/ipc/ipdl/Makefile.in
new file mode 100644
index 000000000..9f1762a0d
--- /dev/null
+++ b/ipc/ipdl/Makefile.in
@@ -0,0 +1,27 @@
+# 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/.
+
+GARBAGE_DIRS += _ipdlheaders
+GARBAGE += ipdl_lextab.py ipdl_yacctab.py $(wildcard *.pyc $(srcdir)/ipdl/*.pyc $(srcdir)/ipdl/cxx/*.pyc)
+
+ifdef COMPILE_ENVIRONMENT
+
+# This file is generated by the moz.build backend.
+include ipdlsrcs.mk
+
+include $(topsrcdir)/config/rules.mk
+
+
+# NB: the IPDL compiler manages .ipdl-->.h/.cpp dependencies itself,
+# which is why we don't have explicit .h/.cpp targets here
+export:: $(ALL_IPDLSRCS)
+ $(PYTHON) $(topsrcdir)/config/pythonpath.py \
+ $(PLY_INCLUDE) \
+ $(srcdir)/ipdl.py \
+ --outheaders-dir=_ipdlheaders \
+ --outcpp-dir=. \
+ $(IPDLDIRS:%=-I%) \
+ $^
+endif
+
diff --git a/ipc/ipdl/ipdl.py b/ipc/ipdl/ipdl.py
new file mode 100755
index 000000000..2220b4f83
--- /dev/null
+++ b/ipc/ipdl/ipdl.py
@@ -0,0 +1,232 @@
+# 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/.
+
+import optparse, os, re, sys
+from cStringIO import StringIO
+from mozbuild.pythonutil import iter_modules_in_path
+import mozpack.path as mozpath
+import itertools
+
+import ipdl
+
+def log(minv, fmt, *args):
+ if _verbosity >= minv:
+ print fmt % args
+
+# process command line
+
+op = optparse.OptionParser(usage='ipdl.py [options] IPDLfiles...')
+op.add_option('-I', '--include', dest='includedirs', default=[ ],
+ action='append',
+ help='Additional directory to search for included protocol specifications')
+op.add_option('-v', '--verbose', dest='verbosity', default=1, action='count',
+ help='Verbose logging (specify -vv or -vvv for very verbose logging)')
+op.add_option('-q', '--quiet', dest='verbosity', action='store_const', const=0,
+ help="Suppress logging output")
+op.add_option('-d', '--outheaders-dir', dest='headersdir', default='.',
+ help="""Directory into which C++ headers will be generated.
+A protocol Foo in the namespace bar will cause the headers
+ dir/bar/Foo.h, dir/bar/FooParent.h, and dir/bar/FooParent.h
+to be generated""")
+op.add_option('-o', '--outcpp-dir', dest='cppdir', default='.',
+ help="""Directory into which C++ sources will be generated
+A protocol Foo in the namespace bar will cause the sources
+ cppdir/FooParent.cpp, cppdir/FooChild.cpp
+to be generated""")
+
+
+options, files = op.parse_args()
+_verbosity = options.verbosity
+headersdir = options.headersdir
+cppdir = options.cppdir
+includedirs = [ os.path.abspath(incdir) for incdir in options.includedirs ]
+
+if not len(files):
+ op.error("No IPDL files specified")
+
+ipcmessagestartpath = os.path.join(headersdir, 'IPCMessageStart.h')
+ipc_msgtype_name_path = os.path.join(cppdir, 'IPCMessageTypeName.cpp')
+
+# Compiling the IPDL files can take a long time, even on a fast machine.
+# Check to see whether we need to do any work.
+latestipdlmod = max(os.stat(f).st_mtime
+ for f in itertools.chain(files,
+ iter_modules_in_path(mozpath.dirname(__file__))))
+
+def outputModTime(f):
+ # A non-existant file is newer than everything.
+ if not os.path.exists(f):
+ return 0
+ return os.stat(f).st_mtime
+
+# Because the IPDL headers are placed into directories reflecting their
+# namespace, collect a list here so we can easily map output names without
+# parsing the actual IPDL files themselves.
+headersmap = {}
+for (path, dirs, headers) in os.walk(headersdir):
+ for h in headers:
+ base = os.path.basename(h)
+ if base in headersmap:
+ root, ext = os.path.splitext(base)
+ print >>sys.stderr, 'A protocol named', root, 'exists in multiple namespaces'
+ sys.exit(1)
+ headersmap[base] = os.path.join(path, h)
+
+def outputfiles(f):
+ base = os.path.basename(f)
+ root, ext = os.path.splitext(base)
+
+ suffixes = ['']
+ if ext == '.ipdl':
+ suffixes += ['Child', 'Parent']
+
+ for suffix in suffixes:
+ yield os.path.join(cppdir, "%s%s.cpp" % (root, suffix))
+ header = "%s%s.h" % (root, suffix)
+ # If the header already exists on disk, use that. Otherwise,
+ # just claim that the header is found in headersdir.
+ if header in headersmap:
+ yield headersmap[header]
+ else:
+ yield os.path.join(headersdir, header)
+
+def alloutputfiles():
+ for f in files:
+ for s in outputfiles(f):
+ yield s
+ yield ipcmessagestartpath
+
+earliestoutputmod = min(outputModTime(f) for f in alloutputfiles())
+
+if latestipdlmod < earliestoutputmod:
+ sys.exit(0)
+
+log(2, 'Generated C++ headers will be generated relative to "%s"', headersdir)
+log(2, 'Generated C++ sources will be generated in "%s"', cppdir)
+
+allmessages = {}
+allprotocols = []
+
+def normalizedFilename(f):
+ if f == '-':
+ return '<stdin>'
+ return f
+
+# First pass: parse and type-check all protocols
+for f in files:
+ log(2, os.path.basename(f))
+ filename = normalizedFilename(f)
+ if f == '-':
+ fd = sys.stdin
+ else:
+ fd = open(f)
+
+ specstring = fd.read()
+ fd.close()
+
+ ast = ipdl.parse(specstring, filename, includedirs=includedirs)
+ if ast is None:
+ print >>sys.stderr, 'Specification could not be parsed.'
+ sys.exit(1)
+
+ log(2, 'checking types')
+ if not ipdl.typecheck(ast):
+ print >>sys.stderr, 'Specification is not well typed.'
+ sys.exit(1)
+
+ if _verbosity > 2:
+ log(3, ' pretty printed code:')
+ ipdl.genipdl(ast, codedir)
+
+# Second pass: generate code
+for f in files:
+ # Read from parser cache
+ filename = normalizedFilename(f)
+ ast = ipdl.parse(None, filename, includedirs=includedirs)
+ ipdl.gencxx(filename, ast, headersdir, cppdir)
+
+ if ast.protocol:
+ allmessages[ast.protocol.name] = ipdl.genmsgenum(ast)
+ allprotocols.append('%sMsgStart' % ast.protocol.name)
+
+allprotocols.sort()
+
+ipcmsgstart = StringIO()
+
+print >>ipcmsgstart, """
+// CODE GENERATED by ipdl.py. Do not edit.
+
+#ifndef IPCMessageStart_h
+#define IPCMessageStart_h
+
+enum IPCMessageStart {
+"""
+
+for name in allprotocols:
+ print >>ipcmsgstart, " %s," % name
+ print >>ipcmsgstart, " %sChild," % name
+
+print >>ipcmsgstart, """
+ LastMsgIndex
+};
+
+static_assert(LastMsgIndex <= 65536, "need to update IPC_MESSAGE_MACRO");
+
+#endif // ifndef IPCMessageStart_h
+"""
+
+ipc_msgtype_name = StringIO()
+print >>ipc_msgtype_name, """
+// CODE GENERATED by ipdl.py. Do not edit.
+#include <cstdint>
+
+#include "IPCMessageStart.h"
+
+using std::uint32_t;
+
+namespace {
+
+enum IPCMessages {
+"""
+
+for protocol in sorted(allmessages.keys()):
+ for (msg, num) in allmessages[protocol].idnums:
+ if num:
+ print >>ipc_msgtype_name, " %s = %s," % (msg, num)
+ elif not msg.endswith('End'):
+ print >>ipc_msgtype_name, " %s__%s," % (protocol, msg)
+
+print >>ipc_msgtype_name, """
+};
+
+} // anonymous namespace
+
+namespace mozilla {
+namespace ipc {
+
+const char* StringFromIPCMessageType(uint32_t aMessageType)
+{
+ switch (aMessageType) {
+"""
+
+for protocol in sorted(allmessages.keys()):
+ for (msg, num) in allmessages[protocol].idnums:
+ if num or msg.endswith('End'):
+ continue
+ print >>ipc_msgtype_name, """
+ case %s__%s:
+ return "%s::%s";""" % (protocol, msg, protocol, msg)
+
+print >>ipc_msgtype_name, """
+ default:
+ return "???";
+ }
+}
+
+} // namespace ipc
+} // namespace mozilla
+"""
+
+ipdl.writeifmodified(ipcmsgstart.getvalue(), ipcmessagestartpath)
+ipdl.writeifmodified(ipc_msgtype_name.getvalue(), ipc_msgtype_name_path)
diff --git a/ipc/ipdl/ipdl/__init__.py b/ipc/ipdl/ipdl/__init__.py
new file mode 100644
index 000000000..d2d883f86
--- /dev/null
+++ b/ipc/ipdl/ipdl/__init__.py
@@ -0,0 +1,77 @@
+# 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/.
+
+__all__ = [ 'gencxx', 'genipdl', 'parse', 'typecheck', 'writeifmodified' ]
+
+import os, sys
+from cStringIO import StringIO
+
+from ipdl.cgen import IPDLCodeGen
+from ipdl.lower import LowerToCxx, msgenums
+from ipdl.parser import Parser
+from ipdl.type import TypeCheck
+
+from ipdl.cxx.cgen import CxxCodeGen
+
+
+def parse(specstring, filename='/stdin', includedirs=[ ], errout=sys.stderr):
+ '''Return an IPDL AST if parsing was successful. Print errors to |errout|
+ if it is not.'''
+ # The file type and name are later enforced by the type checker.
+ # This is just a hint to the parser.
+ prefix, ext = os.path.splitext(filename)
+ name = os.path.basename(prefix)
+ if ext == '.ipdlh':
+ type = 'header'
+ else:
+ type = 'protocol'
+ return Parser(type, name).parse(specstring, os.path.abspath(filename), includedirs, errout)
+
+
+def typecheck(ast, errout=sys.stderr):
+ '''Return True iff |ast| is well typed. Print errors to |errout| if
+ it is not.'''
+ return TypeCheck().check(ast, errout)
+
+
+def gencxx(ipdlfilename, ast, outheadersdir, outcppdir):
+ headers, cpps = LowerToCxx().lower(ast)
+
+ def resolveHeader(hdr):
+ return [
+ hdr,
+ os.path.join(
+ outheadersdir,
+ *([ns.name for ns in ast.namespaces] + [hdr.name]))
+ ]
+ def resolveCpp(cpp):
+ return [ cpp, os.path.join(outcppdir, cpp.name) ]
+
+ for ast, filename in ([ resolveHeader(hdr) for hdr in headers ]
+ + [ resolveCpp(cpp) for cpp in cpps ]):
+ tempfile = StringIO()
+ CxxCodeGen(tempfile).cgen(ast)
+ writeifmodified(tempfile.getvalue(), filename)
+
+
+def genipdl(ast, outdir):
+ return IPDLCodeGen().cgen(ast)
+
+
+def genmsgenum(ast):
+ return msgenums(ast.protocol, pretty=True)
+
+def writeifmodified(contents, file):
+ dir = os.path.dirname(file)
+ os.path.exists(dir) or os.makedirs(dir)
+
+ oldcontents = None
+ if os.path.exists(file):
+ fd = open(file, 'rb')
+ oldcontents = fd.read()
+ fd.close()
+ if oldcontents != contents:
+ fd = open(file, 'wb')
+ fd.write(contents)
+ fd.close()
diff --git a/ipc/ipdl/ipdl/ast.py b/ipc/ipdl/ipdl/ast.py
new file mode 100644
index 000000000..a8bd1e41f
--- /dev/null
+++ b/ipc/ipdl/ipdl/ast.py
@@ -0,0 +1,454 @@
+# 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/.
+
+import sys
+
+NOT_NESTED = 1
+INSIDE_SYNC_NESTED = 2
+INSIDE_CPOW_NESTED = 3
+
+NORMAL_PRIORITY = 1
+HIGH_PRIORITY = 2
+
+class Visitor:
+ def defaultVisit(self, node):
+ raise Exception, "INTERNAL ERROR: no visitor for node type `%s'"% (
+ node.__class__.__name__)
+
+ def visitTranslationUnit(self, tu):
+ for cxxInc in tu.cxxIncludes:
+ cxxInc.accept(self)
+ for inc in tu.includes:
+ inc.accept(self)
+ for su in tu.structsAndUnions:
+ su.accept(self)
+ for using in tu.builtinUsing:
+ using.accept(self)
+ for using in tu.using:
+ using.accept(self)
+ if tu.protocol:
+ tu.protocol.accept(self)
+
+
+ def visitCxxInclude(self, inc):
+ pass
+
+ def visitInclude(self, inc):
+ # Note: we don't visit the child AST here, because that needs delicate
+ # and pass-specific handling
+ pass
+
+ def visitStructDecl(self, struct):
+ for f in struct.fields:
+ f.accept(self)
+
+ def visitStructField(self, field):
+ field.typespec.accept(self)
+
+ def visitUnionDecl(self, union):
+ for t in union.components:
+ t.accept(self)
+
+ def visitUsingStmt(self, using):
+ pass
+
+ def visitProtocol(self, p):
+ for namespace in p.namespaces:
+ namespace.accept(self)
+ for spawns in p.spawnsStmts:
+ spawns.accept(self)
+ for bridges in p.bridgesStmts:
+ bridges.accept(self)
+ for opens in p.opensStmts:
+ opens.accept(self)
+ for mgr in p.managers:
+ mgr.accept(self)
+ for managed in p.managesStmts:
+ managed.accept(self)
+ for msgDecl in p.messageDecls:
+ msgDecl.accept(self)
+ for transitionStmt in p.transitionStmts:
+ transitionStmt.accept(self)
+
+ def visitNamespace(self, ns):
+ pass
+
+ def visitSpawnsStmt(self, spawns):
+ pass
+
+ def visitBridgesStmt(self, bridges):
+ pass
+
+ def visitOpensStmt(self, opens):
+ pass
+
+ def visitManager(self, mgr):
+ pass
+
+ def visitManagesStmt(self, mgs):
+ pass
+
+ def visitMessageDecl(self, md):
+ for inParam in md.inParams:
+ inParam.accept(self)
+ for outParam in md.outParams:
+ outParam.accept(self)
+
+ def visitTransitionStmt(self, ts):
+ ts.state.accept(self)
+ for trans in ts.transitions:
+ trans.accept(self)
+
+ def visitTransition(self, t):
+ for toState in t.toStates:
+ toState.accept(self)
+
+ def visitState(self, s):
+ pass
+
+ def visitParam(self, decl):
+ pass
+
+ def visitTypeSpec(self, ts):
+ pass
+
+ def visitDecl(self, d):
+ pass
+
+class Loc:
+ def __init__(self, filename='<??>', lineno=0):
+ assert filename
+ self.filename = filename
+ self.lineno = lineno
+ def __repr__(self):
+ return '%r:%r'% (self.filename, self.lineno)
+ def __str__(self):
+ return '%s:%s'% (self.filename, self.lineno)
+
+Loc.NONE = Loc(filename='<??>', lineno=0)
+
+class _struct:
+ pass
+
+class Node:
+ def __init__(self, loc=Loc.NONE):
+ self.loc = loc
+
+ def accept(self, visitor):
+ visit = getattr(visitor, 'visit'+ self.__class__.__name__, None)
+ if visit is None:
+ return getattr(visitor, 'defaultVisit')(self)
+ return visit(self)
+
+ def addAttrs(self, attrsName):
+ if not hasattr(self, attrsName):
+ setattr(self, attrsName, _struct())
+
+
+class NamespacedNode(Node):
+ def __init__(self, loc=Loc.NONE, name=None):
+ Node.__init__(self, loc)
+ self.name = name
+ self.namespaces = [ ]
+
+ def addOuterNamespace(self, namespace):
+ self.namespaces.insert(0, namespace)
+
+ def qname(self):
+ return QualifiedId(self.loc, self.name,
+ [ ns.name for ns in self.namespaces ])
+
+class TranslationUnit(NamespacedNode):
+ def __init__(self, type, name):
+ NamespacedNode.__init__(self, name=name)
+ self.filetype = type
+ self.filename = None
+ self.cxxIncludes = [ ]
+ self.includes = [ ]
+ self.builtinUsing = [ ]
+ self.using = [ ]
+ self.structsAndUnions = [ ]
+ self.protocol = None
+
+ def addCxxInclude(self, cxxInclude): self.cxxIncludes.append(cxxInclude)
+ def addInclude(self, inc): self.includes.append(inc)
+ def addStructDecl(self, struct): self.structsAndUnions.append(struct)
+ def addUnionDecl(self, union): self.structsAndUnions.append(union)
+ def addUsingStmt(self, using): self.using.append(using)
+
+ def setProtocol(self, protocol): self.protocol = protocol
+
+class CxxInclude(Node):
+ def __init__(self, loc, cxxFile):
+ Node.__init__(self, loc)
+ self.file = cxxFile
+
+class Include(Node):
+ def __init__(self, loc, type, name):
+ Node.__init__(self, loc)
+ suffix = 'ipdl'
+ if type == 'header':
+ suffix += 'h'
+ self.file = "%s.%s" % (name, suffix)
+
+class UsingStmt(Node):
+ def __init__(self, loc, cxxTypeSpec, cxxHeader=None, kind=None):
+ Node.__init__(self, loc)
+ assert not isinstance(cxxTypeSpec, str)
+ assert cxxHeader is None or isinstance(cxxHeader, str);
+ assert kind is None or kind == 'class' or kind == 'struct'
+ self.type = cxxTypeSpec
+ self.header = cxxHeader
+ self.kind = kind
+ def canBeForwardDeclared(self):
+ return self.isClass() or self.isStruct()
+ def isClass(self):
+ return self.kind == 'class'
+ def isStruct(self):
+ return self.kind == 'struct'
+
+# "singletons"
+class PrettyPrinted:
+ @classmethod
+ def __hash__(cls): return hash(cls.pretty)
+ @classmethod
+ def __str__(cls): return cls.pretty
+
+class ASYNC(PrettyPrinted):
+ pretty = 'async'
+class INTR(PrettyPrinted):
+ pretty = 'intr'
+class SYNC(PrettyPrinted):
+ pretty = 'sync'
+
+class INOUT(PrettyPrinted):
+ pretty = 'inout'
+class IN(PrettyPrinted):
+ pretty = 'in'
+class OUT(PrettyPrinted):
+ pretty = 'out'
+
+
+class Namespace(Node):
+ def __init__(self, loc, namespace):
+ Node.__init__(self, loc)
+ self.name = namespace
+
+class Protocol(NamespacedNode):
+ def __init__(self, loc):
+ NamespacedNode.__init__(self, loc)
+ self.sendSemantics = ASYNC
+ self.nested = NOT_NESTED
+ self.spawnsStmts = [ ]
+ self.bridgesStmts = [ ]
+ self.opensStmts = [ ]
+ self.managers = [ ]
+ self.managesStmts = [ ]
+ self.messageDecls = [ ]
+ self.transitionStmts = [ ]
+ self.startStates = [ ]
+
+class StructField(Node):
+ def __init__(self, loc, type, name):
+ Node.__init__(self, loc)
+ self.typespec = type
+ self.name = name
+
+class StructDecl(NamespacedNode):
+ def __init__(self, loc, name, fields):
+ NamespacedNode.__init__(self, loc, name)
+ self.fields = fields
+
+class UnionDecl(NamespacedNode):
+ def __init__(self, loc, name, components):
+ NamespacedNode.__init__(self, loc, name)
+ self.components = components
+
+class SpawnsStmt(Node):
+ def __init__(self, loc, side, proto, spawnedAs):
+ Node.__init__(self, loc)
+ self.side = side
+ self.proto = proto
+ self.spawnedAs = spawnedAs
+
+class BridgesStmt(Node):
+ def __init__(self, loc, parentSide, childSide):
+ Node.__init__(self, loc)
+ self.parentSide = parentSide
+ self.childSide = childSide
+
+class OpensStmt(Node):
+ def __init__(self, loc, side, proto):
+ Node.__init__(self, loc)
+ self.side = side
+ self.proto = proto
+
+class Manager(Node):
+ def __init__(self, loc, managerName):
+ Node.__init__(self, loc)
+ self.name = managerName
+
+class ManagesStmt(Node):
+ def __init__(self, loc, managedName):
+ Node.__init__(self, loc)
+ self.name = managedName
+
+class MessageDecl(Node):
+ def __init__(self, loc):
+ Node.__init__(self, loc)
+ self.name = None
+ self.sendSemantics = ASYNC
+ self.nested = NOT_NESTED
+ self.prio = NORMAL_PRIORITY
+ self.direction = None
+ self.inParams = [ ]
+ self.outParams = [ ]
+ self.compress = ''
+ self.verify = ''
+
+ def addInParams(self, inParamsList):
+ self.inParams += inParamsList
+
+ def addOutParams(self, outParamsList):
+ self.outParams += outParamsList
+
+ def addModifiers(self, modifiers):
+ for modifier in modifiers:
+ if modifier.startswith('compress'):
+ self.compress = modifier
+ elif modifier == 'verify':
+ self.verify = modifier
+ elif modifier != '':
+ raise Exception, "Unexpected message modifier `%s'"% modifier
+
+class Transition(Node):
+ def __init__(self, loc, trigger, msg, toStates):
+ Node.__init__(self, loc)
+ self.trigger = trigger
+ self.msg = msg
+ self.toStates = toStates
+
+ def __cmp__(self, o):
+ c = cmp(self.msg, o.msg)
+ if c: return c
+ c = cmp(self.trigger, o.trigger)
+ if c: return c
+
+ def __hash__(self): return hash(str(self))
+ def __str__(self): return '%s %s'% (self.trigger, self.msg)
+
+ @staticmethod
+ def nameToTrigger(name):
+ return { 'send': SEND, 'recv': RECV, 'call': CALL, 'answer': ANSWER }[name]
+
+Transition.NULL = Transition(Loc.NONE, None, None, [ ])
+
+class TransitionStmt(Node):
+ def __init__(self, loc, state, transitions):
+ Node.__init__(self, loc)
+ self.state = state
+ self.transitions = transitions
+
+ @staticmethod
+ def makeNullStmt(state):
+ return TransitionStmt(Loc.NONE, state, [ Transition.NULL ])
+
+class SEND:
+ pretty = 'send'
+ @classmethod
+ def __hash__(cls): return hash(cls.pretty)
+ @classmethod
+ def direction(cls): return OUT
+class RECV:
+ pretty = 'recv'
+ @classmethod
+ def __hash__(cls): return hash(cls.pretty)
+ @classmethod
+ def direction(cls): return IN
+class CALL:
+ pretty = 'call'
+ @classmethod
+ def __hash__(cls): return hash(cls.pretty)
+ @classmethod
+ def direction(cls): return OUT
+class ANSWER:
+ pretty = 'answer'
+ @classmethod
+ def __hash__(cls): return hash(cls.pretty)
+ @classmethod
+ def direction(cls): return IN
+
+class State(Node):
+ def __init__(self, loc, name, start=False):
+ Node.__init__(self, loc)
+ self.name = name
+ self.start = start
+ def __eq__(self, o):
+ return (isinstance(o, State)
+ and o.name == self.name
+ and o.start == self.start)
+ def __hash__(self):
+ return hash(repr(self))
+ def __ne__(self, o):
+ return not (self == o)
+ def __repr__(self): return '<State %r start=%r>'% (self.name, self.start)
+ def __str__(self): return '<State %s start=%s>'% (self.name, self.start)
+
+State.ANY = State(Loc.NONE, '[any]', start=True)
+State.DEAD = State(Loc.NONE, '[dead]', start=False)
+State.DYING = State(Loc.NONE, '[dying]', start=False)
+
+class Param(Node):
+ def __init__(self, loc, typespec, name):
+ Node.__init__(self, loc)
+ self.name = name
+ self.typespec = typespec
+
+class TypeSpec(Node):
+ def __init__(self, loc, spec, state=None, array=0, nullable=0,
+ myChmod=None, otherChmod=None):
+ Node.__init__(self, loc)
+ self.spec = spec # QualifiedId
+ self.state = state # None or State
+ self.array = array # bool
+ self.nullable = nullable # bool
+ self.myChmod = myChmod # None or string
+ self.otherChmod = otherChmod # None or string
+
+ def basename(self):
+ return self.spec.baseid
+
+ def isActor(self):
+ return self.state is not None
+
+ def __str__(self): return str(self.spec)
+
+class QualifiedId: # FIXME inherit from node?
+ def __init__(self, loc, baseid, quals=[ ]):
+ assert isinstance(baseid, str)
+ for qual in quals: assert isinstance(qual, str)
+
+ self.loc = loc
+ self.baseid = baseid
+ self.quals = quals
+
+ def qualify(self, id):
+ self.quals.append(self.baseid)
+ self.baseid = id
+
+ def __str__(self):
+ if 0 == len(self.quals):
+ return self.baseid
+ return '::'.join(self.quals) +'::'+ self.baseid
+
+# added by type checking passes
+class Decl(Node):
+ def __init__(self, loc):
+ Node.__init__(self, loc)
+ self.progname = None # what the programmer typed, if relevant
+ self.shortname = None # shortest way to refer to this decl
+ self.fullname = None # full way to refer to this decl
+ self.loc = loc
+ self.type = None
+ self.scope = None
diff --git a/ipc/ipdl/ipdl/builtin.py b/ipc/ipdl/ipdl/builtin.py
new file mode 100644
index 000000000..3a49b5c7e
--- /dev/null
+++ b/ipc/ipdl/ipdl/builtin.py
@@ -0,0 +1,59 @@
+# 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/.
+
+# WARNING: the syntax of the builtin types is not checked, so please
+# don't add something syntactically invalid. It will not be fun to
+# track down the bug.
+
+Types = (
+ # C types
+ 'bool',
+ 'char',
+ 'short',
+ 'int',
+ 'long',
+ 'float',
+ 'double',
+
+ # stdint types
+ 'int8_t',
+ 'uint8_t',
+ 'int16_t',
+ 'uint16_t',
+ 'int32_t',
+ 'uint32_t',
+ 'int64_t',
+ 'uint64_t',
+ 'intptr_t',
+ 'uintptr_t',
+
+ # stddef types
+ 'size_t',
+ 'ssize_t',
+
+ # Mozilla types: "less" standard things we know how serialize/deserialize
+ 'nsresult',
+ 'nsString',
+ 'nsCString',
+ 'mozilla::ipc::Shmem',
+ 'mozilla::ipc::FileDescriptor'
+)
+
+
+HeaderIncludes = (
+ 'mozilla/Attributes.h',
+ 'IPCMessageStart.h',
+ 'ipc/IPCMessageUtils.h',
+ 'mozilla/RefPtr.h',
+ 'nsStringGlue.h',
+ 'nsTArray.h',
+ 'mozilla/ipc/ProtocolUtils.h',
+ 'nsTHashtable.h',
+ 'mozilla/OperatorNewExtensions.h',
+)
+
+CppIncludes = (
+ 'nsIFile.h',
+ 'GeckoProfiler.h',
+)
diff --git a/ipc/ipdl/ipdl/cgen.py b/ipc/ipdl/ipdl/cgen.py
new file mode 100644
index 000000000..fd8951c74
--- /dev/null
+++ b/ipc/ipdl/ipdl/cgen.py
@@ -0,0 +1,101 @@
+# 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/.
+
+import os, sys
+
+from ipdl.ast import Visitor
+from ipdl.ast import IN, OUT, INOUT, ASYNC, SYNC, INTR
+
+class CodePrinter:
+ def __init__(self, outf=sys.stdout, indentCols=4):
+ self.outf = outf
+ self.col = 0
+ self.indentCols = indentCols
+
+ def write(self, str):
+ self.outf.write(str)
+
+ def printdent(self, str=''):
+ self.write((' '* self.col) + str)
+
+ def println(self, str=''):
+ self.write(str +'\n')
+
+ def printdentln(self, str):
+ self.write((' '* self.col) + str +'\n')
+
+ def indent(self): self.col += self.indentCols
+ def dedent(self): self.col -= self.indentCols
+
+
+##-----------------------------------------------------------------------------
+class IPDLCodeGen(CodePrinter, Visitor):
+ '''Spits back out equivalent IPDL to the code that generated this.
+Also known as pretty-printing.'''
+
+ def __init__(self, outf=sys.stdout, indentCols=4, printed=set()):
+ CodePrinter.__init__(self, outf, indentCols)
+ self.printed = printed
+
+ def visitTranslationUnit(self, tu):
+ self.printed.add(tu.filename)
+ self.println('//\n// Automatically generated by ipdlc\n//')
+ CodeGen.visitTranslationUnit(self, tu)
+
+ def visitCxxInclude(self, inc):
+ self.println('include "'+ inc.file +'";')
+
+ def visitProtocolInclude(self, inc):
+ self.println('include protocol "'+ inc.file +'";')
+ if inc.tu.filename not in self.printed:
+ self.println('/* Included file:')
+ IPDLCodeGen(outf=self.outf, indentCols=self.indentCols,
+ printed=self.printed).visitTranslationUnit(inc.tu)
+
+ self.println('*/')
+
+ def visitProtocol(self, p):
+ self.println()
+ for namespace in p.namespaces: namespace.accept(self)
+
+ self.println('%s protocol %s\n{'% (p.sendSemantics[0], p.name))
+ self.indent()
+
+ for mgs in p.managesStmts:
+ mgs.accept(self)
+ if len(p.managesStmts): self.println()
+
+ for msgDecl in p.messageDecls: msgDecl.accept(self)
+ self.println()
+
+ for transStmt in p.transitionStmts: transStmt.accept(self)
+
+ self.dedent()
+ self.println('}')
+ self.write('}\n'* len(p.namespaces))
+
+ def visitManagerStmt(self, mgr):
+ self.printdentln('manager '+ mgr.name +';')
+
+ def visitManagesStmt(self, mgs):
+ self.printdentln('manages '+ mgs.name +';')
+
+ def visitMessageDecl(self, msg):
+ self.printdent('%s %s %s('% (msg.sendSemantics[0], msg.direction[0], msg.name))
+ for i, inp in enumerate(msg.inParams):
+ inp.accept(self)
+ if i != (len(msg.inParams) - 1): self.write(', ')
+ self.write(')')
+ if 0 == len(msg.outParams):
+ self.println(';')
+ return
+
+ self.println()
+ self.indent()
+ self.printdent('returns (')
+ for i, outp in enumerate(msg.outParams):
+ outp.accept(self)
+ if i != (len(msg.outParams) - 1): self.write(', ')
+ self.println(');')
+ self.dedent()
diff --git a/ipc/ipdl/ipdl/cxx/__init__.py b/ipc/ipdl/ipdl/cxx/__init__.py
new file mode 100644
index 000000000..f43d5b0db
--- /dev/null
+++ b/ipc/ipdl/ipdl/cxx/__init__.py
@@ -0,0 +1,6 @@
+# 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/.
+
+import ipdl.cxx.ast
+import ipdl.cxx.cgen
diff --git a/ipc/ipdl/ipdl/cxx/ast.py b/ipc/ipdl/ipdl/cxx/ast.py
new file mode 100644
index 000000000..18c2b3f1d
--- /dev/null
+++ b/ipc/ipdl/ipdl/cxx/ast.py
@@ -0,0 +1,809 @@
+# 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/.
+
+import copy, sys
+
+class Visitor:
+ def defaultVisit(self, node):
+ raise Exception, "INTERNAL ERROR: no visitor for node type `%s'"% (
+ node.__class__.__name__)
+
+ def visitWhitespace(self, ws):
+ pass
+
+ def visitFile(self, f):
+ for thing in f.stuff:
+ thing.accept(self)
+
+ def visitCppDirective(self, ppd):
+ pass
+
+ def visitBlock(self, block):
+ for stmt in block.stmts:
+ stmt.accept(self)
+
+ def visitNamespace(self, ns):
+ self.visitBlock(ns)
+
+ def visitType(self, type):
+ pass
+
+ def visitTypeArray(self, ta):
+ ta.basetype.accept(self)
+ ta.nmemb.accept(self)
+
+ def visitTypeEnum(self, enum):
+ pass
+
+ def visitTypeUnion(self, union):
+ for t, name in union.components:
+ t.accept(self)
+
+ def visitTypedef(self, tdef):
+ tdef.fromtype.accept(self)
+
+ def visitUsing(self, us):
+ us.type.accept(self)
+
+ def visitForwardDecl(self, fd):
+ pass
+
+ def visitDecl(self, decl):
+ decl.type.accept(self)
+
+ def visitParam(self, param):
+ self.visitDecl(param)
+ if param.default is not None:
+ param.default.accept(self)
+
+ def visitClass(self, cls):
+ for inherit in cls.inherits:
+ inherit.accept(self)
+ self.visitBlock(cls)
+
+ def visitInherit(self, inh):
+ pass
+
+ def visitFriendClassDecl(self, fcd):
+ pass
+
+ def visitMethodDecl(self, meth):
+ for param in meth.params:
+ param.accept(self)
+ if meth.ret is not None:
+ meth.ret.accept(self)
+ if meth.typeop is not None:
+ meth.typeop.accept(self)
+ if meth.T is not None:
+ meth.T.accept(self)
+
+ def visitMethodDefn(self, meth):
+ meth.decl.accept(self)
+ self.visitBlock(meth)
+
+ def visitFunctionDecl(self, fun):
+ self.visitMethodDecl(fun)
+
+ def visitFunctionDefn(self, fd):
+ self.visitMethodDefn(fd)
+
+ def visitConstructorDecl(self, ctor):
+ self.visitMethodDecl(ctor)
+
+ def visitConstructorDefn(self, cd):
+ cd.decl.accept(self)
+ for init in cd.memberinits:
+ init.accept(self)
+ self.visitBlock(cd)
+
+ def visitDestructorDecl(self, dtor):
+ self.visitMethodDecl(dtor)
+
+ def visitDestructorDefn(self, dd):
+ dd.decl.accept(self)
+ self.visitBlock(dd)
+
+ def visitExprLiteral(self, l):
+ pass
+
+ def visitExprVar(self, v):
+ pass
+
+ def visitExprPrefixUnop(self, e):
+ e.expr.accept(self)
+
+ def visitExprBinary(self, e):
+ e.left.accept(self)
+ e.right.accept(self)
+
+ def visitExprConditional(self, c):
+ c.cond.accept(self)
+ c.ife.accept(self)
+ c.elsee.accept(self)
+
+ def visitExprAddrOf(self, eao):
+ self.visitExprPrefixUnop(eao)
+
+ def visitExprDeref(self, ed):
+ self.visitExprPrefixUnop(ed)
+
+ def visitExprNot(self, en):
+ self.visitExprPrefixUnop(en)
+
+ def visitExprCast(self, ec):
+ ec.expr.accept(self)
+
+ def visitExprIndex(self, ei):
+ ei.arr.accept(self)
+ ei.idx.accept(self)
+
+ def visitExprSelect(self, es):
+ es.obj.accept(self)
+
+ def visitExprAssn(self, ea):
+ ea.lhs.accept(self)
+ ea.rhs.accept(self)
+
+ def visitExprCall(self, ec):
+ ec.func.accept(self)
+ for arg in ec.args:
+ arg.accept(self)
+
+ def visitExprNew(self, en):
+ en.ctype.accept(self)
+ if en.newargs is not None:
+ for arg in en.newargs:
+ arg.accept(self)
+ if en.args is not None:
+ for arg in en.args:
+ arg.accept(self)
+
+ def visitExprDelete(self, ed):
+ ed.obj.accept(self)
+
+ def visitExprMemberInit(self, minit):
+ self.visitExprCall(minit)
+
+ def visitExprSizeof(self, es):
+ self.visitExprCall(es)
+
+ def visitStmtBlock(self, sb):
+ self.visitBlock(sb)
+
+ def visitStmtDecl(self, sd):
+ sd.decl.accept(self)
+ if sd.init is not None:
+ sd.init.accept(self)
+
+ def visitLabel(self, label):
+ pass
+
+ def visitCaseLabel(self, case):
+ pass
+
+ def visitDefaultLabel(self, dl):
+ pass
+
+ def visitStmtIf(self, si):
+ si.cond.accept(self)
+ si.ifb.accept(self)
+ if si.elseb is not None:
+ si.elseb.accept(self)
+
+ def visitStmtFor(self, sf):
+ if sf.init is not None:
+ sf.init.accept(self)
+ if sf.cond is not None:
+ sf.cond.accept(self)
+ if sf.update is not None:
+ sf.update.accept(self)
+
+ def visitStmtSwitch(self, ss):
+ ss.expr.accept(self)
+ self.visitBlock(ss)
+
+ def visitStmtBreak(self, sb):
+ pass
+
+ def visitStmtExpr(self, se):
+ se.expr.accept(self)
+
+ def visitStmtReturn(self, sr):
+ if sr.expr is not None:
+ sr.expr.accept(self)
+
+##------------------------------
+class Node:
+ def __init__(self):
+ pass
+
+ def accept(self, visitor):
+ visit = getattr(visitor, 'visit'+ self.__class__.__name__, None)
+ if visit is None:
+ return getattr(visitor, 'defaultVisit')(self)
+ return visit(self)
+
+class Whitespace(Node):
+ # yes, this is silly. but we need to stick comments in the
+ # generated code without resorting to more serious hacks
+ def __init__(self, ws, indent=0):
+ Node.__init__(self)
+ self.ws = ws
+ self.indent = indent
+Whitespace.NL = Whitespace('\n')
+
+class File(Node):
+ def __init__(self, filename):
+ Node.__init__(self)
+ self.name = filename
+ # array of stuff in the file --- stmts and preprocessor thingies
+ self.stuff = [ ]
+
+ def addthing(self, thing):
+ assert thing is not None
+ assert not isinstance(thing, list)
+ self.stuff.append(thing)
+
+ def addthings(self, things):
+ for t in things: self.addthing(t)
+
+ # "look like" a Block so code doesn't have to care whether they're
+ # in global scope or not
+ def addstmt(self, stmt):
+ assert stmt is not None
+ assert not isinstance(stmt, list)
+ self.stuff.append(stmt)
+
+ def addstmts(self, stmts):
+ for s in stmts: self.addstmt(s)
+
+class CppDirective(Node):
+ '''represents |#[directive] [rest]|, where |rest| is any string'''
+ def __init__(self, directive, rest=None):
+ Node.__init__(self)
+ self.directive = directive
+ self.rest = rest
+
+class Block(Node):
+ def __init__(self):
+ Node.__init__(self)
+ self.stmts = [ ]
+
+ def addstmt(self, stmt):
+ assert stmt is not None
+ assert not isinstance(stmt, tuple)
+ self.stmts.append(stmt)
+
+ def addstmts(self, stmts):
+ for s in stmts: self.addstmt(s)
+
+##------------------------------
+# type and decl thingies
+class Namespace(Block):
+ def __init__(self, name):
+ assert isinstance(name, str)
+
+ Block.__init__(self)
+ self.name = name
+
+class Type(Node):
+ def __init__(self, name, const=0,
+ ptr=0, ptrconst=0, ptrptr=0, ptrconstptr=0,
+ ref=0,
+ hasimplicitcopyctor=True,
+ T=None):
+ """
+To avoid getting fancy with recursive types, we limit the kinds
+of pointer types that can be be constructed.
+
+ ptr => T*
+ ptrconst => T* const
+ ptrptr => T**
+ ptrconstptr => T* const*
+
+Any type, naked or pointer, can be const (const T) or ref (T&).
+"""
+ assert isinstance(name, str)
+ assert not isinstance(const, str)
+ assert not isinstance(T, str)
+
+ Node.__init__(self)
+ self.name = name
+ self.const = const
+ self.ptr = ptr
+ self.ptrconst = ptrconst
+ self.ptrptr = ptrptr
+ self.ptrconstptr = ptrconstptr
+ self.ref = ref
+ self.hasimplicitcopyctor = hasimplicitcopyctor
+ self.T = T
+ # XXX could get serious here with recursive types, but shouldn't
+ # need that for this codegen
+ def __deepcopy__(self, memo):
+ return Type(self.name,
+ const=self.const,
+ ptr=self.ptr, ptrconst=self.ptrconst,
+ ptrptr=self.ptrptr, ptrconstptr=self.ptrconstptr,
+ ref=self.ref,
+ T=copy.deepcopy(self.T, memo))
+Type.BOOL = Type('bool')
+Type.INT = Type('int')
+Type.INT32 = Type('int32_t')
+Type.INTPTR = Type('intptr_t')
+Type.NSRESULT = Type('nsresult')
+Type.UINT32 = Type('uint32_t')
+Type.UINT32PTR = Type('uint32_t', ptr=1)
+Type.SIZE = Type('size_t')
+Type.VOID = Type('void')
+Type.VOIDPTR = Type('void', ptr=1)
+Type.AUTO = Type('auto')
+
+class TypeArray(Node):
+ def __init__(self, basetype, nmemb):
+ '''the type |basetype DECLNAME[nmemb]|. |nmemb| is an Expr'''
+ self.basetype = basetype
+ self.nmemb = nmemb
+ def __deepcopy__(self, memo):
+ return TypeArray(deepcopy(self.basetype, memo), nmemb)
+
+class TypeEnum(Node):
+ def __init__(self, name=None):
+ '''name can be None'''
+ Node.__init__(self)
+ self.name = name
+ self.idnums = [ ] # pairs of ('Foo', [num]) or ('Foo', None)
+
+ def addId(self, id, num=None):
+ self.idnums.append((id, num))
+
+class TypeUnion(Node):
+ def __init__(self, name=None):
+ Node.__init__(self)
+ self.name = name
+ self.components = [ ] # [ Decl ]
+
+ def addComponent(self, type, name):
+ self.components.append(Decl(type, name))
+
+class Typedef(Node):
+ def __init__(self, fromtype, totypename, templateargs=[]):
+ assert isinstance(totypename, str)
+
+ Node.__init__(self)
+ self.fromtype = fromtype
+ self.totypename = totypename
+ self.templateargs = templateargs
+
+ def __cmp__(self, o):
+ return cmp(self.totypename, o.totypename)
+ def __eq__(self, o):
+ return (self.__class__ == o.__class__
+ and 0 == cmp(self, o))
+ def __hash__(self):
+ return hash(self.totypename)
+
+class Using(Node):
+ def __init__(self, type):
+ Node.__init__(self)
+ self.type = type
+
+class ForwardDecl(Node):
+ def __init__(self, pqname, cls=0, struct=0):
+ assert (not cls and struct) or (cls and not struct)
+
+ self.pqname = pqname
+ self.cls = cls
+ self.struct = struct
+
+class Decl(Node):
+ '''represents |Foo bar|, e.g. in a function signature'''
+ def __init__(self, type, name):
+ assert type is not None
+ assert not isinstance(type, str)
+ assert isinstance(name, str)
+
+ Node.__init__(self)
+ self.type = type
+ self.name = name
+ def __deepcopy__(self, memo):
+ return Decl(copy.deepcopy(self.type, memo), self.name)
+
+class Param(Decl):
+ def __init__(self, type, name, default=None):
+ Decl.__init__(self, type, name)
+ self.default = default
+ def __deepcopy__(self, memo):
+ return Param(copy.deepcopy(self.type, memo), self.name,
+ copy.deepcopy(self.default, memo))
+
+##------------------------------
+# class stuff
+class Class(Block):
+ def __init__(self, name, inherits=[ ],
+ interface=0, abstract=0, final=0,
+ specializes=None, struct=0):
+ assert not (interface and abstract)
+ assert not (abstract and final)
+ assert not (interface and final)
+ assert not (inherits and specializes)
+
+ Block.__init__(self)
+ self.name = name
+ self.inherits = inherits # [ Type ]
+ self.interface = interface # bool
+ self.abstract = abstract # bool
+ self.final = final # bool
+ self.specializes = specializes # Type or None
+ self.struct = struct # bool
+
+class Inherit(Node):
+ def __init__(self, type, viz='public'):
+ assert isinstance(viz, str)
+ Node.__init__(self)
+ self.type = type
+ self.viz = viz
+
+class FriendClassDecl(Node):
+ def __init__(self, friend):
+ Node.__init__(self)
+ self.friend = friend
+
+class MethodDecl(Node):
+ def __init__(self, name, params=[ ], ret=Type('void'),
+ virtual=0, const=0, pure=0, static=0, warn_unused=0,
+ inline=0, force_inline=0, never_inline=0,
+ typeop=None,
+ T=None):
+ assert not (virtual and static)
+ assert not pure or virtual # pure => virtual
+ assert not (static and typeop)
+ assert not (name and typeop)
+ assert name is None or isinstance(name, str)
+ assert not isinstance(ret, list)
+ for decl in params: assert not isinstance(decl, str)
+ assert not isinstance(T, int)
+ assert not (inline and never_inline)
+ assert not (force_inline and never_inline)
+
+ if typeop is not None:
+ ret = None
+
+ Node.__init__(self)
+ self.name = name
+ self.params = params # [ Param ]
+ self.ret = ret # Type or None
+ self.virtual = virtual # bool
+ self.const = const # bool
+ self.pure = pure # bool
+ self.static = static # bool
+ self.warn_unused = warn_unused # bool
+ self.force_inline = (force_inline or T) # bool
+ self.inline = inline # bool
+ self.never_inline = never_inline # bool
+ self.typeop = typeop # Type or None
+ self.T = T # Type or None
+ self.only_for_definition = False
+
+ def __deepcopy__(self, memo):
+ return MethodDecl(
+ self.name,
+ params=copy.deepcopy(self.params, memo),
+ ret=copy.deepcopy(self.ret, memo),
+ virtual=self.virtual,
+ const=self.const,
+ pure=self.pure,
+ static=self.static,
+ warn_unused=self.warn_unused,
+ inline=self.inline,
+ force_inline=self.force_inline,
+ never_inline=self.never_inline,
+ typeop=copy.deepcopy(self.typeop, memo),
+ T=copy.deepcopy(self.T, memo))
+
+class MethodDefn(Block):
+ def __init__(self, decl):
+ Block.__init__(self)
+ self.decl = decl
+
+class FunctionDecl(MethodDecl):
+ def __init__(self, name, params=[ ], ret=Type('void'),
+ static=0, warn_unused=0,
+ inline=0, force_inline=0,
+ T=None):
+ MethodDecl.__init__(self, name, params=params, ret=ret,
+ static=static, warn_unused=warn_unused,
+ inline=inline, force_inline=force_inline,
+ T=T)
+
+class FunctionDefn(MethodDefn):
+ def __init__(self, decl):
+ MethodDefn.__init__(self, decl)
+
+class ConstructorDecl(MethodDecl):
+ def __init__(self, name, params=[ ], explicit=0, force_inline=0):
+ MethodDecl.__init__(self, name, params=params, ret=None,
+ force_inline=force_inline)
+ self.explicit = explicit
+
+ def __deepcopy__(self, memo):
+ return ConstructorDecl(self.name,
+ copy.deepcopy(self.params, memo),
+ self.explicit)
+
+class ConstructorDefn(MethodDefn):
+ def __init__(self, decl, memberinits=[ ]):
+ MethodDefn.__init__(self, decl)
+ self.memberinits = memberinits
+
+class DestructorDecl(MethodDecl):
+ def __init__(self, name, virtual=0, force_inline=0, inline=0):
+ MethodDecl.__init__(self, name, params=[ ], ret=None,
+ virtual=virtual,
+ force_inline=force_inline, inline=inline)
+
+ def __deepcopy__(self, memo):
+ return DestructorDecl(self.name,
+ virtual=self.virtual,
+ force_inline=self.force_inline,
+ inline=self.inline)
+
+
+class DestructorDefn(MethodDefn):
+ def __init__(self, decl): MethodDefn.__init__(self, decl)
+
+##------------------------------
+# expressions
+class ExprVar(Node):
+ def __init__(self, name):
+ assert isinstance(name, str)
+
+ Node.__init__(self)
+ self.name = name
+ExprVar.THIS = ExprVar('this')
+
+class ExprLiteral(Node):
+ def __init__(self, value, type):
+ '''|type| is a Python format specifier; 'd' for example'''
+ Node.__init__(self)
+ self.value = value
+ self.type = type
+
+ @staticmethod
+ def Int(i): return ExprLiteral(i, 'd')
+
+ @staticmethod
+ def String(s): return ExprLiteral('"'+ s +'"', 's')
+
+ @staticmethod
+ def WString(s): return ExprLiteral('L"'+ s +'"', 's')
+
+ def __str__(self):
+ return ('%'+ self.type)% (self.value)
+ExprLiteral.ZERO = ExprLiteral.Int(0)
+ExprLiteral.ONE = ExprLiteral.Int(1)
+ExprLiteral.NULL = ExprVar('nullptr')
+ExprLiteral.TRUE = ExprVar('true')
+ExprLiteral.FALSE = ExprVar('false')
+
+class ExprPrefixUnop(Node):
+ def __init__(self, expr, op):
+ assert not isinstance(expr, tuple)
+ self.expr = expr
+ self.op = op
+
+class ExprNot(ExprPrefixUnop):
+ def __init__(self, expr):
+ ExprPrefixUnop.__init__(self, expr, '!')
+
+class ExprAddrOf(ExprPrefixUnop):
+ def __init__(self, expr):
+ ExprPrefixUnop.__init__(self, expr, '&')
+
+class ExprDeref(ExprPrefixUnop):
+ def __init__(self, expr):
+ ExprPrefixUnop.__init__(self, expr, '*')
+
+class ExprCast(Node):
+ def __init__(self, expr, type,
+ dynamic=0, static=0, reinterpret=0, const=0, C=0):
+ assert 1 == reduce(lambda a, x: a+x, [ dynamic, static, reinterpret, const, C ])
+
+ Node.__init__(self)
+ self.expr = expr
+ self.type = type
+ self.dynamic = dynamic
+ self.static = static
+ self.reinterpret = reinterpret
+ self.const = const
+ self.C = C
+
+class ExprBinary(Node):
+ def __init__(self, left, op, right):
+ Node.__init__(self)
+ self.left = left
+ self.op = op
+ self.right = right
+
+class ExprConditional(Node):
+ def __init__(self, cond, ife, elsee):
+ Node.__init__(self)
+ self.cond = cond
+ self.ife = ife
+ self.elsee = elsee
+
+class ExprIndex(Node):
+ def __init__(self, arr, idx):
+ Node.__init__(self)
+ self.arr = arr
+ self.idx = idx
+
+class ExprSelect(Node):
+ def __init__(self, obj, op, field):
+ assert obj and op and field
+ assert not isinstance(obj, str)
+ assert isinstance(field, str)
+
+ Node.__init__(self)
+ self.obj = obj
+ self.op = op
+ self.field = field
+
+class ExprAssn(Node):
+ def __init__(self, lhs, rhs, op='='):
+ Node.__init__(self)
+ self.lhs = lhs
+ self.op = op
+ self.rhs = rhs
+
+class ExprCall(Node):
+ def __init__(self, func, args=[ ]):
+ assert hasattr(func, 'accept')
+ assert isinstance(args, list)
+ for arg in args: assert arg and not isinstance(arg, str)
+
+ Node.__init__(self)
+ self.func = func
+ self.args = args
+
+class ExprMove(ExprCall):
+ def __init__(self, arg):
+ ExprCall.__init__(self, ExprVar("mozilla::Move"), args=[arg])
+
+class ExprNew(Node):
+ # XXX taking some poetic license ...
+ def __init__(self, ctype, args=[ ], newargs=None):
+ assert not (ctype.const or ctype.ref)
+
+ Node.__init__(self)
+ self.ctype = ctype
+ self.args = args
+ self.newargs = newargs
+
+class ExprDelete(Node):
+ def __init__(self, obj):
+ Node.__init__(self)
+ self.obj = obj
+
+class ExprMemberInit(ExprCall):
+ def __init__(self, member, args=[ ]):
+ ExprCall.__init__(self, member, args)
+
+class ExprSizeof(ExprCall):
+ def __init__(self, t):
+ ExprCall.__init__(self, ExprVar('sizeof'), [ t ])
+
+##------------------------------
+# statements etc.
+class StmtBlock(Block):
+ def __init__(self, stmts=[ ]):
+ Block.__init__(self)
+ self.addstmts(stmts)
+
+class StmtDecl(Node):
+ def __init__(self, decl, init=None, initargs=None):
+ assert not (init and initargs)
+ assert not isinstance(init, str) # easy to confuse with Decl
+ assert not isinstance(init, list)
+ assert not isinstance(decl, tuple)
+
+ Node.__init__(self)
+ self.decl = decl
+ self.init = init
+ self.initargs = initargs
+
+class Label(Node):
+ def __init__(self, name):
+ Node.__init__(self)
+ self.name = name
+Label.PUBLIC = Label('public')
+Label.PROTECTED = Label('protected')
+Label.PRIVATE = Label('private')
+
+class CaseLabel(Node):
+ def __init__(self, name):
+ Node.__init__(self)
+ self.name = name
+
+class DefaultLabel(Node):
+ def __init__(self):
+ Node.__init__(self)
+
+class StmtIf(Node):
+ def __init__(self, cond):
+ Node.__init__(self)
+ self.cond = cond
+ self.ifb = Block()
+ self.elseb = None
+
+ def addifstmt(self, stmt):
+ self.ifb.addstmt(stmt)
+
+ def addifstmts(self, stmts):
+ self.ifb.addstmts(stmts)
+
+ def addelsestmt(self, stmt):
+ if self.elseb is None: self.elseb = Block()
+ self.elseb.addstmt(stmt)
+
+ def addelsestmts(self, stmts):
+ if self.elseb is None: self.elseb = Block()
+ self.elseb.addstmts(stmts)
+
+class StmtFor(Block):
+ def __init__(self, init=None, cond=None, update=None):
+ Block.__init__(self)
+ self.init = init
+ self.cond = cond
+ self.update = update
+
+class StmtRangedFor(Block):
+ def __init__(self, var, iteree):
+ assert isinstance(var, ExprVar)
+ assert iteree
+
+ Block.__init__(self)
+ self.var = var
+ self.iteree = iteree
+
+class StmtSwitch(Block):
+ def __init__(self, expr):
+ Block.__init__(self)
+ self.expr = expr
+ self.nr_cases = 0
+
+ def addcase(self, case, block):
+ '''NOTE: |case| is not checked for uniqueness'''
+ assert not isinstance(case, str)
+ assert (isinstance(block, StmtBreak)
+ or isinstance(block, StmtReturn)
+ or isinstance(block, StmtSwitch)
+ or (hasattr(block, 'stmts')
+ and (isinstance(block.stmts[-1], StmtBreak)
+ or isinstance(block.stmts[-1], StmtReturn))))
+ self.addstmt(case)
+ self.addstmt(block)
+ self.nr_cases += 1
+
+ def addfallthrough(self, case):
+ self.addstmt(case)
+ self.nr_cases += 1
+
+class StmtBreak(Node):
+ def __init__(self):
+ Node.__init__(self)
+
+class StmtExpr(Node):
+ def __init__(self, expr):
+ assert expr is not None
+
+ Node.__init__(self)
+ self.expr = expr
+
+class StmtReturn(Node):
+ def __init__(self, expr=None):
+ Node.__init__(self)
+ self.expr = expr
+
+StmtReturn.TRUE = StmtReturn(ExprLiteral.TRUE)
+StmtReturn.FALSE = StmtReturn(ExprLiteral.FALSE)
diff --git a/ipc/ipdl/ipdl/cxx/cgen.py b/ipc/ipdl/ipdl/cxx/cgen.py
new file mode 100644
index 000000000..30f2f2bca
--- /dev/null
+++ b/ipc/ipdl/ipdl/cxx/cgen.py
@@ -0,0 +1,520 @@
+# 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/.
+
+import sys
+
+from ipdl.cgen import CodePrinter
+from ipdl.cxx.ast import TypeArray, Visitor
+
+class CxxCodeGen(CodePrinter, Visitor):
+ def __init__(self, outf=sys.stdout, indentCols=4):
+ CodePrinter.__init__(self, outf, indentCols)
+
+ def cgen(self, cxxfile):
+ cxxfile.accept(self)
+
+ def visitWhitespace(self, ws):
+ if ws.indent:
+ self.printdent('')
+ self.write(ws.ws)
+
+ def visitCppDirective(self, cd):
+ if cd.rest:
+ self.println('#%s %s'% (cd.directive, cd.rest))
+ else:
+ self.println('#%s'% (cd.directive))
+
+ def visitNamespace(self, ns):
+ self.println('namespace '+ ns.name +' {')
+ self.visitBlock(ns)
+ self.println('} // namespace '+ ns.name)
+
+ def visitType(self, t):
+ if t.const:
+ self.write('const ')
+
+ self.write(t.name)
+
+ if t.T is not None:
+ self.write('<')
+ t.T.accept(self)
+ self.write('>')
+
+ ts = ''
+ if t.ptr: ts += '*'
+ elif t.ptrconst: ts += '* const'
+ elif t.ptrptr: ts += '**'
+ elif t.ptrconstptr: ts += '* const*'
+
+ ts += '&' * t.ref
+
+ self.write(ts)
+
+ def visitTypeEnum(self, te):
+ self.write('enum')
+ if te.name:
+ self.write(' '+ te.name)
+ self.println(' {')
+
+ self.indent()
+ nids = len(te.idnums)
+ for i, (id, num) in enumerate(te.idnums):
+ self.printdent(id)
+ if num:
+ self.write(' = '+ str(num))
+ if i != (nids-1):
+ self.write(',')
+ self.println()
+ self.dedent()
+ self.printdent('}')
+
+ def visitTypeUnion(self, u):
+ self.write('union')
+ if u.name:
+ self.write(' '+ u.name)
+ self.println(' {')
+
+ self.indent()
+ for decl in u.components:
+ self.printdent()
+ decl.accept(self)
+ self.println(';')
+ self.dedent()
+
+ self.printdent('}')
+
+
+ def visitTypedef(self, td):
+ if td.templateargs:
+ formals = ', '.join([ 'class ' + T for T in td.templateargs ])
+ args = ', '.join(td.templateargs)
+ self.printdent('template<' + formals + '> using ' + td.totypename + ' = ')
+ td.fromtype.accept(self)
+ self.println('<' + args + '>;')
+ else:
+ self.printdent('typedef ')
+ td.fromtype.accept(self)
+ self.println(' '+ td.totypename +';')
+
+ def visitUsing(self, us):
+ self.printdent('using ')
+ us.type.accept(self)
+ self.println(';')
+
+ def visitForwardDecl(self, fd):
+ if fd.cls: self.printdent('class ')
+ elif fd.struct: self.printdent('struct ')
+ self.write(str(fd.pqname))
+ self.println(';')
+
+ def visitDecl(self, d):
+ # C-syntax arrays make code generation much more annoying
+ if isinstance(d.type, TypeArray):
+ d.type.basetype.accept(self)
+ else:
+ d.type.accept(self)
+
+ if d.name:
+ self.write(' '+ d.name)
+
+ if isinstance(d.type, TypeArray):
+ self.write('[')
+ d.type.nmemb.accept(self)
+ self.write(']')
+
+ def visitParam(self, p):
+ self.visitDecl(p)
+ if p.default is not None:
+ self.write(' = ')
+ p.default.accept(self)
+
+ def visitClass(self, c):
+ if c.specializes is not None:
+ self.printdentln('template<>')
+
+ if c.struct:
+ self.printdent('struct')
+ else:
+ self.printdent('class')
+ self.write(' '+ c.name)
+ if c.final:
+ self.write(' final')
+
+ if c.specializes is not None:
+ self.write(' <')
+ c.specializes.accept(self)
+ self.write('>')
+
+ ninh = len(c.inherits)
+ if 0 < ninh:
+ self.println(' :')
+ self.indent()
+ for i, inherit in enumerate(c.inherits):
+ self.printdent()
+ inherit.accept(self)
+ if i != (ninh - 1):
+ self.println(',')
+ self.dedent()
+ self.println()
+
+ self.printdentln('{')
+ self.indent()
+
+ self.visitBlock(c)
+
+ self.dedent()
+ self.printdentln('};')
+
+ def visitInherit(self, inh):
+ self.write(inh.viz +' ')
+ inh.type.accept(self)
+
+ def visitFriendClassDecl(self, fcd):
+ self.printdentln('friend class '+ fcd.friend +';')
+
+
+ def visitMethodDecl(self, md):
+ assert not (md.static and md.virtual)
+
+ if md.T:
+ self.write('template<')
+ self.write('typename ')
+ md.T.accept(self)
+ self.println('>')
+ self.printdent()
+
+ if md.warn_unused:
+ self.write('MOZ_MUST_USE ')
+ if md.inline:
+ self.write('inline ')
+ if md.never_inline:
+ self.write('MOZ_NEVER_INLINE ')
+ if md.static:
+ self.write('static ')
+ if md.virtual:
+ self.write('virtual ')
+ if md.ret:
+ if md.only_for_definition:
+ self.write('auto ')
+ else:
+ md.ret.accept(self)
+ self.println()
+ self.printdent()
+ if md.typeop is not None:
+ self.write('operator ')
+ md.typeop.accept(self)
+ else:
+ self.write(md.name)
+
+ self.write('(')
+ self.writeDeclList(md.params)
+ self.write(')')
+
+ if md.const:
+ self.write(' const')
+ if md.ret and md.only_for_definition:
+ self.write(' -> ')
+ md.ret.accept(self)
+ if md.pure:
+ self.write(' = 0')
+
+
+ def visitMethodDefn(self, md):
+ if md.decl.pure:
+ return
+
+ self.printdent()
+ md.decl.accept(self)
+ self.println()
+
+ self.printdentln('{')
+ self.indent()
+ self.visitBlock(md)
+ self.dedent()
+ self.printdentln('}')
+
+
+ def visitConstructorDecl(self, cd):
+ if cd.explicit:
+ self.write('explicit ')
+ else:
+ self.write('MOZ_IMPLICIT ')
+ self.visitMethodDecl(cd)
+
+ def visitConstructorDefn(self, cd):
+ self.printdent()
+ cd.decl.accept(self)
+ if len(cd.memberinits):
+ self.println(' :')
+ self.indent()
+ ninits = len(cd.memberinits)
+ for i, init in enumerate(cd.memberinits):
+ self.printdent()
+ init.accept(self)
+ if i != (ninits-1):
+ self.println(',')
+ self.dedent()
+ self.println()
+
+ self.printdentln('{')
+ self.indent()
+
+ self.visitBlock(cd)
+
+ self.dedent()
+ self.printdentln('}')
+
+
+ def visitDestructorDecl(self, dd):
+ if dd.inline:
+ self.write('inline ')
+ if dd.virtual:
+ self.write('virtual ')
+
+ # hack alert
+ parts = dd.name.split('::')
+ parts[-1] = '~'+ parts[-1]
+
+ self.write('::'.join(parts) +'()')
+
+ def visitDestructorDefn(self, dd):
+ self.printdent()
+ dd.decl.accept(self)
+ self.println()
+
+ self.printdentln('{')
+ self.indent()
+
+ self.visitBlock(dd)
+
+ self.dedent()
+ self.printdentln('}')
+
+
+ def visitExprLiteral(self, el):
+ self.write(str(el))
+
+ def visitExprVar(self, ev):
+ self.write(ev.name)
+
+ def visitExprPrefixUnop(self, e):
+ self.write('(')
+ self.write(e.op)
+ self.write('(')
+ e.expr.accept(self)
+ self.write(')')
+ self.write(')')
+
+ def visitExprCast(self, c):
+ pfx, sfx = '', ''
+ if c.dynamic: pfx, sfx = 'dynamic_cast<', '>'
+ elif c.static: pfx, sfx = 'static_cast<', '>'
+ elif c.reinterpret: pfx, sfx = 'reinterpret_cast<', '>'
+ elif c.const: pfx, sfx = 'const_cast<', '>'
+ elif c.C: pfx, sfx = '(', ')'
+ self.write(pfx)
+ c.type.accept(self)
+ self.write(sfx +'(')
+ c.expr.accept(self)
+ self.write(')')
+
+ def visitExprBinary(self, e):
+ self.write('(')
+ e.left.accept(self)
+ self.write(') '+ e.op +' (')
+ e.right.accept(self)
+ self.write(')')
+
+ def visitExprConditional(self, c):
+ self.write('(')
+ c.cond.accept(self)
+ self.write(' ? ')
+ c.ife.accept(self)
+ self.write(' : ')
+ c.elsee.accept(self)
+ self.write(')')
+
+ def visitExprIndex(self, ei):
+ ei.arr.accept(self)
+ self.write('[')
+ ei.idx.accept(self)
+ self.write(']')
+
+ def visitExprSelect(self, es):
+ self.write('(')
+ es.obj.accept(self)
+ self.write(')')
+ self.write(es.op + es.field)
+
+ def visitExprAssn(self, ea):
+ ea.lhs.accept(self)
+ self.write(' '+ ea.op +' ')
+ ea.rhs.accept(self)
+
+ def visitExprCall(self, ec):
+ ec.func.accept(self)
+ self.write('(')
+ self.writeExprList(ec.args)
+ self.write(')')
+
+ def visitExprMove(self, em):
+ self.visitExprCall(em)
+
+ def visitExprNew(self, en):
+ self.write('new ')
+ if en.newargs is not None:
+ self.write('(')
+ self.writeExprList(en.newargs)
+ self.write(') ')
+ en.ctype.accept(self)
+ if en.args is not None:
+ self.write('(')
+ self.writeExprList(en.args)
+ self.write(')')
+
+ def visitExprDelete(self, ed):
+ self.write('delete ')
+ ed.obj.accept(self)
+
+
+ def visitStmtBlock(self, b):
+ self.printdentln('{')
+ self.indent()
+ self.visitBlock(b)
+ self.dedent()
+ self.printdentln('}')
+
+ def visitLabel(self, label):
+ self.dedent() # better not be at global scope ...
+ self.printdentln(label.name +':')
+ self.indent()
+
+ def visitCaseLabel(self, cl):
+ self.dedent()
+ self.printdentln('case '+ cl.name +':')
+ self.indent()
+
+ def visitDefaultLabel(self, dl):
+ self.dedent()
+ self.printdentln('default:')
+ self.indent()
+
+
+ def visitStmtIf(self, si):
+ self.printdent('if (')
+ si.cond.accept(self)
+ self.println(') {')
+ self.indent()
+ si.ifb.accept(self)
+ self.dedent()
+ self.printdentln('}')
+
+ if si.elseb is not None:
+ self.printdentln('else {')
+ self.indent()
+ si.elseb.accept(self)
+ self.dedent()
+ self.printdentln('}')
+
+
+ def visitStmtFor(self, sf):
+ self.printdent('for (')
+ if sf.init is not None:
+ sf.init.accept(self)
+ self.write('; ')
+ if sf.cond is not None:
+ sf.cond.accept(self)
+ self.write('; ')
+ if sf.update is not None:
+ sf.update.accept(self)
+ self.println(') {')
+
+ self.indent()
+ self.visitBlock(sf)
+ self.dedent()
+ self.printdentln('}')
+
+
+ def visitStmtRangedFor(self, rf):
+ self.printdent('for (auto& ')
+ rf.var.accept(self)
+ self.write(' : ')
+ rf.iteree.accept(self)
+ self.println(') {')
+
+ self.indent()
+ self.visitBlock(rf)
+ self.dedent()
+ self.printdentln('}')
+
+
+ def visitStmtSwitch(self, sw):
+ self.printdent('switch (')
+ sw.expr.accept(self)
+ self.println(') {')
+ self.indent()
+ self.visitBlock(sw)
+ self.dedent()
+ self.printdentln('}')
+
+ def visitStmtBreak(self, sb):
+ self.printdentln('break;')
+
+
+ def visitStmtDecl(self, sd):
+ self.printdent()
+ sd.decl.accept(self)
+ if sd.initargs is not None:
+ self.write('(')
+ self.writeDeclList(sd.initargs)
+ self.write(')')
+ if sd.init is not None:
+ self.write(' = ')
+ sd.init.accept(self)
+ self.println(';')
+
+
+ def visitStmtExpr(self, se):
+ self.printdent()
+ se.expr.accept(self)
+ self.println(';')
+
+
+ def visitStmtReturn(self, sr):
+ self.printdent('return')
+ if sr.expr:
+ self.write (' ')
+ sr.expr.accept(self)
+ self.println(';')
+
+
+ def writeDeclList(self, decls):
+ # FIXME/cjones: try to do nice formatting of these guys
+
+ ndecls = len(decls)
+ if 0 == ndecls:
+ return
+ elif 1 == ndecls:
+ decls[0].accept(self)
+ return
+
+ self.indent()
+ self.indent()
+ for i, decl in enumerate(decls):
+ self.println()
+ self.printdent()
+ decl.accept(self)
+ if i != (ndecls-1):
+ self.write(',')
+ self.dedent()
+ self.dedent()
+
+ def writeExprList(self, exprs):
+ # FIXME/cjones: try to do nice formatting and share code with
+ # writeDeclList()
+ nexprs = len(exprs)
+ for i, expr in enumerate(exprs):
+ expr.accept(self)
+ if i != (nexprs-1):
+ self.write(', ')
diff --git a/ipc/ipdl/ipdl/lower.py b/ipc/ipdl/ipdl/lower.py
new file mode 100644
index 000000000..f810cccb0
--- /dev/null
+++ b/ipc/ipdl/ipdl/lower.py
@@ -0,0 +1,4822 @@
+# 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/.
+
+import os, re, sys
+from copy import deepcopy
+from collections import OrderedDict
+
+import ipdl.ast
+import ipdl.builtin
+from ipdl.cxx.ast import *
+from ipdl.type import Actor, ActorType, ProcessGraph, TypeVisitor, builtinHeaderIncludes
+
+##-----------------------------------------------------------------------------
+## "Public" interface to lowering
+##
+class LowerToCxx:
+ def lower(self, tu):
+ '''returns |[ header: File ], [ cpp : File ]| representing the
+lowered form of |tu|'''
+ # annotate the AST with IPDL/C++ IR-type stuff used later
+ tu.accept(_DecorateWithCxxStuff())
+
+ # Any modifications to the filename scheme here need corresponding
+ # modifications in the ipdl.py driver script.
+ name = tu.name
+ pheader, pcpp = File(name +'.h'), File(name +'.cpp')
+
+ _GenerateProtocolCode().lower(tu, pheader, pcpp)
+ headers = [ pheader ]
+ cpps = [ pcpp ]
+
+ if tu.protocol:
+ pname = tu.protocol.name
+
+ parentheader, parentcpp = File(pname +'Parent.h'), File(pname +'Parent.cpp')
+ _GenerateProtocolParentCode().lower(
+ tu, pname+'Parent', parentheader, parentcpp)
+
+ childheader, childcpp = File(pname +'Child.h'), File(pname +'Child.cpp')
+ _GenerateProtocolChildCode().lower(
+ tu, pname+'Child', childheader, childcpp)
+
+ headers += [ parentheader, childheader ]
+ cpps += [ parentcpp, childcpp ]
+
+ return headers, cpps
+
+
+##-----------------------------------------------------------------------------
+## Helper code
+##
+
+def hashfunc(value):
+ h = hash(value) % 2**32
+ if h < 0: h += 2**32
+ return h
+
+_NULL_ACTOR_ID = ExprLiteral.ZERO
+_FREED_ACTOR_ID = ExprLiteral.ONE
+
+_DISCLAIMER = Whitespace('''//
+// Automatically generated by ipdlc.
+// Edit at your own risk
+//
+
+''')
+
+
+class _struct: pass
+
+def _namespacedHeaderName(name, namespaces):
+ pfx = '/'.join([ ns.name for ns in namespaces ])
+ if pfx:
+ return pfx +'/'+ name
+ else:
+ return name
+
+def _ipdlhHeaderName(tu):
+ assert tu.filetype == 'header'
+ return _namespacedHeaderName(tu.name, tu.namespaces)
+
+def _protocolHeaderName(p, side=''):
+ if side: side = side.title()
+ base = p.name + side
+ return _namespacedHeaderName(base, p.namespaces)
+
+def _includeGuardMacroName(headerfile):
+ return re.sub(r'[./]', '_', headerfile.name)
+
+def _includeGuardStart(headerfile):
+ guard = _includeGuardMacroName(headerfile)
+ return [ CppDirective('ifndef', guard),
+ CppDirective('define', guard) ]
+
+def _includeGuardEnd(headerfile):
+ guard = _includeGuardMacroName(headerfile)
+ return [ CppDirective('endif', '// ifndef '+ guard) ]
+
+def _messageStartName(ptype):
+ return ptype.name() +'MsgStart'
+
+def _protocolId(ptype):
+ return ExprVar(_messageStartName(ptype))
+
+def _protocolIdType():
+ return Type.INT32
+
+def _actorName(pname, side):
+ """|pname| is the protocol name. |side| is 'Parent' or 'Child'."""
+ tag = side
+ if not tag[0].isupper(): tag = side.title()
+ return pname + tag
+
+def _actorIdType():
+ return Type.INT32
+
+def _actorTypeTagType():
+ return Type.INT32
+
+def _actorId(actor=None):
+ if actor is not None:
+ return ExprCall(ExprSelect(actor, '->', 'Id'))
+ return ExprCall(ExprVar('Id'))
+
+def _actorHId(actorhandle):
+ return ExprSelect(actorhandle, '.', 'mId')
+
+def _actorManager(actor):
+ return ExprCall(ExprSelect(actor, '->', 'Manager'), args=[])
+
+def _actorState(actor):
+ return ExprSelect(actor, '->', 'mState')
+
+def _backstagePass():
+ return ExprCall(ExprVar('mozilla::ipc::PrivateIPDLInterface'))
+
+def _iterType(ptr):
+ return Type('PickleIterator', ptr=ptr)
+
+def _nullState(proto=None):
+ pfx = ''
+ if proto is not None: pfx = proto.name() +'::'
+ return ExprVar(pfx +'__Null')
+
+def _errorState(proto=None):
+ pfx = ''
+ if proto is not None: pfx = proto.name() +'::'
+ return ExprVar(pfx +'__Error')
+
+def _deadState(proto=None):
+ pfx = ''
+ if proto is not None: pfx = proto.name() +'::'
+ return ExprVar(pfx +'__Dead')
+
+def _dyingState(proto=None):
+ pfx = ''
+ if proto is not None: pfx = proto.name() +'::'
+ return ExprVar(pfx +'__Dying')
+
+def _startState(proto=None, fq=False):
+ pfx = ''
+ if proto:
+ if fq: pfx = proto.fullname() +'::'
+ else: pfx = proto.name() +'::'
+ return ExprVar(pfx +'__Start')
+
+def _deleteId():
+ return ExprVar('Msg___delete____ID')
+
+def _deleteReplyId():
+ return ExprVar('Reply___delete____ID')
+
+def _lookupListener(idexpr):
+ return ExprCall(ExprVar('Lookup'), args=[ idexpr ])
+
+def _shmemType(ptr=0, const=1, ref=0):
+ return Type('Shmem', ptr=ptr, ref=ref)
+
+def _rawShmemType(ptr=0):
+ return Type('Shmem::SharedMemory', ptr=ptr)
+
+def _shmemIdType(ptr=0):
+ return Type('Shmem::id_t', ptr=ptr)
+
+def _shmemTypeType():
+ return Type('Shmem::SharedMemory::SharedMemoryType')
+
+def _shmemBackstagePass():
+ return ExprCall(ExprVar(
+ 'Shmem::IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead'))
+
+def _shmemCtor(rawmem, idexpr):
+ return ExprCall(ExprVar('Shmem'),
+ args=[ _shmemBackstagePass(), rawmem, idexpr ])
+
+def _shmemId(shmemexpr):
+ return ExprCall(ExprSelect(shmemexpr, '.', 'Id'),
+ args=[ _shmemBackstagePass() ])
+
+def _shmemSegment(shmemexpr):
+ return ExprCall(ExprSelect(shmemexpr, '.', 'Segment'),
+ args=[ _shmemBackstagePass() ])
+
+def _shmemAlloc(size, type, unsafe):
+ # starts out UNprotected
+ return ExprCall(ExprVar('Shmem::Alloc'),
+ args=[ _shmemBackstagePass(), size, type, unsafe ])
+
+def _shmemDealloc(rawmemvar):
+ return ExprCall(ExprVar('Shmem::Dealloc'),
+ args=[ _shmemBackstagePass(), rawmemvar ])
+
+def _shmemShareTo(shmemvar, processvar, route):
+ return ExprCall(ExprSelect(shmemvar, '.', 'ShareTo'),
+ args=[ _shmemBackstagePass(),
+ processvar, route ])
+
+def _shmemOpenExisting(descriptor, outid):
+ # starts out protected
+ return ExprCall(ExprVar('Shmem::OpenExisting'),
+ args=[ _shmemBackstagePass(),
+ # true => protect
+ descriptor, outid, ExprLiteral.TRUE ])
+
+def _shmemUnshareFrom(shmemvar, processvar, route):
+ return ExprCall(ExprSelect(shmemvar, '.', 'UnshareFrom'),
+ args=[ _shmemBackstagePass(),
+ processvar, route ])
+
+def _shmemForget(shmemexpr):
+ return ExprCall(ExprSelect(shmemexpr, '.', 'forget'),
+ args=[ _shmemBackstagePass() ])
+
+def _shmemRevokeRights(shmemexpr):
+ return ExprCall(ExprSelect(shmemexpr, '.', 'RevokeRights'),
+ args=[ _shmemBackstagePass() ])
+
+def _lookupShmem(idexpr):
+ return ExprCall(ExprVar('LookupSharedMemory'), args=[ idexpr ])
+
+def _makeForwardDeclForQClass(clsname, quals, cls=1, struct=0):
+ fd = ForwardDecl(clsname, cls=cls, struct=struct)
+ if 0 == len(quals):
+ return fd
+
+ outerns = Namespace(quals[0])
+ innerns = outerns
+ for ns in quals[1:]:
+ tmpns = Namespace(ns)
+ innerns.addstmt(tmpns)
+ innerns = tmpns
+
+ innerns.addstmt(fd)
+ return outerns
+
+def _makeForwardDeclForActor(ptype, side):
+ return _makeForwardDeclForQClass(_actorName(ptype.qname.baseid, side),
+ ptype.qname.quals)
+
+def _makeForwardDecl(type):
+ return _makeForwardDeclForQClass(type.name(), type.qname.quals)
+
+
+def _putInNamespaces(cxxthing, namespaces):
+ """|namespaces| is in order [ outer, ..., inner ]"""
+ if 0 == len(namespaces): return cxxthing
+
+ outerns = Namespace(namespaces[0].name)
+ innerns = outerns
+ for ns in namespaces[1:]:
+ newns = Namespace(ns.name)
+ innerns.addstmt(newns)
+ innerns = newns
+ innerns.addstmt(cxxthing)
+ return outerns
+
+def _sendPrefix(msgtype):
+ """Prefix of the name of the C++ method that sends |msgtype|."""
+ if msgtype.isInterrupt():
+ return 'Call'
+ return 'Send'
+
+def _recvPrefix(msgtype):
+ """Prefix of the name of the C++ method that handles |msgtype|."""
+ if msgtype.isInterrupt():
+ return 'Answer'
+ return 'Recv'
+
+def _flatTypeName(ipdltype):
+ """Return a 'flattened' IPDL type name that can be used as an
+identifier.
+E.g., |Foo[]| --> |ArrayOfFoo|."""
+ # NB: this logic depends heavily on what IPDL types are allowed to
+ # be constructed; e.g., Foo[][] is disallowed. needs to be kept in
+ # sync with grammar.
+ if ipdltype.isIPDL() and ipdltype.isArray():
+ return 'ArrayOf'+ ipdltype.basetype.name()
+ return ipdltype.name()
+
+
+def _hasVisibleActor(ipdltype):
+ """Return true iff a C++ decl of |ipdltype| would have an Actor* type.
+For example: |Actor[]| would turn into |Array<ActorParent*>|, so this
+function would return true for |Actor[]|."""
+ return (ipdltype.isIPDL()
+ and (ipdltype.isActor()
+ or (ipdltype.isArray()
+ and _hasVisibleActor(ipdltype.basetype))))
+
+def _abortIfFalse(cond, msg):
+ return StmtExpr(ExprCall(
+ ExprVar('MOZ_RELEASE_ASSERT'),
+ [ cond, ExprLiteral.String(msg) ]))
+
+def _refptr(T):
+ return Type('RefPtr', T=T)
+
+def _refptrGet(expr):
+ return ExprCall(ExprSelect(expr, '.', 'get'))
+
+def _refptrForget(expr):
+ return ExprCall(ExprSelect(expr, '.', 'forget'))
+
+def _refptrTake(expr):
+ return ExprCall(ExprSelect(expr, '.', 'take'))
+
+def _uniqueptr(T):
+ return Type('UniquePtr', T=T)
+
+def _uniqueptrGet(expr):
+ return ExprCall(ExprSelect(expr, '.', 'get'))
+
+def _cxxArrayType(basetype, const=0, ref=0):
+ return Type('nsTArray', T=basetype, const=const, ref=ref, hasimplicitcopyctor=False)
+
+def _cxxManagedContainerType(basetype, const=0, ref=0):
+ return Type('ManagedContainer', T=basetype,
+ const=const, ref=ref, hasimplicitcopyctor=False)
+
+def _callCxxArrayLength(arr):
+ return ExprCall(ExprSelect(arr, '.', 'Length'))
+
+def _callCxxArraySetLength(arr, lenexpr, sel='.'):
+ return ExprCall(ExprSelect(arr, sel, 'SetLength'),
+ args=[ lenexpr ])
+
+def _callCxxSwapArrayElements(arr1, arr2, sel='.'):
+ return ExprCall(ExprSelect(arr1, sel, 'SwapElements'),
+ args=[ arr2 ])
+
+def _callInsertManagedActor(managees, actor):
+ return ExprCall(ExprSelect(managees, '.', 'PutEntry'),
+ args=[ actor ])
+
+def _callRemoveManagedActor(managees, actor):
+ return ExprCall(ExprSelect(managees, '.', 'RemoveEntry'),
+ args=[ actor ])
+
+def _callClearManagedActors(managees):
+ return ExprCall(ExprSelect(managees, '.', 'Clear'))
+
+def _callHasManagedActor(managees, actor):
+ return ExprCall(ExprSelect(managees, '.', 'Contains'), args=[ actor ])
+
+def _otherSide(side):
+ if side == 'child': return 'parent'
+ if side == 'parent': return 'child'
+ assert 0
+
+def _sideToTransportMode(side):
+ if side == 'parent': mode = 'SERVER'
+ elif side == 'child': mode = 'CLIENT'
+ return ExprVar('mozilla::ipc::Transport::MODE_'+ mode)
+
+def _ifLogging(topLevelProtocol, stmts):
+ iflogging = StmtIf(ExprCall(ExprVar('mozilla::ipc::LoggingEnabledFor'),
+ args=[ topLevelProtocol ]))
+ iflogging.addifstmts(stmts)
+ return iflogging
+
+# XXX we need to remove these and install proper error handling
+def _printErrorMessage(msg):
+ if isinstance(msg, str):
+ msg = ExprLiteral.String(msg)
+ return StmtExpr(
+ ExprCall(ExprVar('NS_ERROR'), args=[ msg ]))
+
+def _protocolErrorBreakpoint(msg):
+ if isinstance(msg, str):
+ msg = ExprLiteral.String(msg)
+ return StmtExpr(ExprCall(ExprVar('mozilla::ipc::ProtocolErrorBreakpoint'),
+ args=[ msg ]))
+
+def _printWarningMessage(msg):
+ if isinstance(msg, str):
+ msg = ExprLiteral.String(msg)
+ return StmtExpr(
+ ExprCall(ExprVar('NS_WARNING'), args=[ msg ]))
+
+def _fatalError(msg):
+ return StmtExpr(
+ ExprCall(ExprVar('FatalError'), args=[ ExprLiteral.String(msg) ]))
+
+def _logicError(msg):
+ return StmtExpr(
+ ExprCall(ExprVar('mozilla::ipc::LogicError'), args=[ ExprLiteral.String(msg) ]))
+
+def _arrayLengthReadError(elementname):
+ return StmtExpr(
+ ExprCall(ExprVar('mozilla::ipc::ArrayLengthReadError'),
+ args=[ ExprLiteral.String(elementname) ]))
+
+def _unionTypeReadError(unionname):
+ return StmtExpr(
+ ExprCall(ExprVar('mozilla::ipc::UnionTypeReadError'),
+ args=[ ExprLiteral.String(unionname) ]))
+
+def _killProcess(pid):
+ return ExprCall(
+ ExprVar('base::KillProcess'),
+ args=[ pid,
+ # XXX this is meaningless on POSIX
+ ExprVar('base::PROCESS_END_KILLED_BY_USER'),
+ ExprLiteral.FALSE ])
+
+def _badTransition():
+ # FIXME: make this a FatalError()
+ return [ _printWarningMessage('bad state transition!') ]
+
+# Results that IPDL-generated code returns back to *Channel code.
+# Users never see these
+class _Result:
+ @staticmethod
+ def Type():
+ return Type('Result')
+
+ Processed = ExprVar('MsgProcessed')
+ NotKnown = ExprVar('MsgNotKnown')
+ NotAllowed = ExprVar('MsgNotAllowed')
+ PayloadError = ExprVar('MsgPayloadError')
+ ProcessingError = ExprVar('MsgProcessingError')
+ RouteError = ExprVar('MsgRouteError')
+ ValuError = ExprVar('MsgValueError') # [sic]
+
+# these |errfn*| are functions that generate code to be executed on an
+# error, such as "bad actor ID". each is given a Python string
+# containing a description of the error
+
+# used in user-facing Send*() methods
+def errfnSend(msg, errcode=ExprLiteral.FALSE):
+ return [
+ _fatalError(msg),
+ StmtReturn(errcode)
+ ]
+
+def errfnSendCtor(msg): return errfnSend(msg, errcode=ExprLiteral.NULL)
+
+# TODO should this error handling be strengthened for dtors?
+def errfnSendDtor(msg):
+ return [
+ _printErrorMessage(msg),
+ StmtReturn.FALSE
+ ]
+
+# used in |OnMessage*()| handlers that hand in-messages off to Recv*()
+# interface methods
+def errfnRecv(msg, errcode=_Result.ValuError):
+ return [
+ _fatalError(msg),
+ StmtReturn(errcode)
+ ]
+
+# used in Read() methods
+def errfnRead(msg):
+ return [ _fatalError(msg), StmtReturn.FALSE ]
+
+def errfnArrayLength(elementname):
+ return [ _arrayLengthReadError(elementname), StmtReturn.FALSE ]
+
+def errfnUnionType(unionname):
+ return [ _unionTypeReadError(unionname), StmtReturn.FALSE ]
+
+def _destroyMethod():
+ return ExprVar('ActorDestroy')
+
+class _DestroyReason:
+ @staticmethod
+ def Type(): return Type('ActorDestroyReason')
+
+ Deletion = ExprVar('Deletion')
+ AncestorDeletion = ExprVar('AncestorDeletion')
+ NormalShutdown = ExprVar('NormalShutdown')
+ AbnormalShutdown = ExprVar('AbnormalShutdown')
+ FailedConstructor = ExprVar('FailedConstructor')
+
+##-----------------------------------------------------------------------------
+## Intermediate representation (IR) nodes used during lowering
+
+class _ConvertToCxxType(TypeVisitor):
+ def __init__(self, side, fq):
+ self.side = side
+ self.fq = fq
+
+ def typename(self, thing):
+ if self.fq:
+ return thing.fullname()
+ return thing.name()
+
+ def visitBuiltinCxxType(self, t):
+ return Type(self.typename(t))
+
+ def visitImportedCxxType(self, t):
+ return Type(self.typename(t))
+
+ def visitActorType(self, a):
+ return Type(_actorName(self.typename(a.protocol), self.side), ptr=1)
+
+ def visitStructType(self, s):
+ return Type(self.typename(s))
+
+ def visitUnionType(self, u):
+ return Type(self.typename(u))
+
+ def visitArrayType(self, a):
+ basecxxtype = a.basetype.accept(self)
+ return _cxxArrayType(basecxxtype)
+
+ def visitShmemType(self, s):
+ return Type(self.typename(s))
+
+ def visitFDType(self, s):
+ return Type(self.typename(s))
+
+ def visitEndpointType(self, s):
+ return Type(self.typename(s))
+
+ def visitProtocolType(self, p): assert 0
+ def visitMessageType(self, m): assert 0
+ def visitVoidType(self, v): assert 0
+ def visitStateType(self, st): assert 0
+
+def _cxxBareType(ipdltype, side, fq=0):
+ return ipdltype.accept(_ConvertToCxxType(side, fq))
+
+def _cxxRefType(ipdltype, side):
+ t = _cxxBareType(ipdltype, side)
+ t.ref = 1
+ return t
+
+def _cxxConstRefType(ipdltype, side):
+ t = _cxxBareType(ipdltype, side)
+ if ipdltype.isIPDL() and ipdltype.isActor():
+ return t
+ if ipdltype.isIPDL() and ipdltype.isShmem():
+ t.ref = 1
+ return t
+ t.const = 1
+ t.ref = 1
+ return t
+
+def _cxxMoveRefType(ipdltype, side):
+ t = _cxxBareType(ipdltype, side)
+ if ipdltype.isIPDL() and (ipdltype.isArray() or ipdltype.isShmem() or ipdltype.isEndpoint()):
+ t.ref = 2
+ return t
+ return _cxxConstRefType(ipdltype, side)
+
+def _cxxPtrToType(ipdltype, side):
+ t = _cxxBareType(ipdltype, side)
+ if ipdltype.isIPDL() and ipdltype.isActor():
+ t.ptr = 0
+ t.ptrptr = 1
+ return t
+ t.ptr = 1
+ return t
+
+def _cxxConstPtrToType(ipdltype, side):
+ t = _cxxBareType(ipdltype, side)
+ if ipdltype.isIPDL() and ipdltype.isActor():
+ t.ptr = 0
+ t.ptrconstptr = 1
+ return t
+ t.const = 1
+ t.ptr = 1
+ return t
+
+def _allocMethod(ptype, side):
+ return ExprVar('Alloc'+ str(Actor(ptype, side)))
+
+def _deallocMethod(ptype, side):
+ return ExprVar('Dealloc'+ str(Actor(ptype, side)))
+
+##
+## A _HybridDecl straddles IPDL and C++ decls. It knows which C++
+## types correspond to which IPDL types, and it also knows how
+## serialize and deserialize "special" IPDL C++ types.
+##
+class _HybridDecl:
+ """A hybrid decl stores both an IPDL type and all the C++ type
+info needed by later passes, along with a basic name for the decl."""
+ def __init__(self, ipdltype, name):
+ self.ipdltype = ipdltype
+ self.name = name
+ self.idnum = 0
+
+ def var(self):
+ return ExprVar(self.name)
+
+ def bareType(self, side):
+ """Return this decl's unqualified C++ type."""
+ return _cxxBareType(self.ipdltype, side)
+
+ def refType(self, side):
+ """Return this decl's C++ type as a 'reference' type, which is not
+necessarily a C++ reference."""
+ return _cxxRefType(self.ipdltype, side)
+
+ def constRefType(self, side):
+ """Return this decl's C++ type as a const, 'reference' type."""
+ return _cxxConstRefType(self.ipdltype, side)
+
+ def rvalueRefType(self, side):
+ """Return this decl's C++ type as an r-value 'reference' type."""
+ return _cxxMoveRefType(self.ipdltype, side)
+
+ def ptrToType(self, side):
+ return _cxxPtrToType(self.ipdltype, side)
+
+ def constPtrToType(self, side):
+ return _cxxConstPtrToType(self.ipdltype, side)
+
+ def inType(self, side):
+ """Return this decl's C++ Type with inparam semantics."""
+ if self.ipdltype.isIPDL() and self.ipdltype.isActor():
+ return self.bareType(side)
+ return self.constRefType(side)
+
+ def moveType(self, side):
+ """Return this decl's C++ Type with move semantics."""
+ if self.ipdltype.isIPDL() and self.ipdltype.isActor():
+ return self.bareType(side)
+ return self.rvalueRefType(side);
+
+ def outType(self, side):
+ """Return this decl's C++ Type with outparam semantics."""
+ t = self.bareType(side)
+ if self.ipdltype.isIPDL() and self.ipdltype.isActor():
+ t.ptr = 0; t.ptrptr = 1
+ return t
+ t.ptr = 1
+ return t
+
+##--------------------------------------------------
+
+class HasFQName:
+ def fqClassName(self):
+ return self.decl.type.fullname()
+
+class _CompoundTypeComponent(_HybridDecl):
+ def __init__(self, ipdltype, name, side, ct):
+ _HybridDecl.__init__(self, ipdltype, name)
+ self.side = side
+ self.special = _hasVisibleActor(ipdltype)
+ self.recursive = ct.decl.type.mutuallyRecursiveWith(ipdltype)
+
+ def internalType(self):
+ if self.recursive:
+ return self.ptrToType()
+ else:
+ return self.bareType()
+
+ # @override the following methods to pass |self.side| instead of
+ # forcing the caller to remember which side we're declared to
+ # represent.
+ def bareType(self, side=None):
+ return _HybridDecl.bareType(self, self.side)
+ def refType(self, side=None):
+ return _HybridDecl.refType(self, self.side)
+ def constRefType(self, side=None):
+ return _HybridDecl.constRefType(self, self.side)
+ def ptrToType(self, side=None):
+ return _HybridDecl.ptrToType(self, self.side)
+ def constPtrToType(self, side=None):
+ return _HybridDecl.constPtrToType(self, self.side)
+ def inType(self, side=None):
+ return _HybridDecl.inType(self, self.side)
+
+
+class StructDecl(ipdl.ast.StructDecl, HasFQName):
+ @staticmethod
+ def upgrade(structDecl):
+ assert isinstance(structDecl, ipdl.ast.StructDecl)
+ structDecl.__class__ = StructDecl
+ return structDecl
+
+class _StructField(_CompoundTypeComponent):
+ def __init__(self, ipdltype, name, sd, side=None):
+ self.basename = name
+ fname = name
+ special = _hasVisibleActor(ipdltype)
+ if special:
+ fname += side.title()
+
+ _CompoundTypeComponent.__init__(self, ipdltype, fname, side, sd)
+
+ def getMethod(self, thisexpr=None, sel='.'):
+ meth = self.var()
+ if thisexpr is not None:
+ return ExprSelect(thisexpr, sel, meth.name)
+ return meth
+
+ def initExpr(self, thisexpr):
+ expr = ExprCall(self.getMethod(thisexpr=thisexpr))
+ if self.ipdltype.isIPDL() and self.ipdltype.isActor():
+ expr = ExprCast(expr, self.bareType(), const=1)
+ return expr
+
+ def refExpr(self, thisexpr=None):
+ ref = self.memberVar()
+ if thisexpr is not None:
+ ref = ExprSelect(thisexpr, '.', ref.name)
+ if self.recursive:
+ ref = ExprDeref(ref)
+ return ref
+
+ def constRefExpr(self, thisexpr=None):
+ # sigh, gross hack
+ refexpr = self.refExpr(thisexpr)
+ if 'Shmem' == self.ipdltype.name():
+ refexpr = ExprCast(refexpr, Type('Shmem', ref=1), const=1)
+ if 'FileDescriptor' == self.ipdltype.name():
+ refexpr = ExprCast(refexpr, Type('FileDescriptor', ref=1), const=1)
+ return refexpr
+
+ def argVar(self):
+ return ExprVar('_'+ self.name)
+
+ def memberVar(self):
+ return ExprVar(self.name + '_')
+
+ def initStmts(self):
+ if self.recursive:
+ return [ StmtExpr(ExprAssn(self.memberVar(),
+ ExprNew(self.bareType()))) ]
+ elif self.ipdltype.isIPDL() and self.ipdltype.isActor():
+ return [ StmtExpr(ExprAssn(self.memberVar(),
+ ExprLiteral.NULL)) ]
+ else:
+ return []
+
+ def destructStmts(self):
+ if self.recursive:
+ return [ StmtExpr(ExprDelete(self.memberVar())) ]
+ else:
+ return []
+
+
+class UnionDecl(ipdl.ast.UnionDecl, HasFQName):
+ def callType(self, var=None):
+ func = ExprVar('type')
+ if var is not None:
+ func = ExprSelect(var, '.', func.name)
+ return ExprCall(func)
+
+ @staticmethod
+ def upgrade(unionDecl):
+ assert isinstance(unionDecl, ipdl.ast.UnionDecl)
+ unionDecl.__class__ = UnionDecl
+ return unionDecl
+
+
+class _UnionMember(_CompoundTypeComponent):
+ """Not in the AFL sense, but rather a member (e.g. |int;|) of an
+IPDL union type."""
+ def __init__(self, ipdltype, ud, side=None, other=None):
+ flatname = _flatTypeName(ipdltype)
+ special = _hasVisibleActor(ipdltype)
+ if special:
+ flatname += side.title()
+
+ _CompoundTypeComponent.__init__(self, ipdltype, 'V'+ flatname, side, ud)
+ self.flattypename = flatname
+ if special:
+ if other is not None:
+ self.other = other
+ else:
+ self.other = _UnionMember(ipdltype, ud, _otherSide(side), self)
+
+ def enum(self):
+ return 'T' + self.flattypename
+
+ def pqEnum(self):
+ return self.ud.name +'::'+ self.enum()
+
+ def enumvar(self):
+ return ExprVar(self.enum())
+
+ def unionType(self):
+ """Type used for storage in generated C union decl."""
+ if self.recursive:
+ return self.ptrToType()
+ else:
+ return Type('mozilla::AlignedStorage2', T=self.internalType())
+
+ def unionValue(self):
+ # NB: knows that Union's storage C union is named |mValue|
+ return ExprSelect(ExprVar('mValue'), '.', self.name)
+
+ def typedef(self):
+ return self.flattypename +'__tdef'
+
+ def callGetConstPtr(self):
+ """Return an expression of type self.constptrToSelfType()"""
+ return ExprCall(ExprVar(self.getConstPtrName()))
+
+ def callGetPtr(self):
+ """Return an expression of type self.ptrToSelfType()"""
+ return ExprCall(ExprVar(self.getPtrName()))
+
+ def callOperatorEq(self, rhs):
+ if self.ipdltype.isIPDL() and self.ipdltype.isActor():
+ rhs = ExprCast(rhs, self.bareType(), const=1)
+ return ExprAssn(ExprDeref(self.callGetPtr()), rhs)
+
+ def callCtor(self, expr=None):
+ assert not isinstance(expr, list)
+
+ if expr is None:
+ args = None
+ elif self.ipdltype.isIPDL() and self.ipdltype.isActor():
+ args = [ ExprCast(expr, self.bareType(), const=1) ]
+ else:
+ args = [ expr ]
+
+ if self.recursive:
+ return ExprAssn(self.callGetPtr(),
+ ExprNew(self.bareType(self.side),
+ args=args))
+ else:
+ return ExprNew(self.bareType(self.side),
+ args=args,
+ newargs=[ ExprVar('mozilla::KnownNotNull'), self.callGetPtr() ])
+
+ def callDtor(self):
+ if self.recursive:
+ return ExprDelete(self.callGetPtr())
+ else:
+ return ExprCall(
+ ExprSelect(self.callGetPtr(), '->', '~'+ self.typedef()))
+
+ def getTypeName(self): return 'get_'+ self.flattypename
+ def getConstTypeName(self): return 'get_'+ self.flattypename
+
+ def getOtherTypeName(self): return 'get_'+ self.otherflattypename
+
+ def getPtrName(self): return 'ptr_'+ self.flattypename
+ def getConstPtrName(self): return 'constptr_'+ self.flattypename
+
+ def ptrToSelfExpr(self):
+ """|*ptrToSelfExpr()| has type |self.bareType()|"""
+ v = self.unionValue()
+ if self.recursive:
+ return v
+ else:
+ return ExprCall(ExprSelect(v, '.', 'addr'))
+
+ def constptrToSelfExpr(self):
+ """|*constptrToSelfExpr()| has type |self.constType()|"""
+ v = self.unionValue()
+ if self.recursive:
+ return v
+ return ExprCall(ExprSelect(v, '.', 'addr'))
+
+ def ptrToInternalType(self):
+ t = self.ptrToType()
+ if self.recursive:
+ t.ref = 1
+ return t
+
+ def defaultValue(self):
+ # Use the default constructor for any class that does not have an
+ # implicit copy constructor.
+ if not self.bareType().hasimplicitcopyctor:
+ return None
+
+ if self.ipdltype.isIPDL() and self.ipdltype.isActor():
+ return ExprLiteral.NULL
+ # XXX sneaky here, maybe need ExprCtor()?
+ return ExprCall(self.bareType())
+
+ def getConstValue(self):
+ v = ExprDeref(self.callGetConstPtr())
+ # sigh
+ if 'Shmem' == self.ipdltype.name():
+ v = ExprCast(v, Type('Shmem', ref=1), const=1)
+ if 'FileDescriptor' == self.ipdltype.name():
+ v = ExprCast(v, Type('FileDescriptor', ref=1), const=1)
+ return v
+
+##--------------------------------------------------
+
+class MessageDecl(ipdl.ast.MessageDecl):
+ def baseName(self):
+ return self.name
+
+ def recvMethod(self):
+ name = _recvPrefix(self.decl.type) + self.baseName()
+ if self.decl.type.isCtor():
+ name += 'Constructor'
+ return ExprVar(name)
+
+ def sendMethod(self):
+ name = _sendPrefix(self.decl.type) + self.baseName()
+ if self.decl.type.isCtor():
+ name += 'Constructor'
+ return ExprVar(name)
+
+ def hasReply(self):
+ return (self.decl.type.hasReply()
+ or self.decl.type.isCtor()
+ or self.decl.type.isDtor())
+
+ def msgCtorFunc(self):
+ return 'Msg_%s'% (self.decl.progname)
+
+ def prettyMsgName(self, pfx=''):
+ return pfx + self.msgCtorFunc()
+
+ def pqMsgCtorFunc(self):
+ return '%s::%s'% (self.namespace, self.msgCtorFunc())
+
+ def msgId(self): return self.msgCtorFunc()+ '__ID'
+ def pqMsgId(self):
+ return '%s::%s'% (self.namespace, self.msgId())
+
+ def replyCtorFunc(self):
+ return 'Reply_%s'% (self.decl.progname)
+
+ def pqReplyCtorFunc(self):
+ return '%s::%s'% (self.namespace, self.replyCtorFunc())
+
+ def replyId(self): return self.replyCtorFunc()+ '__ID'
+ def pqReplyId(self):
+ return '%s::%s'% (self.namespace, self.replyId())
+
+ def prettyReplyName(self, pfx=''):
+ return pfx + self.replyCtorFunc()
+
+ def actorDecl(self):
+ return self.params[0]
+
+ def makeCxxParams(self, paramsems='in', returnsems='out',
+ side=None, implicit=1):
+ """Return a list of C++ decls per the spec'd configuration.
+|params| and |returns| is the C++ semantics of those: 'in', 'out', or None."""
+
+ def makeDecl(d, sems):
+ if sems is 'in':
+ return Decl(d.inType(side), d.name)
+ elif sems is 'move':
+ return Decl(d.moveType(side), d.name)
+ elif sems is 'out':
+ return Decl(d.outType(side), d.name)
+ else: assert 0
+
+ cxxparams = [ ]
+ if paramsems is not None:
+ cxxparams.extend([ makeDecl(d, paramsems) for d in self.params ])
+
+ if returnsems is not None:
+ cxxparams.extend([ makeDecl(r, returnsems) for r in self.returns ])
+
+ if not implicit and self.decl.type.hasImplicitActorParam():
+ cxxparams = cxxparams[1:]
+
+ return cxxparams
+
+ def makeCxxArgs(self, paramsems='in', retsems='out', retcallsems='out',
+ implicit=1):
+ assert not retcallsems or retsems # retcallsems => returnsems
+ cxxargs = [ ]
+
+ if paramsems is 'move':
+ cxxargs.extend([ ExprMove(p.var()) for p in self.params ])
+ elif paramsems is 'in':
+ cxxargs.extend([ p.var() for p in self.params ])
+ else:
+ assert False
+
+ for ret in self.returns:
+ if retsems is 'in':
+ if retcallsems is 'in':
+ cxxargs.append(ret.var())
+ elif retcallsems is 'out':
+ cxxargs.append(ExprAddrOf(ret.var()))
+ else: assert 0
+ elif retsems is 'out':
+ if retcallsems is 'in':
+ cxxargs.append(ExprDeref(ret.var()))
+ elif retcallsems is 'out':
+ cxxargs.append(ret.var())
+ else: assert 0
+
+ if not implicit:
+ assert self.decl.type.hasImplicitActorParam()
+ cxxargs = cxxargs[1:]
+
+ return cxxargs
+
+
+ @staticmethod
+ def upgrade(messageDecl):
+ assert isinstance(messageDecl, ipdl.ast.MessageDecl)
+ if messageDecl.decl.type.hasImplicitActorParam():
+ messageDecl.params.insert(
+ 0,
+ _HybridDecl(
+ ipdl.type.ActorType(
+ messageDecl.decl.type.constructedType()),
+ 'actor'))
+ messageDecl.__class__ = MessageDecl
+ return messageDecl
+
+##--------------------------------------------------
+def _semsToChannelParts(sems):
+ return [ 'mozilla', 'ipc', 'MessageChannel' ]
+
+def _usesShmem(p):
+ for md in p.messageDecls:
+ for param in md.inParams:
+ if ipdl.type.hasshmem(param.type):
+ return True
+ for ret in md.outParams:
+ if ipdl.type.hasshmem(ret.type):
+ return True
+ return False
+
+def _subtreeUsesShmem(p):
+ if _usesShmem(p):
+ return True
+
+ ptype = p.decl.type
+ for mgd in ptype.manages:
+ if ptype is not mgd:
+ if _subtreeUsesShmem(mgd._ast):
+ return True
+ return False
+
+class Protocol(ipdl.ast.Protocol):
+ def cxxTypedefs(self):
+ return self.decl.cxxtypedefs
+
+ def sendSems(self):
+ return self.decl.type.toplevel().sendSemantics
+
+ def channelName(self):
+ return '::'.join(_semsToChannelParts(self.sendSems()))
+
+ def channelSel(self):
+ if self.decl.type.isToplevel(): return '.'
+ return '->'
+
+ def channelType(self):
+ return Type('Channel', ptr=not self.decl.type.isToplevel())
+
+ def channelHeaderFile(self):
+ return '/'.join(_semsToChannelParts(self.sendSems())) +'.h'
+
+ def managerInterfaceType(self, ptr=0):
+ return Type('mozilla::ipc::IProtocol', ptr=ptr)
+
+ def openedProtocolInterfaceType(self, ptr=0):
+ return Type('mozilla::ipc::IToplevelProtocol',
+ ptr=ptr)
+
+ def _ipdlmgrtype(self):
+ assert 1 == len(self.decl.type.managers)
+ for mgr in self.decl.type.managers: return mgr
+
+ def managerActorType(self, side, ptr=0):
+ return Type(_actorName(self._ipdlmgrtype().name(), side),
+ ptr=ptr)
+
+ def stateMethod(self):
+ return ExprVar('state');
+
+ def registerMethod(self):
+ return ExprVar('Register')
+
+ def registerIDMethod(self):
+ return ExprVar('RegisterID')
+
+ def lookupIDMethod(self):
+ return ExprVar('Lookup')
+
+ def unregisterMethod(self, actorThis=None):
+ if actorThis is not None:
+ return ExprSelect(actorThis, '->', 'Unregister')
+ return ExprVar('Unregister')
+
+ def removeManageeMethod(self):
+ return ExprVar('RemoveManagee')
+
+ def createSharedMemory(self):
+ return ExprVar('CreateSharedMemory')
+
+ def lookupSharedMemory(self):
+ return ExprVar('LookupSharedMemory')
+
+ def isTrackingSharedMemory(self):
+ return ExprVar('IsTrackingSharedMemory')
+
+ def destroySharedMemory(self):
+ return ExprVar('DestroySharedMemory')
+
+ def otherPidMethod(self):
+ return ExprVar('OtherPid')
+
+ def callOtherPid(self, actorThis=None):
+ fn = self.otherPidMethod()
+ if actorThis is not None:
+ fn = ExprSelect(actorThis, '->', fn.name)
+ return ExprCall(fn)
+
+ def getChannelMethod(self):
+ return ExprVar('GetIPCChannel')
+
+ def callGetChannel(self, actorThis=None):
+ fn = self.getChannelMethod()
+ if actorThis is not None:
+ fn = ExprSelect(actorThis, '->', fn.name)
+ return ExprCall(fn)
+
+ def processingErrorVar(self):
+ assert self.decl.type.isToplevel()
+ return ExprVar('ProcessingError')
+
+ def shouldContinueFromTimeoutVar(self):
+ assert self.decl.type.isToplevel()
+ return ExprVar('ShouldContinueFromReplyTimeout')
+
+ def enteredCxxStackVar(self):
+ assert self.decl.type.isToplevel()
+ return ExprVar('EnteredCxxStack')
+
+ def exitedCxxStackVar(self):
+ assert self.decl.type.isToplevel()
+ return ExprVar('ExitedCxxStack')
+
+ def enteredCallVar(self):
+ assert self.decl.type.isToplevel()
+ return ExprVar('EnteredCall')
+
+ def exitedCallVar(self):
+ assert self.decl.type.isToplevel()
+ return ExprVar('ExitedCall')
+
+ def onCxxStackVar(self):
+ assert self.decl.type.isToplevel()
+ return ExprVar('IsOnCxxStack')
+
+ # an actor's C++ private variables
+ def channelVar(self, actorThis=None):
+ if actorThis is not None:
+ return ExprSelect(actorThis, '->', 'mChannel')
+ return ExprVar('mChannel')
+
+ def routingId(self, actorThis=None):
+ if self.decl.type.isToplevel():
+ return ExprVar('MSG_ROUTING_CONTROL')
+ if actorThis is not None:
+ return ExprCall(ExprSelect(actorThis, '->', 'Id'))
+ return ExprCall(ExprVar('Id'))
+
+ def stateVar(self, actorThis=None):
+ if actorThis is not None:
+ return ExprSelect(actorThis, '->', 'mState')
+ return ExprVar('mState')
+
+ def fqStateType(self):
+ return Type(self.decl.type.name() +'::State')
+
+ def startState(self):
+ return _startState(self.decl.type)
+
+ def nullState(self):
+ return _nullState(self.decl.type)
+
+ def deadState(self):
+ return _deadState(self.decl.type)
+
+ def managerVar(self, thisexpr=None):
+ assert thisexpr is not None or not self.decl.type.isToplevel()
+ mvar = ExprCall(ExprVar('Manager'), args=[])
+ if thisexpr is not None:
+ mvar = ExprCall(ExprSelect(thisexpr, '->', 'Manager'), args=[])
+ return mvar
+
+ def managedCxxType(self, actortype, side):
+ assert self.decl.type.isManagerOf(actortype)
+ return Type(_actorName(actortype.name(), side), ptr=1)
+
+ def managedMethod(self, actortype, side):
+ assert self.decl.type.isManagerOf(actortype)
+ return ExprVar('Managed'+ _actorName(actortype.name(), side))
+
+ def managedVar(self, actortype, side):
+ assert self.decl.type.isManagerOf(actortype)
+ return ExprVar('mManaged'+ _actorName(actortype.name(), side))
+
+ def managedVarType(self, actortype, side, const=0, ref=0):
+ assert self.decl.type.isManagerOf(actortype)
+ return _cxxManagedContainerType(Type(_actorName(actortype.name(), side)),
+ const=const, ref=ref)
+
+ # XXX this is sucky, fix
+ def usesShmem(self):
+ return _usesShmem(self)
+
+ def subtreeUsesShmem(self):
+ return _subtreeUsesShmem(self)
+
+ @staticmethod
+ def upgrade(protocol):
+ assert isinstance(protocol, ipdl.ast.Protocol)
+ protocol.__class__ = Protocol
+ return protocol
+
+
+class TranslationUnit(ipdl.ast.TranslationUnit):
+ @staticmethod
+ def upgrade(tu):
+ assert isinstance(tu, ipdl.ast.TranslationUnit)
+ tu.__class__ = TranslationUnit
+ return tu
+
+##-----------------------------------------------------------------------------
+
+class _DecorateWithCxxStuff(ipdl.ast.Visitor):
+ """Phase 1 of lowering: decorate the IPDL AST with information
+relevant to C++ code generation.
+
+This pass results in an AST that is a poor man's "IR"; in reality, a
+"hybrid" AST mainly consisting of IPDL nodes with new C++ info along
+with some new IPDL/C++ nodes that are tuned for C++ codegen."""
+
+ def __init__(self):
+ self.visitedTus = set()
+ # the set of typedefs that allow generated classes to
+ # reference known C++ types by their "short name" rather than
+ # fully-qualified name. e.g. |Foo| rather than |a::b::Foo|.
+ self.typedefs = [ ]
+ self.typedefSet = set([ Typedef(Type('mozilla::ipc::ActorHandle'),
+ 'ActorHandle'),
+ Typedef(Type('base::ProcessId'),
+ 'ProcessId'),
+ Typedef(Type('mozilla::ipc::ProtocolId'),
+ 'ProtocolId'),
+ Typedef(Type('mozilla::ipc::Transport'),
+ 'Transport'),
+ Typedef(Type('mozilla::ipc::Endpoint'),
+ 'Endpoint', ['FooSide']),
+ Typedef(Type('mozilla::ipc::TransportDescriptor'),
+ 'TransportDescriptor') ])
+ self.protocolName = None
+
+ def visitTranslationUnit(self, tu):
+ if tu not in self.visitedTus:
+ self.visitedTus.add(tu)
+ ipdl.ast.Visitor.visitTranslationUnit(self, tu)
+ if not isinstance(tu, TranslationUnit):
+ TranslationUnit.upgrade(tu)
+ self.typedefs[:] = sorted(list(self.typedefSet))
+
+ def visitInclude(self, inc):
+ if inc.tu.filetype == 'header':
+ inc.tu.accept(self)
+
+ def visitProtocol(self, pro):
+ self.protocolName = pro.name
+ pro.decl.cxxtypedefs = self.typedefs
+ Protocol.upgrade(pro)
+ return ipdl.ast.Visitor.visitProtocol(self, pro)
+
+
+ def visitUsingStmt(self, using):
+ if using.decl.fullname is not None:
+ self.typedefSet.add(Typedef(Type(using.decl.fullname),
+ using.decl.shortname))
+
+ def visitStructDecl(self, sd):
+ if not isinstance(sd, StructDecl):
+ sd.decl.special = 0
+ newfields = [ ]
+ for f in sd.fields:
+ ftype = f.decl.type
+ if _hasVisibleActor(ftype):
+ sd.decl.special = 1
+ # if ftype has a visible actor, we need both
+ # |ActorParent| and |ActorChild| fields
+ newfields.append(_StructField(ftype, f.name, sd,
+ side='parent'))
+ newfields.append(_StructField(ftype, f.name, sd,
+ side='child'))
+ else:
+ newfields.append(_StructField(ftype, f.name, sd))
+ sd.fields = newfields
+ StructDecl.upgrade(sd)
+
+ if sd.decl.fullname is not None:
+ self.typedefSet.add(Typedef(Type(sd.fqClassName()), sd.name))
+
+
+ def visitUnionDecl(self, ud):
+ ud.decl.special = 0
+ newcomponents = [ ]
+ for ctype in ud.decl.type.components:
+ if _hasVisibleActor(ctype):
+ ud.decl.special = 1
+ # if ctype has a visible actor, we need both
+ # |ActorParent| and |ActorChild| union members
+ newcomponents.append(_UnionMember(ctype, ud, side='parent'))
+ newcomponents.append(_UnionMember(ctype, ud, side='child'))
+ else:
+ newcomponents.append(_UnionMember(ctype, ud))
+ ud.components = newcomponents
+ UnionDecl.upgrade(ud)
+
+ if ud.decl.fullname is not None:
+ self.typedefSet.add(Typedef(Type(ud.fqClassName()), ud.name))
+
+
+ def visitDecl(self, decl):
+ return _HybridDecl(decl.type, decl.progname)
+
+ def visitMessageDecl(self, md):
+ md.namespace = self.protocolName
+ md.params = [ param.accept(self) for param in md.inParams ]
+ md.returns = [ ret.accept(self) for ret in md.outParams ]
+ MessageDecl.upgrade(md)
+
+ def visitTransitionStmt(self, ts):
+ name = ts.state.decl.progname
+ ts.state.decl.cxxname = name
+ ts.state.decl.cxxenum = ExprVar(self.protocolName +'::'+ name)
+
+##-----------------------------------------------------------------------------
+
+def msgenums(protocol, pretty=False):
+ msgenum = TypeEnum('MessageType')
+ msgstart = _messageStartName(protocol.decl.type) +' << 16'
+ msgenum.addId(protocol.name + 'Start', msgstart)
+
+ for md in protocol.messageDecls:
+ msgenum.addId(md.prettyMsgName() if pretty else md.msgId())
+ if md.hasReply():
+ msgenum.addId(md.prettyReplyName() if pretty else md.replyId())
+
+ msgenum.addId(protocol.name +'End')
+ return msgenum
+
+class _GenerateProtocolCode(ipdl.ast.Visitor):
+ '''Creates code common to both the parent and child actors.'''
+ def __init__(self):
+ self.protocol = None # protocol we're generating a class for
+ self.hdrfile = None # what will become Protocol.h
+ self.cppfile = None # what will become Protocol.cpp
+ self.cppIncludeHeaders = []
+ self.structUnionDefns = []
+ self.funcDefns = []
+
+ def lower(self, tu, cxxHeaderFile, cxxFile):
+ self.protocol = tu.protocol
+ self.hdrfile = cxxHeaderFile
+ self.cppfile = cxxFile
+ tu.accept(self)
+
+ def visitTranslationUnit(self, tu):
+ hf = self.hdrfile
+
+ hf.addthing(_DISCLAIMER)
+ hf.addthings(_includeGuardStart(hf))
+ hf.addthing(Whitespace.NL)
+
+ for inc in builtinHeaderIncludes:
+ self.visitBuiltinCxxInclude(inc)
+
+ # Compute the set of includes we need for declared structure/union
+ # classes for this protocol.
+ typesToIncludes = {}
+ for using in tu.using:
+ typestr = str(using.type.spec)
+ assert typestr not in typesToIncludes
+ typesToIncludes[typestr] = using.header
+
+ aggregateTypeIncludes = set()
+ for su in tu.structsAndUnions:
+ typedeps = _ComputeTypeDeps(su.decl.type, True)
+ if isinstance(su, ipdl.ast.StructDecl):
+ for f in su.fields:
+ f.ipdltype.accept(typedeps)
+ elif isinstance(su, ipdl.ast.UnionDecl):
+ for c in su.components:
+ c.ipdltype.accept(typedeps)
+
+ for typename in [t.fromtype.name for t in typedeps.usingTypedefs]:
+ if typename in typesToIncludes:
+ aggregateTypeIncludes.add(typesToIncludes[typename])
+
+ if len(aggregateTypeIncludes) != 0:
+ hf.addthing(Whitespace.NL)
+ hf.addthings([ Whitespace("// Headers for typedefs"), Whitespace.NL ])
+
+ for headername in sorted(iter(aggregateTypeIncludes)):
+ hf.addthing(CppDirective('include', '"' + headername + '"'))
+
+ ipdl.ast.Visitor.visitTranslationUnit(self, tu)
+ if tu.filetype == 'header':
+ self.cppIncludeHeaders.append(_ipdlhHeaderName(tu))
+
+ hf.addthing(Whitespace.NL)
+ hf.addthings(_includeGuardEnd(hf))
+
+ cf = self.cppfile
+ cf.addthings((
+ [ _DISCLAIMER, Whitespace.NL ]
+ + [ CppDirective('include','"'+h+'.h"')
+ for h in self.cppIncludeHeaders ]
+ + [ Whitespace.NL ]
+ ))
+
+ if self.protocol:
+ # construct the namespace into which we'll stick all our defns
+ ns = Namespace(self.protocol.name)
+ cf.addthing(_putInNamespaces(ns, self.protocol.namespaces))
+ ns.addstmts(([ Whitespace.NL]
+ + self.funcDefns
+ +[ Whitespace.NL ]))
+
+ cf.addthings(self.structUnionDefns)
+
+
+ def visitBuiltinCxxInclude(self, inc):
+ self.hdrfile.addthing(CppDirective('include', '"'+ inc.file +'"'))
+
+ def visitInclude(self, inc):
+ if inc.tu.filetype == 'header':
+ self.hdrfile.addthing(CppDirective(
+ 'include', '"'+ _ipdlhHeaderName(inc.tu) +'.h"'))
+
+ def processStructOrUnionClass(self, su, which, forwarddecls, cls):
+ clsdecl, methoddefns = _splitClassDeclDefn(cls)
+
+ self.hdrfile.addthings(
+ [ Whitespace.NL ]
+ + forwarddecls
+ + [ Whitespace("""
+//-----------------------------------------------------------------------------
+// Declaration of the IPDL type |%s %s|
+//
+"""% (which, su.name)),
+ _putInNamespaces(clsdecl, su.namespaces),
+ ])
+
+ self.structUnionDefns.extend([
+ Whitespace("""
+//-----------------------------------------------------------------------------
+// Method definitions for the IPDL type |%s %s|
+//
+"""% (which, su.name)),
+ _putInNamespaces(methoddefns, su.namespaces),
+ ])
+
+ def visitStructDecl(self, sd):
+ return self.processStructOrUnionClass(sd, 'struct',
+ *_generateCxxStruct(sd))
+
+ def visitUnionDecl(self, ud):
+ return self.processStructOrUnionClass(ud, 'union',
+ *_generateCxxUnion(ud))
+
+ def visitProtocol(self, p):
+ self.cppIncludeHeaders.append(_protocolHeaderName(self.protocol, ''))
+
+ # Forward declare our own actors.
+ self.hdrfile.addthings([
+ Whitespace.NL,
+ _makeForwardDeclForActor(p.decl.type, 'Parent'),
+ _makeForwardDeclForActor(p.decl.type, 'Child')
+ ])
+
+ bridges = ProcessGraph.bridgesOf(p.decl.type)
+ for bridge in bridges:
+ ppt, pside = bridge.parent.ptype, _otherSide(bridge.parent.side)
+ cpt, cside = bridge.child.ptype, _otherSide(bridge.child.side)
+ self.hdrfile.addthings([
+ Whitespace.NL,
+ _makeForwardDeclForActor(ppt, pside),
+ _makeForwardDeclForActor(cpt, cside)
+ ])
+ self.cppIncludeHeaders.append(_protocolHeaderName(ppt._ast, pside))
+ self.cppIncludeHeaders.append(_protocolHeaderName(cpt._ast, cside))
+
+ opens = ProcessGraph.opensOf(p.decl.type)
+ for o in opens:
+ optype, oside = o.opener.ptype, o.opener.side
+ self.hdrfile.addthings([
+ Whitespace.NL,
+ _makeForwardDeclForActor(optype, oside)
+ ])
+ self.cppIncludeHeaders.append(_protocolHeaderName(optype._ast, oside))
+
+ self.hdrfile.addthing(Whitespace("""
+//-----------------------------------------------------------------------------
+// Code common to %sChild and %sParent
+//
+"""% (p.name, p.name)))
+
+ # construct the namespace into which we'll stick all our decls
+ ns = Namespace(self.protocol.name)
+ self.hdrfile.addthing(_putInNamespaces(ns, p.namespaces))
+ ns.addstmt(Whitespace.NL)
+
+ # user-facing methods for connecting two process with a new channel
+ for bridge in bridges:
+ bdecl, bdefn = _splitFuncDeclDefn(self.genBridgeFunc(bridge))
+ ns.addstmts([ bdecl, Whitespace.NL ])
+ self.funcDefns.append(bdefn)
+
+ # user-facing methods for opening a new channel across two
+ # existing endpoints
+ for o in opens:
+ odecl, odefn = _splitFuncDeclDefn(self.genOpenFunc(o))
+ ns.addstmts([ odecl, Whitespace.NL ])
+ self.funcDefns.append(odefn)
+
+ edecl, edefn = _splitFuncDeclDefn(self.genEndpointFunc())
+ ns.addstmts([ edecl, Whitespace.NL ])
+ self.funcDefns.append(edefn)
+
+ # state information
+ stateenum = TypeEnum('State')
+ # NB: __Dead is the first state on purpose, so that it has
+ # value '0'
+ stateenum.addId(_deadState().name)
+ stateenum.addId(_nullState().name)
+ stateenum.addId(_errorState().name)
+ stateenum.addId(_dyingState().name)
+ for ts in p.transitionStmts:
+ stateenum.addId(ts.state.decl.cxxname)
+ if len(p.transitionStmts):
+ startstate = p.transitionStmts[0].state.decl.cxxname
+ else:
+ startstate = _nullState().name
+ stateenum.addId(_startState().name, startstate)
+
+ ns.addstmts([ StmtDecl(Decl(stateenum,'')), Whitespace.NL ])
+
+ # spit out message type enum and classes
+ msgenum = msgenums(self.protocol)
+ ns.addstmts([ StmtDecl(Decl(msgenum, '')), Whitespace.NL ])
+
+ tfDecl, tfDefn = _splitFuncDeclDefn(self.genTransitionFunc())
+ ns.addstmts([ tfDecl, Whitespace.NL ])
+ self.funcDefns.append(tfDefn)
+
+ for md in p.messageDecls:
+ decls = []
+
+ mfDecl, mfDefn = _splitFuncDeclDefn(
+ _generateMessageConstructor(md.msgCtorFunc(), md.msgId(),
+ md.decl.type.nested,
+ md.decl.type.prio,
+ md.prettyMsgName(p.name+'::'),
+ md.decl.type.compress))
+ decls.append(mfDecl)
+ self.funcDefns.append(mfDefn)
+
+ if md.hasReply():
+ rfDecl, rfDefn = _splitFuncDeclDefn(
+ _generateMessageConstructor(
+ md.replyCtorFunc(), md.replyId(),
+ md.decl.type.nested,
+ md.decl.type.prio,
+ md.prettyReplyName(p.name+'::'),
+ md.decl.type.compress))
+ decls.append(rfDecl)
+ self.funcDefns.append(rfDefn)
+
+ decls.append(Whitespace.NL)
+ ns.addstmts(decls)
+
+ ns.addstmts([ Whitespace.NL, Whitespace.NL ])
+
+
+ def genBridgeFunc(self, bridge):
+ p = self.protocol
+ parentHandleType = _cxxBareType(ActorType(bridge.parent.ptype),
+ _otherSide(bridge.parent.side),
+ fq=1)
+ parentvar = ExprVar('parentHandle')
+
+ childHandleType = _cxxBareType(ActorType(bridge.child.ptype),
+ _otherSide(bridge.child.side),
+ fq=1)
+ childvar = ExprVar('childHandle')
+
+ bridgefunc = MethodDefn(MethodDecl(
+ 'Bridge',
+ params=[ Decl(parentHandleType, parentvar.name),
+ Decl(childHandleType, childvar.name) ],
+ ret=Type.NSRESULT))
+ bridgefunc.addstmt(StmtReturn(ExprCall(
+ ExprVar('mozilla::ipc::Bridge'),
+ args=[ _backstagePass(),
+ p.callGetChannel(parentvar), p.callOtherPid(parentvar),
+ p.callGetChannel(childvar), p.callOtherPid(childvar),
+ _protocolId(p.decl.type),
+ ExprVar(_messageStartName(p.decl.type) + 'Child')
+ ])))
+ return bridgefunc
+
+
+ def genOpenFunc(self, o):
+ p = self.protocol
+ localside = o.opener.side
+ openertype = _cxxBareType(ActorType(o.opener.ptype), o.opener.side,
+ fq=1)
+ openervar = ExprVar('opener')
+ openfunc = MethodDefn(MethodDecl(
+ 'Open',
+ params=[ Decl(openertype, openervar.name) ],
+ ret=Type.BOOL))
+ openfunc.addstmt(StmtReturn(ExprCall(
+ ExprVar('mozilla::ipc::Open'),
+ args=[ _backstagePass(),
+ p.callGetChannel(openervar), p.callOtherPid(openervar),
+ _sideToTransportMode(localside),
+ _protocolId(p.decl.type),
+ ExprVar(_messageStartName(p.decl.type) + 'Child')
+ ])))
+ return openfunc
+
+
+ # Generate code for PFoo::CreateEndpoints.
+ def genEndpointFunc(self):
+ p = self.protocol.decl.type
+ tparent = _cxxBareType(ActorType(p), 'Parent', fq=1)
+ tchild = _cxxBareType(ActorType(p), 'Child', fq=1)
+ methodvar = ExprVar('CreateEndpoints')
+ rettype = Type.NSRESULT
+ parentpidvar = ExprVar('aParentDestPid')
+ childpidvar = ExprVar('aChildDestPid')
+ parentvar = ExprVar('aParent')
+ childvar = ExprVar('aChild')
+
+ openfunc = MethodDefn(MethodDecl(
+ methodvar.name,
+ params=[ Decl(Type('base::ProcessId'), parentpidvar.name),
+ Decl(Type('base::ProcessId'), childpidvar.name),
+ Decl(Type('mozilla::ipc::Endpoint<' + tparent.name + '>', ptr=1), parentvar.name),
+ Decl(Type('mozilla::ipc::Endpoint<' + tchild.name + '>', ptr=1), childvar.name) ],
+ ret=rettype))
+ openfunc.addstmt(StmtReturn(ExprCall(
+ ExprVar('mozilla::ipc::CreateEndpoints'),
+ args=[ _backstagePass(),
+ parentpidvar, childpidvar,
+ _protocolId(p),
+ ExprVar(_messageStartName(p) + 'Child'),
+ parentvar, childvar
+ ])))
+ return openfunc
+
+
+ def genTransitionFunc(self):
+ ptype = self.protocol.decl.type
+ usesend, sendvar = set(), ExprVar('Send__')
+ userecv, recvvar = set(), ExprVar('Recv__')
+
+ def sameTrigger(trigger, actionexpr):
+ if trigger is ipdl.ast.SEND or trigger is ipdl.ast.CALL:
+ usesend.add('yes')
+ return ExprBinary(sendvar, '==', actionexpr)
+ else:
+ userecv.add('yes')
+ return ExprBinary(recvvar, '==',
+ actionexpr)
+
+ def stateEnum(s):
+ if s is ipdl.ast.State.DEAD:
+ return _deadState()
+ else:
+ return ExprVar(s.decl.cxxname)
+
+ # bool Transition(Trigger trigger, State* next)
+ # The state we are transitioning from is stored in *next.
+ fromvar = ExprVar('from')
+ triggervar = ExprVar('trigger')
+ nextvar = ExprVar('next')
+ msgexpr = ExprSelect(triggervar, '.', 'mMessage')
+ actionexpr = ExprSelect(triggervar, '.', 'mAction')
+
+ transitionfunc = FunctionDefn(FunctionDecl(
+ 'Transition',
+ params=[ Decl(Type('mozilla::ipc::Trigger'), triggervar.name),
+ Decl(Type('State', ptr=1), nextvar.name) ],
+ ret=Type.BOOL))
+
+ fromswitch = StmtSwitch(fromvar)
+
+ for ts in self.protocol.transitionStmts:
+ msgswitch = StmtSwitch(msgexpr)
+
+ msgToTransitions = { }
+
+ for t in ts.transitions:
+ msgid = t.msg._md.msgId()
+
+ ifsametrigger = StmtIf(sameTrigger(t.trigger, actionexpr))
+ # FIXME multi-out states
+ for nextstate in t.toStates: break
+ ifsametrigger.addifstmts([
+ StmtExpr(ExprAssn(ExprDeref(nextvar),
+ stateEnum(nextstate))),
+ StmtReturn(ExprLiteral.TRUE)
+ ])
+
+ transitions = msgToTransitions.get(msgid, [ ])
+ transitions.append(ifsametrigger)
+ msgToTransitions[msgid] = transitions
+
+ for msgid, transitions in msgToTransitions.iteritems():
+ block = Block()
+ block.addstmts(transitions +[ StmtBreak() ])
+ msgswitch.addcase(CaseLabel(msgid), block)
+
+ msgblock = Block()
+ msgblock.addstmts([
+ msgswitch,
+ StmtBreak()
+ ])
+ fromswitch.addcase(CaseLabel(ts.state.decl.cxxname), msgblock)
+
+ # special cases for Null and Error
+ nullerrorblock = Block()
+ if ptype.hasDelete:
+ ifdelete = StmtIf(ExprBinary(_deleteId(), '==', msgexpr))
+ if ptype.hasReentrantDelete:
+ nextState = _dyingState()
+ else:
+ nextState = _deadState()
+ ifdelete.addifstmts([
+ StmtExpr(ExprAssn(ExprDeref(nextvar), nextState)),
+ StmtReturn(ExprLiteral.TRUE) ])
+ nullerrorblock.addstmt(ifdelete)
+ nullerrorblock.addstmt(
+ StmtReturn(ExprBinary(_nullState(), '==', fromvar)))
+ fromswitch.addfallthrough(CaseLabel(_nullState().name))
+ fromswitch.addcase(CaseLabel(_errorState().name), nullerrorblock)
+
+ # special case for Dead
+ deadblock = Block()
+ deadblock.addstmts([
+ _logicError('__delete__()d actor'),
+ StmtReturn(ExprLiteral.FALSE) ])
+ fromswitch.addcase(CaseLabel(_deadState().name), deadblock)
+
+ # special case for Dying
+ dyingblock = Block()
+ if ptype.hasReentrantDelete:
+ ifdelete = StmtIf(ExprBinary(_deleteReplyId(), '==', msgexpr))
+ ifdelete.addifstmt(
+ StmtExpr(ExprAssn(ExprDeref(nextvar), _deadState())))
+ dyingblock.addstmt(ifdelete)
+ dyingblock.addstmt(
+ StmtReturn(ExprLiteral.TRUE))
+ else:
+ dyingblock.addstmts([
+ _logicError('__delete__()d (and unexpectedly dying) actor'),
+ StmtReturn(ExprLiteral.FALSE) ])
+ fromswitch.addcase(CaseLabel(_dyingState().name), dyingblock)
+
+ unreachedblock = Block()
+ unreachedblock.addstmts([
+ _logicError('corrupted actor state'),
+ StmtReturn(ExprLiteral.FALSE) ])
+ fromswitch.addcase(DefaultLabel(), unreachedblock)
+
+ if usesend:
+ transitionfunc.addstmt(
+ StmtDecl(Decl(Type('int32_t', const=1), sendvar.name),
+ init=ExprVar('mozilla::ipc::Trigger::Send')))
+ if userecv:
+ transitionfunc.addstmt(
+ StmtDecl(Decl(Type('int32_t', const=1), recvvar.name),
+ init=ExprVar('mozilla::ipc::Trigger::Recv')))
+ if usesend or userecv:
+ transitionfunc.addstmt(Whitespace.NL)
+
+ transitionfunc.addstmt(StmtDecl(Decl(Type('State'), fromvar.name),
+ init=ExprDeref(nextvar)))
+ transitionfunc.addstmt(fromswitch)
+ # all --> Error transitions break to here. But only insert this
+ # block if there is any possibility of such transitions.
+ if self.protocol.transitionStmts:
+ transitionfunc.addstmts([
+ StmtExpr(ExprAssn(ExprDeref(nextvar), _errorState())),
+ StmtReturn(ExprLiteral.FALSE),
+ ])
+
+ return transitionfunc
+
+##--------------------------------------------------
+
+def _generateMessageConstructor(clsname, msgid, nested, prio, prettyName, compress):
+ routingId = ExprVar('routingId')
+
+ func = FunctionDefn(FunctionDecl(
+ clsname,
+ params=[ Decl(Type('int32_t'), routingId.name) ],
+ ret=Type('IPC::Message', ptr=1)))
+
+ if compress == 'compress':
+ compression = ExprVar('IPC::Message::COMPRESSION_ENABLED')
+ elif compress:
+ assert compress == 'compressall'
+ compression = ExprVar('IPC::Message::COMPRESSION_ALL')
+ else:
+ compression = ExprVar('IPC::Message::COMPRESSION_NONE')
+
+ if nested == ipdl.ast.NOT_NESTED:
+ nestedEnum = 'IPC::Message::NOT_NESTED'
+ elif nested == ipdl.ast.INSIDE_SYNC_NESTED:
+ nestedEnum = 'IPC::Message::NESTED_INSIDE_SYNC'
+ else:
+ assert nested == ipdl.ast.INSIDE_CPOW_NESTED
+ nestedEnum = 'IPC::Message::NESTED_INSIDE_CPOW'
+
+ if prio == ipdl.ast.NORMAL_PRIORITY:
+ prioEnum = 'IPC::Message::NORMAL_PRIORITY'
+ else:
+ assert prio == ipdl.ast.HIGH_PRIORITY
+ prioEnum = 'IPC::Message::HIGH_PRIORITY'
+
+ func.addstmt(
+ StmtReturn(ExprNew(Type('IPC::Message'),
+ args=[ routingId,
+ ExprVar(msgid),
+ ExprVar(nestedEnum),
+ ExprVar(prioEnum),
+ compression,
+ ExprLiteral.String(prettyName) ])))
+
+ return func
+
+##--------------------------------------------------
+
+class _ComputeTypeDeps(TypeVisitor):
+ '''Pass that gathers the C++ types that a particular IPDL type
+(recursively) depends on. There are two kinds of dependencies: (i)
+types that need forward declaration; (ii) types that need a |using|
+stmt. Some types generate both kinds.'''
+
+ def __init__(self, fortype, unqualifiedTypedefs=False):
+ ipdl.type.TypeVisitor.__init__(self)
+ self.usingTypedefs = [ ]
+ self.forwardDeclStmts = [ ]
+ self.fortype = fortype
+ self.unqualifiedTypedefs = unqualifiedTypedefs
+
+ def maybeTypedef(self, fqname, name):
+ if fqname != name or self.unqualifiedTypedefs:
+ self.usingTypedefs.append(Typedef(Type(fqname), name))
+
+ def visitBuiltinCxxType(self, t):
+ if t in self.visited: return
+ self.visited.add(t)
+ self.maybeTypedef(t.fullname(), t.name())
+
+ def visitImportedCxxType(self, t):
+ if t in self.visited: return
+ self.visited.add(t)
+ self.maybeTypedef(t.fullname(), t.name())
+
+ def visitActorType(self, t):
+ if t in self.visited: return
+ self.visited.add(t)
+
+ fqname, name = t.fullname(), t.name()
+
+ self.maybeTypedef(_actorName(fqname, 'Parent'),
+ _actorName(name, 'Parent'))
+ self.maybeTypedef(_actorName(fqname, 'Child'),
+ _actorName(name, 'Child'))
+
+ self.forwardDeclStmts.extend([
+ _makeForwardDeclForActor(t.protocol, 'parent'), Whitespace.NL,
+ _makeForwardDeclForActor(t.protocol, 'child'), Whitespace.NL
+ ])
+
+ def visitStructOrUnionType(self, su, defaultVisit):
+ if su in self.visited or su == self.fortype: return
+ self.visited.add(su)
+ self.maybeTypedef(su.fullname(), su.name())
+
+ if su.mutuallyRecursiveWith(self.fortype):
+ self.forwardDeclStmts.append(_makeForwardDecl(su))
+
+ return defaultVisit(self, su)
+
+ def visitStructType(self, t):
+ return self.visitStructOrUnionType(t, TypeVisitor.visitStructType)
+
+ def visitUnionType(self, t):
+ return self.visitStructOrUnionType(t, TypeVisitor.visitUnionType)
+
+ def visitArrayType(self, t):
+ return TypeVisitor.visitArrayType(self, t)
+
+ def visitShmemType(self, s):
+ if s in self.visited: return
+ self.visited.add(s)
+ self.maybeTypedef('mozilla::ipc::Shmem', 'Shmem')
+
+ def visitFDType(self, s):
+ if s in self.visited: return
+ self.visited.add(s)
+ self.maybeTypedef('mozilla::ipc::FileDescriptor', 'FileDescriptor')
+
+ def visitVoidType(self, v): assert 0
+ def visitMessageType(self, v): assert 0
+ def visitProtocolType(self, v): assert 0
+ def visitStateType(self, v): assert 0
+
+
+def _generateCxxStruct(sd):
+ ''' '''
+ # compute all the typedefs and forward decls we need to make
+ gettypedeps = _ComputeTypeDeps(sd.decl.type)
+ for f in sd.fields:
+ f.ipdltype.accept(gettypedeps)
+
+ usingTypedefs = gettypedeps.usingTypedefs
+ forwarddeclstmts = gettypedeps.forwardDeclStmts
+
+ struct = Class(sd.name, final=1)
+ struct.addstmts([ Label.PRIVATE ]
+ + usingTypedefs
+ + [ Whitespace.NL, Label.PUBLIC ])
+
+ constreftype = Type(sd.name, const=1, ref=1)
+ initvar = ExprVar('Init')
+ callinit = ExprCall(initvar)
+ assignvar = ExprVar('Assign')
+
+ def fieldsAsParamList():
+ return [ Decl(f.inType(), f.argVar().name) for f in sd.fields ]
+
+ def assignFromOther(oexpr):
+ return ExprCall(assignvar,
+ args=[ f.initExpr(oexpr) for f in sd.fields ])
+
+ # If this is an empty struct (no fields), then the default ctor
+ # and "create-with-fields" ctors are equivalent. So don't bother
+ # with the default ctor.
+ if len(sd.fields):
+ # Struct()
+ defctor = ConstructorDefn(ConstructorDecl(sd.name))
+ defctor.addstmt(StmtExpr(callinit))
+ defctor.memberinits = []
+ for f in sd.fields:
+ # Only generate default values for primitives.
+ if not (f.ipdltype.isCxx() and f.ipdltype.isAtom()):
+ continue
+ defctor.memberinits.append(ExprMemberInit(f.memberVar()))
+ struct.addstmts([ defctor, Whitespace.NL ])
+
+ # Struct(const field1& _f1, ...)
+ valctor = ConstructorDefn(ConstructorDecl(sd.name,
+ params=fieldsAsParamList(),
+ force_inline=1))
+ valctor.addstmts([
+ StmtExpr(callinit),
+ StmtExpr(ExprCall(assignvar,
+ args=[ f.argVar() for f in sd.fields ]))
+ ])
+ struct.addstmts([ valctor, Whitespace.NL ])
+
+ # Struct(const Struct& _o)
+ ovar = ExprVar('_o')
+ copyctor = ConstructorDefn(ConstructorDecl(
+ sd.name,
+ params=[ Decl(constreftype, ovar.name) ],
+ force_inline=1))
+ copyctor.addstmts([
+ StmtExpr(callinit),
+ StmtExpr(assignFromOther(ovar))
+ ])
+ struct.addstmts([ copyctor, Whitespace.NL ])
+
+ # ~Struct()
+ dtor = DestructorDefn(DestructorDecl(sd.name))
+ for f in sd.fields:
+ dtor.addstmts(f.destructStmts())
+ struct.addstmts([ dtor, Whitespace.NL ])
+
+ # Struct& operator=(const Struct& _o)
+ opeq = MethodDefn(MethodDecl(
+ 'operator=',
+ params=[ Decl(constreftype, ovar.name) ],
+ force_inline=1))
+ opeq.addstmt(StmtExpr(assignFromOther(ovar)))
+ struct.addstmts([ opeq, Whitespace.NL ])
+
+ # bool operator==(const Struct& _o)
+ opeqeq = MethodDefn(MethodDecl(
+ 'operator==',
+ params=[ Decl(constreftype, ovar.name) ],
+ ret=Type.BOOL,
+ const=1))
+ for f in sd.fields:
+ ifneq = StmtIf(ExprNot(
+ ExprBinary(ExprCall(f.getMethod()), '==',
+ ExprCall(f.getMethod(ovar)))))
+ ifneq.addifstmt(StmtReturn.FALSE)
+ opeqeq.addstmt(ifneq)
+ opeqeq.addstmt(StmtReturn.TRUE)
+ struct.addstmts([ opeqeq, Whitespace.NL ])
+
+ # field1& f1()
+ # const field1& f1() const
+ for f in sd.fields:
+ get = MethodDefn(MethodDecl(f.getMethod().name,
+ params=[ ],
+ ret=f.refType(),
+ force_inline=1))
+ get.addstmt(StmtReturn(f.refExpr()))
+
+ getconstdecl = deepcopy(get.decl)
+ getconstdecl.ret = f.constRefType()
+ getconstdecl.const = 1
+ getconst = MethodDefn(getconstdecl)
+ getconst.addstmt(StmtReturn(f.constRefExpr()))
+
+ struct.addstmts([ get, getconst, Whitespace.NL ])
+
+ # private:
+ struct.addstmt(Label.PRIVATE)
+
+ # Init()
+ init = MethodDefn(MethodDecl(initvar.name))
+ for f in sd.fields:
+ init.addstmts(f.initStmts())
+ struct.addstmts([ init, Whitespace.NL ])
+
+ # Assign(const field1& _f1, ...)
+ assign = MethodDefn(MethodDecl(assignvar.name,
+ params=fieldsAsParamList()))
+ assign.addstmts([ StmtExpr(ExprAssn(f.refExpr(), f.argVar()))
+ for f in sd.fields ])
+ struct.addstmts([ assign, Whitespace.NL ])
+
+ # members
+ struct.addstmts([ StmtDecl(Decl(f.internalType(), f.memberVar().name))
+ for f in sd.fields ])
+
+ return forwarddeclstmts, struct
+
+##--------------------------------------------------
+
+def _generateCxxUnion(ud):
+ # This Union class basically consists of a type (enum) and a
+ # union for storage. The union can contain POD and non-POD
+ # types. Each type needs a copy ctor, assignment operator,
+ # and dtor.
+ #
+ # Rather than templating this class and only providing
+ # specializations for the types we support, which is slightly
+ # "unsafe" in that C++ code can add additional specializations
+ # without the IPDL compiler's knowledge, we instead explicitly
+ # implement non-templated methods for each supported type.
+ #
+ # The one complication that arises is that C++, for arcane
+ # reasons, does not allow the placement destructor of a
+ # builtin type, like int, to be directly invoked. So we need
+ # to hack around this by internally typedef'ing all
+ # constituent types. Sigh.
+ #
+ # So, for each type, this "Union" class needs:
+ # (private)
+ # - entry in the type enum
+ # - entry in the storage union
+ # - [type]ptr() method to get a type* from the underlying union
+ # - same as above to get a const type*
+ # - typedef to hack around placement delete limitations
+ # (public)
+ # - placement delete case for dtor
+ # - copy ctor
+ # - case in generic copy ctor
+ # - operator= impl
+ # - case in generic operator=
+ # - operator [type&]
+ # - operator [const type&] const
+ # - [type&] get_[type]()
+ # - [const type&] get_[type]() const
+ #
+ cls = Class(ud.name, final=1)
+ # const Union&, i.e., Union type with inparam semantics
+ inClsType = Type(ud.name, const=1, ref=1)
+ refClsType = Type(ud.name, ref=1)
+ typetype = Type('Type')
+ valuetype = Type('Value')
+ mtypevar = ExprVar('mType')
+ mvaluevar = ExprVar('mValue')
+ maybedtorvar = ExprVar('MaybeDestroy')
+ assertsanityvar = ExprVar('AssertSanity')
+ tnonevar = ExprVar('T__None')
+ tlastvar = ExprVar('T__Last')
+
+ def callAssertSanity(uvar=None, expectTypeVar=None):
+ func = assertsanityvar
+ args = [ ]
+ if uvar is not None:
+ func = ExprSelect(uvar, '.', assertsanityvar.name)
+ if expectTypeVar is not None:
+ args.append(expectTypeVar)
+ return ExprCall(func, args=args)
+
+ def callMaybeDestroy(newTypeVar):
+ return ExprCall(maybedtorvar, args=[ newTypeVar ])
+
+ def maybeReconstruct(memb, newTypeVar):
+ ifdied = StmtIf(callMaybeDestroy(newTypeVar))
+ ifdied.addifstmt(StmtExpr(memb.callCtor()))
+ return ifdied
+
+ # compute all the typedefs and forward decls we need to make
+ gettypedeps = _ComputeTypeDeps(ud.decl.type)
+ for c in ud.components:
+ c.ipdltype.accept(gettypedeps)
+
+ usingTypedefs = gettypedeps.usingTypedefs
+ forwarddeclstmts = gettypedeps.forwardDeclStmts
+
+ # the |Type| enum, used to switch on the discunion's real type
+ cls.addstmt(Label.PUBLIC)
+ typeenum = TypeEnum(typetype.name)
+ typeenum.addId(tnonevar.name, 0)
+ firstid = ud.components[0].enum()
+ typeenum.addId(firstid, 1)
+ for c in ud.components[1:]:
+ typeenum.addId(c.enum())
+ typeenum.addId(tlastvar.name, ud.components[-1].enum())
+ cls.addstmts([ StmtDecl(Decl(typeenum,'')),
+ Whitespace.NL ])
+
+ cls.addstmt(Label.PRIVATE)
+ cls.addstmts(
+ usingTypedefs
+ # hacky typedef's that allow placement dtors of builtins
+ + [ Typedef(c.internalType(), c.typedef()) for c in ud.components ])
+ cls.addstmt(Whitespace.NL)
+
+ # the C++ union the discunion use for storage
+ valueunion = TypeUnion(valuetype.name)
+ for c in ud.components:
+ valueunion.addComponent(c.unionType(), c.name)
+ cls.addstmts([ StmtDecl(Decl(valueunion,'')),
+ Whitespace.NL ])
+
+ # for each constituent type T, add private accessors that
+ # return a pointer to the Value union storage casted to |T*|
+ # and |const T*|
+ for c in ud.components:
+ getptr = MethodDefn(MethodDecl(
+ c.getPtrName(), params=[ ], ret=c.ptrToInternalType(),
+ force_inline=1))
+ getptr.addstmt(StmtReturn(c.ptrToSelfExpr()))
+
+ getptrconst = MethodDefn(MethodDecl(
+ c.getConstPtrName(), params=[ ], ret=c.constPtrToType(),
+ const=1, force_inline=1))
+ getptrconst.addstmt(StmtReturn(c.constptrToSelfExpr()))
+
+ cls.addstmts([ getptr, getptrconst ])
+ cls.addstmt(Whitespace.NL)
+
+ # add a helper method that invokes the placement dtor on the
+ # current underlying value, only if |aNewType| is different
+ # than the current type, and returns true if the underlying
+ # value needs to be re-constructed
+ newtypevar = ExprVar('aNewType')
+ maybedtor = MethodDefn(MethodDecl(
+ maybedtorvar.name,
+ params=[ Decl(typetype, newtypevar.name) ],
+ ret=Type.BOOL))
+ # wasn't /actually/ dtor'd, but it needs to be re-constructed
+ ifnone = StmtIf(ExprBinary(mtypevar, '==', tnonevar))
+ ifnone.addifstmt(StmtReturn.TRUE)
+ # same type, nothing to see here
+ ifnochange = StmtIf(ExprBinary(mtypevar, '==', newtypevar))
+ ifnochange.addifstmt(StmtReturn.FALSE)
+ # need to destroy. switch on underlying type
+ dtorswitch = StmtSwitch(mtypevar)
+ for c in ud.components:
+ dtorswitch.addcase(
+ CaseLabel(c.enum()),
+ StmtBlock([ StmtExpr(c.callDtor()),
+ StmtBreak() ]))
+ dtorswitch.addcase(
+ DefaultLabel(),
+ StmtBlock([ _logicError("not reached"), StmtBreak() ]))
+ maybedtor.addstmts([
+ ifnone,
+ ifnochange,
+ dtorswitch,
+ StmtReturn.TRUE
+ ])
+ cls.addstmts([ maybedtor, Whitespace.NL ])
+
+ # add helper methods that ensure the discunion has a
+ # valid type
+ sanity = MethodDefn(MethodDecl(
+ assertsanityvar.name, ret=Type.VOID, const=1, force_inline=1))
+ sanity.addstmts([
+ _abortIfFalse(ExprBinary(tnonevar, '<=', mtypevar),
+ 'invalid type tag'),
+ _abortIfFalse(ExprBinary(mtypevar, '<=', tlastvar),
+ 'invalid type tag') ])
+ cls.addstmt(sanity)
+
+ atypevar = ExprVar('aType')
+ sanity2 = MethodDefn(
+ MethodDecl(assertsanityvar.name,
+ params=[ Decl(typetype, atypevar.name) ],
+ ret=Type.VOID,
+ const=1, force_inline=1))
+ sanity2.addstmts([
+ StmtExpr(ExprCall(assertsanityvar)),
+ _abortIfFalse(ExprBinary(mtypevar, '==', atypevar),
+ 'unexpected type tag') ])
+ cls.addstmts([ sanity2, Whitespace.NL ])
+
+ ## ---- begin public methods -----
+
+ # Union() default ctor
+ cls.addstmts([
+ Label.PUBLIC,
+ ConstructorDefn(
+ ConstructorDecl(ud.name, force_inline=1),
+ memberinits=[ ExprMemberInit(mtypevar, [ tnonevar ]) ]),
+ Whitespace.NL
+ ])
+
+ # Union(const T&) copy ctors
+ othervar = ExprVar('aOther')
+ for c in ud.components:
+ copyctor = ConstructorDefn(ConstructorDecl(
+ ud.name, params=[ Decl(c.inType(), othervar.name) ]))
+ copyctor.addstmts([
+ StmtExpr(c.callCtor(othervar)),
+ StmtExpr(ExprAssn(mtypevar, c.enumvar())) ])
+ cls.addstmts([ copyctor, Whitespace.NL ])
+
+ # Union(const Union&) copy ctor
+ copyctor = ConstructorDefn(ConstructorDecl(
+ ud.name, params=[ Decl(inClsType, othervar.name) ]))
+ othertype = ud.callType(othervar)
+ copyswitch = StmtSwitch(othertype)
+ for c in ud.components:
+ copyswitch.addcase(
+ CaseLabel(c.enum()),
+ StmtBlock([
+ StmtExpr(c.callCtor(
+ ExprCall(ExprSelect(othervar,
+ '.', c.getConstTypeName())))),
+ StmtBreak()
+ ]))
+ copyswitch.addcase(CaseLabel(tnonevar.name),
+ StmtBlock([ StmtBreak() ]))
+ copyswitch.addcase(
+ DefaultLabel(),
+ StmtBlock([ _logicError('unreached'), StmtReturn() ]))
+ copyctor.addstmts([
+ StmtExpr(callAssertSanity(uvar=othervar)),
+ copyswitch,
+ StmtExpr(ExprAssn(mtypevar, othertype))
+ ])
+ cls.addstmts([ copyctor, Whitespace.NL ])
+
+ # ~Union()
+ dtor = DestructorDefn(DestructorDecl(ud.name))
+ # The void cast prevents Coverity from complaining about missing return
+ # value checks.
+ dtor.addstmt(StmtExpr(ExprCast(callMaybeDestroy(tnonevar), Type.VOID,
+ static=1)))
+ cls.addstmts([ dtor, Whitespace.NL ])
+
+ # type()
+ typemeth = MethodDefn(MethodDecl('type', ret=typetype,
+ const=1, force_inline=1))
+ typemeth.addstmt(StmtReturn(mtypevar))
+ cls.addstmts([ typemeth, Whitespace.NL ])
+
+ # Union& operator=(const T&) methods
+ rhsvar = ExprVar('aRhs')
+ for c in ud.components:
+ opeq = MethodDefn(MethodDecl(
+ 'operator=',
+ params=[ Decl(c.inType(), rhsvar.name) ],
+ ret=refClsType))
+ opeq.addstmts([
+ # might need to placement-delete old value first
+ maybeReconstruct(c, c.enumvar()),
+ StmtExpr(c.callOperatorEq(rhsvar)),
+ StmtExpr(ExprAssn(mtypevar, c.enumvar())),
+ StmtReturn(ExprDeref(ExprVar.THIS))
+ ])
+ cls.addstmts([ opeq, Whitespace.NL ])
+
+ # Union& operator=(const Union&)
+ opeq = MethodDefn(MethodDecl(
+ 'operator=',
+ params=[ Decl(inClsType, rhsvar.name) ],
+ ret=refClsType))
+ rhstypevar = ExprVar('t')
+ opeqswitch = StmtSwitch(rhstypevar)
+ for c in ud.components:
+ case = StmtBlock()
+ case.addstmts([
+ maybeReconstruct(c, rhstypevar),
+ StmtExpr(c.callOperatorEq(
+ ExprCall(ExprSelect(rhsvar, '.', c.getConstTypeName())))),
+ StmtBreak()
+ ])
+ opeqswitch.addcase(CaseLabel(c.enum()), case)
+ opeqswitch.addcase(
+ CaseLabel(tnonevar.name),
+ # The void cast prevents Coverity from complaining about missing return
+ # value checks.
+ StmtBlock([ StmtExpr(ExprCast(callMaybeDestroy(rhstypevar), Type.VOID,
+ static=1)),
+ StmtBreak() ])
+ )
+ opeqswitch.addcase(
+ DefaultLabel(),
+ StmtBlock([ _logicError('unreached'), StmtBreak() ]))
+ opeq.addstmts([
+ StmtExpr(callAssertSanity(uvar=rhsvar)),
+ StmtDecl(Decl(typetype, rhstypevar.name), init=ud.callType(rhsvar)),
+ opeqswitch,
+ StmtExpr(ExprAssn(mtypevar, rhstypevar)),
+ StmtReturn(ExprDeref(ExprVar.THIS))
+ ])
+ cls.addstmts([ opeq, Whitespace.NL ])
+
+ # bool operator==(const T&)
+ for c in ud.components:
+ opeqeq = MethodDefn(MethodDecl(
+ 'operator==',
+ params=[ Decl(c.inType(), rhsvar.name) ],
+ ret=Type.BOOL,
+ const=1))
+ opeqeq.addstmt(StmtReturn(ExprBinary(
+ ExprCall(ExprVar(c.getTypeName())), '==', rhsvar)))
+ cls.addstmts([ opeqeq, Whitespace.NL ])
+
+ # bool operator==(const Union&)
+ opeqeq = MethodDefn(MethodDecl(
+ 'operator==',
+ params=[ Decl(inClsType, rhsvar.name) ],
+ ret=Type.BOOL,
+ const=1))
+ iftypesmismatch = StmtIf(ExprBinary(ud.callType(), '!=',
+ ud.callType(rhsvar)))
+ iftypesmismatch.addifstmt(StmtReturn.FALSE)
+ opeqeq.addstmts([ iftypesmismatch, Whitespace.NL ])
+
+ opeqeqswitch = StmtSwitch(ud.callType())
+ for c in ud.components:
+ case = StmtBlock()
+ case.addstmt(StmtReturn(ExprBinary(
+ ExprCall(ExprVar(c.getTypeName())), '==',
+ ExprCall(ExprSelect(rhsvar, '.', c.getTypeName())))))
+ opeqeqswitch.addcase(CaseLabel(c.enum()), case)
+ opeqeqswitch.addcase(
+ DefaultLabel(),
+ StmtBlock([ _logicError('unreached'),
+ StmtReturn.FALSE ]))
+ opeqeq.addstmt(opeqeqswitch)
+
+ cls.addstmts([ opeqeq, Whitespace.NL ])
+
+ # accessors for each type: operator T&, operator const T&,
+ # T& get(), const T& get()
+ for c in ud.components:
+ getValueVar = ExprVar(c.getTypeName())
+ getConstValueVar = ExprVar(c.getConstTypeName())
+
+ getvalue = MethodDefn(MethodDecl(getValueVar.name,
+ ret=c.refType(),
+ force_inline=1))
+ getvalue.addstmts([
+ StmtExpr(callAssertSanity(expectTypeVar=c.enumvar())),
+ StmtReturn(ExprDeref(c.callGetPtr()))
+ ])
+
+ getconstvalue = MethodDefn(MethodDecl(
+ getConstValueVar.name, ret=c.constRefType(),
+ const=1, force_inline=1))
+ getconstvalue.addstmts([
+ StmtExpr(callAssertSanity(expectTypeVar=c.enumvar())),
+ StmtReturn(c.getConstValue())
+ ])
+
+ readvalue = MethodDefn(MethodDecl(
+ 'get', ret=Type.VOID, const=1,
+ params=[Decl(c.ptrToType(), 'aOutValue')]))
+ readvalue.addstmts([
+ StmtExpr(ExprAssn(ExprDeref(ExprVar('aOutValue')),
+ ExprCall(getConstValueVar)))
+ ])
+
+ optype = MethodDefn(MethodDecl('', typeop=c.refType(), force_inline=1))
+ optype.addstmt(StmtReturn(ExprCall(getValueVar)))
+ opconsttype = MethodDefn(MethodDecl(
+ '', const=1, typeop=c.constRefType(), force_inline=1))
+ opconsttype.addstmt(StmtReturn(ExprCall(getConstValueVar)))
+
+ cls.addstmts([ getvalue, getconstvalue, readvalue,
+ optype, opconsttype,
+ Whitespace.NL ])
+
+ # private vars
+ cls.addstmts([
+ Label.PRIVATE,
+ StmtDecl(Decl(valuetype, mvaluevar.name)),
+ StmtDecl(Decl(typetype, mtypevar.name))
+ ])
+
+ return forwarddeclstmts, cls
+
+##-----------------------------------------------------------------------------
+
+class _FindFriends(ipdl.ast.Visitor):
+ def __init__(self):
+ self.mytype = None # ProtocolType
+ self.vtype = None # ProtocolType
+ self.friends = set() # set<ProtocolType>
+
+ def findFriends(self, ptype):
+ self.mytype = ptype
+ for toplvl in ptype.toplevels():
+ self.walkDownTheProtocolTree(toplvl);
+ return self.friends
+
+ # TODO could make this into a _iterProtocolTreeHelper ...
+ def walkDownTheProtocolTree(self, ptype):
+ if ptype != self.mytype:
+ # don't want to |friend| ourself!
+ self.visit(ptype)
+ for mtype in ptype.manages:
+ if mtype is not ptype:
+ self.walkDownTheProtocolTree(mtype)
+
+ def visit(self, ptype):
+ # |vtype| is the type currently being visited
+ savedptype = self.vtype
+ self.vtype = ptype
+ ptype._ast.accept(self)
+ self.vtype = savedptype
+
+ def visitMessageDecl(self, md):
+ for it in self.iterActorParams(md):
+ if it.protocol == self.mytype:
+ self.friends.add(self.vtype)
+
+ def iterActorParams(self, md):
+ for param in md.inParams:
+ for actor in ipdl.type.iteractortypes(param.type):
+ yield actor
+ for ret in md.outParams:
+ for actor in ipdl.type.iteractortypes(ret.type):
+ yield actor
+
+
+class _GenerateProtocolActorCode(ipdl.ast.Visitor):
+ def __init__(self, myside):
+ self.side = myside # "parent" or "child"
+ self.prettyside = myside.title()
+ self.clsname = None
+ self.protocol = None
+ self.hdrfile = None
+ self.cppfile = None
+ self.ns = None
+ self.cls = None
+ self.includedActorTypedefs = [ ]
+ self.protocolCxxIncludes = [ ]
+ self.actorForwardDecls = [ ]
+ self.usingDecls = [ ]
+ self.externalIncludes = set()
+ self.nonForwardDeclaredHeaders = set()
+
+ def lower(self, tu, clsname, cxxHeaderFile, cxxFile):
+ self.clsname = clsname
+ self.hdrfile = cxxHeaderFile
+ self.cppfile = cxxFile
+ tu.accept(self)
+
+ def standardTypedefs(self):
+ return [
+ Typedef(Type('mozilla::ipc::IProtocol'), 'ProtocolBase'),
+ Typedef(Type('IPC::Message'), 'Message'),
+ Typedef(Type(self.protocol.channelName()), 'Channel'),
+ Typedef(Type('mozilla::ipc::IProtocol'), 'ChannelListener'),
+ Typedef(Type('base::ProcessHandle'), 'ProcessHandle'),
+ Typedef(Type('mozilla::ipc::MessageChannel'), 'MessageChannel'),
+ Typedef(Type('mozilla::ipc::SharedMemory'), 'SharedMemory'),
+ Typedef(Type('mozilla::ipc::Trigger'), 'Trigger'),
+ ]
+
+
+ def visitTranslationUnit(self, tu):
+ self.protocol = tu.protocol
+
+ hf = self.hdrfile
+ cf = self.cppfile
+
+ # make the C++ header
+ hf.addthings(
+ [ _DISCLAIMER ]
+ + _includeGuardStart(hf)
+ +[
+ Whitespace.NL,
+ CppDirective(
+ 'include',
+ '"'+ _protocolHeaderName(tu.protocol) +'.h"')
+ ])
+
+ for inc in tu.includes:
+ inc.accept(self)
+ for inc in tu.cxxIncludes:
+ inc.accept(self)
+
+ for using in tu.using:
+ using.accept(self)
+
+ # this generates the actor's full impl in self.cls
+ tu.protocol.accept(self)
+
+ clsdecl, clsdefn = _splitClassDeclDefn(self.cls)
+
+ # XXX damn C++ ... return types in the method defn aren't in
+ # class scope
+ for stmt in clsdefn.stmts:
+ if isinstance(stmt, MethodDefn):
+ if stmt.decl.ret and stmt.decl.ret.name == 'Result':
+ stmt.decl.ret.name = clsdecl.name +'::'+ stmt.decl.ret.name
+
+ def setToIncludes(s):
+ return [ CppDirective('include', '"%s"' % i)
+ for i in sorted(iter(s)) ]
+
+ def makeNamespace(p, file):
+ if 0 == len(p.namespaces):
+ return file
+ ns = Namespace(p.namespaces[-1].name)
+ outerns = _putInNamespaces(ns, p.namespaces[:-1])
+ file.addthing(outerns)
+ return ns
+
+ if len(self.nonForwardDeclaredHeaders) != 0:
+ self.hdrfile.addthings(
+ [ Whitespace('// Headers for things that cannot be forward declared'),
+ Whitespace.NL ]
+ + setToIncludes(self.nonForwardDeclaredHeaders)
+ + [ Whitespace.NL ]
+ )
+ self.hdrfile.addthings(self.actorForwardDecls)
+ self.hdrfile.addthings(self.usingDecls)
+
+ hdrns = makeNamespace(self.protocol, self.hdrfile)
+ hdrns.addstmts([
+ Whitespace.NL,
+ Whitespace.NL,
+ clsdecl,
+ Whitespace.NL,
+ Whitespace.NL
+ ])
+
+ self.hdrfile.addthings(
+ ([
+ Whitespace.NL,
+ CppDirective('if', '0') ])
+ + _GenerateSkeletonImpl(
+ _actorName(self.protocol.name, self.side)[1:],
+ self.protocol.namespaces).fromclass(self.cls)
+ +([
+ CppDirective('endif', '// if 0'),
+ Whitespace.NL ])
+ + _includeGuardEnd(hf))
+
+ # make the .cpp file
+ cf.addthings([
+ _DISCLAIMER,
+ Whitespace.NL,
+ CppDirective(
+ 'include',
+ '"'+ _protocolHeaderName(self.protocol, self.side) +'.h"') ]
+ + setToIncludes(self.externalIncludes))
+
+ if self.protocol.decl.type.isToplevel():
+ cf.addthings([
+ CppDirective('ifdef', 'MOZ_CRASHREPORTER'),
+ CppDirective(' include', '"nsXULAppAPI.h"'),
+ CppDirective('endif')
+ ])
+
+ cppheaders = [CppDirective('include', '"%s"' % filename)
+ for filename in ipdl.builtin.CppIncludes]
+
+ cf.addthings((
+ [ Whitespace.NL ]
+ + [ CppDirective(
+ 'include',
+ '"%s.h"' % (inc)) for inc in self.protocolCxxIncludes ]
+ + [ Whitespace.NL ]
+ + cppheaders
+ + [ Whitespace.NL ]))
+
+ cppns = makeNamespace(self.protocol, cf)
+ cppns.addstmts([
+ Whitespace.NL,
+ Whitespace.NL,
+ clsdefn,
+ Whitespace.NL,
+ Whitespace.NL
+ ])
+
+ def visitUsingStmt(self, using):
+ if using.header is None:
+ return
+
+ if using.canBeForwardDeclared():
+ spec = using.type.spec
+
+ self.usingDecls.extend([
+ _makeForwardDeclForQClass(spec.baseid, spec.quals,
+ cls=using.isClass(),
+ struct=using.isStruct()),
+ Whitespace.NL
+ ])
+ self.externalIncludes.add(using.header)
+ else:
+ self.nonForwardDeclaredHeaders.add(using.header)
+
+ def visitCxxInclude(self, inc):
+ self.nonForwardDeclaredHeaders.add(inc.file)
+
+ def visitInclude(self, inc):
+ ip = inc.tu.protocol
+ if not ip:
+ return
+
+ self.actorForwardDecls.extend([
+ _makeForwardDeclForActor(ip.decl.type, self.side),
+ _makeForwardDeclForActor(ip.decl.type, _otherSide(self.side)),
+ Whitespace.NL
+ ])
+ self.protocolCxxIncludes.append(_protocolHeaderName(ip, self.side))
+
+ if ip.decl.fullname is not None:
+ self.includedActorTypedefs.append(Typedef(
+ Type(_actorName(ip.decl.fullname, self.side.title())),
+ _actorName(ip.decl.shortname, self.side.title())))
+
+ self.includedActorTypedefs.append(Typedef(
+ Type(_actorName(ip.decl.fullname, _otherSide(self.side).title())),
+ _actorName(ip.decl.shortname, _otherSide(self.side).title())))
+
+
+ def visitProtocol(self, p):
+ self.hdrfile.addthings([
+ CppDirective('ifdef', 'DEBUG'),
+ CppDirective('include', '"prenv.h"'),
+ CppDirective('endif', '// DEBUG')
+ ])
+
+ self.protocol = p
+ ptype = p.decl.type
+ toplevel = p.decl.type.toplevel()
+
+ # FIXME: all actors impl Iface for now
+ if ptype.isManager() or 1:
+ self.hdrfile.addthing(CppDirective('include', '"base/id_map.h"'))
+
+ self.hdrfile.addthings([
+ CppDirective('include', '"'+ p.channelHeaderFile() +'"'),
+ Whitespace.NL ])
+
+ inherits = []
+ if ptype.isToplevel():
+ inherits.append(Inherit(p.openedProtocolInterfaceType(),
+ viz='public'))
+ else:
+ inherits.append(Inherit(p.managerInterfaceType(), viz='public'))
+
+ if ptype.isToplevel() and self.side is 'parent':
+ self.hdrfile.addthings([
+ _makeForwardDeclForQClass('nsIFile', []),
+ Whitespace.NL
+ ])
+
+ self.cls = Class(
+ self.clsname,
+ inherits=inherits,
+ abstract=True)
+
+ bridgeActorsCreated = ProcessGraph.bridgeEndpointsOf(ptype, self.side)
+ opensActorsCreated = ProcessGraph.opensEndpointsOf(ptype, self.side)
+ channelOpenedActors = OrderedDict.fromkeys(bridgeActorsCreated + opensActorsCreated, None)
+
+ friends = _FindFriends().findFriends(ptype)
+ if ptype.isManaged():
+ friends.update(ptype.managers)
+
+ # |friend| managed actors so that they can call our Dealloc*()
+ friends.update(ptype.manages)
+
+ # don't friend ourself if we're a self-managed protocol
+ friends.discard(ptype)
+
+ for friend in friends:
+ self.actorForwardDecls.extend([
+ _makeForwardDeclForActor(friend, self.prettyside),
+ Whitespace.NL
+ ])
+ self.cls.addstmts([
+ FriendClassDecl(_actorName(friend.fullname(),
+ self.prettyside)),
+ Whitespace.NL ])
+
+ for actor in channelOpenedActors:
+ self.hdrfile.addthings([
+ Whitespace.NL,
+ _makeForwardDeclForActor(actor.ptype, actor.side),
+ Whitespace.NL
+ ])
+
+ self.cls.addstmt(Label.PROTECTED)
+ for typedef in p.cxxTypedefs():
+ self.cls.addstmt(typedef)
+ for typedef in self.includedActorTypedefs:
+ self.cls.addstmt(typedef)
+
+ self.cls.addstmt(Whitespace.NL)
+
+ self.cls.addstmts([ Typedef(p.fqStateType(), 'State'), Whitespace.NL ])
+
+ # interface methods that the concrete subclass has to impl
+ for md in p.messageDecls:
+ isctor, isdtor = md.decl.type.isCtor(), md.decl.type.isDtor()
+
+ if self.receivesMessage(md):
+ # generate Recv/Answer* interface
+ implicit = (not isdtor)
+ recvDecl = MethodDecl(
+ md.recvMethod().name,
+ params=md.makeCxxParams(paramsems='move', returnsems='out',
+ side=self.side, implicit=implicit),
+ ret=Type.BOOL, virtual=1)
+
+ if isctor or isdtor:
+ defaultRecv = MethodDefn(recvDecl)
+ defaultRecv.addstmt(StmtReturn.TRUE)
+ self.cls.addstmt(defaultRecv)
+ else:
+ recvDecl.pure = 1
+ self.cls.addstmt(StmtDecl(recvDecl))
+
+ for md in p.messageDecls:
+ managed = md.decl.type.constructedType()
+ if not ptype.isManagerOf(managed) or md.decl.type.isDtor():
+ continue
+
+ # add the Alloc/Dealloc interface for managed actors
+ actortype = md.actorDecl().bareType(self.side)
+
+ self.cls.addstmt(StmtDecl(MethodDecl(
+ _allocMethod(managed, self.side).name,
+ params=md.makeCxxParams(side=self.side, implicit=0),
+ ret=actortype,
+ virtual=1, pure=1)))
+
+ self.cls.addstmt(StmtDecl(MethodDecl(
+ _deallocMethod(managed, self.side).name,
+ params=[ Decl(actortype, 'aActor') ],
+ ret=Type.BOOL,
+ virtual=1, pure=1)))
+
+ for actor in channelOpenedActors:
+ # add the Alloc interface for actors created when a
+ # new channel is opened
+ actortype = _cxxBareType(actor.asType(), actor.side)
+ self.cls.addstmt(StmtDecl(MethodDecl(
+ _allocMethod(actor.ptype, actor.side).name,
+ params=[ Decl(Type('Transport', ptr=1), 'aTransport'),
+ Decl(Type('ProcessId'), 'aOtherPid') ],
+ ret=actortype,
+ virtual=1, pure=1)))
+
+ # ActorDestroy() method; default is no-op
+ self.cls.addstmts([
+ Whitespace.NL,
+ MethodDefn(MethodDecl(
+ _destroyMethod().name,
+ params=[ Decl(_DestroyReason.Type(), 'aWhy') ],
+ ret=Type.VOID,
+ virtual=1, pure=(self.side == 'parent'))),
+ Whitespace.NL
+ ])
+
+ if ptype.isToplevel():
+ # void ProcessingError(code); default to no-op
+ processingerror = MethodDefn(
+ MethodDecl(p.processingErrorVar().name,
+ params=[ Param(_Result.Type(), 'aCode'),
+ Param(Type('char', const=1, ptr=1), 'aReason') ],
+ virtual=1))
+
+ # bool ShouldContinueFromReplyTimeout(); default to |true|
+ shouldcontinue = MethodDefn(
+ MethodDecl(p.shouldContinueFromTimeoutVar().name,
+ ret=Type.BOOL, virtual=1))
+ shouldcontinue.addstmt(StmtReturn.TRUE)
+
+ # void Entered*()/Exited*(); default to no-op
+ entered = MethodDefn(
+ MethodDecl(p.enteredCxxStackVar().name, virtual=1))
+ exited = MethodDefn(
+ MethodDecl(p.exitedCxxStackVar().name, virtual=1))
+ enteredcall = MethodDefn(
+ MethodDecl(p.enteredCallVar().name, virtual=1))
+ exitedcall = MethodDefn(
+ MethodDecl(p.exitedCallVar().name, virtual=1))
+
+ self.cls.addstmts([ processingerror,
+ shouldcontinue,
+ entered, exited,
+ enteredcall, exitedcall,
+ Whitespace.NL ])
+
+ self.cls.addstmts((
+ [ Label.PUBLIC ]
+ + self.standardTypedefs()
+ + [ Whitespace.NL ]
+ ))
+
+ self.cls.addstmt(Label.PUBLIC)
+ # Actor()
+ ctor = ConstructorDefn(ConstructorDecl(self.clsname))
+ side = ExprVar('mozilla::ipc::' + self.side.title() + 'Side')
+ if ptype.isToplevel():
+ ctor.memberinits = [
+ ExprMemberInit(ExprVar('mozilla::ipc::IToplevelProtocol'),
+ [_protocolId(ptype), side]),
+ ExprMemberInit(p.channelVar(), [
+ ExprCall(ExprVar('ALLOW_THIS_IN_INITIALIZER_LIST'),
+ [ ExprVar.THIS ]) ]),
+ ExprMemberInit(p.stateVar(),
+ [ p.startState() ])
+ ]
+ else:
+ ctor.memberinits = [
+ ExprMemberInit(ExprVar('mozilla::ipc::IProtocol'), [side]),
+ ExprMemberInit(p.stateVar(),
+ [ p.deadState() ])
+ ]
+
+ ctor.addstmt(StmtExpr(ExprCall(ExprVar('MOZ_COUNT_CTOR'),
+ [ ExprVar(self.clsname) ])))
+ self.cls.addstmts([ ctor, Whitespace.NL ])
+
+ # ~Actor()
+ dtor = DestructorDefn(
+ DestructorDecl(self.clsname, virtual=True))
+ dtor.addstmt(StmtExpr(ExprCall(ExprVar('MOZ_COUNT_DTOR'),
+ [ ExprVar(self.clsname) ])))
+
+ self.cls.addstmts([ dtor, Whitespace.NL ])
+
+ if not ptype.isToplevel():
+ if 1 == len(p.managers):
+ ## manager() const
+ managertype = p.managerActorType(self.side, ptr=1)
+ managermeth = MethodDefn(MethodDecl(
+ 'Manager', ret=managertype, const=1))
+ managerexp = ExprCall(ExprVar('IProtocol::Manager'), args=[])
+ managermeth.addstmt(StmtReturn(
+ ExprCast(managerexp, managertype, static=1)))
+
+ self.cls.addstmts([ managermeth, Whitespace.NL ])
+
+ def actorFromIter(itervar):
+ return ExprCall(ExprSelect(ExprCall(ExprSelect(itervar, '.', 'Get')),
+ '->', 'GetKey'))
+ def forLoopOverHashtable(hashtable, itervar, const=False):
+ return StmtFor(
+ init=Param(Type.AUTO, itervar.name,
+ ExprCall(ExprSelect(hashtable, '.', 'ConstIter' if const else 'Iter'))),
+ cond=ExprNot(ExprCall(ExprSelect(itervar, '.', 'Done'))),
+ update=ExprCall(ExprSelect(itervar, '.', 'Next')))
+
+ ## Managed[T](Array& inout) const
+ ## const Array<T>& Managed() const
+ for managed in ptype.manages:
+ arrvar = ExprVar('aArr')
+ meth = MethodDefn(MethodDecl(
+ p.managedMethod(managed, self.side).name,
+ params=[ Decl(_cxxArrayType(p.managedCxxType(managed, self.side), ref=1),
+ arrvar.name) ],
+ const=1))
+ meth.addstmt(StmtExpr(
+ ExprCall(ExprSelect(p.managedVar(managed, self.side),
+ '.', 'ToArray'),
+ args=[ arrvar ])))
+
+ refmeth = MethodDefn(MethodDecl(
+ p.managedMethod(managed, self.side).name,
+ params=[ ],
+ ret=p.managedVarType(managed, self.side, const=1, ref=1),
+ const=1))
+ refmeth.addstmt(StmtReturn(p.managedVar(managed, self.side)))
+
+ self.cls.addstmts([ meth, refmeth, Whitespace.NL ])
+
+ statemethod = MethodDefn(MethodDecl(
+ p.stateMethod().name,
+ ret=p.fqStateType()))
+ statemethod.addstmt(StmtReturn(p.stateVar()))
+ self.cls.addstmts([ statemethod, Whitespace.NL ])
+
+ ## OnMessageReceived()/OnCallReceived()
+
+ # save these away for use in message handler case stmts
+ msgvar = ExprVar('msg__')
+ self.msgvar = msgvar
+ replyvar = ExprVar('reply__')
+ self.replyvar = replyvar
+ itervar = ExprVar('iter__')
+ self.itervar = itervar
+ var = ExprVar('v__')
+ self.var = var
+ # for ctor recv cases, we can't read the actor ID into a PFoo*
+ # because it doesn't exist on this side yet. Use a "special"
+ # actor handle instead
+ handlevar = ExprVar('handle__')
+ self.handlevar = handlevar
+
+ msgtype = ExprCall(ExprSelect(msgvar, '.', 'type'), [ ])
+ self.asyncSwitch = StmtSwitch(msgtype)
+ self.syncSwitch = None
+ self.interruptSwitch = None
+ if toplevel.isSync() or toplevel.isInterrupt():
+ self.syncSwitch = StmtSwitch(msgtype)
+ if toplevel.isInterrupt():
+ self.interruptSwitch = StmtSwitch(msgtype)
+
+ # implement Send*() methods and add dispatcher cases to
+ # message switch()es
+ for md in p.messageDecls:
+ self.visitMessageDecl(md)
+
+ # Handlers for the creation of actors when a new channel is
+ # opened
+ if len(channelOpenedActors):
+ self.makeChannelOpenedHandlers(channelOpenedActors)
+
+ # add default cases
+ default = StmtBlock()
+ default.addstmt(StmtReturn(_Result.NotKnown))
+ self.asyncSwitch.addcase(DefaultLabel(), default)
+ if toplevel.isSync() or toplevel.isInterrupt():
+ self.syncSwitch.addcase(DefaultLabel(), default)
+ if toplevel.isInterrupt():
+ self.interruptSwitch.addcase(DefaultLabel(), default)
+
+ # FIXME/bug 535053: only manager protocols and non-manager
+ # protocols with union types need Lookup(). we'll give it to
+ # all for the time being (simpler)
+ if 1 or ptype.isManager():
+ self.cls.addstmts(self.implementManagerIface())
+
+ def makeHandlerMethod(name, switch, hasReply, dispatches=0):
+ params = [ Decl(Type('Message', const=1, ref=1), msgvar.name) ]
+ if hasReply:
+ params.append(Decl(Type('Message', ref=1, ptr=1),
+ replyvar.name))
+
+ method = MethodDefn(MethodDecl(name, virtual=True,
+ params=params, ret=_Result.Type()))
+
+ if not switch:
+ crash = StmtExpr(ExprCall(ExprVar('MOZ_ASSERT_UNREACHABLE'),
+ args=[ExprLiteral.String('message protocol not supported')]))
+ method.addstmts([crash, StmtReturn(_Result.NotKnown)])
+ return method
+
+ if dispatches:
+ routevar = ExprVar('route__')
+ routedecl = StmtDecl(
+ Decl(_actorIdType(), routevar.name),
+ init=ExprCall(ExprSelect(msgvar, '.', 'routing_id')))
+
+ routeif = StmtIf(ExprBinary(
+ ExprVar('MSG_ROUTING_CONTROL'), '!=', routevar))
+ routedvar = ExprVar('routed__')
+ routeif.ifb.addstmt(
+ StmtDecl(Decl(Type('ChannelListener', ptr=1),
+ routedvar.name),
+ _lookupListener(routevar)))
+ failif = StmtIf(ExprPrefixUnop(routedvar, '!'))
+ failif.ifb.addstmt(StmtReturn(_Result.RouteError))
+ routeif.ifb.addstmt(failif)
+
+ routeif.ifb.addstmt(StmtReturn(ExprCall(
+ ExprSelect(routedvar, '->', name),
+ args=[ ExprVar(p.name) for p in params ])))
+
+ method.addstmts([ routedecl, routeif, Whitespace.NL ])
+
+ # in the event of an Interrupt delete message, we want to loudly complain about
+ # messages that are received that are not a reply to the original message
+ if ptype.hasReentrantDelete:
+ msgVar = ExprVar(params[0].name)
+ ifdying = StmtIf(ExprBinary(
+ ExprBinary(ExprVar('mState'), '==', _dyingState(ptype)),
+ '&&',
+ ExprBinary(
+ ExprBinary(ExprCall(ExprSelect(msgVar, '.', 'is_reply')), '!=', ExprLiteral.TRUE),
+ '||',
+ ExprBinary(ExprCall(ExprSelect(msgVar, '.', 'is_interrupt')), '!=', ExprLiteral.TRUE))))
+ ifdying.addifstmts([_fatalError('incoming message racing with actor deletion'),
+ StmtReturn(_Result.Processed)])
+ method.addstmt(ifdying)
+
+ # bug 509581: don't generate the switch stmt if there
+ # is only the default case; MSVC doesn't like that
+ if switch.nr_cases > 1:
+ method.addstmt(switch)
+ else:
+ method.addstmt(StmtReturn(_Result.NotKnown))
+
+ return method
+
+ dispatches = (ptype.isToplevel() and ptype.isManager())
+ self.cls.addstmts([
+ makeHandlerMethod('OnMessageReceived', self.asyncSwitch,
+ hasReply=0, dispatches=dispatches),
+ Whitespace.NL
+ ])
+ self.cls.addstmts([
+ makeHandlerMethod('OnMessageReceived', self.syncSwitch,
+ hasReply=1, dispatches=dispatches),
+ Whitespace.NL
+ ])
+ self.cls.addstmts([
+ makeHandlerMethod('OnCallReceived', self.interruptSwitch,
+ hasReply=1, dispatches=dispatches),
+ Whitespace.NL
+ ])
+
+ destroysubtreevar = ExprVar('DestroySubtree')
+ deallocsubtreevar = ExprVar('DeallocSubtree')
+ deallocshmemvar = ExprVar('DeallocShmems')
+ deallocselfvar = ExprVar('Dealloc' + _actorName(ptype.name(), self.side))
+
+ # int32_t GetProtocolTypeId() { return PFoo; }
+ gettypetag = MethodDefn(
+ MethodDecl('GetProtocolTypeId', ret=_actorTypeTagType()))
+ gettypetag.addstmt(StmtReturn(_protocolId(ptype)))
+ self.cls.addstmts([ gettypetag, Whitespace.NL ])
+
+ if ptype.isToplevel():
+ # OnChannelClose()
+ onclose = MethodDefn(MethodDecl('OnChannelClose'))
+ onclose.addstmts([
+ StmtExpr(ExprCall(destroysubtreevar,
+ args=[ _DestroyReason.NormalShutdown ])),
+ StmtExpr(ExprCall(deallocsubtreevar)),
+ StmtExpr(ExprCall(deallocshmemvar)),
+ StmtExpr(ExprCall(deallocselfvar))
+ ])
+ self.cls.addstmts([ onclose, Whitespace.NL ])
+
+ # OnChannelError()
+ onerror = MethodDefn(MethodDecl('OnChannelError'))
+ onerror.addstmts([
+ StmtExpr(ExprCall(destroysubtreevar,
+ args=[ _DestroyReason.AbnormalShutdown ])),
+ StmtExpr(ExprCall(deallocsubtreevar)),
+ StmtExpr(ExprCall(deallocshmemvar)),
+ StmtExpr(ExprCall(deallocselfvar))
+ ])
+ self.cls.addstmts([ onerror, Whitespace.NL ])
+
+ if (ptype.isToplevel() and ptype.isInterrupt()):
+
+ processnative = MethodDefn(
+ MethodDecl('ProcessNativeEventsInInterruptCall', ret=Type.VOID))
+
+ processnative.addstmts([
+ CppDirective('ifdef', 'OS_WIN'),
+ StmtExpr(ExprCall(
+ ExprSelect(p.channelVar(), '.',
+ 'ProcessNativeEventsInInterruptCall'))),
+ CppDirective('else'),
+ _fatalError('This method is Windows-only'),
+ CppDirective('endif'),
+ ])
+
+ self.cls.addstmts([ processnative, Whitespace.NL ])
+
+ ## private methods
+ self.cls.addstmt(Label.PRIVATE)
+
+ ## ProtocolName()
+ actorname = _actorName(p.name, self.side)
+ protocolname = MethodDefn(MethodDecl(
+ 'ProtocolName', params=[],
+ const=1, virtual=1, ret=Type('char', const=1, ptr=1)))
+ protocolname.addstmts([
+ StmtReturn(ExprLiteral.String(actorname))
+ ])
+ self.cls.addstmts([ protocolname, Whitespace.NL ])
+
+ ## DestroySubtree(bool normal)
+ whyvar = ExprVar('why')
+ subtreewhyvar = ExprVar('subtreewhy')
+ kidsvar = ExprVar('kids')
+ ivar = ExprVar('i')
+ itervar = ExprVar('iter')
+ ithkid = ExprIndex(kidsvar, ivar)
+
+ destroysubtree = MethodDefn(MethodDecl(
+ destroysubtreevar.name,
+ params=[ Decl(_DestroyReason.Type(), whyvar.name) ]))
+
+ if ptype.isManaged():
+ destroysubtree.addstmt(
+ Whitespace('// Unregister from our manager.\n', indent=1))
+ destroysubtree.addstmts(self.unregisterActor())
+ destroysubtree.addstmt(Whitespace.NL)
+
+ if ptype.isManager():
+ # only declare this for managers to avoid unused var warnings
+ destroysubtree.addstmts([
+ StmtDecl(
+ Decl(_DestroyReason.Type(), subtreewhyvar.name),
+ init=ExprConditional(
+ ExprBinary(
+ ExprBinary(whyvar, '==',
+ _DestroyReason.Deletion),
+ '||',
+ ExprBinary(whyvar, '==',
+ _DestroyReason.FailedConstructor)),
+ _DestroyReason.AncestorDeletion, whyvar)),
+ Whitespace.NL
+ ])
+
+ for managed in ptype.manages:
+ managedVar = p.managedVar(managed, self.side)
+ lenvar = ExprVar('len')
+ kidvar = ExprVar('kid')
+
+ foreachdestroy = StmtRangedFor(kidvar, kidsvar)
+
+ foreachdestroy.addstmt(
+ Whitespace('// Guarding against a child removing a sibling from the list during the iteration.\n', indent=1))
+ ifhas = StmtIf(_callHasManagedActor(managedVar, kidvar))
+ ifhas.addifstmt(StmtExpr(ExprCall(
+ ExprSelect(kidvar, '->', destroysubtreevar.name),
+ args=[ subtreewhyvar ])))
+ foreachdestroy.addstmt(ifhas)
+
+ block = StmtBlock()
+ block.addstmts([
+ Whitespace(
+ '// Recursively shutting down %s kids\n'% (managed.name()),
+ indent=1),
+ StmtDecl(
+ Decl(_cxxArrayType(p.managedCxxType(managed, self.side)), kidsvar.name)),
+ Whitespace(
+ '// Accumulate kids into a stable structure to iterate over\n',
+ indent=1),
+ StmtExpr(ExprCall(p.managedMethod(managed, self.side),
+ args=[ kidsvar ])),
+ foreachdestroy,
+ ])
+ destroysubtree.addstmt(block)
+
+ if len(ptype.manages):
+ destroysubtree.addstmt(Whitespace.NL)
+ destroysubtree.addstmts([ Whitespace('// Finally, destroy "us".\n',
+ indent=1),
+ StmtExpr(ExprCall(_destroyMethod(),
+ args=[ whyvar ]))
+ ])
+
+ self.cls.addstmts([ destroysubtree, Whitespace.NL ])
+
+ ## DeallocSubtree()
+ deallocsubtree = MethodDefn(MethodDecl(deallocsubtreevar.name))
+ for managed in ptype.manages:
+ managedVar = p.managedVar(managed, self.side)
+
+ foreachrecurse = forLoopOverHashtable(managedVar, itervar)
+ foreachrecurse.addstmt(StmtExpr(ExprCall(
+ ExprSelect(actorFromIter(itervar), '->', deallocsubtreevar.name))))
+
+ foreachdealloc = forLoopOverHashtable(managedVar, itervar)
+ foreachdealloc.addstmts([
+ StmtExpr(ExprCall(_deallocMethod(managed, self.side),
+ args=[ actorFromIter(itervar) ]))
+ ])
+
+ block = StmtBlock()
+ block.addstmts([
+ Whitespace(
+ '// Recursively deleting %s kids\n'% (managed.name()),
+ indent=1),
+ foreachrecurse,
+ Whitespace.NL,
+ foreachdealloc,
+ StmtExpr(_callClearManagedActors(managedVar)),
+
+ ])
+ deallocsubtree.addstmt(block)
+ # don't delete outselves: either the manager will do it, or
+ # we're toplevel
+ self.cls.addstmts([ deallocsubtree, Whitespace.NL ])
+
+ if ptype.isToplevel():
+ deallocself = MethodDefn(MethodDecl(deallocselfvar.name, virtual=1))
+ self.cls.addstmts([ deallocself, Whitespace.NL ])
+
+ self.implementPickling()
+
+ ## private members
+ if ptype.isToplevel():
+ self.cls.addstmt(StmtDecl(Decl(p.channelType(), 'mChannel')))
+
+ self.cls.addstmt(StmtDecl(Decl(Type('State'), p.stateVar().name)))
+
+ for managed in ptype.manages:
+ self.cls.addstmts([
+ StmtDecl(Decl(
+ p.managedVarType(managed, self.side),
+ p.managedVar(managed, self.side).name)) ])
+
+ def implementManagerIface(self):
+ p = self.protocol
+ routedvar = ExprVar('aRouted')
+ idvar = ExprVar('aId')
+ shmemvar = ExprVar('shmem')
+ rawvar = ExprVar('segment')
+ sizevar = ExprVar('aSize')
+ typevar = ExprVar('aType')
+ unsafevar = ExprVar('aUnsafe')
+ protocolbase = Type('ProtocolBase', ptr=1)
+ sourcevar = ExprVar('aSource')
+ ivar = ExprVar('i')
+ kidsvar = ExprVar('kids')
+ ithkid = ExprIndex(kidsvar, ivar)
+
+ methods = []
+
+ if p.decl.type.isToplevel():
+ getchannel = MethodDefn(MethodDecl(
+ p.getChannelMethod().name,
+ ret=Type('MessageChannel', ptr=1),
+ virtual=1))
+ getchannel.addstmt(StmtReturn(ExprAddrOf(p.channelVar())))
+
+ getchannelconst = MethodDefn(MethodDecl(
+ p.getChannelMethod().name,
+ ret=Type('MessageChannel', ptr=1, const=1),
+ virtual=1, const=1))
+ getchannelconst.addstmt(StmtReturn(ExprAddrOf(p.channelVar())))
+
+ methods += [ getchannel,
+ getchannelconst ]
+
+ if p.decl.type.isToplevel():
+ tmpvar = ExprVar('tmp')
+
+ # "private" message that passes shmem mappings from one process
+ # to the other
+ if p.subtreeUsesShmem():
+ self.asyncSwitch.addcase(
+ CaseLabel('SHMEM_CREATED_MESSAGE_TYPE'),
+ self.genShmemCreatedHandler())
+ self.asyncSwitch.addcase(
+ CaseLabel('SHMEM_DESTROYED_MESSAGE_TYPE'),
+ self.genShmemDestroyedHandler())
+ else:
+ abort = StmtBlock()
+ abort.addstmts([
+ _fatalError('this protocol tree does not use shmem'),
+ StmtReturn(_Result.NotKnown)
+ ])
+ self.asyncSwitch.addcase(
+ CaseLabel('SHMEM_CREATED_MESSAGE_TYPE'), abort)
+ self.asyncSwitch.addcase(
+ CaseLabel('SHMEM_DESTROYED_MESSAGE_TYPE'), abort)
+
+ othervar = ExprVar('other')
+ managertype = Type(_actorName(p.name, self.side), ptr=1)
+
+ # Keep track of types created with an INOUT ctor. We need to call
+ # Register() or RegisterID() for them depending on the side the managee
+ # is created.
+ inoutCtorTypes = []
+ for msg in p.messageDecls:
+ msgtype = msg.decl.type
+ if msgtype.isCtor() and msgtype.isInout():
+ inoutCtorTypes.append(msgtype.constructedType())
+
+ # all protocols share the "same" RemoveManagee() implementation
+ pvar = ExprVar('aProtocolId')
+ listenervar = ExprVar('aListener')
+ removemanagee = MethodDefn(MethodDecl(
+ p.removeManageeMethod().name,
+ params=[ Decl(_protocolIdType(), pvar.name),
+ Decl(protocolbase, listenervar.name) ],
+ virtual=1))
+
+ if not len(p.managesStmts):
+ removemanagee.addstmts([ _fatalError('unreached'), StmtReturn() ])
+ else:
+ switchontype = StmtSwitch(pvar)
+ for managee in p.managesStmts:
+ case = StmtBlock()
+ actorvar = ExprVar('actor')
+ manageeipdltype = managee.decl.type
+ manageecxxtype = _cxxBareType(ipdl.type.ActorType(manageeipdltype),
+ self.side)
+ manageearray = p.managedVar(manageeipdltype, self.side)
+ containervar = ExprVar('container')
+
+ case.addstmts([
+ StmtDecl(Decl(manageecxxtype, actorvar.name),
+ ExprCast(listenervar, manageecxxtype, static=1)),
+ # Use a temporary variable here so all the assertion expressions
+ # in the _abortIfFalse call below are textually identical; the
+ # linker can then merge the strings from the assertion macro(s).
+ StmtDecl(Decl(Type('auto', ref=1), containervar.name),
+ manageearray),
+ _abortIfFalse(
+ _callHasManagedActor(containervar, actorvar),
+ "actor not managed by this!"),
+ Whitespace.NL,
+ StmtExpr(_callRemoveManagedActor(containervar, actorvar)),
+ StmtExpr(ExprCall(_deallocMethod(manageeipdltype, self.side),
+ args=[ actorvar ])),
+ StmtReturn()
+ ])
+ switchontype.addcase(CaseLabel(_protocolId(manageeipdltype).name),
+ case)
+ default = StmtBlock()
+ default.addstmts([ _fatalError('unreached'), StmtReturn() ])
+ switchontype.addcase(DefaultLabel(), default)
+ removemanagee.addstmt(switchontype)
+
+ return methods + [removemanagee, Whitespace.NL]
+
+ def genShmemCreatedHandler(self):
+ p = self.protocol
+ assert p.decl.type.isToplevel()
+
+ case = StmtBlock()
+
+ ifstmt = StmtIf(ExprNot(ExprCall(ExprVar('ShmemCreated'), args=[self.msgvar])))
+ case.addstmts([
+ ifstmt,
+ StmtReturn(_Result.Processed)
+ ])
+ ifstmt.addifstmt(StmtReturn(_Result.PayloadError))
+
+ return case
+
+ def genShmemDestroyedHandler(self):
+ p = self.protocol
+ assert p.decl.type.isToplevel()
+
+ case = StmtBlock()
+
+ ifstmt = StmtIf(ExprNot(ExprCall(ExprVar('ShmemDestroyed'), args=[self.msgvar])))
+ case.addstmts([
+ ifstmt,
+ StmtReturn(_Result.Processed)
+ ])
+ ifstmt.addifstmt(StmtReturn(_Result.PayloadError))
+
+ return case
+
+
+ def makeChannelOpenedHandlers(self, actors):
+ handlers = StmtBlock()
+
+ # unpack the transport descriptor et al.
+ msgvar = self.msgvar
+ tdvar = ExprVar('td')
+ pidvar = ExprVar('pid')
+ pvar = ExprVar('protocolid')
+ iffail = StmtIf(ExprNot(ExprCall(
+ ExprVar('mozilla::ipc::UnpackChannelOpened'),
+ args=[ _backstagePass(),
+ msgvar,
+ ExprAddrOf(tdvar), ExprAddrOf(pidvar), ExprAddrOf(pvar) ])))
+ iffail.addifstmt(StmtReturn(_Result.PayloadError))
+ handlers.addstmts([
+ StmtDecl(Decl(Type('TransportDescriptor'), tdvar.name)),
+ StmtDecl(Decl(Type('ProcessId'), pidvar.name)),
+ StmtDecl(Decl(Type('ProtocolId'), pvar.name)),
+ iffail,
+ Whitespace.NL
+ ])
+
+ def makeHandlerCase(actor):
+ self.protocolCxxIncludes.append(_protocolHeaderName(actor.ptype._ast,
+ actor.side))
+
+ case = StmtBlock()
+ modevar = _sideToTransportMode(actor.side)
+ tvar = ExprVar('t')
+ iffailopen = StmtIf(ExprNot(ExprAssn(
+ tvar,
+ ExprCall(ExprVar('mozilla::ipc::OpenDescriptor'),
+ args=[ tdvar, modevar ]))))
+ iffailopen.addifstmt(StmtReturn(_Result.ValuError))
+
+ pvar = ExprVar('p')
+ iffailalloc = StmtIf(ExprNot(ExprAssn(
+ pvar,
+ ExprCall(
+ _allocMethod(actor.ptype, actor.side),
+ args=[ _uniqueptrGet(tvar), pidvar ]))))
+ iffailalloc.addifstmt(StmtReturn(_Result.ProcessingError))
+
+ settrans = StmtExpr(ExprCall(
+ ExprSelect(pvar, '->', 'IToplevelProtocol::SetTransport'),
+ args=[ExprMove(tvar)]))
+
+ case.addstmts([
+ StmtDecl(Decl(_uniqueptr(Type('Transport')), tvar.name)),
+ StmtDecl(Decl(Type(_actorName(actor.ptype.name(), actor.side),
+ ptr=1), pvar.name)),
+ iffailopen,
+ iffailalloc,
+ settrans,
+ StmtBreak()
+ ])
+ label = _messageStartName(actor.ptype)
+ if actor.side == 'child':
+ label += 'Child'
+ return CaseLabel(label), case
+
+ pswitch = StmtSwitch(pvar)
+ for actor in actors:
+ label, case = makeHandlerCase(actor)
+ pswitch.addcase(label, case)
+
+ die = Block()
+ die.addstmts([ _fatalError('Invalid protocol'),
+ StmtReturn(_Result.ValuError) ])
+ pswitch.addcase(DefaultLabel(), die)
+
+ handlers.addstmts([
+ pswitch,
+ StmtReturn(_Result.Processed)
+ ])
+ self.asyncSwitch.addcase(CaseLabel('CHANNEL_OPENED_MESSAGE_TYPE'),
+ handlers)
+
+ ##-------------------------------------------------------------------------
+ ## The next few functions are the crux of the IPDL code generator.
+ ## They generate code for all the nasty work of message
+ ## serialization/deserialization and dispatching handlers for
+ ## received messages.
+ ##
+ def implementPickling(self):
+ # pickling of "normal", non-IPDL types
+ self.implementGenericPickling()
+
+ # pickling for IPDL types
+ specialtypes = set()
+ class findSpecialTypes(TypeVisitor):
+ def visitActorType(self, a): specialtypes.add(a)
+ def visitShmemType(self, s): specialtypes.add(s)
+ def visitFDType(self, s): specialtypes.add(s)
+ def visitStructType(self, s):
+ specialtypes.add(s)
+ return TypeVisitor.visitStructType(self, s)
+ def visitUnionType(self, u):
+ specialtypes.add(u)
+ return TypeVisitor.visitUnionType(self, u)
+ def visitArrayType(self, a):
+ if a.basetype.isIPDL():
+ specialtypes.add(a)
+ return a.basetype.accept(self)
+
+ for md in self.protocol.messageDecls:
+ for param in md.params:
+ mtype = md.decl.type
+ # special case for top-level __delete__(), which isn't
+ # understood yet
+ if mtype.isDtor() and mtype.constructedType().isToplevel():
+ continue
+ param.ipdltype.accept(findSpecialTypes())
+ for ret in md.returns:
+ ret.ipdltype.accept(findSpecialTypes())
+
+ for t in specialtypes:
+ if t.isActor(): self.implementActorPickling(t)
+ elif t.isArray(): self.implementSpecialArrayPickling(t)
+ elif t.isShmem(): self.implementShmemPickling(t)
+ elif t.isFD(): self.implementFDPickling(t)
+ elif t.isStruct(): self.implementStructPickling(t)
+ elif t.isUnion(): self.implementUnionPickling(t)
+ else:
+ assert 0 and 'unknown special type'
+
+ def implementGenericPickling(self):
+ var = self.var
+ msgvar = self.msgvar
+ itervar = self.itervar
+
+ write = MethodDefn(self.writeMethodDecl(
+ Type('T', const=1, ref=1), var, template=Type('T')))
+ write.addstmt(StmtExpr(ExprCall(ExprVar('IPC::WriteParam'),
+ args=[ msgvar, var ])))
+
+ read = MethodDefn(self.readMethodDecl(
+ Type('T', ptr=1), var, template=Type('T')))
+ read.addstmt(StmtReturn(ExprCall(ExprVar('IPC::ReadParam'),
+ args=[ msgvar, itervar, var ])))
+
+ self.cls.addstmts([ write, Whitespace.NL, read, Whitespace.NL ])
+
+ def implementActorPickling(self, actortype):
+ # Note that we pickle based on *protocol* type and *not* actor
+ # type. The actor type includes a |nullable| qualifier, but
+ # this method is not specialized based on nullability. The
+ # |actortype| nullability is ignored in this method.
+ var = self.var
+ idvar = ExprVar('id')
+ intype = _cxxConstRefType(actortype, self.side)
+ # XXX the writer code can treat the actor as logically const; many
+ # other places that call _cxxConstRefType cannot treat the actor
+ # as logically const, particularly callers that can leak out to
+ # Gecko directly.
+ intype.const = 1
+ cxxtype = _cxxBareType(actortype, self.side)
+ outtype = _cxxPtrToType(actortype, self.side)
+
+ ## Write([const] PFoo* var)
+ write = MethodDefn(self.writeMethodDecl(intype, var))
+ nullablevar = ExprVar('nullable__')
+ write.decl.params.append(Decl(Type.BOOL, nullablevar.name))
+ # id_t id;
+ # if (!var)
+ # if(!nullable)
+ # abort()
+ # id = NULL_ID
+ write.addstmt(StmtDecl(Decl(_actorIdType(), idvar.name)))
+
+ ifnull = StmtIf(ExprNot(var))
+ ifnotnullable = StmtIf(ExprNot(nullablevar))
+ ifnotnullable.addifstmt(
+ _fatalError("NULL actor value passed to non-nullable param"))
+ ifnull.addifstmt(ifnotnullable)
+ ifnull.addifstmt(StmtExpr(ExprAssn(idvar, _NULL_ACTOR_ID)))
+ # else
+ # id = var->mId
+ # if (id == FREED_ID)
+ # abort()
+ # Write(msg, id)
+ ifnull.addelsestmt(StmtExpr(ExprAssn(idvar, _actorId(var))))
+ iffreed = StmtIf(ExprBinary(_FREED_ACTOR_ID, '==', idvar))
+ # this is always a hard-abort, because it means that some C++
+ # code has a live pointer to a freed actor, so we're playing
+ # Russian roulette with invalid memory
+ iffreed.addifstmt(_fatalError("actor has been |delete|d"))
+ ifnull.addelsestmt(iffreed)
+
+ write.addstmts([
+ ifnull,
+ Whitespace.NL,
+ StmtExpr(self.write(None, idvar, self.msgvar))
+ ])
+
+ ## Read(PFoo** var)
+ read = MethodDefn(self.readMethodDecl(outtype, var))
+ read.decl.params.append(Decl(Type.BOOL, nullablevar.name))
+
+ actorvar = ExprVar('actor')
+ read.addstmts([
+ StmtDecl(Decl(Type('Maybe', T=Type('mozilla::ipc::IProtocol', ptr=1)), actorvar.name),
+ init=ExprCall(ExprVar('ReadActor'),
+ args=[ self.msgvar, self.itervar, nullablevar,
+ ExprLiteral.String(actortype.name()),
+ _protocolId(actortype) ])),
+ ])
+
+ # if (actor.isNothing())
+ # return false
+ #
+ # Reading the actor failed in some way, and the appropriate error was raised.
+ ifnothing = StmtIf(ExprCall(ExprSelect(actorvar, '.', 'isNothing')))
+ ifnothing.addifstmts([
+ StmtReturn.FALSE,
+ ])
+
+ read.addstmts([ ifnothing, Whitespace.NL ])
+
+ read.addstmts([
+ StmtExpr(ExprAssn(ExprDeref(var),
+ ExprCast(ExprCall(ExprSelect(actorvar, '.', 'value')), cxxtype, static=1))),
+ StmtReturn.TRUE
+ ])
+
+ self.cls.addstmts([ write, Whitespace.NL, read, Whitespace.NL ])
+
+
+ def implementSpecialArrayPickling(self, arraytype):
+ var = self.var
+ msgvar = self.msgvar
+ itervar = self.itervar
+ lenvar = ExprVar('length')
+ ivar = ExprVar('i')
+ eltipdltype = arraytype.basetype
+ intype = _cxxConstRefType(arraytype, self.side)
+ outtype = _cxxPtrToType(arraytype, self.side)
+
+ # We access elements directly in Read and Write to avoid array bounds
+ # checking.
+ directtype = _cxxBareType(arraytype.basetype, self.side)
+ if directtype.ptr:
+ typeinit = { 'ptrptr': 1 }
+ else:
+ typeinit = { 'ptr': 1 }
+ directtype = Type(directtype.name, **typeinit)
+ elemsvar = ExprVar('elems')
+ elemvar = ExprVar('elem')
+
+ write = MethodDefn(self.writeMethodDecl(intype, var))
+ forwrite = StmtRangedFor(elemvar, var)
+ forwrite.addstmt(
+ self.checkedWrite(eltipdltype, elemvar, msgvar,
+ sentinelKey=arraytype.name()))
+ write.addstmts([
+ StmtDecl(Decl(Type.UINT32, lenvar.name),
+ init=_callCxxArrayLength(var)),
+ self.checkedWrite(None, lenvar, msgvar, sentinelKey=('length', arraytype.name())),
+ Whitespace.NL,
+ forwrite
+ ])
+
+ read = MethodDefn(self.readMethodDecl(outtype, var))
+ favar = ExprVar('fa')
+ forread = StmtFor(init=ExprAssn(Decl(Type.UINT32, ivar.name),
+ ExprLiteral.ZERO),
+ cond=ExprBinary(ivar, '<', lenvar),
+ update=ExprPrefixUnop(ivar, '++'))
+ forread.addstmt(
+ self.checkedRead(eltipdltype, ExprAddrOf(ExprIndex(elemsvar, ivar)),
+ msgvar, itervar, errfnRead,
+ '\'' + eltipdltype.name() + '[i]\'',
+ sentinelKey=arraytype.name()))
+ appendstmt = StmtDecl(Decl(directtype, elemsvar.name),
+ init=ExprCall(ExprSelect(favar, '.', 'AppendElements'),
+ args=[ lenvar ]))
+ read.addstmts([
+ StmtDecl(Decl(_cxxArrayType(_cxxBareType(arraytype.basetype, self.side)), favar.name)),
+ StmtDecl(Decl(Type.UINT32, lenvar.name)),
+ self.checkedRead(None, ExprAddrOf(lenvar),
+ msgvar, itervar, errfnArrayLength,
+ [ arraytype.name() ],
+ sentinelKey=('length', arraytype.name())),
+ Whitespace.NL,
+ appendstmt,
+ forread,
+ StmtExpr(_callCxxSwapArrayElements(var, favar, '->')),
+ StmtReturn.TRUE
+ ])
+
+ self.cls.addstmts([ write, Whitespace.NL, read, Whitespace.NL ])
+
+
+ def implementShmemPickling(self, shmemtype):
+ msgvar = self.msgvar
+ itervar = self.itervar
+ var = self.var
+ tmpvar = ExprVar('tmp')
+ idvar = ExprVar('shmemid')
+ rawvar = ExprVar('rawmem')
+ baretype = _cxxBareType(shmemtype, self.side)
+ intype = _cxxConstRefType(shmemtype, self.side)
+ outtype = _cxxPtrToType(shmemtype, self.side)
+
+ write = MethodDefn(self.writeMethodDecl(intype, var))
+ write.addstmts([
+ StmtExpr(ExprCall(ExprVar('IPC::WriteParam'),
+ args=[ msgvar, var ])),
+ StmtExpr(_shmemRevokeRights(var)),
+ StmtExpr(_shmemForget(var))
+ ])
+
+ read = MethodDefn(self.readMethodDecl(outtype, var))
+ ifread = StmtIf(ExprNot(ExprCall(ExprVar('IPC::ReadParam'),
+ args=[ msgvar, itervar,
+ ExprAddrOf(tmpvar) ])))
+ ifread.addifstmt(StmtReturn.FALSE)
+
+ iffound = StmtIf(rawvar)
+ iffound.addifstmt(StmtExpr(ExprAssn(
+ ExprDeref(var), _shmemCtor(rawvar, idvar))))
+ iffound.addifstmt(StmtReturn.TRUE)
+
+ read.addstmts([
+ StmtDecl(Decl(_shmemType(), tmpvar.name)),
+ ifread,
+ Whitespace.NL,
+ StmtDecl(Decl(_shmemIdType(), idvar.name),
+ init=_shmemId(tmpvar)),
+ StmtDecl(Decl(_rawShmemType(ptr=1), rawvar.name),
+ init=_lookupShmem(idvar)),
+ iffound,
+ # This is ugly: we failed to look the shmem up, most likely because
+ # we failed to map it the first time it was deserialized. we create
+ # an empty shmem and let the user of the shmem deal with it.
+ # if we returned false here we would crash.
+ StmtExpr(ExprAssn(ExprDeref(var), ExprCall(ExprVar('Shmem'), args=[]) )),
+ StmtReturn.TRUE
+ ])
+
+ self.cls.addstmts([ write, Whitespace.NL, read, Whitespace.NL ])
+
+ def implementFDPickling(self, fdtype):
+ msgvar = self.msgvar
+ itervar = self.itervar
+ var = self.var
+ tmpvar = ExprVar('fd')
+ picklevar = ExprVar('pfd')
+ intype = _cxxConstRefType(fdtype, self.side)
+ outtype = _cxxPtrToType(fdtype, self.side)
+
+ def _fdType():
+ return Type('FileDescriptor')
+
+ def _fdPickleType():
+ return Type('FileDescriptor::PickleType')
+
+ def _fdBackstagePass():
+ return ExprCall(ExprVar('FileDescriptor::IPDLPrivate'))
+
+ write = MethodDefn(self.writeMethodDecl(intype, var))
+ write.addstmts([
+ StmtDecl(Decl(_fdPickleType(), picklevar.name),
+ init=ExprCall(ExprSelect(var, '.', 'ShareTo'),
+ args=[ _fdBackstagePass(),
+ self.protocol.callOtherPid() ])),
+ StmtExpr(ExprCall(ExprVar('IPC::WriteParam'),
+ args=[ msgvar, picklevar ])),
+ ])
+
+ read = MethodDefn(self.readMethodDecl(outtype, var))
+ ifread = StmtIf(ExprNot(ExprCall(ExprVar('IPC::ReadParam'),
+ args=[ msgvar, itervar,
+ ExprAddrOf(picklevar) ])))
+ ifread.addifstmt(StmtReturn.FALSE)
+
+ ifnvalid = StmtIf(ExprNot(ExprCall(ExprSelect(tmpvar, '.', 'IsValid'))))
+ ifnvalid.addifstmt(
+ _protocolErrorBreakpoint('[' +
+ _actorName(self.protocol.name, self.side) +
+ '] Received an invalid file descriptor!'))
+
+ read.addstmts([
+ StmtDecl(Decl(_fdPickleType(), picklevar.name)),
+ ifread,
+ Whitespace.NL,
+ StmtDecl(Decl(_fdType(), tmpvar.name),
+ init=ExprCall(ExprVar('FileDescriptor'),
+ args=[ _fdBackstagePass(), picklevar ])),
+ ifnvalid,
+ Whitespace.NL,
+ StmtExpr(ExprAssn(ExprDeref(var), tmpvar)),
+ StmtReturn.TRUE
+ ])
+
+ self.cls.addstmts([ write, Whitespace.NL, read, Whitespace.NL ])
+
+ def implementStructPickling(self, structtype):
+ msgvar = self.msgvar
+ itervar = self.itervar
+ var = self.var
+ intype = _cxxConstRefType(structtype, self.side)
+ outtype = _cxxPtrToType(structtype, self.side)
+ sd = structtype._ast
+
+ write = MethodDefn(self.writeMethodDecl(intype, var))
+ read = MethodDefn(self.readMethodDecl(outtype, var))
+
+ def get(sel, f):
+ return ExprCall(f.getMethod(thisexpr=var, sel=sel))
+
+ for f in sd.fields:
+ desc = '\'' + f.getMethod().name + '\' (' + f.ipdltype.name() + \
+ ') member of \'' + intype.name + '\''
+ writefield = self.checkedWrite(f.ipdltype, get('.', f), msgvar, sentinelKey=f.basename)
+ readfield = self.checkedRead(f.ipdltype,
+ ExprAddrOf(get('->', f)),
+ msgvar, itervar, errfnRead, desc, sentinelKey=f.basename)
+ if f.special and f.side != self.side:
+ writefield = Whitespace(
+ "// skipping actor field that's meaningless on this side\n", indent=1)
+ readfield = Whitespace(
+ "// skipping actor field that's meaningless on this side\n", indent=1)
+ write.addstmt(writefield)
+ read.addstmt(readfield)
+
+ read.addstmt(StmtReturn.TRUE)
+
+ self.cls.addstmts([ write, Whitespace.NL, read, Whitespace.NL ])
+
+
+ def implementUnionPickling(self, uniontype):
+ msgvar = self.msgvar
+ itervar = self.itervar
+ var = self.var
+ intype = _cxxConstRefType(uniontype, self.side)
+ outtype = _cxxPtrToType(uniontype, self.side)
+ ud = uniontype._ast
+
+ typename = 'type__'
+ uniontdef = Typedef(_cxxBareType(uniontype, typename), typename)
+
+ typevar = ExprVar('type')
+ writeswitch = StmtSwitch(ud.callType(var))
+ readswitch = StmtSwitch(typevar)
+
+ for c in ud.components:
+ ct = c.ipdltype
+ isactor = (ct.isIPDL() and ct.isActor())
+ caselabel = CaseLabel(typename +'::'+ c.enum())
+ origenum = c.enum()
+
+ writecase = StmtBlock()
+ if c.special and c.side != self.side:
+ writecase.addstmt(_fatalError('wrong side!'))
+ else:
+ wexpr = ExprCall(ExprSelect(var, '.', c.getTypeName()))
+ writecase.addstmt(self.checkedWrite(ct, wexpr, msgvar, sentinelKey=c.enum()))
+
+ writecase.addstmt(StmtReturn())
+ writeswitch.addcase(caselabel, writecase)
+
+ readcase = StmtBlock()
+ if c.special and c.side == self.side:
+ # the type comes across flipped from what the actor
+ # will be on this side; i.e. child->parent messages
+ # have type PFooChild when received on the parent side
+ # XXX: better error message
+ readcase.addstmt(StmtReturn.FALSE)
+ else:
+ if c.special:
+ c = c.other # see above
+ tmpvar = ExprVar('tmp')
+ ct = c.bareType()
+ readcase.addstmts([
+ StmtDecl(Decl(ct, tmpvar.name), init=c.defaultValue()),
+ StmtExpr(ExprAssn(ExprDeref(var), tmpvar)),
+ self.checkedRead(
+ c.ipdltype,
+ ExprAddrOf(ExprCall(ExprSelect(var, '->',
+ c.getTypeName()))),
+ msgvar, itervar, errfnRead, 'Union type', sentinelKey=origenum),
+ StmtReturn(ExprLiteral.TRUE)
+ ])
+
+ readswitch.addcase(caselabel, readcase)
+
+ unknowntype = 'unknown union type'
+ writeswitch.addcase(DefaultLabel(),
+ StmtBlock([ _fatalError(unknowntype),
+ StmtReturn() ]))
+ readswitch.addcase(DefaultLabel(), StmtBlock(errfnRead(unknowntype)))
+
+ write = MethodDefn(self.writeMethodDecl(intype, var))
+ write.addstmts([
+ uniontdef,
+ self.checkedWrite(
+ None, ExprCall(Type.INT, args=[ ud.callType(var) ]), msgvar,
+ sentinelKey=uniontype.name()),
+ Whitespace.NL,
+ writeswitch
+ ])
+
+ read = MethodDefn(self.readMethodDecl(outtype, var))
+ read.addstmts([
+ uniontdef,
+ StmtDecl(Decl(Type.INT, typevar.name)),
+ self.checkedRead(
+ None, ExprAddrOf(typevar), msgvar, itervar, errfnUnionType,
+ [ uniontype.name() ],
+ sentinelKey=uniontype.name()),
+ Whitespace.NL,
+ readswitch,
+ ])
+
+ self.cls.addstmts([ write, Whitespace.NL, read, Whitespace.NL ])
+
+
+ def writeMethodDecl(self, intype, var, template=None):
+ return MethodDecl(
+ 'Write',
+ params=[ Decl(intype, var.name),
+ Decl(Type('Message', ptr=1), self.msgvar.name) ],
+ T=template)
+
+ def readMethodDecl(self, outtype, var, template=None):
+ return MethodDecl(
+ 'Read',
+ params=[ Decl(outtype, var.name),
+ Decl(Type('Message', ptr=1, const=1),
+ self.msgvar.name),
+ Decl(_iterType(ptr=1), self.itervar.name)],
+ warn_unused=not template,
+ T=template,
+ ret=Type.BOOL)
+
+ def maybeAddNullabilityArg(self, ipdltype, call):
+ if ipdltype and ipdltype.isIPDL() and ipdltype.isActor():
+ if ipdltype.nullable:
+ call.args.append(ExprLiteral.TRUE)
+ else:
+ call.args.append(ExprLiteral.FALSE)
+ return call
+
+ def write(self, ipdltype, expr, to, this=None):
+ write = ExprVar('Write')
+ if this: write = ExprSelect(this, '->', write.name)
+ return self.maybeAddNullabilityArg(ipdltype,
+ ExprCall(write, args=[ expr, to ]))
+
+ def read(self, ipdltype, expr, from_, iterexpr, this=None):
+ read = ExprVar('Read')
+ if this: read = ExprSelect(this, '->', read.name)
+ return self.maybeAddNullabilityArg(
+ ipdltype, ExprCall(read, args=[ expr, from_, iterexpr ]))
+
+ def checkedWrite(self, ipdltype, expr, msgvar, sentinelKey, this=None):
+ assert sentinelKey
+
+ write = StmtExpr(self.write(ipdltype, expr, msgvar, this))
+
+ sentinel = StmtExpr(ExprCall(ExprSelect(msgvar, '->', 'WriteSentinel'),
+ args=[ ExprLiteral.Int(hashfunc(sentinelKey)) ]))
+ block = Block()
+ block.addstmts([
+ write,
+ Whitespace('// Sentinel = ' + repr(sentinelKey) + '\n', indent=1),
+ sentinel ])
+ return block
+
+
+ def visitMessageDecl(self, md):
+ isctor = md.decl.type.isCtor()
+ isdtor = md.decl.type.isDtor()
+ decltype = md.decl.type
+ sendmethod = None
+ helpermethod = None
+ recvlbl, recvcase = None, None
+
+ def addRecvCase(lbl, case):
+ if decltype.isAsync():
+ self.asyncSwitch.addcase(lbl, case)
+ elif decltype.isSync():
+ self.syncSwitch.addcase(lbl, case)
+ elif decltype.isInterrupt():
+ self.interruptSwitch.addcase(lbl, case)
+ else: assert 0
+
+ if self.sendsMessage(md):
+ isasync = decltype.isAsync()
+
+ if isctor:
+ self.cls.addstmts([ self.genHelperCtor(md), Whitespace.NL ])
+
+ if isctor and isasync:
+ sendmethod, (recvlbl, recvcase) = self.genAsyncCtor(md)
+ elif isctor:
+ sendmethod = self.genBlockingCtorMethod(md)
+ elif isdtor and isasync:
+ sendmethod, (recvlbl, recvcase) = self.genAsyncDtor(md)
+ elif isdtor:
+ sendmethod = self.genBlockingDtorMethod(md)
+ elif isasync:
+ sendmethod = self.genAsyncSendMethod(md)
+ else:
+ sendmethod = self.genBlockingSendMethod(md)
+
+ # XXX figure out what to do here
+ if isdtor and md.decl.type.constructedType().isToplevel():
+ sendmethod = None
+
+ if sendmethod is not None:
+ self.cls.addstmts([ sendmethod, Whitespace.NL ])
+ if recvcase is not None:
+ addRecvCase(recvlbl, recvcase)
+ recvlbl, recvcase = None, None
+
+ if self.receivesMessage(md):
+ if isctor:
+ recvlbl, recvcase = self.genCtorRecvCase(md)
+ elif isdtor:
+ recvlbl, recvcase = self.genDtorRecvCase(md)
+ else:
+ recvlbl, recvcase = self.genRecvCase(md)
+
+ # XXX figure out what to do here
+ if isdtor and md.decl.type.constructedType().isToplevel():
+ return
+
+ addRecvCase(recvlbl, recvcase)
+
+
+ def genAsyncCtor(self, md):
+ actor = md.actorDecl()
+ method = MethodDefn(self.makeSendMethodDecl(md))
+ method.addstmts(self.ctorPrologue(md) + [ Whitespace.NL ])
+
+ msgvar, stmts = self.makeMessage(md, errfnSendCtor)
+ sendok, sendstmts = self.sendAsync(md, msgvar)
+ method.addstmts(
+ stmts
+ + self.genVerifyMessage(md.decl.type.verify, md.params,
+ errfnSendCtor, ExprVar('msg__'))
+ + sendstmts
+ + self.failCtorIf(md, ExprNot(sendok))
+ + [ StmtReturn(actor.var()) ])
+
+ lbl = CaseLabel(md.pqReplyId())
+ case = StmtBlock()
+ case.addstmt(StmtReturn(_Result.Processed))
+ # TODO not really sure what to do with async ctor "replies" yet.
+ # destroy actor if there was an error? tricky ...
+
+ return method, (lbl, case)
+
+
+ def genBlockingCtorMethod(self, md):
+ actor = md.actorDecl()
+ method = MethodDefn(self.makeSendMethodDecl(md))
+ method.addstmts(self.ctorPrologue(md) + [ Whitespace.NL ])
+
+ msgvar, stmts = self.makeMessage(md, errfnSendCtor)
+
+ replyvar = self.replyvar
+ sendok, sendstmts = self.sendBlocking(md, msgvar, replyvar)
+ method.addstmts(
+ stmts
+ + [ Whitespace.NL,
+ StmtDecl(Decl(Type('Message'), replyvar.name)) ]
+ + self.genVerifyMessage(md.decl.type.verify, md.params,
+ errfnSendCtor, ExprVar('msg__'))
+ + sendstmts
+ + self.failCtorIf(md, ExprNot(sendok)))
+
+ def errfnCleanupCtor(msg):
+ return self.failCtorIf(md, ExprLiteral.TRUE)
+ stmts = self.deserializeReply(
+ md, ExprAddrOf(replyvar), self.side, errfnCleanupCtor)
+ method.addstmts(stmts + [ StmtReturn(actor.var()) ])
+
+ return method
+
+
+ def ctorPrologue(self, md, errfn=ExprLiteral.NULL, idexpr=None):
+ actordecl = md.actorDecl()
+ actorvar = actordecl.var()
+ actorproto = actordecl.ipdltype.protocol
+ actortype = ipdl.type.ActorType(actorproto)
+
+ if idexpr is None:
+ idexpr = ExprCall(self.protocol.registerMethod(),
+ args=[ actorvar ])
+ else:
+ idexpr = ExprCall(self.protocol.registerIDMethod(),
+ args=[ actorvar, idexpr ])
+
+ return [
+ self.failIfNullActor(actorvar, errfn, msg="Error constructing actor %s" % actortype.name() + self.side.capitalize()),
+ StmtExpr(ExprCall(ExprSelect(actorvar, '->', 'SetId'), args=[idexpr])),
+ StmtExpr(ExprCall(ExprSelect(actorvar, '->', 'SetManager'), args=[ExprVar.THIS])),
+ StmtExpr(ExprCall(ExprSelect(actorvar, '->', 'SetIPCChannel'),
+ args=[self.protocol.callGetChannel()])),
+ StmtExpr(_callInsertManagedActor(
+ self.protocol.managedVar(md.decl.type.constructedType(),
+ self.side),
+ actorvar)),
+ StmtExpr(ExprAssn(_actorState(actorvar),
+ _startState(actorproto, fq=1)))
+ ]
+
+ def failCtorIf(self, md, cond):
+ actorvar = md.actorDecl().var()
+ type = md.decl.type.constructedType()
+ failif = StmtIf(cond)
+
+ if self.side=='child':
+ # in the child process this should not fail
+ failif.addifstmt(_fatalError('constructor for actor failed'))
+ else:
+ failif.addifstmts(self.destroyActor(md, actorvar,
+ why=_DestroyReason.FailedConstructor))
+
+ failif.addifstmt(StmtReturn(ExprLiteral.NULL))
+ return [ failif ]
+
+ def genHelperCtor(self, md):
+ helperdecl = self.makeSendMethodDecl(md)
+ helperdecl.params = helperdecl.params[1:]
+ helper = MethodDefn(helperdecl)
+
+ callctor = self.callAllocActor(md, retsems='out', side=self.side)
+ helper.addstmt(StmtReturn(ExprCall(
+ ExprVar(helperdecl.name), args=[ callctor ] + callctor.args)))
+ return helper
+
+
+ def genAsyncDtor(self, md):
+ actor = md.actorDecl()
+ actorvar = actor.var()
+ method = MethodDefn(self.makeDtorMethodDecl(md))
+
+ method.addstmts(self.dtorPrologue(actorvar))
+
+ msgvar, stmts = self.makeMessage(md, errfnSendDtor, actorvar)
+ sendok, sendstmts = self.sendAsync(md, msgvar, actorvar)
+ method.addstmts(
+ stmts
+ + self.genVerifyMessage(md.decl.type.verify, md.params,
+ errfnSendDtor, ExprVar('msg__'))
+ + sendstmts
+ + [ Whitespace.NL ]
+ + self.dtorEpilogue(md, actor.var())
+ + [ StmtReturn(sendok) ])
+
+ lbl = CaseLabel(md.pqReplyId())
+ case = StmtBlock()
+ case.addstmt(StmtReturn(_Result.Processed))
+ # TODO if the dtor is "inherently racy", keep the actor alive
+ # until the other side acks
+
+ return method, (lbl, case)
+
+
+ def genBlockingDtorMethod(self, md):
+ actor = md.actorDecl()
+ actorvar = actor.var()
+ method = MethodDefn(self.makeDtorMethodDecl(md))
+
+ method.addstmts(self.dtorPrologue(actorvar))
+
+ msgvar, stmts = self.makeMessage(md, errfnSendDtor, actorvar)
+
+ replyvar = self.replyvar
+ sendok, sendstmts = self.sendBlocking(md, msgvar, replyvar, actorvar)
+ method.addstmts(
+ stmts
+ + self.genVerifyMessage(md.decl.type.verify, md.params,
+ errfnSendDtor, ExprVar('msg__'))
+ + [ Whitespace.NL,
+ StmtDecl(Decl(Type('Message'), replyvar.name)) ]
+ + sendstmts)
+
+ destmts = self.deserializeReply(
+ md, ExprAddrOf(replyvar), self.side, errfnSend, actorvar)
+ ifsendok = StmtIf(ExprLiteral.FALSE)
+ ifsendok.addifstmts(destmts)
+ ifsendok.addifstmts([ Whitespace.NL,
+ StmtExpr(ExprAssn(sendok, ExprLiteral.FALSE, '&=')) ])
+
+ method.addstmt(ifsendok)
+
+ if self.protocol.decl.type.hasReentrantDelete:
+ method.addstmts(self.transition(md, 'in', actor.var(), reply=True))
+
+ method.addstmts(
+ self.dtorEpilogue(md, actor.var())
+ + [ Whitespace.NL, StmtReturn(sendok) ])
+
+ return method
+
+ def destroyActor(self, md, actorexpr, why=_DestroyReason.Deletion):
+ if md.decl.type.isCtor():
+ destroyedType = md.decl.type.constructedType()
+ else:
+ destroyedType = self.protocol.decl.type
+ return ([ StmtExpr(self.callActorDestroy(actorexpr, why)),
+ StmtExpr(self.callDeallocSubtree(md, actorexpr)),
+ StmtExpr(self.callRemoveActor(
+ actorexpr,
+ manager=self.protocol.managerVar(actorexpr),
+ ipdltype=destroyedType))
+ ])
+
+ def dtorPrologue(self, actorexpr):
+ return [ self.failIfNullActor(actorexpr), Whitespace.NL ]
+
+ def dtorEpilogue(self, md, actorexpr):
+ return self.destroyActor(md, actorexpr)
+
+ def genAsyncSendMethod(self, md):
+ method = MethodDefn(self.makeSendMethodDecl(md))
+ msgvar, stmts = self.makeMessage(md, errfnSend)
+ sendok, sendstmts = self.sendAsync(md, msgvar)
+ method.addstmts(stmts
+ +[ Whitespace.NL ]
+ + self.genVerifyMessage(md.decl.type.verify, md.params,
+ errfnSend, ExprVar('msg__'))
+ + sendstmts
+ +[ StmtReturn(sendok) ])
+ return method
+
+
+ def genBlockingSendMethod(self, md, fromActor=None):
+ method = MethodDefn(self.makeSendMethodDecl(md))
+
+ msgvar, serstmts = self.makeMessage(md, errfnSend, fromActor)
+ replyvar = self.replyvar
+
+ sendok, sendstmts = self.sendBlocking(md, msgvar, replyvar)
+ failif = StmtIf(ExprNot(sendok))
+ failif.addifstmt(StmtReturn.FALSE)
+
+ desstmts = self.deserializeReply(
+ md, ExprAddrOf(replyvar), self.side, errfnSend)
+
+ method.addstmts(
+ serstmts
+ + self.genVerifyMessage(md.decl.type.verify, md.params, errfnSend,
+ ExprVar('msg__'))
+ + [ Whitespace.NL,
+ StmtDecl(Decl(Type('Message'), replyvar.name)) ]
+ + sendstmts
+ + [ failif ]
+ + desstmts
+ + [ Whitespace.NL,
+ StmtReturn.TRUE ])
+
+ return method
+
+
+ def genCtorRecvCase(self, md):
+ lbl = CaseLabel(md.pqMsgId())
+ case = StmtBlock()
+ actorvar = md.actorDecl().var()
+ actorhandle = self.handlevar
+
+ stmts = self.deserializeMessage(md, self.side, errfnRecv)
+
+ idvar, saveIdStmts = self.saveActorId(md)
+ case.addstmts(
+ stmts
+ + self.transition(md, 'in')
+ + [ StmtDecl(Decl(r.bareType(self.side), r.var().name))
+ for r in md.returns ]
+ # alloc the actor, register it under the foreign ID
+ + [ StmtExpr(ExprAssn(
+ actorvar,
+ self.callAllocActor(md, retsems='in', side=self.side))) ]
+ + self.ctorPrologue(md, errfn=_Result.ValuError,
+ idexpr=_actorHId(actorhandle))
+ + [ Whitespace.NL ]
+ + saveIdStmts
+ + self.invokeRecvHandler(md)
+ + self.makeReply(md, errfnRecv, idvar)
+ + self.genVerifyMessage(md.decl.type.verify, md.returns, errfnRecv,
+ self.replyvar)
+ + [ Whitespace.NL,
+ StmtReturn(_Result.Processed) ])
+
+ return lbl, case
+
+
+ def genDtorRecvCase(self, md):
+ lbl = CaseLabel(md.pqMsgId())
+ case = StmtBlock()
+
+ stmts = self.deserializeMessage(md, self.side, errfnRecv)
+
+ idvar, saveIdStmts = self.saveActorId(md)
+ case.addstmts(
+ stmts
+ + self.transition(md, 'in')
+ + [ StmtDecl(Decl(r.bareType(self.side), r.var().name))
+ for r in md.returns ]
+ + self.invokeRecvHandler(md, implicit=0)
+ + [ Whitespace.NL ]
+ + saveIdStmts
+ + self.makeReply(md, errfnRecv, routingId=idvar)
+ + [ Whitespace.NL ]
+ + self.genVerifyMessage(md.decl.type.verify, md.returns, errfnRecv,
+ self.replyvar)
+ + self.dtorEpilogue(md, md.actorDecl().var())
+ + [ Whitespace.NL,
+ StmtReturn(_Result.Processed) ])
+
+ return lbl, case
+
+
+ def genRecvCase(self, md):
+ lbl = CaseLabel(md.pqMsgId())
+ case = StmtBlock()
+
+ stmts = self.deserializeMessage(md, self.side, errfn=errfnRecv)
+
+ idvar, saveIdStmts = self.saveActorId(md)
+ case.addstmts(
+ stmts
+ + self.transition(md, 'in')
+ + [ StmtDecl(Decl(r.bareType(self.side), r.var().name))
+ for r in md.returns ]
+ + saveIdStmts
+ + self.invokeRecvHandler(md)
+ + [ Whitespace.NL ]
+ + self.makeReply(md, errfnRecv, routingId=idvar)
+ + self.genVerifyMessage(md.decl.type.verify, md.returns, errfnRecv,
+ self.replyvar)
+ + [ StmtReturn(_Result.Processed) ])
+
+ return lbl, case
+
+
+ # helper methods
+
+ def failIfNullActor(self, actorExpr, retOnNull=ExprLiteral.FALSE, msg=None):
+ failif = StmtIf(ExprNot(actorExpr))
+ if msg:
+ failif.addifstmt(_printWarningMessage(msg))
+ failif.addifstmt(StmtReturn(retOnNull))
+ return failif
+
+ def unregisterActor(self):
+ return [ StmtExpr(ExprCall(self.protocol.unregisterMethod(),
+ args=[ _actorId() ])),
+ StmtExpr(ExprCall(ExprVar('SetId'), args=[_FREED_ACTOR_ID])) ]
+
+ def makeMessage(self, md, errfn, fromActor=None):
+ msgvar = self.msgvar
+ routingId = self.protocol.routingId(fromActor)
+ this = None
+ if md.decl.type.isDtor(): this = md.actorDecl().var()
+
+ stmts = ([ StmtDecl(Decl(Type('IPC::Message', ptr=1), msgvar.name),
+ init=ExprCall(ExprVar(md.pqMsgCtorFunc()),
+ args=[ routingId ])) ]
+ + [ Whitespace.NL ]
+ + [ self.checkedWrite(p.ipdltype, p.var(), msgvar, sentinelKey=p.name, this=this)
+ for p in md.params ]
+ + [ Whitespace.NL ]
+ + self.setMessageFlags(md, msgvar, reply=0))
+ return msgvar, stmts
+
+
+ def makeReply(self, md, errfn, routingId):
+ if routingId is None:
+ routingId = self.protocol.routingId()
+ # TODO special cases for async ctor/dtor replies
+ if not md.decl.type.hasReply():
+ return [ ]
+
+ replyvar = self.replyvar
+ return (
+ [ StmtExpr(ExprAssn(
+ replyvar, ExprCall(ExprVar(md.pqReplyCtorFunc()), args=[ routingId ]))),
+ Whitespace.NL ]
+ + [ self.checkedWrite(r.ipdltype, r.var(), replyvar, sentinelKey=r.name)
+ for r in md.returns ]
+ + self.setMessageFlags(md, replyvar, reply=1)
+ + [ self.logMessage(md, replyvar, 'Sending reply ') ])
+
+ def genVerifyMessage(self, verify, params, errfn, msgsrcVar):
+ stmts = [ ]
+ if not verify:
+ return stmts
+ if len(params) == 0:
+ return stmts
+
+ msgvar = ExprVar('msgverify__')
+ side = self.side
+
+ msgexpr = ExprAddrOf(msgvar)
+ itervar = ExprVar('msgverifyIter__')
+ # IPC::Message msgverify__ = Move(*(reply__)); or
+ # IPC::Message msgverify__ = Move(*(msg__));
+ stmts.append(StmtDecl(Decl(Type('IPC::Message', ptr=0), 'msgverify__'),
+ init=ExprMove(ExprDeref(msgsrcVar))))
+
+ stmts.extend((
+ # PickleIterator msgverifyIter__ = PickleIterator(msgverify__);
+ [ StmtDecl(Decl(_iterType(ptr=0), itervar.name),
+ init=ExprCall(ExprVar('PickleIterator'),
+ args=[ msgvar ])) ]
+ # declare varCopy for each variable to deserialize.
+ + [ StmtDecl(Decl(p.bareType(side), p.var().name + 'Copy'))
+ for p in params ]
+ + [ Whitespace.NL ]
+ # checked Read(&(varCopy), &(msgverify__), &(msgverifyIter__))
+ + [ self.checkedRead(p.ipdltype,
+ ExprAddrOf(ExprVar(p.var().name + 'Copy')),
+ msgexpr, ExprAddrOf(itervar),
+ errfn, p.bareType(side).name,
+ p.name)
+ for p in params ]
+ + [ self.endRead(msgvar, itervar) ]
+ # Move the message back to its source before sending.
+ + [ StmtExpr(ExprAssn(ExprDeref(msgsrcVar), ExprMove(msgvar))) ]
+ ))
+
+ return stmts
+
+ def setMessageFlags(self, md, var, reply):
+ stmts = [ ]
+
+ if md.decl.type.isSync():
+ stmts.append(StmtExpr(ExprCall(
+ ExprSelect(var, '->', 'set_sync'))))
+ elif md.decl.type.isInterrupt():
+ stmts.append(StmtExpr(ExprCall(
+ ExprSelect(var, '->', 'set_interrupt'))))
+
+ if reply:
+ stmts.append(StmtExpr(ExprCall(
+ ExprSelect(var, '->', 'set_reply'))))
+
+ return stmts + [ Whitespace.NL ]
+
+
+ def deserializeMessage(self, md, side, errfn):
+ msgvar = self.msgvar
+ itervar = self.itervar
+ msgexpr = ExprAddrOf(msgvar)
+ isctor = md.decl.type.isCtor()
+ stmts = ([
+ self.logMessage(md, msgexpr, 'Received ',
+ receiving=True),
+ self.profilerLabel(md),
+ Whitespace.NL
+ ])
+
+ if 0 == len(md.params):
+ return stmts
+
+ start, decls, reads = 0, [], []
+ if isctor:
+ # return the raw actor handle so that its ID can be used
+ # to construct the "real" actor
+ handlevar = self.handlevar
+ handletype = Type('ActorHandle')
+ decls = [ StmtDecl(Decl(handletype, handlevar.name)) ]
+ reads = [ self.checkedRead(None, ExprAddrOf(handlevar), msgexpr,
+ ExprAddrOf(self.itervar),
+ errfn, "'%s'" % handletype.name,
+ sentinelKey='actor') ]
+ start = 1
+
+ stmts.extend((
+ [ StmtDecl(Decl(_iterType(ptr=0), self.itervar.name),
+ init=ExprCall(ExprVar('PickleIterator'),
+ args=[ msgvar ])) ]
+ + decls + [ StmtDecl(Decl(p.bareType(side), p.var().name))
+ for p in md.params ]
+ + [ Whitespace.NL ]
+ + reads + [ self.checkedRead(p.ipdltype, ExprAddrOf(p.var()),
+ msgexpr, ExprAddrOf(itervar),
+ errfn, "'%s'" % p.bareType(side).name,
+ sentinelKey=p.name)
+ for p in md.params[start:] ]
+ + [ self.endRead(msgvar, itervar) ]))
+
+ return stmts
+
+
+ def deserializeReply(self, md, replyexpr, side, errfn, actor=None):
+ stmts = [ Whitespace.NL,
+ self.logMessage(md, replyexpr,
+ 'Received reply ', actor, receiving=True) ]
+ if 0 == len(md.returns):
+ return stmts
+
+ itervar = self.itervar
+ stmts.extend(
+ [ Whitespace.NL,
+ StmtDecl(Decl(_iterType(ptr=0), itervar.name),
+ init=ExprCall(ExprVar('PickleIterator'),
+ args=[ self.replyvar ])) ]
+ + [ self.checkedRead(r.ipdltype, r.var(),
+ ExprAddrOf(self.replyvar),
+ ExprAddrOf(self.itervar),
+ errfn, "'%s'" % r.bareType(side).name,
+ sentinelKey=r.name)
+ for r in md.returns ]
+ + [ self.endRead(self.replyvar, itervar) ])
+
+ return stmts
+
+ def sendAsync(self, md, msgexpr, actor=None):
+ sendok = ExprVar('sendok__')
+ return (
+ sendok,
+ ([ Whitespace.NL,
+ self.logMessage(md, msgexpr, 'Sending ', actor),
+ self.profilerLabel(md) ]
+ + self.transition(md, 'out', actor)
+ + [ Whitespace.NL,
+ StmtDecl(Decl(Type.BOOL, sendok.name),
+ init=ExprCall(
+ ExprSelect(self.protocol.callGetChannel(actor),
+ '->', 'Send'),
+ args=[ msgexpr ]))
+ ])
+ )
+
+ def sendBlocking(self, md, msgexpr, replyexpr, actor=None):
+ sendok = ExprVar('sendok__')
+ return (
+ sendok,
+ ([ Whitespace.NL,
+ self.logMessage(md, msgexpr, 'Sending ', actor),
+ self.profilerLabel(md) ]
+ + self.transition(md, 'out', actor)
+ + [ Whitespace.NL,
+ StmtDecl(
+ Decl(Type.BOOL, sendok.name),
+ init=ExprCall(ExprSelect(self.protocol.callGetChannel(actor),
+ '->',
+ _sendPrefix(md.decl.type)),
+ args=[ msgexpr, ExprAddrOf(replyexpr) ]))
+ ])
+ )
+
+ def callAllocActor(self, md, retsems, side):
+ return ExprCall(
+ _allocMethod(md.decl.type.constructedType(), side),
+ args=md.makeCxxArgs(retsems=retsems, retcallsems='out',
+ implicit=0))
+
+ def callActorDestroy(self, actorexpr, why=_DestroyReason.Deletion):
+ return ExprCall(ExprSelect(actorexpr, '->', 'DestroySubtree'),
+ args=[ why ])
+
+ def callRemoveActor(self, actorexpr, manager=None, ipdltype=None):
+ if ipdltype is None: ipdltype = self.protocol.decl.type
+
+ if not ipdltype.isManaged():
+ return Whitespace('// unmanaged protocol')
+
+ removefunc = self.protocol.removeManageeMethod()
+ if manager is not None:
+ removefunc = ExprSelect(manager, '->', removefunc.name)
+
+ return ExprCall(removefunc,
+ args=[ _protocolId(ipdltype),
+ actorexpr ])
+
+ def callDeallocSubtree(self, md, actorexpr):
+ return ExprCall(ExprSelect(actorexpr, '->', 'DeallocSubtree'))
+
+ def invokeRecvHandler(self, md, implicit=1):
+ failif = StmtIf(ExprNot(
+ ExprCall(md.recvMethod(),
+ args=md.makeCxxArgs(paramsems='move', retsems='in',
+ retcallsems='out',
+ implicit=implicit))))
+ failif.addifstmts([
+ _protocolErrorBreakpoint('Handler returned error code!'),
+ StmtReturn(_Result.ProcessingError)
+ ])
+ return [ failif ]
+
+ def makeDtorMethodDecl(self, md):
+ decl = self.makeSendMethodDecl(md)
+ decl.static = 1
+ return decl
+
+ def makeSendMethodDecl(self, md):
+ implicit = md.decl.type.hasImplicitActorParam()
+ decl = MethodDecl(
+ md.sendMethod().name,
+ params=md.makeCxxParams(paramsems='in', returnsems='out',
+ side=self.side, implicit=implicit),
+ warn_unused=(self.side == 'parent'),
+ ret=Type.BOOL)
+ if md.decl.type.isCtor():
+ decl.ret = md.actorDecl().bareType(self.side)
+ return decl
+
+ def logMessage(self, md, msgptr, pfx, actor=None, receiving=False):
+ actorname = _actorName(self.protocol.name, self.side)
+
+ return _ifLogging(ExprLiteral.String(actorname),
+ [ StmtExpr(ExprCall(
+ ExprVar('mozilla::ipc::LogMessageForProtocol'),
+ args=[ ExprLiteral.String(actorname),
+ self.protocol.callOtherPid(actor),
+ ExprLiteral.String(pfx),
+ ExprCall(ExprSelect(msgptr, '->', 'type')),
+ ExprVar('mozilla::ipc::MessageDirection::eReceiving'
+ if receiving
+ else 'mozilla::ipc::MessageDirection::eSending') ])) ])
+
+ def profilerLabel(self, md):
+ return StmtExpr(ExprCall(ExprVar('PROFILER_LABEL'),
+ [ ExprLiteral.String(self.protocol.name),
+ ExprLiteral.String(md.prettyMsgName()),
+ ExprVar('js::ProfileEntry::Category::OTHER') ]))
+
+ def saveActorId(self, md):
+ idvar = ExprVar('id__')
+ if md.decl.type.hasReply():
+ # only save the ID if we're actually going to use it, to
+ # avoid unused-variable warnings
+ saveIdStmts = [ StmtDecl(Decl(_actorIdType(), idvar.name),
+ self.protocol.routingId()) ]
+ else:
+ saveIdStmts = [ ]
+ return idvar, saveIdStmts
+
+ def transition(self, md, direction, actor=None, reply=False):
+ if actor is not None: stateexpr = _actorState(actor)
+ else: stateexpr = self.protocol.stateVar()
+
+ if (self.side is 'parent' and direction is 'out'
+ or self.side is 'child' and direction is 'in'):
+ action = ExprVar('Trigger::Send')
+ elif (self.side is 'parent' and direction is 'in'
+ or self.side is 'child' and direction is 'out'):
+ action = ExprVar('Trigger::Recv')
+ else: assert 0 and 'unknown combo %s/%s'% (self.side, direction)
+
+ msgid = md.pqMsgId() if not reply else md.pqReplyId()
+ ifbad = StmtIf(ExprNot(
+ ExprCall(
+ ExprVar(self.protocol.name +'::Transition'),
+ args=[ ExprCall(ExprVar('Trigger'),
+ args=[ action, ExprVar(msgid) ]),
+ ExprAddrOf(stateexpr) ])))
+ ifbad.addifstmts(_badTransition())
+ return [ ifbad ]
+
+ def checkedRead(self, ipdltype, expr, msgexpr, iterexpr, errfn, paramtype, sentinelKey, sentinel=True):
+ ifbad = StmtIf(ExprNot(self.read(ipdltype, expr, msgexpr, iterexpr)))
+ if isinstance(paramtype, list):
+ errorcall = errfn(*paramtype)
+ else:
+ errorcall = errfn('Error deserializing ' + paramtype)
+ ifbad.addifstmts(errorcall)
+
+ block = Block()
+ block.addstmt(ifbad)
+
+ if sentinel:
+ assert sentinelKey
+
+ block.addstmt(Whitespace('// Sentinel = ' + repr(sentinelKey) + '\n', indent=1))
+ read = ExprCall(ExprSelect(msgexpr, '->', 'ReadSentinel'),
+ args=[ iterexpr, ExprLiteral.Int(hashfunc(sentinelKey)) ])
+ ifsentinel = StmtIf(ExprNot(read))
+ ifsentinel.addifstmts(errorcall)
+ block.addstmt(ifsentinel)
+
+ return block
+
+ def endRead(self, msgexpr, iterexpr):
+ return StmtExpr(ExprCall(ExprSelect(msgexpr, '.', 'EndRead'),
+ args=[ iterexpr ]))
+
+class _GenerateProtocolParentCode(_GenerateProtocolActorCode):
+ def __init__(self):
+ _GenerateProtocolActorCode.__init__(self, 'parent')
+
+ def sendsMessage(self, md):
+ return not md.decl.type.isIn()
+
+ def receivesMessage(self, md):
+ return md.decl.type.isInout() or md.decl.type.isIn()
+
+class _GenerateProtocolChildCode(_GenerateProtocolActorCode):
+ def __init__(self):
+ _GenerateProtocolActorCode.__init__(self, 'child')
+
+ def sendsMessage(self, md):
+ return not md.decl.type.isOut()
+
+ def receivesMessage(self, md):
+ return md.decl.type.isInout() or md.decl.type.isOut()
+
+
+##-----------------------------------------------------------------------------
+## Utility passes
+##
+
+def _splitClassDeclDefn(cls):
+ """Destructively split |cls| methods into declarations and
+definitions (if |not methodDecl.force_inline|). Return classDecl,
+methodDefns."""
+ defns = Block()
+
+ for i, stmt in enumerate(cls.stmts):
+ if isinstance(stmt, MethodDefn) and not stmt.decl.force_inline:
+ decl, defn = _splitMethodDefn(stmt, cls.name)
+ cls.stmts[i] = StmtDecl(decl)
+ defns.addstmts([ defn, Whitespace.NL ])
+
+ return cls, defns
+
+def _splitMethodDefn(md, clsname):
+ saveddecl = deepcopy(md.decl)
+ md.decl.name = (clsname +'::'+ md.decl.name)
+ md.decl.virtual = 0
+ md.decl.static = 0
+ md.decl.warn_unused = 0
+ md.decl.never_inline = 0
+ md.decl.only_for_definition = True
+ for param in md.decl.params:
+ if isinstance(param, Param):
+ param.default = None
+ return saveddecl, md
+
+
+def _splitFuncDeclDefn(fun):
+ assert not fun.decl.inline
+ return StmtDecl(fun.decl), fun
+
+
+# XXX this is tantalizingly similar to _splitClassDeclDefn, but just
+# different enough that I don't see the need to define
+# _GenerateSkeleton in terms of that
+class _GenerateSkeletonImpl(Visitor):
+ def __init__(self, name, namespaces):
+ self.name = name
+ self.cls = None
+ self.namespaces = namespaces
+ self.methodimpls = Block()
+
+ def fromclass(self, cls):
+ cls.accept(self)
+
+ nsclass = _putInNamespaces(self.cls, self.namespaces)
+ nsmethodimpls = _putInNamespaces(self.methodimpls, self.namespaces)
+
+ return [
+ Whitespace('''
+//-----------------------------------------------------------------------------
+// Skeleton implementation of abstract actor class
+
+'''),
+ Whitespace('// Header file contents\n'),
+ nsclass,
+ Whitespace.NL,
+ Whitespace('\n// C++ file contents\n'),
+ nsmethodimpls
+ ]
+
+
+ def visitClass(self, cls):
+ self.cls = Class(self.name, inherits=[ Inherit(Type(cls.name)) ])
+ Visitor.visitClass(self, cls)
+
+ def visitMethodDecl(self, md):
+ if not md.pure:
+ return
+ decl = deepcopy(md)
+ decl.pure = 0
+ impl = MethodDefn(MethodDecl(self.implname(md.name),
+ params=md.params,
+ ret=md.ret))
+ if md.ret.ptr:
+ impl.addstmt(StmtReturn(ExprLiteral.ZERO))
+ elif md.ret == Type.BOOL:
+ impl.addstmt(StmtReturn(ExprVar('false')))
+
+ self.cls.addstmts([ StmtDecl(decl), Whitespace.NL ])
+ self.addmethodimpl(impl)
+
+ def visitConstructorDecl(self, cd):
+ self.cls.addstmt(StmtDecl(ConstructorDecl(self.name)))
+ ctor = ConstructorDefn(ConstructorDecl(self.implname(self.name)))
+ ctor.addstmt(StmtExpr(ExprCall(ExprVar( 'MOZ_COUNT_CTOR'),
+ [ ExprVar(self.name) ])))
+ self.addmethodimpl(ctor)
+
+ def visitDestructorDecl(self, dd):
+ self.cls.addstmt(
+ StmtDecl(DestructorDecl(self.name, virtual=1)))
+ # FIXME/cjones: hack!
+ dtor = DestructorDefn(ConstructorDecl(self.implname('~' +self.name)))
+ dtor.addstmt(StmtExpr(ExprCall(ExprVar( 'MOZ_COUNT_DTOR'),
+ [ ExprVar(self.name) ])))
+ self.addmethodimpl(dtor)
+
+ def addmethodimpl(self, impl):
+ self.methodimpls.addstmts([ impl, Whitespace.NL ])
+
+ def implname(self, method):
+ return self.name +'::'+ method
diff --git a/ipc/ipdl/ipdl/parser.py b/ipc/ipdl/ipdl/parser.py
new file mode 100644
index 000000000..38c46dc73
--- /dev/null
+++ b/ipc/ipdl/ipdl/parser.py
@@ -0,0 +1,807 @@
+# 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/.
+
+import os, sys
+from ply import lex, yacc
+
+from ipdl.ast import *
+
+def _getcallerpath():
+ '''Return the absolute path of the file containing the code that
+**CALLED** this function.'''
+ return os.path.abspath(sys._getframe(1).f_code.co_filename)
+
+##-----------------------------------------------------------------------------
+
+class ParseError(Exception):
+ def __init__(self, loc, fmt, *args):
+ self.loc = loc
+ self.error = ('%s%s: error: %s'% (
+ Parser.includeStackString(), loc, fmt)) % args
+ def __str__(self):
+ return self.error
+
+def _safeLinenoValue(t):
+ lineno, value = 0, '???'
+ if hasattr(t, 'lineno'): lineno = t.lineno
+ if hasattr(t, 'value'): value = t.value
+ return lineno, value
+
+def _error(loc, fmt, *args):
+ raise ParseError(loc, fmt, *args)
+
+class Parser:
+ # when we reach an |include [protocol] foo;| statement, we need to
+ # save the current parser state and create a new one. this "stack" is
+ # where that state is saved
+ #
+ # there is one Parser per file
+ current = None
+ parseStack = [ ]
+ parsed = { }
+
+ def __init__(self, type, name, debug=0):
+ assert type and name
+ self.type = type
+ self.debug = debug
+ self.filename = None
+ self.includedirs = None
+ self.loc = None # not always up to date
+ self.lexer = None
+ self.parser = None
+ self.tu = TranslationUnit(type, name)
+ self.direction = None
+ self.errout = None
+
+ def parse(self, input, filename, includedirs, errout):
+ assert os.path.isabs(filename)
+
+ if filename in Parser.parsed:
+ return Parser.parsed[filename].tu
+
+ self.lexer = lex.lex(debug=self.debug,
+ optimize=not self.debug,
+ lextab="ipdl_lextab")
+ self.parser = yacc.yacc(debug=self.debug,
+ optimize=not self.debug,
+ tabmodule="ipdl_yacctab")
+ self.filename = filename
+ self.includedirs = includedirs
+ self.tu.filename = filename
+ self.errout = errout
+
+ Parser.parsed[filename] = self
+ Parser.parseStack.append(Parser.current)
+ Parser.current = self
+
+ try:
+ ast = self.parser.parse(input=input, lexer=self.lexer,
+ debug=self.debug)
+ except ParseError, p:
+ print >>errout, p
+ return None
+
+ Parser.current = Parser.parseStack.pop()
+ return ast
+
+ def resolveIncludePath(self, filepath):
+ '''Return the absolute path from which the possibly partial
+|filepath| should be read, or |None| if |filepath| cannot be located.'''
+ for incdir in self.includedirs +[ '' ]:
+ realpath = os.path.join(incdir, filepath)
+ if os.path.isfile(realpath):
+ return os.path.abspath(realpath)
+ return None
+
+ # returns a GCC-style string representation of the include stack.
+ # e.g.,
+ # in file included from 'foo.ipdl', line 120:
+ # in file included from 'bar.ipd', line 12:
+ # which can be printed above a proper error message or warning
+ @staticmethod
+ def includeStackString():
+ s = ''
+ for parse in Parser.parseStack[1:]:
+ s += " in file included from `%s', line %d:\n"% (
+ parse.loc.filename, parse.loc.lineno)
+ return s
+
+def locFromTok(p, num):
+ return Loc(Parser.current.filename, p.lineno(num))
+
+
+##-----------------------------------------------------------------------------
+
+reserved = set((
+ 'answer',
+ 'as',
+ 'async',
+ 'both',
+ 'bridges',
+ 'call',
+ 'child',
+ 'class',
+ 'compress',
+ 'compressall',
+ '__delete__',
+ 'delete', # reserve 'delete' to prevent its use
+ 'from',
+ 'goto',
+ 'include',
+ 'intr',
+ 'manager',
+ 'manages',
+ 'namespace',
+ 'nested',
+ 'nullable',
+ 'opens',
+ 'or',
+ 'parent',
+ 'prio',
+ 'protocol',
+ 'recv',
+ 'returns',
+ 'send',
+ 'spawns',
+ 'start',
+ 'state',
+ 'struct',
+ 'sync',
+ 'union',
+ 'upto',
+ 'using',
+ 'verify'))
+tokens = [
+ 'COLONCOLON', 'ID', 'STRING',
+] + [ r.upper() for r in reserved ]
+
+t_COLONCOLON = '::'
+
+literals = '(){}[]<>;:,~'
+t_ignore = ' \f\t\v'
+
+def t_linecomment(t):
+ r'//[^\n]*'
+
+def t_multilinecomment(t):
+ r'/\*(\n|.)*?\*/'
+ t.lexer.lineno += t.value.count('\n')
+
+def t_NL(t):
+ r'(?:\r\n|\n|\n)+'
+ t.lexer.lineno += len(t.value)
+
+def t_ID(t):
+ r'[a-zA-Z_][a-zA-Z0-9_]*'
+ if t.value in reserved:
+ t.type = t.value.upper()
+ return t
+
+def t_STRING(t):
+ r'"[^"\n]*"'
+ t.value = t.value[1:-1]
+ return t
+
+def t_error(t):
+ _error(Loc(Parser.current.filename, t.lineno),
+ 'lexically invalid characters `%s', t.value)
+
+##-----------------------------------------------------------------------------
+
+def p_TranslationUnit(p):
+ """TranslationUnit : Preamble NamespacedStuff"""
+ tu = Parser.current.tu
+ tu.loc = Loc(tu.filename)
+ for stmt in p[1]:
+ if isinstance(stmt, CxxInclude):
+ tu.addCxxInclude(stmt)
+ elif isinstance(stmt, Include):
+ tu.addInclude(stmt)
+ elif isinstance(stmt, UsingStmt):
+ tu.addUsingStmt(stmt)
+ else:
+ assert 0
+
+ for thing in p[2]:
+ if isinstance(thing, StructDecl):
+ tu.addStructDecl(thing)
+ elif isinstance(thing, UnionDecl):
+ tu.addUnionDecl(thing)
+ elif isinstance(thing, Protocol):
+ if tu.protocol is not None:
+ _error(thing.loc, "only one protocol definition per file")
+ tu.protocol = thing
+ else:
+ assert(0)
+
+ # The "canonical" namespace of the tu, what it's considered to be
+ # in for the purposes of C++: |#include "foo/bar/TU.h"|
+ if tu.protocol:
+ assert tu.filetype == 'protocol'
+ tu.namespaces = tu.protocol.namespaces
+ tu.name = tu.protocol.name
+ else:
+ assert tu.filetype == 'header'
+ # There's not really a canonical "thing" in headers. So
+ # somewhat arbitrarily use the namespace of the last
+ # interesting thing that was declared.
+ for thing in reversed(tu.structsAndUnions):
+ tu.namespaces = thing.namespaces
+ break
+
+ p[0] = tu
+
+##--------------------
+## Preamble
+def p_Preamble(p):
+ """Preamble : Preamble PreambleStmt ';'
+ |"""
+ if 1 == len(p):
+ p[0] = [ ]
+ else:
+ p[1].append(p[2])
+ p[0] = p[1]
+
+def p_PreambleStmt(p):
+ """PreambleStmt : CxxIncludeStmt
+ | IncludeStmt
+ | UsingStmt"""
+ p[0] = p[1]
+
+def p_CxxIncludeStmt(p):
+ """CxxIncludeStmt : INCLUDE STRING"""
+ p[0] = CxxInclude(locFromTok(p, 1), p[2])
+
+def p_IncludeStmt(p):
+ """IncludeStmt : INCLUDE PROTOCOL ID
+ | INCLUDE ID"""
+ loc = locFromTok(p, 1)
+
+ Parser.current.loc = loc
+ if 4 == len(p):
+ id = p[3]
+ type = 'protocol'
+ else:
+ id = p[2]
+ type = 'header'
+ inc = Include(loc, type, id)
+
+ path = Parser.current.resolveIncludePath(inc.file)
+ if path is None:
+ raise ParseError(loc, "can't locate include file `%s'"% (
+ inc.file))
+
+ inc.tu = Parser(type, id).parse(open(path).read(), path, Parser.current.includedirs, Parser.current.errout)
+ p[0] = inc
+
+def p_UsingStmt(p):
+ """UsingStmt : USING CxxType FROM STRING
+ | USING CLASS CxxType FROM STRING
+ | USING STRUCT CxxType FROM STRING"""
+ if 6 == len(p):
+ header = p[5]
+ elif 5 == len(p):
+ header = p[4]
+ else:
+ header = None
+ if 6 == len(p):
+ kind = p[2]
+ else:
+ kind = None
+ if 6 == len(p):
+ cxxtype = p[3]
+ else:
+ cxxtype = p[2]
+ p[0] = UsingStmt(locFromTok(p, 1), cxxtype, header, kind)
+
+##--------------------
+## Namespaced stuff
+def p_NamespacedStuff(p):
+ """NamespacedStuff : NamespacedStuff NamespaceThing
+ | NamespaceThing"""
+ if 2 == len(p):
+ p[0] = p[1]
+ else:
+ p[1].extend(p[2])
+ p[0] = p[1]
+
+def p_NamespaceThing(p):
+ """NamespaceThing : NAMESPACE ID '{' NamespacedStuff '}'
+ | StructDecl
+ | UnionDecl
+ | ProtocolDefn"""
+ if 2 == len(p):
+ p[0] = [ p[1] ]
+ else:
+ for thing in p[4]:
+ thing.addOuterNamespace(Namespace(locFromTok(p, 1), p[2]))
+ p[0] = p[4]
+
+def p_StructDecl(p):
+ """StructDecl : STRUCT ID '{' StructFields '}' ';'
+ | STRUCT ID '{' '}' ';'"""
+ if 7 == len(p):
+ p[0] = StructDecl(locFromTok(p, 1), p[2], p[4])
+ else:
+ p[0] = StructDecl(locFromTok(p, 1), p[2], [ ])
+
+def p_StructFields(p):
+ """StructFields : StructFields StructField ';'
+ | StructField ';'"""
+ if 3 == len(p):
+ p[0] = [ p[1] ]
+ else:
+ p[1].append(p[2])
+ p[0] = p[1]
+
+def p_StructField(p):
+ """StructField : Type ID"""
+ p[0] = StructField(locFromTok(p, 1), p[1], p[2])
+
+def p_UnionDecl(p):
+ """UnionDecl : UNION ID '{' ComponentTypes '}' ';'"""
+ p[0] = UnionDecl(locFromTok(p, 1), p[2], p[4])
+
+def p_ComponentTypes(p):
+ """ComponentTypes : ComponentTypes Type ';'
+ | Type ';'"""
+ if 3 == len(p):
+ p[0] = [ p[1] ]
+ else:
+ p[1].append(p[2])
+ p[0] = p[1]
+
+def p_ProtocolDefn(p):
+ """ProtocolDefn : OptionalProtocolSendSemanticsQual PROTOCOL ID '{' ProtocolBody '}' ';'"""
+ protocol = p[5]
+ protocol.loc = locFromTok(p, 2)
+ protocol.name = p[3]
+ protocol.nestedRange = p[1][0]
+ protocol.sendSemantics = p[1][1]
+ p[0] = protocol
+
+ if Parser.current.type == 'header':
+ _error(protocol.loc, 'can\'t define a protocol in a header. Do it in a protocol spec instead.')
+
+
+def p_ProtocolBody(p):
+ """ProtocolBody : SpawnsStmtsOpt"""
+ p[0] = p[1]
+
+##--------------------
+## spawns/bridges/opens stmts
+
+def p_SpawnsStmtsOpt(p):
+ """SpawnsStmtsOpt : SpawnsStmt SpawnsStmtsOpt
+ | BridgesStmtsOpt"""
+ if 2 == len(p):
+ p[0] = p[1]
+ else:
+ p[2].spawnsStmts.insert(0, p[1])
+ p[0] = p[2]
+
+def p_SpawnsStmt(p):
+ """SpawnsStmt : PARENT SPAWNS ID AsOpt ';'
+ | CHILD SPAWNS ID AsOpt ';'"""
+ p[0] = SpawnsStmt(locFromTok(p, 1), p[1], p[3], p[4])
+
+def p_AsOpt(p):
+ """AsOpt : AS PARENT
+ | AS CHILD
+ | """
+ if 3 == len(p):
+ p[0] = p[2]
+ else:
+ p[0] = 'child'
+
+def p_BridgesStmtsOpt(p):
+ """BridgesStmtsOpt : BridgesStmt BridgesStmtsOpt
+ | OpensStmtsOpt"""
+ if 2 == len(p):
+ p[0] = p[1]
+ else:
+ p[2].bridgesStmts.insert(0, p[1])
+ p[0] = p[2]
+
+def p_BridgesStmt(p):
+ """BridgesStmt : BRIDGES ID ',' ID ';'"""
+ p[0] = BridgesStmt(locFromTok(p, 1), p[2], p[4])
+
+def p_OpensStmtsOpt(p):
+ """OpensStmtsOpt : OpensStmt OpensStmtsOpt
+ | ManagersStmtOpt"""
+ if 2 == len(p):
+ p[0] = p[1]
+ else:
+ p[2].opensStmts.insert(0, p[1])
+ p[0] = p[2]
+
+def p_OpensStmt(p):
+ """OpensStmt : PARENT OPENS ID ';'
+ | CHILD OPENS ID ';'"""
+ p[0] = OpensStmt(locFromTok(p, 1), p[1], p[3])
+
+##--------------------
+## manager/manages stmts
+
+def p_ManagersStmtOpt(p):
+ """ManagersStmtOpt : ManagersStmt ManagesStmtsOpt
+ | ManagesStmtsOpt"""
+ if 2 == len(p):
+ p[0] = p[1]
+ else:
+ p[2].managers = p[1]
+ p[0] = p[2]
+
+def p_ManagersStmt(p):
+ """ManagersStmt : MANAGER ManagerList ';'"""
+ if 1 == len(p):
+ p[0] = [ ]
+ else:
+ p[0] = p[2]
+
+def p_ManagerList(p):
+ """ManagerList : ID
+ | ManagerList OR ID"""
+ if 2 == len(p):
+ p[0] = [ Manager(locFromTok(p, 1), p[1]) ]
+ else:
+ p[1].append(Manager(locFromTok(p, 3), p[3]))
+ p[0] = p[1]
+
+def p_ManagesStmtsOpt(p):
+ """ManagesStmtsOpt : ManagesStmt ManagesStmtsOpt
+ | MessageDeclsOpt"""
+ if 2 == len(p):
+ p[0] = p[1]
+ else:
+ p[2].managesStmts.insert(0, p[1])
+ p[0] = p[2]
+
+def p_ManagesStmt(p):
+ """ManagesStmt : MANAGES ID ';'"""
+ p[0] = ManagesStmt(locFromTok(p, 1), p[2])
+
+
+##--------------------
+## Message decls
+
+def p_MessageDeclsOpt(p):
+ """MessageDeclsOpt : MessageDeclThing MessageDeclsOpt
+ | TransitionStmtsOpt"""
+ if 2 == len(p):
+ p[0] = p[1]
+ else:
+ p[2].messageDecls.insert(0, p[1])
+ p[0] = p[2]
+
+def p_MessageDeclThing(p):
+ """MessageDeclThing : MessageDirectionLabel ':' MessageDecl ';'
+ | MessageDecl ';'"""
+ if 3 == len(p):
+ p[0] = p[1]
+ else:
+ p[0] = p[3]
+
+def p_MessageDirectionLabel(p):
+ """MessageDirectionLabel : PARENT
+ | CHILD
+ | BOTH"""
+ if p[1] == 'parent':
+ Parser.current.direction = IN
+ elif p[1] == 'child':
+ Parser.current.direction = OUT
+ elif p[1] == 'both':
+ Parser.current.direction = INOUT
+ else:
+ assert 0
+
+def p_MessageDecl(p):
+ """MessageDecl : SendSemanticsQual MessageBody"""
+ msg = p[2]
+ msg.nested = p[1][0]
+ msg.prio = p[1][1]
+ msg.sendSemantics = p[1][2]
+
+ if Parser.current.direction is None:
+ _error(msg.loc, 'missing message direction')
+ msg.direction = Parser.current.direction
+
+ p[0] = msg
+
+def p_MessageBody(p):
+ """MessageBody : MessageId MessageInParams MessageOutParams OptionalMessageModifiers"""
+ # FIXME/cjones: need better loc info: use one of the quals
+ loc, name = p[1]
+ msg = MessageDecl(loc)
+ msg.name = name
+ msg.addInParams(p[2])
+ msg.addOutParams(p[3])
+ msg.addModifiers(p[4])
+
+ p[0] = msg
+
+def p_MessageId(p):
+ """MessageId : ID
+ | __DELETE__
+ | DELETE
+ | '~' ID"""
+ loc = locFromTok(p, 1)
+ if 3 == len(p):
+ _error(loc, "sorry, `%s()' destructor syntax is a relic from a bygone era. Declare `__delete__()' in the `%s' protocol instead", p[1]+p[2], p[2])
+ elif 'delete' == p[1]:
+ _error(loc, "`delete' is a reserved identifier")
+ p[0] = [ loc, p[1] ]
+
+def p_MessageInParams(p):
+ """MessageInParams : '(' ParamList ')'"""
+ p[0] = p[2]
+
+def p_MessageOutParams(p):
+ """MessageOutParams : RETURNS '(' ParamList ')'
+ | """
+ if 1 == len(p):
+ p[0] = [ ]
+ else:
+ p[0] = p[3]
+
+def p_OptionalMessageModifiers(p):
+ """OptionalMessageModifiers : OptionalMessageModifiers MessageModifier
+ | MessageModifier
+ | """
+ if 1 == len(p):
+ p[0] = [ ]
+ elif 2 == len(p):
+ p[0] = [ p[1] ]
+ else:
+ p[1].append(p[2])
+ p[0] = p[1]
+
+def p_MessageModifier(p):
+ """ MessageModifier : MessageVerify
+ | MessageCompress """
+ p[0] = p[1]
+
+def p_MessageVerify(p):
+ """MessageVerify : VERIFY"""
+ p[0] = p[1]
+
+def p_MessageCompress(p):
+ """MessageCompress : COMPRESS
+ | COMPRESSALL"""
+ p[0] = p[1]
+
+##--------------------
+## State machine
+
+def p_TransitionStmtsOpt(p):
+ """TransitionStmtsOpt : TransitionStmt TransitionStmtsOpt
+ |"""
+ if 1 == len(p):
+ # we fill in |loc| in the Protocol rule
+ p[0] = Protocol(None)
+ else:
+ p[2].transitionStmts.insert(0, p[1])
+ p[0] = p[2]
+
+def p_TransitionStmt(p):
+ """TransitionStmt : OptionalStart STATE State ':' Transitions"""
+ p[3].start = p[1]
+ p[0] = TransitionStmt(locFromTok(p, 2), p[3], p[5])
+
+def p_OptionalStart(p):
+ """OptionalStart : START
+ | """
+ p[0] = (len(p) == 2) # True iff 'start' specified
+
+def p_Transitions(p):
+ """Transitions : Transitions Transition
+ | Transition"""
+ if 3 == len(p):
+ p[1].append(p[2])
+ p[0] = p[1]
+ else:
+ p[0] = [ p[1] ]
+
+def p_Transition(p):
+ """Transition : Trigger ID GOTO StateList ';'
+ | Trigger __DELETE__ ';'
+ | Trigger DELETE ';'"""
+ if 'delete' == p[2]:
+ _error(locFromTok(p, 1), "`delete' is a reserved identifier")
+
+ loc, trigger = p[1]
+ if 6 == len(p):
+ nextstates = p[4]
+ else:
+ nextstates = [ State.DEAD ]
+ p[0] = Transition(loc, trigger, p[2], nextstates)
+
+def p_Trigger(p):
+ """Trigger : SEND
+ | RECV
+ | CALL
+ | ANSWER"""
+ p[0] = [ locFromTok(p, 1), Transition.nameToTrigger(p[1]) ]
+
+def p_StateList(p):
+ """StateList : StateList OR State
+ | State"""
+ if 2 == len(p):
+ p[0] = [ p[1] ]
+ else:
+ p[1].append(p[3])
+ p[0] = p[1]
+
+def p_State(p):
+ """State : ID"""
+ p[0] = State(locFromTok(p, 1), p[1])
+
+##--------------------
+## Minor stuff
+def p_Nested(p):
+ """Nested : ID"""
+ kinds = {'not': 1,
+ 'inside_sync': 2,
+ 'inside_cpow': 3}
+ if p[1] not in kinds:
+ _error(locFromTok(p, 1), "Expected not, inside_sync, or inside_cpow for nested()")
+
+ p[0] = { 'nested': kinds[p[1]] }
+
+def p_Priority(p):
+ """Priority : ID"""
+ kinds = {'normal': 1,
+ 'high': 2}
+ if p[1] not in kinds:
+ _error(locFromTok(p, 1), "Expected normal or high for prio()")
+
+ p[0] = { 'prio': kinds[p[1]] }
+
+def p_SendQualifier(p):
+ """SendQualifier : NESTED '(' Nested ')'
+ | PRIO '(' Priority ')'"""
+ p[0] = p[3]
+
+def p_SendQualifierList(p):
+ """SendQualifierList : SendQualifier SendQualifierList
+ | """
+ if len(p) > 1:
+ p[0] = p[1]
+ p[0].update(p[2])
+ else:
+ p[0] = {}
+
+def p_SendSemanticsQual(p):
+ """SendSemanticsQual : SendQualifierList ASYNC
+ | SendQualifierList SYNC
+ | INTR"""
+ quals = {}
+ if len(p) == 3:
+ quals = p[1]
+ mtype = p[2]
+ else:
+ mtype = 'intr'
+
+ if mtype == 'async': mtype = ASYNC
+ elif mtype == 'sync': mtype = SYNC
+ elif mtype == 'intr': mtype = INTR
+ else: assert 0
+
+ p[0] = [ quals.get('nested', NOT_NESTED), quals.get('prio', NORMAL_PRIORITY), mtype ]
+
+def p_OptionalProtocolSendSemanticsQual(p):
+ """OptionalProtocolSendSemanticsQual : ProtocolSendSemanticsQual
+ | """
+ if 2 == len(p): p[0] = p[1]
+ else: p[0] = [ (NOT_NESTED, NOT_NESTED), ASYNC ]
+
+def p_ProtocolSendSemanticsQual(p):
+ """ProtocolSendSemanticsQual : ASYNC
+ | SYNC
+ | NESTED '(' UPTO Nested ')' ASYNC
+ | NESTED '(' UPTO Nested ')' SYNC
+ | INTR"""
+ if p[1] == 'nested':
+ mtype = p[6]
+ nested = (NOT_NESTED, p[4])
+ else:
+ mtype = p[1]
+ nested = (NOT_NESTED, NOT_NESTED)
+
+ if mtype == 'async': mtype = ASYNC
+ elif mtype == 'sync': mtype = SYNC
+ elif mtype == 'intr': mtype = INTR
+ else: assert 0
+
+ p[0] = [ nested, mtype ]
+
+def p_ParamList(p):
+ """ParamList : ParamList ',' Param
+ | Param
+ | """
+ if 1 == len(p):
+ p[0] = [ ]
+ elif 2 == len(p):
+ p[0] = [ p[1] ]
+ else:
+ p[1].append(p[3])
+ p[0] = p[1]
+
+def p_Param(p):
+ """Param : Type ID"""
+ p[0] = Param(locFromTok(p, 1), p[1], p[2])
+
+def p_Type(p):
+ """Type : MaybeNullable BasicType"""
+ # only actor types are nullable; we check this in the type checker
+ p[2].nullable = p[1]
+ p[0] = p[2]
+
+def p_BasicType(p):
+ """BasicType : ScalarType
+ | ScalarType '[' ']'"""
+ if 4 == len(p):
+ p[1].array = 1
+ p[0] = p[1]
+
+def p_ScalarType(p):
+ """ScalarType : ActorType
+ | CxxID""" # ID == CxxType; we forbid qnames here,
+ # in favor of the |using| declaration
+ if isinstance(p[1], TypeSpec):
+ p[0] = p[1]
+ else:
+ loc, id = p[1]
+ p[0] = TypeSpec(loc, QualifiedId(loc, id))
+
+def p_ActorType(p):
+ """ActorType : ID ':' State"""
+ loc = locFromTok(p, 1)
+ p[0] = TypeSpec(loc, QualifiedId(loc, p[1]), state=p[3])
+
+def p_MaybeNullable(p):
+ """MaybeNullable : NULLABLE
+ | """
+ p[0] = (2 == len(p))
+
+##--------------------
+## C++ stuff
+def p_CxxType(p):
+ """CxxType : QualifiedID
+ | CxxID"""
+ if isinstance(p[1], QualifiedId):
+ p[0] = TypeSpec(p[1].loc, p[1])
+ else:
+ loc, id = p[1]
+ p[0] = TypeSpec(loc, QualifiedId(loc, id))
+
+def p_QualifiedID(p):
+ """QualifiedID : QualifiedID COLONCOLON CxxID
+ | CxxID COLONCOLON CxxID"""
+ if isinstance(p[1], QualifiedId):
+ loc, id = p[3]
+ p[1].qualify(id)
+ p[0] = p[1]
+ else:
+ loc1, id1 = p[1]
+ _, id2 = p[3]
+ p[0] = QualifiedId(loc1, id2, [ id1 ])
+
+def p_CxxID(p):
+ """CxxID : ID
+ | CxxTemplateInst"""
+ if isinstance(p[1], tuple):
+ p[0] = p[1]
+ else:
+ p[0] = (locFromTok(p, 1), str(p[1]))
+
+def p_CxxTemplateInst(p):
+ """CxxTemplateInst : ID '<' ID '>'"""
+ p[0] = (locFromTok(p, 1), str(p[1]) +'<'+ str(p[3]) +'>')
+
+def p_error(t):
+ lineno, value = _safeLinenoValue(t)
+ _error(Loc(Parser.current.filename, lineno),
+ "bad syntax near `%s'", value)
diff --git a/ipc/ipdl/ipdl/type.py b/ipc/ipdl/ipdl/type.py
new file mode 100644
index 000000000..68a10cd01
--- /dev/null
+++ b/ipc/ipdl/ipdl/type.py
@@ -0,0 +1,2200 @@
+# vim: set ts=4 sw=4 tw=99 et:
+# 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/.
+
+import os, sys
+
+from ipdl.ast import CxxInclude, Decl, Loc, QualifiedId, State, StructDecl, TransitionStmt
+from ipdl.ast import TypeSpec, UnionDecl, UsingStmt, Visitor
+from ipdl.ast import ASYNC, SYNC, INTR
+from ipdl.ast import IN, OUT, INOUT, ANSWER, CALL, RECV, SEND
+from ipdl.ast import NOT_NESTED, INSIDE_SYNC_NESTED, INSIDE_CPOW_NESTED
+import ipdl.builtin as builtin
+
+_DELETE_MSG = '__delete__'
+
+
+def _otherside(side):
+ if side == 'parent': return 'child'
+ elif side == 'child': return 'parent'
+ else: assert 0 and 'unknown side "%s"'% (side)
+
+def unique_pairs(s):
+ n = len(s)
+ for i, e1 in enumerate(s):
+ for j in xrange(i+1, n):
+ yield (e1, s[j])
+
+def cartesian_product(s1, s2):
+ for e1 in s1:
+ for e2 in s2:
+ yield (e1, e2)
+
+
+class TypeVisitor:
+ def __init__(self):
+ self.visited = set()
+
+ def defaultVisit(self, node, *args):
+ raise Exception, "INTERNAL ERROR: no visitor for node type `%s'"% (
+ node.__class__.__name__)
+
+ def visitVoidType(self, v, *args):
+ pass
+
+ def visitBuiltinCxxType(self, t, *args):
+ pass
+
+ def visitImportedCxxType(self, t, *args):
+ pass
+
+ def visitStateType(self, s, *args):
+ pass
+
+ def visitMessageType(self, m, *args):
+ for param in m.params:
+ param.accept(self, *args)
+ for ret in m.returns:
+ ret.accept(self, *args)
+ if m.cdtype is not None:
+ m.cdtype.accept(self, *args)
+
+ def visitProtocolType(self, p, *args):
+ # NB: don't visit manager and manages. a naive default impl
+ # could result in an infinite loop
+ pass
+
+ def visitActorType(self, a, *args):
+ a.protocol.accept(self, *args)
+ a.state.accept(self, *args)
+
+ def visitStructType(self, s, *args):
+ if s in self.visited:
+ return
+
+ self.visited.add(s)
+ for field in s.fields:
+ field.accept(self, *args)
+
+ def visitUnionType(self, u, *args):
+ if u in self.visited:
+ return
+
+ self.visited.add(u)
+ for component in u.components:
+ component.accept(self, *args)
+
+ def visitArrayType(self, a, *args):
+ a.basetype.accept(self, *args)
+
+ def visitShmemType(self, s, *args):
+ pass
+
+ def visitShmemChmodType(self, c, *args):
+ c.shmem.accept(self)
+
+ def visitFDType(self, s, *args):
+ pass
+
+ def visitEndpointType(self, s, *args):
+ pass
+
+class Type:
+ def __cmp__(self, o):
+ return cmp(self.fullname(), o.fullname())
+ def __eq__(self, o):
+ return (self.__class__ == o.__class__
+ and self.fullname() == o.fullname())
+ def __hash__(self):
+ return hash(self.fullname())
+
+ # Is this a C++ type?
+ def isCxx(self):
+ return False
+ # Is this an IPDL type?
+ def isIPDL(self):
+ return False
+ # Is this type neither compound nor an array?
+ def isAtom(self):
+ return False
+ # Can this type appear in IPDL programs?
+ def isVisible(self):
+ return False
+ def isVoid(self):
+ return False
+ def typename(self):
+ return self.__class__.__name__
+
+ def name(self): raise Exception, 'NYI'
+ def fullname(self): raise Exception, 'NYI'
+
+ def accept(self, visitor, *args):
+ visit = getattr(visitor, 'visit'+ self.__class__.__name__, None)
+ if visit is None:
+ return getattr(visitor, 'defaultVisit')(self, *args)
+ return visit(self, *args)
+
+class VoidType(Type):
+ def isCxx(self):
+ return True
+ def isIPDL(self):
+ return False
+ def isAtom(self):
+ return True
+ def isVisible(self):
+ return False
+ def isVoid(self):
+ return True
+
+ def name(self): return 'void'
+ def fullname(self): return 'void'
+
+VOID = VoidType()
+
+##--------------------
+class CxxType(Type):
+ def isCxx(self):
+ return True
+ def isAtom(self):
+ return True
+ def isBuiltin(self):
+ return False
+ def isImported(self):
+ return False
+ def isGenerated(self):
+ return False
+ def isVisible(self):
+ return True
+
+class BuiltinCxxType(CxxType):
+ def __init__(self, qname):
+ assert isinstance(qname, QualifiedId)
+ self.loc = qname.loc
+ self.qname = qname
+ def isBuiltin(self): return True
+
+ def name(self):
+ return self.qname.baseid
+ def fullname(self):
+ return str(self.qname)
+
+class ImportedCxxType(CxxType):
+ def __init__(self, qname):
+ assert isinstance(qname, QualifiedId)
+ self.loc = qname.loc
+ self.qname = qname
+ def isImported(self): return True
+
+ def name(self):
+ return self.qname.baseid
+ def fullname(self):
+ return str(self.qname)
+
+##--------------------
+class IPDLType(Type):
+ def isIPDL(self): return True
+ def isVisible(self): return True
+ def isState(self): return False
+ def isMessage(self): return False
+ def isProtocol(self): return False
+ def isActor(self): return False
+ def isStruct(self): return False
+ def isUnion(self): return False
+ def isArray(self): return False
+ def isAtom(self): return True
+ def isCompound(self): return False
+ def isShmem(self): return False
+ def isChmod(self): return False
+ def isFD(self): return False
+ def isEndpoint(self): return False
+
+ def isAsync(self): return self.sendSemantics == ASYNC
+ def isSync(self): return self.sendSemantics == SYNC
+ def isInterrupt(self): return self.sendSemantics is INTR
+
+ def hasReply(self): return (self.isSync() or self.isInterrupt())
+
+ @classmethod
+ def convertsTo(cls, lesser, greater):
+ if (lesser.nestedRange[0] < greater.nestedRange[0] or
+ lesser.nestedRange[1] > greater.nestedRange[1]):
+ return False
+
+ # Protocols that use intr semantics are not allowed to use
+ # message nesting.
+ if (greater.isInterrupt() and
+ lesser.nestedRange != (NOT_NESTED, NOT_NESTED)):
+ return False
+
+ if lesser.isAsync():
+ return True
+ elif lesser.isSync() and not greater.isAsync():
+ return True
+ elif greater.isInterrupt():
+ return True
+
+ return False
+
+ def needsMoreJuiceThan(self, o):
+ return not IPDLType.convertsTo(self, o)
+
+class StateType(IPDLType):
+ def __init__(self, protocol, name, start=False):
+ self.protocol = protocol
+ self.name = name
+ self.start = start
+ def isState(self): return True
+ def name(self):
+ return self.name
+ def fullname(self):
+ return self.name()
+
+class MessageType(IPDLType):
+ def __init__(self, nested, prio, sendSemantics, direction,
+ ctor=False, dtor=False, cdtype=None, compress=False,
+ verify=False):
+ assert not (ctor and dtor)
+ assert not (ctor or dtor) or type is not None
+
+ self.nested = nested
+ self.prio = prio
+ self.nestedRange = (nested, nested)
+ self.sendSemantics = sendSemantics
+ self.direction = direction
+ self.params = [ ]
+ self.returns = [ ]
+ self.ctor = ctor
+ self.dtor = dtor
+ self.cdtype = cdtype
+ self.compress = compress
+ self.verify = verify
+ def isMessage(self): return True
+
+ def isCtor(self): return self.ctor
+ def isDtor(self): return self.dtor
+ def constructedType(self): return self.cdtype
+
+ def isIn(self): return self.direction is IN
+ def isOut(self): return self.direction is OUT
+ def isInout(self): return self.direction is INOUT
+
+ def hasImplicitActorParam(self):
+ return self.isCtor() or self.isDtor()
+
+class Bridge:
+ def __init__(self, parentPtype, childPtype):
+ assert parentPtype.isToplevel() and childPtype.isToplevel()
+ self.parent = parentPtype
+ self.child = childPtype
+
+ def __cmp__(self, o):
+ return cmp(self.parent, o.parent) or cmp(self.child, o.child)
+ def __eq__(self, o):
+ return self.parent == o.parent and self.child == o.child
+ def __hash__(self):
+ return hash(self.parent) + hash(self.child)
+
+class ProtocolType(IPDLType):
+ def __init__(self, qname, nestedRange, sendSemantics, stateless=False):
+ self.qname = qname
+ self.nestedRange = nestedRange
+ self.sendSemantics = sendSemantics
+ self.spawns = set() # ProtocolType
+ self.opens = set() # ProtocolType
+ self.managers = [] # ProtocolType
+ self.manages = [ ]
+ self.stateless = stateless
+ self.hasDelete = False
+ self.hasReentrantDelete = False
+ def isProtocol(self): return True
+
+ def name(self):
+ return self.qname.baseid
+ def fullname(self):
+ return str(self.qname)
+
+ def addManager(self, mgrtype):
+ assert mgrtype.isIPDL() and mgrtype.isProtocol()
+ self.managers.append(mgrtype)
+
+ def addSpawn(self, ptype):
+ assert self.isToplevel() and ptype.isToplevel()
+ self.spawns.add(ptype)
+
+ def addOpen(self, ptype):
+ assert self.isToplevel() and ptype.isToplevel()
+ self.opens.add(ptype)
+
+ def managedBy(self, mgr):
+ self.managers = list(mgr)
+
+ def toplevel(self):
+ if self.isToplevel():
+ return self
+ for mgr in self.managers:
+ if mgr is not self:
+ return mgr.toplevel()
+
+ def toplevels(self):
+ if self.isToplevel():
+ return [self]
+ toplevels = list()
+ for mgr in self.managers:
+ if mgr is not self:
+ toplevels.extend(mgr.toplevels())
+ return set(toplevels)
+
+ def isManagerOf(self, pt):
+ for managed in self.manages:
+ if pt is managed:
+ return True
+ return False
+ def isManagedBy(self, pt):
+ return pt in self.managers
+
+ def isManager(self):
+ return len(self.manages) > 0
+ def isManaged(self):
+ return 0 < len(self.managers)
+ def isToplevel(self):
+ return not self.isManaged()
+
+ def manager(self):
+ assert 1 == len(self.managers)
+ for mgr in self.managers: return mgr
+
+class ActorType(IPDLType):
+ def __init__(self, protocol, state=None, nullable=0):
+ self.protocol = protocol
+ self.state = state
+ self.nullable = nullable
+ def isActor(self): return True
+
+ def name(self):
+ return self.protocol.name()
+ def fullname(self):
+ return self.protocol.fullname()
+
+class _CompoundType(IPDLType):
+ def __init__(self):
+ self.defined = False # bool
+ self.mutualRec = set() # set(_CompoundType | ArrayType)
+ def isAtom(self):
+ return False
+ def isCompound(self):
+ return True
+ def itercomponents(self):
+ raise Exception('"pure virtual" method')
+
+ def mutuallyRecursiveWith(self, t, exploring=None):
+ '''|self| is mutually recursive with |t| iff |self| and |t|
+are in a cycle in the type graph rooted at |self|. This function
+looks for such a cycle and returns True if found.'''
+ if exploring is None:
+ exploring = set()
+
+ if t.isAtom():
+ return False
+ elif t is self or t in self.mutualRec:
+ return True
+ elif t.isArray():
+ isrec = self.mutuallyRecursiveWith(t.basetype, exploring)
+ if isrec: self.mutualRec.add(t)
+ return isrec
+ elif t in exploring:
+ return False
+
+ exploring.add(t)
+ for c in t.itercomponents():
+ if self.mutuallyRecursiveWith(c, exploring):
+ self.mutualRec.add(c)
+ return True
+ exploring.remove(t)
+
+ return False
+
+class StructType(_CompoundType):
+ def __init__(self, qname, fields):
+ _CompoundType.__init__(self)
+ self.qname = qname
+ self.fields = fields # [ Type ]
+
+ def isStruct(self): return True
+ def itercomponents(self):
+ for f in self.fields:
+ yield f
+
+ def name(self): return self.qname.baseid
+ def fullname(self): return str(self.qname)
+
+class UnionType(_CompoundType):
+ def __init__(self, qname, components):
+ _CompoundType.__init__(self)
+ self.qname = qname
+ self.components = components # [ Type ]
+
+ def isUnion(self): return True
+ def itercomponents(self):
+ for c in self.components:
+ yield c
+
+ def name(self): return self.qname.baseid
+ def fullname(self): return str(self.qname)
+
+class ArrayType(IPDLType):
+ def __init__(self, basetype):
+ self.basetype = basetype
+ def isAtom(self): return False
+ def isArray(self): return True
+
+ def name(self): return self.basetype.name() +'[]'
+ def fullname(self): return self.basetype.fullname() +'[]'
+
+class ShmemType(IPDLType):
+ def __init__(self, qname):
+ self.qname = qname
+ def isShmem(self): return True
+
+ def name(self):
+ return self.qname.baseid
+ def fullname(self):
+ return str(self.qname)
+
+class FDType(IPDLType):
+ def __init__(self, qname):
+ self.qname = qname
+ def isFD(self): return True
+
+ def name(self):
+ return self.qname.baseid
+ def fullname(self):
+ return str(self.qname)
+
+class EndpointType(IPDLType):
+ def __init__(self, qname):
+ self.qname = qname
+ def isEndpoint(self): return True
+
+ def name(self):
+ return self.qname.baseid
+ def fullname(self):
+ return str(self.qname)
+
+def iteractortypes(t, visited=None):
+ """Iterate over any actor(s) buried in |type|."""
+ if visited is None:
+ visited = set()
+
+ # XXX |yield| semantics makes it hard to use TypeVisitor
+ if not t.isIPDL():
+ return
+ elif t.isActor():
+ yield t
+ elif t.isArray():
+ for actor in iteractortypes(t.basetype, visited):
+ yield actor
+ elif t.isCompound() and t not in visited:
+ visited.add(t)
+ for c in t.itercomponents():
+ for actor in iteractortypes(c, visited):
+ yield actor
+
+def hasactor(type):
+ """Return true iff |type| is an actor or has one buried within."""
+ for _ in iteractortypes(type): return True
+ return False
+
+def hasshmem(type):
+ """Return true iff |type| is shmem or has it buried within."""
+ class found: pass
+ class findShmem(TypeVisitor):
+ def visitShmemType(self, s): raise found()
+ try:
+ type.accept(findShmem())
+ except found:
+ return True
+ return False
+
+def hasfd(type):
+ """Return true iff |type| is fd or has it buried within."""
+ class found: pass
+ class findFD(TypeVisitor):
+ def visitFDType(self, s): raise found()
+ try:
+ type.accept(findFD())
+ except found:
+ return True
+ return False
+
+##--------------------
+_builtinloc = Loc('<builtin>', 0)
+def makeBuiltinUsing(tname):
+ quals = tname.split('::')
+ base = quals.pop()
+ quals = quals[0:]
+ return UsingStmt(_builtinloc,
+ TypeSpec(_builtinloc,
+ QualifiedId(_builtinloc, base, quals)))
+
+builtinUsing = [ makeBuiltinUsing(t) for t in builtin.Types ]
+builtinHeaderIncludes = [ CxxInclude(_builtinloc, f) for f in builtin.HeaderIncludes ]
+
+def errormsg(loc, fmt, *args):
+ while not isinstance(loc, Loc):
+ if loc is None: loc = Loc.NONE
+ else: loc = loc.loc
+ return '%s: error: %s'% (str(loc), fmt % args)
+
+##--------------------
+class SymbolTable:
+ def __init__(self, errors):
+ self.errors = errors
+ self.scopes = [ { } ] # stack({})
+ self.globalScope = self.scopes[0]
+ self.currentScope = self.globalScope
+
+ def enterScope(self, node):
+ assert (isinstance(self.scopes[0], dict)
+ and self.globalScope is self.scopes[0])
+ assert (isinstance(self.currentScope, dict))
+
+ if not hasattr(node, 'symtab'):
+ node.symtab = { }
+
+ self.scopes.append(node.symtab)
+ self.currentScope = self.scopes[-1]
+
+ def exitScope(self, node):
+ symtab = self.scopes.pop()
+ assert self.currentScope is symtab
+
+ self.currentScope = self.scopes[-1]
+
+ assert (isinstance(self.scopes[0], dict)
+ and self.globalScope is self.scopes[0])
+ assert isinstance(self.currentScope, dict)
+
+ def lookup(self, sym):
+ # NB: since IPDL doesn't allow any aliased names of different types,
+ # it doesn't matter in which order we walk the scope chain to resolve
+ # |sym|
+ for scope in self.scopes:
+ decl = scope.get(sym, None)
+ if decl is not None: return decl
+ return None
+
+ def declare(self, decl):
+ assert decl.progname or decl.shortname or decl.fullname
+ assert decl.loc
+ assert decl.type
+
+ def tryadd(name):
+ olddecl = self.lookup(name)
+ if olddecl is not None:
+ self.errors.append(errormsg(
+ decl.loc,
+ "redeclaration of symbol `%s', first declared at %s",
+ name, olddecl.loc))
+ return
+ self.currentScope[name] = decl
+ decl.scope = self.currentScope
+
+ if decl.progname: tryadd(decl.progname)
+ if decl.shortname: tryadd(decl.shortname)
+ if decl.fullname: tryadd(decl.fullname)
+
+
+class TypeCheck:
+ '''This pass sets the .type attribute of every AST node. For some
+nodes, the type is meaningless and it is set to "VOID." This pass
+also sets the .decl attribute of AST nodes for which that is relevant;
+a decl says where, with what type, and under what name(s) a node was
+declared.
+
+With this information, it finally type checks the AST.'''
+
+ def __init__(self):
+ # NB: no IPDL compile will EVER print a warning. A program has
+ # one of two attributes: it is either well typed, or not well typed.
+ self.errors = [ ] # [ string ]
+
+ def check(self, tu, errout=sys.stderr):
+ def runpass(tcheckpass):
+ tu.accept(tcheckpass)
+ if len(self.errors):
+ self.reportErrors(errout)
+ return False
+ return True
+
+ # tag each relevant node with "decl" information, giving type, name,
+ # and location of declaration
+ if not runpass(GatherDecls(builtinUsing, self.errors)):
+ return False
+
+ # now that the nodes have decls, type checking is much easier.
+ if not runpass(CheckTypes(self.errors)):
+ return False
+
+ if not (runpass(BuildProcessGraph(self.errors))
+ and runpass(CheckProcessGraph(self.errors))):
+ return False
+
+ if (tu.protocol
+ and len(tu.protocol.startStates)
+ and not runpass(CheckStateMachine(self.errors))):
+ return False
+ return True
+
+ def reportErrors(self, errout):
+ for error in self.errors:
+ print >>errout, error
+
+
+class TcheckVisitor(Visitor):
+ def __init__(self, symtab, errors):
+ self.symtab = symtab
+ self.errors = errors
+
+ def error(self, loc, fmt, *args):
+ self.errors.append(errormsg(loc, fmt, *args))
+
+ def declare(self, loc, type, shortname=None, fullname=None, progname=None):
+ d = Decl(loc)
+ d.type = type
+ d.progname = progname
+ d.shortname = shortname
+ d.fullname = fullname
+ self.symtab.declare(d)
+ return d
+
+class GatherDecls(TcheckVisitor):
+ def __init__(self, builtinUsing, errors):
+ # |self.symtab| is the symbol table for the translation unit
+ # currently being visited
+ TcheckVisitor.__init__(self, None, errors)
+ self.builtinUsing = builtinUsing
+
+ def visitTranslationUnit(self, tu):
+ # all TranslationUnits declare symbols in global scope
+ if hasattr(tu, 'symtab'):
+ return
+ tu.symtab = SymbolTable(self.errors)
+ savedSymtab = self.symtab
+ self.symtab = tu.symtab
+
+ # pretend like the translation unit "using"-ed these for the
+ # sake of type checking and C++ code generation
+ tu.builtinUsing = self.builtinUsing
+
+ # for everyone's sanity, enforce that the filename and tu name
+ # match
+ basefilename = os.path.basename(tu.filename)
+ expectedfilename = '%s.ipdl'% (tu.name)
+ if not tu.protocol:
+ # header
+ expectedfilename += 'h'
+ if basefilename != expectedfilename:
+ self.error(tu.loc,
+ "expected file for translation unit `%s' to be named `%s'; instead it's named `%s'",
+ tu.name, expectedfilename, basefilename)
+
+ if tu.protocol:
+ assert tu.name == tu.protocol.name
+
+ p = tu.protocol
+
+ # FIXME/cjones: it's a little weird and counterintuitive
+ # to put both the namespace and non-namespaced name in the
+ # global scope. try to figure out something better; maybe
+ # a type-neutral |using| that works for C++ and protocol
+ # types?
+ qname = p.qname()
+ if 0 == len(qname.quals):
+ fullname = None
+ else:
+ fullname = str(qname)
+ p.decl = self.declare(
+ loc=p.loc,
+ type=ProtocolType(qname, p.nestedRange, p.sendSemantics,
+ stateless=(0 == len(p.transitionStmts))),
+ shortname=p.name,
+ fullname=fullname)
+
+ p.parentEndpointDecl = self.declare(
+ loc=p.loc,
+ type=EndpointType(QualifiedId(p.loc, 'Endpoint<' + fullname + 'Parent>', ['mozilla', 'ipc'])),
+ shortname='Endpoint<' + p.name + 'Parent>')
+ p.childEndpointDecl = self.declare(
+ loc=p.loc,
+ type=EndpointType(QualifiedId(p.loc, 'Endpoint<' + fullname + 'Child>', ['mozilla', 'ipc'])),
+ shortname='Endpoint<' + p.name + 'Child>')
+
+ # XXX ugh, this sucks. but we need this information to compute
+ # what friend decls we need in generated C++
+ p.decl.type._ast = p
+
+ # make sure we have decls for all dependent protocols
+ for pinc in tu.includes:
+ pinc.accept(self)
+
+ # declare imported (and builtin) C++ types
+ for using in tu.builtinUsing:
+ using.accept(self)
+ for using in tu.using:
+ using.accept(self)
+
+ # first pass to "forward-declare" all structs and unions in
+ # order to support recursive definitions
+ for su in tu.structsAndUnions:
+ self.declareStructOrUnion(su)
+
+ # second pass to check each definition
+ for su in tu.structsAndUnions:
+ su.accept(self)
+ for inc in tu.includes:
+ if inc.tu.filetype == 'header':
+ for su in inc.tu.structsAndUnions:
+ su.accept(self)
+
+ if tu.protocol:
+ # grab symbols in the protocol itself
+ p.accept(self)
+
+
+ tu.type = VOID
+
+ self.symtab = savedSymtab
+
+ def declareStructOrUnion(self, su):
+ if hasattr(su, 'decl'):
+ self.symtab.declare(su.decl)
+ return
+
+ qname = su.qname()
+ if 0 == len(qname.quals):
+ fullname = None
+ else:
+ fullname = str(qname)
+
+ if isinstance(su, StructDecl):
+ sutype = StructType(qname, [ ])
+ elif isinstance(su, UnionDecl):
+ sutype = UnionType(qname, [ ])
+ else: assert 0 and 'unknown type'
+
+ # XXX more suckage. this time for pickling structs/unions
+ # declared in headers.
+ sutype._ast = su
+
+ su.decl = self.declare(
+ loc=su.loc,
+ type=sutype,
+ shortname=su.name,
+ fullname=fullname)
+
+
+ def visitInclude(self, inc):
+ if inc.tu is None:
+ self.error(
+ inc.loc,
+ "(type checking here will be unreliable because of an earlier error)")
+ return
+ inc.tu.accept(self)
+ if inc.tu.protocol:
+ self.symtab.declare(inc.tu.protocol.decl)
+ self.symtab.declare(inc.tu.protocol.parentEndpointDecl)
+ self.symtab.declare(inc.tu.protocol.childEndpointDecl)
+ else:
+ # This is a header. Import its "exported" globals into
+ # our scope.
+ for using in inc.tu.using:
+ using.accept(self)
+ for su in inc.tu.structsAndUnions:
+ self.declareStructOrUnion(su)
+
+ def visitStructDecl(self, sd):
+ # If we've already processed this struct, don't do it again.
+ if hasattr(sd, 'symtab'):
+ return
+
+ stype = sd.decl.type
+
+ self.symtab.enterScope(sd)
+
+ for f in sd.fields:
+ ftypedecl = self.symtab.lookup(str(f.typespec))
+ if ftypedecl is None:
+ self.error(f.loc, "field `%s' of struct `%s' has unknown type `%s'",
+ f.name, sd.name, str(f.typespec))
+ continue
+
+ f.decl = self.declare(
+ loc=f.loc,
+ type=self._canonicalType(ftypedecl.type, f.typespec),
+ shortname=f.name,
+ fullname=None)
+ stype.fields.append(f.decl.type)
+
+ self.symtab.exitScope(sd)
+
+ def visitUnionDecl(self, ud):
+ utype = ud.decl.type
+
+ # If we've already processed this union, don't do it again.
+ if len(utype.components):
+ return
+
+ for c in ud.components:
+ cdecl = self.symtab.lookup(str(c))
+ if cdecl is None:
+ self.error(c.loc, "unknown component type `%s' of union `%s'",
+ str(c), ud.name)
+ continue
+ utype.components.append(self._canonicalType(cdecl.type, c))
+
+ def visitUsingStmt(self, using):
+ fullname = str(using.type)
+ if using.type.basename() == fullname:
+ fullname = None
+ if fullname == 'mozilla::ipc::Shmem':
+ ipdltype = ShmemType(using.type.spec)
+ elif fullname == 'mozilla::ipc::FileDescriptor':
+ ipdltype = FDType(using.type.spec)
+ else:
+ ipdltype = ImportedCxxType(using.type.spec)
+ existingType = self.symtab.lookup(ipdltype.fullname())
+ if existingType and existingType.fullname == ipdltype.fullname():
+ using.decl = existingType
+ return
+ using.decl = self.declare(
+ loc=using.loc,
+ type=ipdltype,
+ shortname=using.type.basename(),
+ fullname=fullname)
+
+ def visitProtocol(self, p):
+ # protocol scope
+ self.symtab.enterScope(p)
+
+ for spawns in p.spawnsStmts:
+ spawns.accept(self)
+
+ for bridges in p.bridgesStmts:
+ bridges.accept(self)
+
+ for opens in p.opensStmts:
+ opens.accept(self)
+
+ seenmgrs = set()
+ for mgr in p.managers:
+ if mgr.name in seenmgrs:
+ self.error(mgr.loc, "manager `%s' appears multiple times",
+ mgr.name)
+ continue
+
+ seenmgrs.add(mgr.name)
+ mgr.of = p
+ mgr.accept(self)
+
+ for managed in p.managesStmts:
+ managed.manager = p
+ managed.accept(self)
+
+ if 0 == len(p.managers) and 0 == len(p.messageDecls):
+ self.error(p.loc,
+ "top-level protocol `%s' cannot be empty",
+ p.name)
+
+ setattr(self, 'currentProtocolDecl', p.decl)
+ for msg in p.messageDecls:
+ msg.accept(self)
+ del self.currentProtocolDecl
+
+ p.decl.type.hasDelete = (not not self.symtab.lookup(_DELETE_MSG))
+ if not (p.decl.type.hasDelete or p.decl.type.isToplevel()):
+ self.error(
+ p.loc,
+ "destructor declaration `%s(...)' required for managed protocol `%s'",
+ _DELETE_MSG, p.name)
+
+ p.decl.type.hasReentrantDelete = p.decl.type.hasDelete and self.symtab.lookup(_DELETE_MSG).type.isInterrupt()
+
+ for managed in p.managesStmts:
+ mgdname = managed.name
+ ctordecl = self.symtab.lookup(mgdname +'Constructor')
+
+ if not (ctordecl and ctordecl.type.isCtor()):
+ self.error(
+ managed.loc,
+ "constructor declaration required for managed protocol `%s' (managed by protocol `%s')",
+ mgdname, p.name)
+
+ p.states = { }
+
+ if len(p.transitionStmts):
+ p.startStates = [ ts for ts in p.transitionStmts
+ if ts.state.start ]
+ if 0 == len(p.startStates):
+ p.startStates = [ p.transitionStmts[0] ]
+
+ # declare implicit "any", "dead", and "dying" states
+ self.declare(loc=State.ANY.loc,
+ type=StateType(p.decl.type, State.ANY.name, start=False),
+ progname=State.ANY.name)
+ self.declare(loc=State.DEAD.loc,
+ type=StateType(p.decl.type, State.DEAD.name, start=False),
+ progname=State.DEAD.name)
+ if p.decl.type.hasReentrantDelete:
+ self.declare(loc=State.DYING.loc,
+ type=StateType(p.decl.type, State.DYING.name, start=False),
+ progname=State.DYING.name)
+
+ # declare each state before decorating their mention
+ for trans in p.transitionStmts:
+ p.states[trans.state] = trans
+ trans.state.decl = self.declare(
+ loc=trans.state.loc,
+ type=StateType(p.decl.type, trans.state, trans.state.start),
+ progname=trans.state.name)
+
+ for trans in p.transitionStmts:
+ self.seentriggers = set()
+ trans.accept(self)
+
+ if not (p.decl.type.stateless
+ or (p.decl.type.isToplevel()
+ and None is self.symtab.lookup(_DELETE_MSG))):
+ # add a special state |state DEAD: null goto DEAD;|
+ deadtrans = TransitionStmt.makeNullStmt(State.DEAD)
+ p.states[State.DEAD] = deadtrans
+ if p.decl.type.hasReentrantDelete:
+ dyingtrans = TransitionStmt.makeNullStmt(State.DYING)
+ p.states[State.DYING] = dyingtrans
+
+ # visit the message decls once more and resolve the state names
+ # attached to actor params and returns
+ def resolvestate(loc, actortype):
+ assert actortype.isIPDL() and actortype.isActor()
+
+ # already resolved this guy's state
+ if isinstance(actortype.state, Decl):
+ return
+
+ if actortype.state is None:
+ # we thought this was a C++ type until type checking,
+ # when we realized it was an IPDL actor type. But
+ # that means that the actor wasn't specified to be in
+ # any particular state
+ actortype.state = State.ANY
+
+ statename = actortype.state.name
+ # FIXME/cjones: this is just wrong. we need the symbol table
+ # of the protocol this actor refers to. low priority bug
+ # since nobody's using this feature yet
+ statedecl = self.symtab.lookup(statename)
+ if statedecl is None:
+ self.error(
+ loc,
+ "protocol `%s' does not have the state `%s'",
+ actortype.protocol.name(),
+ statename)
+ elif not statedecl.type.isState():
+ self.error(
+ loc,
+ "tag `%s' is supposed to be of state type, but is instead of type `%s'",
+ statename,
+ statedecl.type.typename())
+ else:
+ actortype.state = statedecl.type
+
+ for msg in p.messageDecls:
+ for iparam in msg.inParams:
+ loc = iparam.loc
+ for actortype in iteractortypes(iparam.type):
+ resolvestate(loc, actortype)
+ for oparam in msg.outParams:
+ loc = oparam.loc
+ for actortype in iteractortypes(oparam.type):
+ resolvestate(loc, actortype)
+
+ # FIXME/cjones declare all the little C++ thingies that will
+ # be generated. they're not relevant to IPDL itself, but
+ # those ("invisible") symbols can clash with others in the
+ # IPDL spec, and we'd like to catch those before C++ compilers
+ # are allowed to obfuscate the error
+
+ self.symtab.exitScope(p)
+
+
+ def visitSpawnsStmt(self, spawns):
+ pname = spawns.proto
+ spawns.proto = self.symtab.lookup(pname)
+ if spawns.proto is None:
+ self.error(spawns.loc,
+ "spawned protocol `%s' has not been declared",
+ pname)
+
+ def visitBridgesStmt(self, bridges):
+ def lookup(p):
+ decl = self.symtab.lookup(p)
+ if decl is None:
+ self.error(bridges.loc,
+ "bridged protocol `%s' has not been declared", p)
+ return decl
+ bridges.parentSide = lookup(bridges.parentSide)
+ bridges.childSide = lookup(bridges.childSide)
+
+ def visitOpensStmt(self, opens):
+ pname = opens.proto
+ opens.proto = self.symtab.lookup(pname)
+ if opens.proto is None:
+ self.error(opens.loc,
+ "opened protocol `%s' has not been declared",
+ pname)
+
+
+ def visitManager(self, mgr):
+ mgrdecl = self.symtab.lookup(mgr.name)
+ pdecl = mgr.of.decl
+ assert pdecl
+
+ pname, mgrname = pdecl.shortname, mgr.name
+ loc = mgr.loc
+
+ if mgrdecl is None:
+ self.error(
+ loc,
+ "protocol `%s' referenced as |manager| of `%s' has not been declared",
+ mgrname, pname)
+ elif not isinstance(mgrdecl.type, ProtocolType):
+ self.error(
+ loc,
+ "entity `%s' referenced as |manager| of `%s' is not of `protocol' type; instead it is of type `%s'",
+ mgrname, pname, mgrdecl.type.typename())
+ else:
+ mgr.decl = mgrdecl
+ pdecl.type.addManager(mgrdecl.type)
+
+
+ def visitManagesStmt(self, mgs):
+ mgsdecl = self.symtab.lookup(mgs.name)
+ pdecl = mgs.manager.decl
+ assert pdecl
+
+ pname, mgsname = pdecl.shortname, mgs.name
+ loc = mgs.loc
+
+ if mgsdecl is None:
+ self.error(loc,
+ "protocol `%s', managed by `%s', has not been declared",
+ mgsname, pname)
+ elif not isinstance(mgsdecl.type, ProtocolType):
+ self.error(
+ loc,
+ "%s declares itself managing a non-`protocol' entity `%s' of type `%s'",
+ pname, mgsname, mgsdecl.type.typename())
+ else:
+ mgs.decl = mgsdecl
+ pdecl.type.manages.append(mgsdecl.type)
+
+
+ def visitMessageDecl(self, md):
+ msgname = md.name
+ loc = md.loc
+
+ isctor = False
+ isdtor = False
+ cdtype = None
+
+ decl = self.symtab.lookup(msgname)
+ if decl is not None and decl.type.isProtocol():
+ # probably a ctor. we'll check validity later.
+ msgname += 'Constructor'
+ isctor = True
+ cdtype = decl.type
+ elif decl is not None:
+ self.error(loc, "message name `%s' already declared as `%s'",
+ msgname, decl.type.typename())
+ # if we error here, no big deal; move on to find more
+
+ if _DELETE_MSG == msgname:
+ isdtor = True
+ cdtype = self.currentProtocolDecl.type
+
+
+ # enter message scope
+ self.symtab.enterScope(md)
+
+ msgtype = MessageType(md.nested, md.prio, md.sendSemantics, md.direction,
+ ctor=isctor, dtor=isdtor, cdtype=cdtype,
+ compress=md.compress, verify=md.verify)
+
+ # replace inparam Param nodes with proper Decls
+ def paramToDecl(param):
+ ptname = param.typespec.basename()
+ ploc = param.typespec.loc
+
+ ptdecl = self.symtab.lookup(ptname)
+ if ptdecl is None:
+ self.error(
+ ploc,
+ "argument typename `%s' of message `%s' has not been declared",
+ ptname, msgname)
+ ptype = VOID
+ else:
+ ptype = self._canonicalType(ptdecl.type, param.typespec,
+ chmodallowed=1)
+ return self.declare(loc=ploc,
+ type=ptype,
+ progname=param.name)
+
+ for i, inparam in enumerate(md.inParams):
+ pdecl = paramToDecl(inparam)
+ msgtype.params.append(pdecl.type)
+ md.inParams[i] = pdecl
+ for i, outparam in enumerate(md.outParams):
+ pdecl = paramToDecl(outparam)
+ msgtype.returns.append(pdecl.type)
+ md.outParams[i] = pdecl
+
+ self.symtab.exitScope(md)
+
+ md.decl = self.declare(
+ loc=loc,
+ type=msgtype,
+ progname=msgname)
+ md.protocolDecl = self.currentProtocolDecl
+ md.decl._md = md
+
+
+ def visitTransitionStmt(self, ts):
+ self.seentriggers = set()
+ TcheckVisitor.visitTransitionStmt(self, ts)
+
+ def visitTransition(self, t):
+ loc = t.loc
+
+ # check the trigger message
+ mname = t.msg
+ if t in self.seentriggers:
+ self.error(loc, "trigger `%s' appears multiple times", t.msg)
+ self.seentriggers.add(t)
+
+ mdecl = self.symtab.lookup(mname)
+ if mdecl is not None and mdecl.type.isIPDL() and mdecl.type.isProtocol():
+ mdecl = self.symtab.lookup(mname +'Constructor')
+
+ if mdecl is None:
+ self.error(loc, "message `%s' has not been declared", mname)
+ elif not mdecl.type.isMessage():
+ self.error(
+ loc,
+ "`%s' should have message type, but instead has type `%s'",
+ mname, mdecl.type.typename())
+ else:
+ t.msg = mdecl
+
+ # check the to-states
+ seenstates = set()
+ for toState in t.toStates:
+ sname = toState.name
+ sdecl = self.symtab.lookup(sname)
+
+ if sname in seenstates:
+ self.error(loc, "to-state `%s' appears multiple times", sname)
+ seenstates.add(sname)
+
+ if sdecl is None:
+ self.error(loc, "state `%s' has not been declared", sname)
+ elif not sdecl.type.isState():
+ self.error(
+ loc, "`%s' should have state type, but instead has type `%s'",
+ sname, sdecl.type.typename())
+ else:
+ toState.decl = sdecl
+ toState.start = sdecl.type.start
+
+ t.toStates = set(t.toStates)
+
+
+ def _canonicalType(self, itype, typespec, chmodallowed=0):
+ loc = typespec.loc
+
+ if itype.isIPDL():
+ if itype.isProtocol():
+ itype = ActorType(itype,
+ state=typespec.state,
+ nullable=typespec.nullable)
+ # FIXME/cjones: ShmemChmod is disabled until bug 524193
+ if 0 and chmodallowed and itype.isShmem():
+ itype = ShmemChmodType(
+ itype,
+ myChmod=typespec.myChmod,
+ otherChmod=typespec.otherChmod)
+
+ if ((typespec.myChmod or typespec.otherChmod)
+ and not (itype.isIPDL() and (itype.isShmem() or itype.isChmod()))):
+ self.error(
+ loc,
+ "fine-grained access controls make no sense for type `%s'",
+ itype.name())
+
+ if not chmodallowed and (typespec.myChmod or typespec.otherChmod):
+ self.error(loc, "fine-grained access controls not allowed here")
+
+ if typespec.nullable and not (itype.isIPDL() and itype.isActor()):
+ self.error(
+ loc,
+ "`nullable' qualifier for type `%s' makes no sense",
+ itype.name())
+
+ if typespec.array:
+ itype = ArrayType(itype)
+
+ return itype
+
+
+##-----------------------------------------------------------------------------
+
+def checkcycles(p, stack=None):
+ cycles = []
+
+ if stack is None:
+ stack = []
+
+ for cp in p.manages:
+ # special case for self-managed protocols
+ if cp is p:
+ continue
+
+ if cp in stack:
+ return [stack + [p, cp]]
+ cycles += checkcycles(cp, stack + [p])
+
+ return cycles
+
+def formatcycles(cycles):
+ r = []
+ for cycle in cycles:
+ s = " -> ".join([ptype.name() for ptype in cycle])
+ r.append("`%s'" % s)
+ return ", ".join(r)
+
+
+def fullyDefined(t, exploring=None):
+ '''The rules for "full definition" of a type are
+ defined(atom) := true
+ defined(array basetype) := defined(basetype)
+ defined(struct f1 f2...) := defined(f1) and defined(f2) and ...
+ defined(union c1 c2 ...) := defined(c1) or defined(c2) or ...
+'''
+ if exploring is None:
+ exploring = set()
+
+ if t.isAtom():
+ return True
+ elif t.isArray():
+ return fullyDefined(t.basetype, exploring)
+ elif t.defined:
+ return True
+ assert t.isCompound()
+
+ if t in exploring:
+ return False
+
+ exploring.add(t)
+ for c in t.itercomponents():
+ cdefined = fullyDefined(c, exploring)
+ if t.isStruct() and not cdefined:
+ t.defined = False
+ break
+ elif t.isUnion() and cdefined:
+ t.defined = True
+ break
+ else:
+ if t.isStruct(): t.defined = True
+ elif t.isUnion(): t.defined = False
+ exploring.remove(t)
+
+ return t.defined
+
+
+class CheckTypes(TcheckVisitor):
+ def __init__(self, errors):
+ # don't need the symbol table, we just want the error reporting
+ TcheckVisitor.__init__(self, None, errors)
+ self.visited = set()
+ self.ptype = None
+
+ def visitInclude(self, inc):
+ if inc.tu.filename in self.visited:
+ return
+ self.visited.add(inc.tu.filename)
+ if inc.tu.protocol:
+ inc.tu.protocol.accept(self)
+
+
+ def visitStructDecl(self, sd):
+ if not fullyDefined(sd.decl.type):
+ self.error(sd.decl.loc,
+ "struct `%s' is only partially defined", sd.name)
+
+ def visitUnionDecl(self, ud):
+ if not fullyDefined(ud.decl.type):
+ self.error(ud.decl.loc,
+ "union `%s' is only partially defined", ud.name)
+
+
+ def visitProtocol(self, p):
+ self.ptype = p.decl.type
+
+ # check that we require no more "power" than our manager protocols
+ ptype, pname = p.decl.type, p.decl.shortname
+
+ if len(p.spawnsStmts) and not ptype.isToplevel():
+ self.error(p.decl.loc,
+ "protocol `%s' is not top-level and so cannot declare |spawns|",
+ pname)
+
+ if len(p.bridgesStmts) and not ptype.isToplevel():
+ self.error(p.decl.loc,
+ "protocol `%s' is not top-level and so cannot declare |bridges|",
+ pname)
+
+ if len(p.opensStmts) and not ptype.isToplevel():
+ self.error(p.decl.loc,
+ "protocol `%s' is not top-level and so cannot declare |opens|",
+ pname)
+
+ for mgrtype in ptype.managers:
+ if mgrtype is not None and ptype.needsMoreJuiceThan(mgrtype):
+ self.error(
+ p.decl.loc,
+ "protocol `%s' requires more powerful send semantics than its manager `%s' provides",
+ pname, mgrtype.name())
+
+ # XXX currently we don't require a delete() message of top-level
+ # actors. need to let experience guide this decision
+ if not ptype.isToplevel():
+ for md in p.messageDecls:
+ if _DELETE_MSG == md.name: break
+ else:
+ self.error(
+ p.decl.loc,
+ "managed protocol `%s' requires a `delete()' message to be declared",
+ p.name)
+ else:
+ cycles = checkcycles(p.decl.type)
+ if cycles:
+ self.error(
+ p.decl.loc,
+ "cycle(s) detected in manager/manages heirarchy: %s",
+ formatcycles(cycles))
+
+ if 1 == len(ptype.managers) and ptype is ptype.manager():
+ self.error(
+ p.decl.loc,
+ "top-level protocol `%s' cannot manage itself",
+ p.name)
+
+ return Visitor.visitProtocol(self, p)
+
+
+ def visitSpawnsStmt(self, spawns):
+ if not self.ptype.isToplevel():
+ self.error(spawns.loc,
+ "only top-level protocols can have |spawns| statements; `%s' cannot",
+ self.ptype.name())
+ return
+
+ spawnedType = spawns.proto.type
+ if not (spawnedType.isIPDL() and spawnedType.isProtocol()
+ and spawnedType.isToplevel()):
+ self.error(spawns.loc,
+ "cannot spawn non-top-level-protocol `%s'",
+ spawnedType.name())
+ else:
+ self.ptype.addSpawn(spawnedType)
+
+
+ def visitBridgesStmt(self, bridges):
+ if not self.ptype.isToplevel():
+ self.error(bridges.loc,
+ "only top-level protocols can have |bridges| statements; `%s' cannot",
+ self.ptype.name())
+ return
+
+ parentType = bridges.parentSide.type
+ childType = bridges.childSide.type
+ if not (parentType.isIPDL() and parentType.isProtocol()
+ and childType.isIPDL() and childType.isProtocol()
+ and parentType.isToplevel() and childType.isToplevel()):
+ self.error(bridges.loc,
+ "cannot bridge non-top-level-protocol(s) `%s' and `%s'",
+ parentType.name(), childType.name())
+
+
+ def visitOpensStmt(self, opens):
+ if not self.ptype.isToplevel():
+ self.error(opens.loc,
+ "only top-level protocols can have |opens| statements; `%s' cannot",
+ self.ptype.name())
+ return
+
+ openedType = opens.proto.type
+ if not (openedType.isIPDL() and openedType.isProtocol()
+ and openedType.isToplevel()):
+ self.error(opens.loc,
+ "cannot open non-top-level-protocol `%s'",
+ openedType.name())
+ else:
+ self.ptype.addOpen(openedType)
+
+
+ def visitManagesStmt(self, mgs):
+ pdecl = mgs.manager.decl
+ ptype, pname = pdecl.type, pdecl.shortname
+
+ mgsdecl = mgs.decl
+ mgstype, mgsname = mgsdecl.type, mgsdecl.shortname
+
+ loc = mgs.loc
+
+ # we added this information; sanity check it
+ assert ptype.isManagerOf(mgstype)
+
+ # check that the "managed" protocol agrees
+ if not mgstype.isManagedBy(ptype):
+ self.error(
+ loc,
+ "|manages| declaration in protocol `%s' does not match any |manager| declaration in protocol `%s'",
+ pname, mgsname)
+
+
+ def visitManager(self, mgr):
+ # FIXME/bug 541126: check that the protocol graph is acyclic
+
+ pdecl = mgr.of.decl
+ ptype, pname = pdecl.type, pdecl.shortname
+
+ mgrdecl = mgr.decl
+ mgrtype, mgrname = mgrdecl.type, mgrdecl.shortname
+
+ # we added this information; sanity check it
+ assert ptype.isManagedBy(mgrtype)
+
+ loc = mgr.loc
+
+ # check that the "manager" protocol agrees
+ if not mgrtype.isManagerOf(ptype):
+ self.error(
+ loc,
+ "|manager| declaration in protocol `%s' does not match any |manages| declaration in protocol `%s'",
+ pname, mgrname)
+
+
+ def visitMessageDecl(self, md):
+ mtype, mname = md.decl.type, md.decl.progname
+ ptype, pname = md.protocolDecl.type, md.protocolDecl.shortname
+
+ loc = md.decl.loc
+
+ if mtype.nested == INSIDE_SYNC_NESTED and not mtype.isSync():
+ self.error(
+ loc,
+ "inside_sync nested messages must be sync (here, message `%s' in protocol `%s')",
+ mname, pname)
+
+ if mtype.nested == INSIDE_CPOW_NESTED and (mtype.isOut() or mtype.isInout()):
+ self.error(
+ loc,
+ "inside_cpow nested parent-to-child messages are verboten (here, message `%s' in protocol `%s')",
+ mname, pname)
+
+ # We allow inside_sync messages that are themselves sync to be sent from the
+ # parent. Normal and inside_cpow nested messages that are sync can only come from
+ # the child.
+ if mtype.isSync() and mtype.nested == NOT_NESTED and (mtype.isOut() or mtype.isInout()):
+ self.error(
+ loc,
+ "sync parent-to-child messages are verboten (here, message `%s' in protocol `%s')",
+ mname, pname)
+
+ if mtype.needsMoreJuiceThan(ptype):
+ self.error(
+ loc,
+ "message `%s' requires more powerful send semantics than its protocol `%s' provides",
+ mname, pname)
+
+ if mtype.isAsync() and len(mtype.returns):
+ # XXX/cjones could modify grammar to disallow this ...
+ self.error(loc,
+ "asynchronous message `%s' declares return values",
+ mname)
+
+ if (mtype.compress and
+ (not mtype.isAsync() or mtype.isCtor() or mtype.isDtor())):
+ self.error(
+ loc,
+ "message `%s' in protocol `%s' requests compression but is not async or is special (ctor or dtor)",
+ mname[:-len('constructor')], pname)
+
+ if mtype.isCtor() and not ptype.isManagerOf(mtype.constructedType()):
+ self.error(
+ loc,
+ "ctor for protocol `%s', which is not managed by protocol `%s'",
+ mname[:-len('constructor')], pname)
+
+
+ def visitTransition(self, t):
+ _YNC = [ ASYNC, SYNC ]
+
+ loc = t.loc
+ impliedDirection, impliedSems = {
+ SEND: [ OUT, _YNC ], RECV: [ IN, _YNC ],
+ CALL: [ OUT, INTR ], ANSWER: [ IN, INTR ],
+ } [t.trigger]
+
+ if (OUT is impliedDirection and t.msg.type.isIn()
+ or IN is impliedDirection and t.msg.type.isOut()
+ or _YNC is impliedSems and t.msg.type.isInterrupt()
+ or INTR is impliedSems and (not t.msg.type.isInterrupt())):
+ mtype = t.msg.type
+
+ self.error(
+ loc, "%s %s message `%s' is not `%s'd",
+ mtype.sendSemantics.pretty, mtype.direction.pretty,
+ t.msg.progname,
+ t.trigger.pretty)
+
+##-----------------------------------------------------------------------------
+
+class Process:
+ def __init__(self):
+ self.actors = set() # set(Actor)
+ self.edges = { } # Actor -> [ SpawnsEdge ]
+ self.spawn = set() # set(Actor)
+
+ def edge(self, spawner, spawn):
+ if spawner not in self.edges: self.edges[spawner] = [ ]
+ self.edges[spawner].append(SpawnsEdge(spawner, spawn))
+ self.spawn.add(spawn)
+
+ def iteredges(self):
+ for edgelist in self.edges.itervalues():
+ for edge in edgelist:
+ yield edge
+
+ def merge(self, o):
+ 'Merge the Process |o| into this Process'
+ if self == o:
+ return
+ for actor in o.actors:
+ ProcessGraph.actorToProcess[actor] = self
+ self.actors.update(o.actors)
+ self.edges.update(o.edges)
+ self.spawn.update(o.spawn)
+ ProcessGraph.processes.remove(o)
+
+ def spawns(self, actor):
+ return actor in self.spawn
+
+ def __cmp__(self, o): return cmp(self.actors, o.actors)
+ def __eq__(self, o): return self.actors == o.actors
+ def __hash__(self): return hash(id(self))
+ def __repr__(self):
+ return reduce(lambda a, x: str(a) + str(x) +'|', self.actors, '|')
+ def __str__(self): return repr(self)
+
+class Actor:
+ def __init__(self, ptype, side):
+ self.ptype = ptype
+ self.side = side
+
+ def asType(self):
+ return ActorType(self.ptype)
+ def other(self):
+ return Actor(self.ptype, _otherside(self.side))
+
+ def __cmp__(self, o):
+ return cmp(self.ptype, o.ptype) or cmp(self.side, o.side)
+ def __eq__(self, o):
+ return self.ptype == o.ptype and self.side == o.side
+ def __hash__(self): return hash(repr(self))
+ def __repr__(self): return '%s%s'% (self.ptype.name(), self.side.title())
+ def __str__(self): return repr(self)
+
+class SpawnsEdge:
+ def __init__(self, spawner, spawn):
+ self.spawner = spawner # Actor
+ self.spawn = spawn # Actor
+ def __repr__(self):
+ return '(%r)--spawns-->(%r)'% (self.spawner, self.spawn)
+ def __str__(self): return repr(self)
+
+class BridgeEdge:
+ def __init__(self, bridgeProto, parent, child):
+ self.bridgeProto = bridgeProto # ProtocolType
+ self.parent = parent # Actor
+ self.child = child # Actor
+ def __repr__(self):
+ return '(%r)--%s bridge-->(%r)'% (
+ self.parent, self.bridgeProto.name(), self.child)
+ def __str__(self): return repr(self)
+
+class OpensEdge:
+ def __init__(self, opener, openedProto):
+ self.opener = opener # Actor
+ self.openedProto = openedProto # ProtocolType
+ def __repr__(self):
+ return '(%r)--opens-->(%s)'% (self.opener, self.openedProto.name())
+ def __str__(self): return repr(self)
+
+# "singleton" class with state that persists across type checking of
+# all protocols
+class ProcessGraph:
+ processes = set() # set(Process)
+ bridges = { } # ProtocolType -> [ BridgeEdge ]
+ opens = { } # ProtocolType -> [ OpensEdge ]
+ actorToProcess = { } # Actor -> Process
+ visitedSpawns = set() # set(ActorType)
+ visitedBridges = set() # set(ActorType)
+
+ @classmethod
+ def findProcess(cls, actor):
+ return cls.actorToProcess.get(actor, None)
+
+ @classmethod
+ def getProcess(cls, actor):
+ if actor not in cls.actorToProcess:
+ p = Process()
+ p.actors.add(actor)
+ cls.processes.add(p)
+ cls.actorToProcess[actor] = p
+ return cls.actorToProcess[actor]
+
+ @classmethod
+ def bridgesOf(cls, bridgeP):
+ return cls.bridges.get(bridgeP, [])
+
+ @classmethod
+ def bridgeEndpointsOf(cls, ptype, side):
+ actor = Actor(ptype, side)
+ endpoints = []
+ for b in cls.iterbridges():
+ if b.parent == actor:
+ endpoints.append(Actor(b.bridgeProto, 'parent'))
+ if b.child == actor:
+ endpoints.append(Actor(b.bridgeProto, 'child'))
+ return endpoints
+
+ @classmethod
+ def iterbridges(cls):
+ for edges in cls.bridges.itervalues():
+ for bridge in edges:
+ yield bridge
+
+ @classmethod
+ def opensOf(cls, openedP):
+ return cls.opens.get(openedP, [])
+
+ @classmethod
+ def opensEndpointsOf(cls, ptype, side):
+ actor = Actor(ptype, side)
+ endpoints = []
+ for o in cls.iteropens():
+ if actor == o.opener:
+ endpoints.append(Actor(o.openedProto, o.opener.side))
+ elif actor == o.opener.other():
+ endpoints.append(Actor(o.openedProto, o.opener.other().side))
+ return endpoints
+
+ @classmethod
+ def iteropens(cls):
+ for edges in cls.opens.itervalues():
+ for opens in edges:
+ yield opens
+
+ @classmethod
+ def spawn(cls, spawner, remoteSpawn):
+ localSpawn = remoteSpawn.other()
+ spawnerProcess = ProcessGraph.getProcess(spawner)
+ spawnerProcess.merge(ProcessGraph.getProcess(localSpawn))
+ spawnerProcess.edge(spawner, remoteSpawn)
+
+ @classmethod
+ def bridge(cls, parent, child, bridgeP):
+ bridgeParent = Actor(bridgeP, 'parent')
+ parentProcess = ProcessGraph.getProcess(parent)
+ parentProcess.merge(ProcessGraph.getProcess(bridgeParent))
+ bridgeChild = Actor(bridgeP, 'child')
+ childProcess = ProcessGraph.getProcess(child)
+ childProcess.merge(ProcessGraph.getProcess(bridgeChild))
+ if bridgeP not in cls.bridges:
+ cls.bridges[bridgeP] = [ ]
+ cls.bridges[bridgeP].append(BridgeEdge(bridgeP, parent, child))
+
+ @classmethod
+ def open(cls, opener, opened, openedP):
+ remoteOpener, remoteOpened, = opener.other(), opened.other()
+ openerProcess = ProcessGraph.getProcess(opener)
+ openerProcess.merge(ProcessGraph.getProcess(opened))
+ remoteOpenerProcess = ProcessGraph.getProcess(remoteOpener)
+ remoteOpenerProcess.merge(ProcessGraph.getProcess(remoteOpened))
+ if openedP not in cls.opens:
+ cls.opens[openedP] = [ ]
+ cls.opens[openedP].append(OpensEdge(opener, openedP))
+
+
+class BuildProcessGraph(TcheckVisitor):
+ class findSpawns(TcheckVisitor):
+ def __init__(self, errors):
+ TcheckVisitor.__init__(self, None, errors)
+
+ def visitTranslationUnit(self, tu):
+ TcheckVisitor.visitTranslationUnit(self, tu)
+
+ def visitInclude(self, inc):
+ if inc.tu.protocol:
+ inc.tu.protocol.accept(self)
+
+ def visitProtocol(self, p):
+ ptype = p.decl.type
+ # non-top-level protocols don't add any information
+ if not ptype.isToplevel() or ptype in ProcessGraph.visitedSpawns:
+ return
+
+ ProcessGraph.visitedSpawns.add(ptype)
+ self.visiting = ptype
+ ProcessGraph.getProcess(Actor(ptype, 'parent'))
+ ProcessGraph.getProcess(Actor(ptype, 'child'))
+ return TcheckVisitor.visitProtocol(self, p)
+
+ def visitSpawnsStmt(self, spawns):
+ # The picture here is:
+ # [ spawner | localSpawn | ??? ] (process 1)
+ # |
+ # |
+ # [ remoteSpawn | ???] (process 2)
+ #
+ # A spawns stmt tells us that |spawner| and |localSpawn|
+ # are in the same process.
+ spawner = Actor(self.visiting, spawns.side)
+ remoteSpawn = Actor(spawns.proto.type, spawns.spawnedAs)
+ ProcessGraph.spawn(spawner, remoteSpawn)
+
+ def __init__(self, errors):
+ TcheckVisitor.__init__(self, None, errors)
+ self.visiting = None # ActorType
+ self.visited = set() # set(ActorType)
+
+ def visitTranslationUnit(self, tu):
+ tu.accept(self.findSpawns(self.errors))
+ TcheckVisitor.visitTranslationUnit(self, tu)
+
+ def visitInclude(self, inc):
+ if inc.tu.protocol:
+ inc.tu.protocol.accept(self)
+
+ def visitProtocol(self, p):
+ ptype = p.decl.type
+ # non-top-level protocols don't add any information
+ if not ptype.isToplevel() or ptype in ProcessGraph.visitedBridges:
+ return
+
+ ProcessGraph.visitedBridges.add(ptype)
+ self.visiting = ptype
+ return TcheckVisitor.visitProtocol(self, p)
+
+ def visitBridgesStmt(self, bridges):
+ bridgeProto = self.visiting
+ parentSideProto = bridges.parentSide.type
+ childSideProto = bridges.childSide.type
+
+ # the picture here is:
+ # (process 1|
+ # [ parentSide(Parent|Child) | childSide(Parent|Child) | ... ]
+ # | |
+ # | (process 2| |
+ # [ parentSide(Child|Parent) | bridgeParent ] |
+ # | |
+ # | | (process 3|
+ # [ bridgeChild | childSide(Child|Parent) ]
+ #
+ # First we have to figure out which parentSide/childSide
+ # actors live in the same process. The possibilities are {
+ # parent, child } x { parent, child }. (Multiple matches
+ # aren't allowed yet.) Then we make ProcessGraph aware of the
+ # new bridge.
+ parentSideActor, childSideActor = None, None
+ pc = ( 'parent', 'child' )
+ for parentSide, childSide in cartesian_product(pc, pc):
+ pactor = Actor(parentSideProto, parentSide)
+ pproc = ProcessGraph.findProcess(pactor)
+ cactor = Actor(childSideProto, childSide)
+ cproc = ProcessGraph.findProcess(cactor)
+ assert pproc and cproc
+
+ if pproc == cproc:
+ if parentSideActor is not None:
+ if parentSideProto != childSideProto:
+ self.error(bridges.loc,
+ "ambiguous bridge `%s' between `%s' and `%s'",
+ bridgeProto.name(),
+ parentSideProto.name(),
+ childSideProto.name())
+ else:
+ parentSideActor, childSideActor = pactor.other(), cactor.other()
+
+ if parentSideActor is None:
+ self.error(bridges.loc,
+ "`%s' and `%s' cannot be bridged by `%s' ",
+ parentSideProto.name(), childSideProto.name(),
+ bridgeProto.name())
+
+ ProcessGraph.bridge(parentSideActor, childSideActor, bridgeProto)
+
+ def visitOpensStmt(self, opens):
+ openedP = opens.proto.type
+ opener = Actor(self.visiting, opens.side)
+ opened = Actor(openedP, opens.side)
+
+ # The picture here is:
+ # [ opener | opened ] (process 1)
+ # | |
+ # | |
+ # [ remoteOpener | remoteOpened ] (process 2)
+ #
+ # An opens stmt tells us that the pairs |opener|/|opened|
+ # and |remoteOpener|/|remoteOpened| are each in the same
+ # process.
+ ProcessGraph.open(opener, opened, openedP)
+
+
+class CheckProcessGraph(TcheckVisitor):
+ def __init__(self, errors):
+ TcheckVisitor.__init__(self, None, errors)
+
+ # TODO: verify spawns-per-process assumption and check that graph
+ # is a dag
+ def visitTranslationUnit(self, tu):
+ if 0:
+ print 'Processes'
+ for process in ProcessGraph.processes:
+ print ' ', process
+ for edge in process.iteredges():
+ print ' ', edge
+ print 'Bridges'
+ for bridgeList in ProcessGraph.bridges.itervalues():
+ for bridge in bridgeList:
+ print ' ', bridge
+ print 'Opens'
+ for opensList in ProcessGraph.opens.itervalues():
+ for opens in opensList:
+ print ' ', opens
+
+##-----------------------------------------------------------------------------
+
+class CheckStateMachine(TcheckVisitor):
+ def __init__(self, errors):
+ # don't need the symbol table, we just want the error reporting
+ TcheckVisitor.__init__(self, None, errors)
+ self.p = None
+
+ def visitProtocol(self, p):
+ self.p = p
+ self.checkReachability(p)
+ for ts in p.transitionStmts:
+ ts.accept(self)
+
+ def visitTransitionStmt(self, ts):
+ # We want to disallow "race conditions" in protocols. These
+ # can occur when a protocol state machine has a state that
+ # allows triggers of opposite direction. That declaration
+ # allows the parent to send the child a message at the
+ # exact instance the child sends the parent a message. One of
+ # those messages would (probably) violate the state machine
+ # and cause the child to be terminated. It's obviously very
+ # nice if we can forbid this at the level of IPDL state
+ # machines, rather than resorting to static or dynamic
+ # checking of C++ implementation code.
+ #
+ # An easy way to avoid this problem in IPDL is to only allow
+ # "unidirectional" protocol states; that is, from each state,
+ # only send or only recv triggers are allowed. This approach
+ # is taken by the Singularity project's "contract-based
+ # message channels." However, this can be something of a
+ # notational burden for stateful protocols.
+ #
+ # If two messages race, the effect is that the parent's and
+ # child's states get temporarily out of sync. Informally,
+ # IPDL allows this *only if* the state machines get out of
+ # sync for only *one* step (state machine transition), then
+ # sync back up. This is a design decision: the states could
+ # be allowd to get out of sync for any constant k number of
+ # steps. (If k is unbounded, there's no point in presenting
+ # the abstraction of parent and child actor states being
+ # "entangled".) The working hypothesis is that the more steps
+ # the states are allowed to be out of sync, the harder it is
+ # to reason about the protocol.
+ #
+ # Slightly less informally, two messages are allowed to race
+ # only if processing them in either order leaves the protocol
+ # in the same state. That is, messages A and B are allowed to
+ # race only if processing A then B leaves the protocol in
+ # state S, *and* processing B then A also leaves the protocol
+ # in state S. Technically, if this holds, then messages A and
+ # B could be called "commutative" wrt to actor state.
+ #
+ # "Formally", state machine definitions must adhere to two
+ # rules.
+ #
+ # *Rule 1*: from a state S, all sync triggers must be of the same
+ # "direction," i.e. only |send| or only |recv|
+ #
+ # (Pairs of sync messages can't commute, because otherwise
+ # deadlock can occur from simultaneously in-flight sync
+ # requests.)
+ #
+ # *Rule 2*: the "Diamond Rule".
+ # from a state S,
+ # for any pair of triggers t1 and t2,
+ # where t1 and t2 have opposite direction,
+ # and t1 transitions to state T1 and t2 to T2,
+ # then the following must be true:
+ # (T2 allows the trigger t1, transitioning to state U)
+ # and
+ # (T1 allows the trigger t2, transitioning to state U)
+ # and
+ # (
+ # (
+ # (all of T1's triggers have the same direction as t2)
+ # and
+ # (all of T2's triggers have the same direction as t1)
+ # )
+ # or
+ # (T1, T2, and U are the same "terminal state")
+ # )
+ #
+ # A "terminal state" S is one from which all triggers
+ # transition back to S itself.
+ #
+ # The presence of triggers with multiple out states complicates
+ # this check slightly, but doesn't fundamentally change it.
+ #
+ # from a state S,
+ # for any pair of triggers t1 and t2,
+ # where t1 and t2 have opposite direction,
+ # for each pair of states (T1, T2) \in t1_out x t2_out,
+ # where t1_out is the set of outstates from t1
+ # t2_out is the set of outstates from t2
+ # t1_out x t2_out is their Cartesian product
+ # and t1 transitions to state T1 and t2 to T2,
+ # then the following must be true:
+ # (T2 allows the trigger t1, with out-state set { U })
+ # and
+ # (T1 allows the trigger t2, with out-state set { U })
+ # and
+ # (
+ # (
+ # (all of T1's triggers have the same direction as t2)
+ # and
+ # (all of T2's triggers have the same direction as t1)
+ # )
+ # or
+ # (T1, T2, and U are the same "terminal state")
+ # )
+
+ # check Rule 1
+ syncdirection = None
+ syncok = True
+ for trans in ts.transitions:
+ if not trans.msg.type.isSync(): continue
+ if syncdirection is None:
+ syncdirection = trans.trigger.direction()
+ elif syncdirection is not trans.trigger.direction():
+ self.error(
+ trans.loc,
+ "sync trigger at state `%s' in protocol `%s' has different direction from earlier sync trigger at same state",
+ ts.state.name, self.p.name)
+ syncok = False
+ # don't check the Diamond Rule if Rule 1 doesn't hold
+ if not syncok:
+ return
+
+ # helper functions
+ def triggerTargets(S, t):
+ '''Return the set of states transitioned to from state |S|
+upon trigger |t|, or { } if |t| is not a trigger in |S|.'''
+ for trans in self.p.states[S].transitions:
+ if t.trigger is trans.trigger and t.msg is trans.msg:
+ return trans.toStates
+ return set()
+
+ def allTriggersSameDirectionAs(S, t):
+ '''Return true iff all the triggers from state |S| have the same
+direction as trigger |t|'''
+ direction = t.direction()
+ for trans in self.p.states[S].transitions:
+ if direction != trans.trigger.direction():
+ return False
+ return True
+
+ def terminalState(S):
+ '''Return true iff |S| is a "terminal state".'''
+ for trans in self.p.states[S].transitions:
+ for S_ in trans.toStates:
+ if S_ != S: return False
+ return True
+
+ def sameTerminalState(S1, S2, S3):
+ '''Return true iff states |S1|, |S2|, and |S3| are all the same
+"terminal state".'''
+ if isinstance(S3, set):
+ assert len(S3) == 1
+ for S3_ in S3: pass
+ S3 = S3_
+
+ return (S1 == S2 == S3) and terminalState(S1)
+
+ S = ts.state.name
+
+ # check the Diamond Rule
+ for (t1, t2) in unique_pairs(ts.transitions):
+ # if the triggers have the same direction, they can't race,
+ # since only one endpoint can initiate either (and delivery
+ # is in-order)
+ if t1.trigger.direction() == t2.trigger.direction():
+ continue
+
+ loc = t1.loc
+ t1_out = t1.toStates
+ t2_out = t2.toStates
+
+ for (T1, T2) in cartesian_product(t1_out, t2_out):
+ # U1 <- { u | T1 --t2--> u }
+ U1 = triggerTargets(T1, t2)
+ # U2 <- { u | T2 --t1--> u }
+ U2 = triggerTargets(T2, t1)
+
+ # don't report more than one Diamond Rule violation
+ # per state. there may be O(n^4) total, way too many
+ # for a human to parse
+ #
+ # XXX/cjones: could set a limit on #printed and stop
+ # after that limit ...
+ raceError = False
+ errT1 = None
+ errT2 = None
+
+ if 0 == len(U1) or 0 == len(U2):
+ print "******* case 1"
+ raceError = True
+ elif 1 < len(U1) or 1 < len(U2):
+ raceError = True
+ # there are potentially many unpaired states; just
+ # pick two
+ print "******* case 2"
+ for u1, u2 in cartesian_product(U1, U2):
+ if u1 != u2:
+ errT1, errT2 = u1, u2
+ break
+ elif U1 != U2:
+ print "******* case 3"
+ raceError = True
+ for errT1 in U1: pass
+ for errT2 in U2: pass
+
+ if raceError:
+ self.reportRaceError(loc, S,
+ [ T1, t1, errT1 ],
+ [ T2, t2, errT2 ])
+ return
+
+ if not ((allTriggersSameDirectionAs(T1, t2.trigger)
+ and allTriggersSameDirectionAs(T2, t1.trigger))
+ or sameTerminalState(T1, T2, U1)):
+ self.reportRunawayError(loc, S, [ T1, t1, None ], [ T2, t2, None ])
+ return
+
+ def checkReachability(self, p):
+ def explore(ts, visited):
+ if ts.state in visited:
+ return
+ visited.add(ts.state)
+ for outedge in ts.transitions:
+ for toState in outedge.toStates:
+ explore(p.states[toState], visited)
+
+ checkfordelete = (State.DEAD in p.states)
+
+ allvisited = set() # set(State)
+ for root in p.startStates:
+ visited = set()
+
+ explore(root, visited)
+ allvisited.update(visited)
+
+ if checkfordelete and State.DEAD not in visited:
+ self.error(
+ root.loc,
+ "when starting from state `%s', actors of protocol `%s' cannot be deleted", root.state.name, p.name)
+
+ for ts in p.states.itervalues():
+ if ts.state is not State.DEAD and ts.state not in allvisited:
+ self.error(ts.loc,
+ "unreachable state `%s' in protocol `%s'",
+ ts.state.name, p.name)
+
+
+ def _normalizeTransitionSequences(self, t1Seq, t2Seq):
+ T1, M1, U1 = t1Seq
+ T2, M2, U2 = t2Seq
+ assert M1 is not None and M2 is not None
+
+ # make sure that T1/M1/U1 is the parent side of the race
+ if M1.trigger is RECV or M1.trigger is ANSWER:
+ T1, M1, U1, T2, M2, U2 = T2, M2, U2, T1, M1, U1
+
+ def stateName(S):
+ if S: return S.name
+ return '[error]'
+
+ T1 = stateName(T1)
+ T2 = stateName(T2)
+ U1 = stateName(U1)
+ U2 = stateName(U2)
+
+ return T1, M1.msg.progname, U1, T2, M2.msg.progname, U2
+
+
+ def reportRaceError(self, loc, S, t1Seq, t2Seq):
+ T1, M1, U1, T2, M2, U2 = self._normalizeTransitionSequences(t1Seq, t2Seq)
+ self.error(
+ loc,
+"""in protocol `%(P)s', the sequence of events
+ parent: +--`send %(M1)s'-->( state `%(T1)s' )--`recv %(M2)s'-->( state %(U1)s )
+ /
+ ( state `%(S)s' )
+ \\
+ child: +--`send %(M2)s'-->( state `%(T2)s' )--`recv %(M1)s'-->( state %(U2)s )
+results in error(s) or leaves parent/child state out of sync for more than one step and is thus a race hazard; i.e., triggers `%(M1)s' and `%(M2)s' fail to commute in state `%(S)s'"""% {
+ 'P': self.p.name, 'S': S, 'M1': M1, 'M2': M2,
+ 'T1': T1, 'T2': T2, 'U1': U1, 'U2': U2
+ })
+
+
+ def reportRunawayError(self, loc, S, t1Seq, t2Seq):
+ T1, M1, _, T2, M2, __ = self._normalizeTransitionSequences(t1Seq, t2Seq)
+ self.error(
+ loc,
+ """in protocol `%(P)s', the sequence of events
+ parent: +--`send %(M1)s'-->( state `%(T1)s' )
+ /
+ ( state `%(S)s' )
+ \\
+ child: +--`send %(M2)s'-->( state `%(T2)s' )
+lead to parent/child states in which parent/child state can become more than one step out of sync (though this divergence might not lead to error conditions)"""% {
+ 'P': self.p.name, 'S': S, 'M1': M1, 'M2': M2, 'T1': T1, 'T2': T2
+ })
diff --git a/ipc/ipdl/moz.build b/ipc/ipdl/moz.build
new file mode 100644
index 000000000..47d840603
--- /dev/null
+++ b/ipc/ipdl/moz.build
@@ -0,0 +1,19 @@
+# -*- 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/.
+
+if CONFIG['MOZ_IPDL_TESTS']:
+ DIRS += ['test']
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+# Generated by ipdl.py
+SOURCES += ['!IPCMessageTypeName.cpp']
+
+FINAL_LIBRARY = 'xul'
+
+# We #include some things in the dom/plugins/ directory that rely on
+# toolkit libraries.
+CXXFLAGS += CONFIG['TK_CFLAGS']
diff --git a/ipc/ipdl/msgtype-components b/ipc/ipdl/msgtype-components
new file mode 100644
index 000000000..e411234f9
--- /dev/null
+++ b/ipc/ipdl/msgtype-components
@@ -0,0 +1,13 @@
+#!/usr/bin/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/.
+
+
+import sys
+
+msgid = int(sys.argv[1])
+protocol = (msgid >> 16)
+msg = (msgid - (protocol << 16))
+
+print 'protocol', protocol, 'message', msg
diff --git a/ipc/ipdl/test/README.txt b/ipc/ipdl/test/README.txt
new file mode 100644
index 000000000..8c5eb0411
--- /dev/null
+++ b/ipc/ipdl/test/README.txt
@@ -0,0 +1,10 @@
+There are two major categories of tests, segregated into different
+top-level directories under test/.
+
+The first category (ipdl/) is IPDL-compiler tests. These tests check
+that the IPDL compiler is successfully compiling correct
+specifications, and successfully rejecting erroneous specifications.
+
+The second category (cxx/) is C++ tests of IPDL semantics. These
+tests check that async/sync/rpc semantics are implemented correctly,
+ctors/dtors behave as they should, etc.
diff --git a/ipc/ipdl/test/cxx/IPDLUnitTestProcessChild.cpp b/ipc/ipdl/test/cxx/IPDLUnitTestProcessChild.cpp
new file mode 100644
index 000000000..7123e3db8
--- /dev/null
+++ b/ipc/ipdl/test/cxx/IPDLUnitTestProcessChild.cpp
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* 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 "mozilla/ipc/IOThreadChild.h"
+
+#include "IPDLUnitTestProcessChild.h"
+#include "IPDLUnitTests.h"
+
+#include "nsRegion.h"
+
+using mozilla::ipc::IOThreadChild;
+
+namespace mozilla {
+namespace _ipdltest {
+
+bool
+IPDLUnitTestProcessChild::Init()
+{
+ IPDLUnitTestChildInit(IOThreadChild::channel(),
+ ParentPid(),
+ IOThreadChild::message_loop());
+
+ if (NS_FAILED(nsRegion::InitStatic()))
+ return false;
+
+ return true;
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/IPDLUnitTestProcessChild.h b/ipc/ipdl/test/cxx/IPDLUnitTestProcessChild.h
new file mode 100644
index 000000000..693cc7360
--- /dev/null
+++ b/ipc/ipdl/test/cxx/IPDLUnitTestProcessChild.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla__ipdltest_IPDLUnitTestThreadChild_h
+#define mozilla__ipdltest_IPDLUnitTestThreadChild_h 1
+
+#include "mozilla/ipc/ProcessChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+class IPDLUnitTestProcessChild : public mozilla::ipc::ProcessChild
+{
+ typedef mozilla::ipc::ProcessChild ProcessChild;
+
+public:
+ explicit IPDLUnitTestProcessChild(ProcessId aParentPid) :
+ ProcessChild(aParentPid)
+ { }
+
+ ~IPDLUnitTestProcessChild()
+ { }
+
+ virtual bool Init();
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+#endif // ifndef mozilla__ipdltest_IPDLUnitTestThreadChild_h
diff --git a/ipc/ipdl/test/cxx/IPDLUnitTestSubprocess.cpp b/ipc/ipdl/test/cxx/IPDLUnitTestSubprocess.cpp
new file mode 100644
index 000000000..87fe0ddc7
--- /dev/null
+++ b/ipc/ipdl/test/cxx/IPDLUnitTestSubprocess.cpp
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* 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 "IPDLUnitTestSubprocess.h"
+
+using mozilla::ipc::GeckoChildProcessHost;
+
+namespace mozilla {
+namespace _ipdltest {
+
+IPDLUnitTestSubprocess::IPDLUnitTestSubprocess() :
+ GeckoChildProcessHost(GeckoProcessType_IPDLUnitTest)
+{
+}
+
+IPDLUnitTestSubprocess::~IPDLUnitTestSubprocess()
+{
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/IPDLUnitTestSubprocess.h b/ipc/ipdl/test/cxx/IPDLUnitTestSubprocess.h
new file mode 100644
index 000000000..cdb212df2
--- /dev/null
+++ b/ipc/ipdl/test/cxx/IPDLUnitTestSubprocess.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla__ipdltest_IPDLUnitTestTestSubprocess_h
+#define mozilla__ipdltest_IPDLUnitTestTestSubprocess_h 1
+
+
+#include "mozilla/ipc/GeckoChildProcessHost.h"
+
+namespace mozilla {
+namespace _ipdltest {
+//-----------------------------------------------------------------------------
+
+class IPDLUnitTestSubprocess : public mozilla::ipc::GeckoChildProcessHost
+{
+public:
+ IPDLUnitTestSubprocess();
+ ~IPDLUnitTestSubprocess();
+
+ /**
+ * Asynchronously launch the plugin process.
+ */
+ // Could override parent Launch, but don't need to here
+ //bool Launch();
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(IPDLUnitTestSubprocess);
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_IPDLUnitTestTestSubprocess_h
diff --git a/ipc/ipdl/test/cxx/IPDLUnitTestTypes.h b/ipc/ipdl/test/cxx/IPDLUnitTestTypes.h
new file mode 100644
index 000000000..6da135a0a
--- /dev/null
+++ b/ipc/ipdl/test/cxx/IPDLUnitTestTypes.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla__ipdltest_IPDLUnitTestTypes_h
+#define mozilla__ipdltest_IPDLUnitTestTypes_h
+
+#include "mozilla/ipc/ProtocolUtils.h" // ActorDestroyReason
+
+namespace mozilla {
+namespace _ipdltest {
+
+struct DirtyRect
+{
+ int x; int y; int w; int h;
+};
+
+}
+}
+
+namespace IPC {
+template<>
+struct ParamTraits<mozilla::_ipdltest::DirtyRect>
+{
+ typedef mozilla::_ipdltest::DirtyRect paramType;
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.x);
+ WriteParam(aMsg, aParam.y);
+ WriteParam(aMsg, aParam.w);
+ WriteParam(aMsg, aParam.h);
+ }
+ static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+ {
+ return (ReadParam(aMsg, aIter, &aResult->x) &&
+ ReadParam(aMsg, aIter, &aResult->y) &&
+ ReadParam(aMsg, aIter, &aResult->w) &&
+ ReadParam(aMsg, aIter, &aResult->h));
+ }
+};
+}
+
+
+#endif // ifndef mozilla__ipdltest_IPDLUnitTestTypes_h
diff --git a/ipc/ipdl/test/cxx/IPDLUnitTestUtils.h b/ipc/ipdl/test/cxx/IPDLUnitTestUtils.h
new file mode 100644
index 000000000..ad9da374d
--- /dev/null
+++ b/ipc/ipdl/test/cxx/IPDLUnitTestUtils.h
@@ -0,0 +1,27 @@
+
+#ifndef mozilla__ipdltest_IPDLUnitTestUtils
+#define mozilla__ipdltest_IPDLUnitTestUtils 1
+
+namespace mozilla {
+namespace _ipdltest {
+
+struct Bad {};
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+namespace IPC {
+
+template<>
+struct ParamTraits<mozilla::_ipdltest::Bad>
+{
+ typedef mozilla::_ipdltest::Bad paramType;
+
+ // Defined in TestActorPunning.cpp.
+ static void Write(Message* aMsg, const paramType& aParam);
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult);
+};
+
+} // namespace IPC
+
+#endif // mozilla__ipdltest_IPDLUnitTestUtils
diff --git a/ipc/ipdl/test/cxx/IPDLUnitTests.h b/ipc/ipdl/test/cxx/IPDLUnitTests.h
new file mode 100644
index 000000000..544bc0ffb
--- /dev/null
+++ b/ipc/ipdl/test/cxx/IPDLUnitTests.h
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla__ipdltest_IPDLUnitTests_h
+#define mozilla__ipdltest_IPDLUnitTests_h 1
+
+#include "base/message_loop.h"
+#include "base/process.h"
+#include "chrome/common/ipc_channel.h"
+
+#include "nsIAppShell.h"
+
+#include "nsCOMPtr.h"
+#include "nsDebug.h"
+#include "nsServiceManagerUtils.h" // do_GetService()
+#include "nsWidgetsCID.h" // NS_APPSHELL_CID
+#include "nsXULAppAPI.h"
+
+
+#define MOZ_IPDL_TESTFAIL_LABEL "TEST-UNEXPECTED-FAIL"
+#define MOZ_IPDL_TESTPASS_LABEL "TEST-PASS"
+#define MOZ_IPDL_TESTINFO_LABEL "TEST-INFO"
+
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// both processes
+const char* IPDLUnitTestName();
+
+// NB: these are named like the similar functions in
+// xpcom/test/TestHarness.h. The names should nominally be kept in
+// sync.
+
+inline void fail(const char* fmt, ...)
+{
+ va_list ap;
+
+ fprintf(stderr, MOZ_IPDL_TESTFAIL_LABEL " | %s | ", IPDLUnitTestName());
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ fputc('\n', stderr);
+
+ NS_RUNTIMEABORT("failed test");
+}
+
+inline void passed(const char* fmt, ...)
+{
+ va_list ap;
+
+ printf(MOZ_IPDL_TESTPASS_LABEL " | %s | ", IPDLUnitTestName());
+
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+
+ fputc('\n', stdout);
+}
+
+//-----------------------------------------------------------------------------
+// parent process only
+
+class IPDLUnitTestSubprocess;
+
+extern void* gParentActor;
+extern IPDLUnitTestSubprocess* gSubprocess;
+
+void IPDLUnitTestMain(void* aData);
+
+void QuitParent();
+
+//-----------------------------------------------------------------------------
+// child process only
+
+extern void* gChildActor;
+
+void IPDLUnitTestChildInit(IPC::Channel* transport,
+ base::ProcessId parentPid,
+ MessageLoop* worker);
+
+void QuitChild();
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_IPDLUnitTests_h
diff --git a/ipc/ipdl/test/cxx/IPDLUnitTests.template.cpp b/ipc/ipdl/test/cxx/IPDLUnitTests.template.cpp
new file mode 100644
index 000000000..6a91033bf
--- /dev/null
+++ b/ipc/ipdl/test/cxx/IPDLUnitTests.template.cpp
@@ -0,0 +1,390 @@
+//
+// Autogenerated from Python template. Hands off.
+//
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "IPDLUnitTests.h"
+
+#include "base/command_line.h"
+#include "base/string_util.h"
+#include "base/task.h"
+#include "base/thread.h"
+
+#include "nsRegion.h"
+
+#include "IPDLUnitTestSubprocess.h"
+
+//-----------------------------------------------------------------------------
+//===== TEMPLATED =====
+${INCLUDES}
+//-----------------------------------------------------------------------------
+
+using namespace std;
+
+using base::Thread;
+
+namespace mozilla {
+namespace _ipdltest {
+
+void* gParentActor;
+IPDLUnitTestSubprocess* gSubprocess;
+
+void* gChildActor;
+
+// Note: in threaded mode, this will be non-null (for both parent and
+// child, since they share one set of globals).
+Thread* gChildThread;
+MessageLoop *gParentMessageLoop;
+bool gParentDone;
+bool gChildDone;
+
+void
+DeleteChildActor();
+
+//-----------------------------------------------------------------------------
+// data/functions accessed by both parent and child processes
+
+char* gIPDLUnitTestName = nullptr;
+
+const char*
+IPDLUnitTestName()
+{
+ if (!gIPDLUnitTestName) {
+#if defined(OS_WIN)
+ vector<wstring> args =
+ CommandLine::ForCurrentProcess()->GetLooseValues();
+ gIPDLUnitTestName = ::strdup(WideToUTF8(args[0]).c_str());
+#elif defined(OS_POSIX)
+ vector<string> argv = CommandLine::ForCurrentProcess()->argv();
+ gIPDLUnitTestName = ::moz_xstrdup(argv[1].c_str());
+#else
+# error Sorry
+#endif
+ }
+ return gIPDLUnitTestName;
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+namespace {
+
+enum IPDLUnitTestType {
+ NoneTest = 0,
+
+//-----------------------------------------------------------------------------
+//===== TEMPLATED =====
+${ENUM_VALUES}
+
+ LastTest = ${LAST_ENUM}
+//-----------------------------------------------------------------------------
+};
+
+
+IPDLUnitTestType
+IPDLUnitTestFromString(const char* const aString)
+{
+ if (!aString)
+ return static_cast<IPDLUnitTestType>(0);
+//-----------------------------------------------------------------------------
+//===== TEMPLATED =====
+${STRING_TO_ENUMS}
+//-----------------------------------------------------------------------------
+ else
+ return static_cast<IPDLUnitTestType>(0);
+}
+
+
+const char*
+IPDLUnitTestToString(IPDLUnitTestType aTest)
+{
+ switch (aTest) {
+//-----------------------------------------------------------------------------
+//===== TEMPLATED =====
+${ENUM_TO_STRINGS}
+//-----------------------------------------------------------------------------
+
+ default:
+ return nullptr;
+ }
+}
+
+
+IPDLUnitTestType
+IPDLUnitTest()
+{
+ return IPDLUnitTestFromString(::mozilla::_ipdltest::IPDLUnitTestName());
+}
+
+
+} // namespace <anon>
+
+
+//-----------------------------------------------------------------------------
+// parent process only
+
+namespace mozilla {
+namespace _ipdltest {
+
+void
+DeferredParentShutdown();
+
+void
+IPDLUnitTestThreadMain(char *testString);
+
+void
+IPDLUnitTestMain(void* aData)
+{
+ char* testString = reinterpret_cast<char*>(aData);
+
+ // Check if we are to run the test using threads instead:
+ const char *prefix = "thread:";
+ const int prefixLen = strlen(prefix);
+ if (!strncmp(testString, prefix, prefixLen)) {
+ IPDLUnitTestThreadMain(testString + prefixLen);
+ return;
+ }
+
+ IPDLUnitTestType test = IPDLUnitTestFromString(testString);
+ if (!test) {
+ // use this instead of |fail()| because we don't know what the test is
+ fprintf(stderr, MOZ_IPDL_TESTFAIL_LABEL "| %s | unknown unit test %s\n",
+ "<--->", testString);
+ NS_RUNTIMEABORT("can't continue");
+ }
+ gIPDLUnitTestName = testString;
+
+ // Check whether this test is enabled for processes:
+ switch (test) {
+//-----------------------------------------------------------------------------
+//===== TEMPLATED =====
+${PARENT_ENABLED_CASES_PROC}
+//-----------------------------------------------------------------------------
+
+ default:
+ fail("not reached");
+ return; // unreached
+ }
+
+ printf(MOZ_IPDL_TESTINFO_LABEL "| running test | %s\n", gIPDLUnitTestName);
+
+ std::vector<std::string> testCaseArgs;
+ testCaseArgs.push_back(testString);
+
+ gSubprocess = new IPDLUnitTestSubprocess();
+ if (!gSubprocess->SyncLaunch(testCaseArgs))
+ fail("problem launching subprocess");
+
+ IPC::Channel* transport = gSubprocess->GetChannel();
+ if (!transport)
+ fail("no transport");
+
+ base::ProcessId child = base::GetProcId(gSubprocess->GetChildProcessHandle());
+
+ switch (test) {
+//-----------------------------------------------------------------------------
+//===== TEMPLATED =====
+${PARENT_MAIN_CASES_PROC}
+//-----------------------------------------------------------------------------
+
+ default:
+ fail("not reached");
+ return; // unreached
+ }
+}
+
+void
+IPDLUnitTestThreadMain(char *testString)
+{
+ IPDLUnitTestType test = IPDLUnitTestFromString(testString);
+ if (!test) {
+ // use this instead of |fail()| because we don't know what the test is
+ fprintf(stderr, MOZ_IPDL_TESTFAIL_LABEL "| %s | unknown unit test %s\n",
+ "<--->", testString);
+ NS_RUNTIMEABORT("can't continue");
+ }
+ gIPDLUnitTestName = testString;
+
+ // Check whether this test is enabled for threads:
+ switch (test) {
+//-----------------------------------------------------------------------------
+//===== TEMPLATED =====
+${PARENT_ENABLED_CASES_THREAD}
+//-----------------------------------------------------------------------------
+
+ default:
+ fail("not reached");
+ return; // unreached
+ }
+
+ printf(MOZ_IPDL_TESTINFO_LABEL "| running test | %s\n", gIPDLUnitTestName);
+
+ std::vector<std::string> testCaseArgs;
+ testCaseArgs.push_back(testString);
+
+ gChildThread = new Thread("ParentThread");
+ if (!gChildThread->Start())
+ fail("starting parent thread");
+
+ gParentMessageLoop = MessageLoop::current();
+ MessageLoop *childMessageLoop = gChildThread->message_loop();
+
+ switch (test) {
+//-----------------------------------------------------------------------------
+//===== TEMPLATED =====
+${PARENT_MAIN_CASES_THREAD}
+//-----------------------------------------------------------------------------
+
+ default:
+ fail("not reached");
+ return; // unreached
+ }
+}
+
+void
+DeleteParentActor()
+{
+ if (!gParentActor)
+ return;
+
+ switch (IPDLUnitTest()) {
+//-----------------------------------------------------------------------------
+//===== TEMPLATED =====
+${PARENT_DELETE_CASES}
+//-----------------------------------------------------------------------------
+ default: ::mozilla::_ipdltest::fail("???");
+ }
+}
+
+void
+QuitXPCOM()
+{
+ DeleteParentActor();
+
+ static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
+ nsCOMPtr<nsIAppShell> appShell (do_GetService(kAppShellCID));
+ appShell->Exit();
+}
+
+void
+DeleteSubprocess(MessageLoop* uiLoop)
+{
+ // pong to QuitXPCOM
+ delete gSubprocess;
+ uiLoop->PostTask(NewRunnableFunction(QuitXPCOM));
+}
+
+void
+DeferredParentShutdown()
+{
+ // ping to DeleteSubprocess
+ XRE_GetIOMessageLoop()->PostTask(
+ NewRunnableFunction(DeleteSubprocess, MessageLoop::current()));
+}
+
+void
+TryThreadedShutdown()
+{
+ // Stop if either:
+ // - the child has not finished,
+ // - the parent has not finished,
+ // - or this code has already executed.
+ // Remember: this TryThreadedShutdown() task is enqueued
+ // by both parent and child (though always on parent's msg loop).
+ if (!gChildDone || !gParentDone || !gChildThread)
+ return;
+
+ delete gChildThread;
+ gChildThread = 0;
+ DeferredParentShutdown();
+}
+
+void
+ChildCompleted()
+{
+ // Executes on the parent message loop once child has completed.
+ gChildDone = true;
+ TryThreadedShutdown();
+}
+
+void
+QuitParent()
+{
+ if (gChildThread) {
+ gParentDone = true;
+ MessageLoop::current()->PostTask(
+ NewRunnableFunction(TryThreadedShutdown));
+ } else {
+ // defer "real" shutdown to avoid *Channel::Close() racing with the
+ // deletion of the subprocess
+ MessageLoop::current()->PostTask(
+ NewRunnableFunction(DeferredParentShutdown));
+ }
+}
+
+static void
+ChildDie()
+{
+ DeleteChildActor();
+ XRE_ShutdownChildProcess();
+}
+
+void
+QuitChild()
+{
+ if (gChildThread) { // Threaded-mode test
+ gParentMessageLoop->PostTask(
+ NewRunnableFunction(ChildCompleted));
+ } else { // Process-mode test
+ MessageLoop::current()->PostTask(
+ NewRunnableFunction(ChildDie));
+ }
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+//-----------------------------------------------------------------------------
+// child process only
+
+namespace mozilla {
+namespace _ipdltest {
+
+void
+DeleteChildActor()
+{
+ if (!gChildActor)
+ return;
+
+ switch (IPDLUnitTest()) {
+//-----------------------------------------------------------------------------
+//===== TEMPLATED =====
+${CHILD_DELETE_CASES}
+//-----------------------------------------------------------------------------
+ default: ::mozilla::_ipdltest::fail("???");
+ }
+}
+
+void
+IPDLUnitTestChildInit(IPC::Channel* transport,
+ base::ProcessId parentPid,
+ MessageLoop* worker)
+{
+ switch (IPDLUnitTest()) {
+//-----------------------------------------------------------------------------
+//===== TEMPLATED =====
+${CHILD_INIT_CASES}
+//-----------------------------------------------------------------------------
+
+ default:
+ fail("not reached");
+ return; // unreached
+ }
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/Makefile.in b/ipc/ipdl/test/cxx/Makefile.in
new file mode 100644
index 000000000..8c9cdaa99
--- /dev/null
+++ b/ipc/ipdl/test/cxx/Makefile.in
@@ -0,0 +1,46 @@
+# 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/.
+
+IPDLTESTSRCS = $(filter Test%,$(CPPSRCS))
+IPDLTESTS = $(IPDLTESTSRCS:.cpp=)
+
+EXTRA_PROTOCOLS = \
+ TestBridgeSub \
+ TestEndpointBridgeSub \
+ $(NULL)
+
+IPDLTESTHDRS = $(addprefix $(srcdir)/,$(addsuffix .h,$(IPDLTESTS)))
+
+TESTER_TEMPLATE := $(srcdir)/IPDLUnitTests.template.cpp
+GENTESTER := $(srcdir)/genIPDLUnitTests.py
+
+include $(topsrcdir)/config/rules.mk
+
+
+IPDLUNITTEST_BIN = $(DEPTH)/dist/bin/ipdlunittest$(BIN_SUFFIX)
+
+IPDLUnitTests.cpp : Makefile.in moz.build $(GENTESTER) $(TESTER_TEMPLATE) $(IPDLTESTHDRS)
+ $(PYTHON) $(GENTESTER) $(TESTER_TEMPLATE) -t $(IPDLTESTS) -e $(EXTRA_PROTOCOLS) > $@
+
+check-proc::
+ @$(EXIT_ON_ERROR) \
+ for test in $(IPDLTESTS); do \
+ $(RUN_TEST_PROGRAM) $(IPDLUNITTEST_BIN) $$test ; \
+ done
+
+check-thread::
+ @$(EXIT_ON_ERROR) \
+ for test in $(IPDLTESTS); do \
+ $(RUN_TEST_PROGRAM) $(IPDLUNITTEST_BIN) thread:$$test ; \
+ done
+
+check:: check-proc check-thread
+
+check-valgrind::
+ @$(EXIT_ON_ERROR) \
+ for test in $(IPDLTESTS); do \
+ $(RUN_TEST_PROGRAM) -g -d \
+ valgrind -a '--leak-check=full --trace-children=yes -q' \
+ $(IPDLUNITTEST_BIN) $$test ; \
+ done
diff --git a/ipc/ipdl/test/cxx/PTestActorPunning.ipdl b/ipc/ipdl/test/cxx/PTestActorPunning.ipdl
new file mode 100644
index 000000000..54deb277a
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestActorPunning.ipdl
@@ -0,0 +1,39 @@
+
+include protocol PTestActorPunningPunned;
+include protocol PTestActorPunningSub;
+include "mozilla/_ipdltest/IPDLUnitTestUtils.h";
+
+using struct mozilla::_ipdltest::Bad from "mozilla/_ipdltest/IPDLUnitTestUtils.h";
+
+namespace mozilla {
+namespace _ipdltest {
+
+protocol PTestActorPunning {
+ manages PTestActorPunningPunned;
+ manages PTestActorPunningSub;
+
+child:
+ async Start();
+
+parent:
+ async PTestActorPunningPunned();
+ async PTestActorPunningSub();
+ async Pun(PTestActorPunningSub a, Bad bad);
+ async __delete__();
+
+
+state PING:
+ send Start goto CONSTRUCTING;
+
+state CONSTRUCTING:
+ recv PTestActorPunningPunned goto CONSTRUCTING;
+ recv PTestActorPunningSub goto CONSTRUCTING;
+ recv Pun goto DEAD;
+ // We never make it past this transition, --> error.
+
+state DEAD:
+ recv __delete__;
+};
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestActorPunningPunned.ipdl b/ipc/ipdl/test/cxx/PTestActorPunningPunned.ipdl
new file mode 100644
index 000000000..a6b875920
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestActorPunningPunned.ipdl
@@ -0,0 +1,15 @@
+
+include protocol PTestActorPunning;
+
+namespace mozilla {
+namespace _ipdltest {
+
+protocol PTestActorPunningPunned {
+ manager PTestActorPunning;
+
+child:
+ async __delete__();
+};
+
+} // namespace mozilla
+} // namespace _ipdltes
diff --git a/ipc/ipdl/test/cxx/PTestActorPunningSub.ipdl b/ipc/ipdl/test/cxx/PTestActorPunningSub.ipdl
new file mode 100644
index 000000000..1441219c3
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestActorPunningSub.ipdl
@@ -0,0 +1,16 @@
+
+include protocol PTestActorPunning;
+
+namespace mozilla {
+namespace _ipdltest {
+
+protocol PTestActorPunningSub {
+ manager PTestActorPunning;
+
+child:
+ async Bad();
+ async __delete__();
+};
+
+} // namespace mozilla
+} // namespace _ipdltes
diff --git a/ipc/ipdl/test/cxx/PTestBadActor.ipdl b/ipc/ipdl/test/cxx/PTestBadActor.ipdl
new file mode 100644
index 000000000..841d89ff6
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestBadActor.ipdl
@@ -0,0 +1,18 @@
+include protocol PTestBadActorSub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+// Test that a parent sending a reentrant __delete__ message
+// is not killed if a child's message races with the reply.
+
+intr protocol PTestBadActor {
+ manages PTestBadActorSub;
+
+child:
+ async PTestBadActorSub();
+ async __delete__();
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestBadActorSub.ipdl b/ipc/ipdl/test/cxx/PTestBadActorSub.ipdl
new file mode 100644
index 000000000..99c78f4ac
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestBadActorSub.ipdl
@@ -0,0 +1,17 @@
+include protocol PTestBadActor;
+
+namespace mozilla {
+namespace _ipdltest {
+
+intr protocol PTestBadActorSub {
+ manager PTestBadActor;
+
+child:
+ intr __delete__();
+
+parent:
+ async Ping();
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestBridgeMain.ipdl b/ipc/ipdl/test/cxx/PTestBridgeMain.ipdl
new file mode 100644
index 000000000..229820b17
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestBridgeMain.ipdl
@@ -0,0 +1,26 @@
+include protocol PTestBridgeMainSub;
+include protocol PTestBridgeSub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+protocol PTestBridgeMain {
+ child spawns PTestBridgeSub;
+ child opens PTestBridgeMainSub;
+
+child:
+ async Start();
+
+parent:
+ async __delete__();
+
+state START:
+ send Start goto DEAD;
+state DEAD:
+ recv __delete__;
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestBridgeMainSub.ipdl b/ipc/ipdl/test/cxx/PTestBridgeMainSub.ipdl
new file mode 100644
index 000000000..67e50fdd4
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestBridgeMainSub.ipdl
@@ -0,0 +1,33 @@
+include protocol PTestBridgeMain;
+include protocol PTestBridgeSub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+// (Bridge protocols can have different semantics than the endpoints
+// they bridge)
+intr protocol PTestBridgeMainSub {
+ bridges PTestBridgeMain, PTestBridgeSub;
+
+child:
+ async Hi();
+ intr HiRpc();
+
+parent:
+ async Hello();
+ sync HelloSync();
+ intr HelloRpc();
+ async __delete__();
+
+state START: recv Hello goto HI;
+state HI: send Hi goto HELLO_SYNC;
+state HELLO_SYNC: recv HelloSync goto HELLO_RPC;
+state HELLO_RPC: answer HelloRpc goto HI_RPC;
+state HI_RPC: call HiRpc goto DEAD;
+state DEAD:
+ recv __delete__;
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestBridgeSub.ipdl b/ipc/ipdl/test/cxx/PTestBridgeSub.ipdl
new file mode 100644
index 000000000..6c798c84e
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestBridgeSub.ipdl
@@ -0,0 +1,25 @@
+include protocol PTestBridgeMainSub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+protocol PTestBridgeSub {
+child:
+ async Ping();
+
+parent:
+ async BridgeEm();
+ async __delete__();
+
+state START:
+ send Ping goto BRIDGEEM;
+state BRIDGEEM:
+ recv BridgeEm goto DEAD;
+state DEAD:
+ recv __delete__;
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestCancel.ipdl b/ipc/ipdl/test/cxx/PTestCancel.ipdl
new file mode 100644
index 000000000..3a6b46b43
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestCancel.ipdl
@@ -0,0 +1,36 @@
+namespace mozilla {
+namespace _ipdltest {
+
+nested(upto inside_sync) sync protocol PTestCancel
+{
+// Test1
+child:
+ nested(inside_sync) sync Test1_1();
+parent:
+ async Done1();
+
+// Test2
+child:
+ async Start2();
+ nested(inside_sync) sync Test2_2();
+parent:
+ sync Test2_1();
+
+// Test3
+child:
+ nested(inside_sync) sync Test3_1();
+parent:
+ async Start3();
+ nested(inside_sync) sync Test3_2();
+
+parent:
+ async Done();
+
+child:
+ nested(inside_sync) sync CheckChild() returns (uint32_t reply);
+parent:
+ nested(inside_sync) sync CheckParent() returns (uint32_t reply);
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestCrashCleanup.ipdl b/ipc/ipdl/test/cxx/PTestCrashCleanup.ipdl
new file mode 100644
index 000000000..eae428211
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestCrashCleanup.ipdl
@@ -0,0 +1,22 @@
+// See bug 538586: if the top-level protocol's actor is deleted before
+// the "connection error" notification comes in from the IO thread,
+// IPDL teardown never occurs, even if Channel::Close() is called
+// after the error.
+
+namespace mozilla {
+namespace _ipdltest {
+
+// NB: needs to be RPC so that the parent blocks on the child's crash.
+intr protocol PTestCrashCleanup {
+child:
+ intr DIEDIEDIE();
+ async __delete__();
+
+state ALIVE:
+ call DIEDIEDIE goto CRASH;
+state CRASH:
+ send __delete__;
+};
+
+}
+}
diff --git a/ipc/ipdl/test/cxx/PTestDataStructures.ipdl b/ipc/ipdl/test/cxx/PTestDataStructures.ipdl
new file mode 100644
index 000000000..59bf74652
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestDataStructures.ipdl
@@ -0,0 +1,132 @@
+include protocol PTestDataStructuresSub;
+include PTestDataStructuresCommon;
+
+include "mozilla/GfxMessageUtils.h";
+
+namespace mozilla {
+namespace _ipdltest {
+
+sync protocol PTestDataStructures {
+ manages PTestDataStructuresSub;
+
+child:
+ async PTestDataStructuresSub(int i);
+
+ async Start();
+
+parent:
+ async __delete__();
+
+ sync Test1(int[] i1)
+ returns (int[] o1);
+
+ sync Test2(PTestDataStructuresSub[] i1)
+ returns (PTestDataStructuresSub[] o1);
+
+ sync Test3(IntDouble i1,
+ IntDouble i2)
+ returns (IntDouble o1,
+ IntDouble o2);
+
+ sync Test4(IntDouble[] i1)
+ returns (IntDouble[] o1);
+
+ sync Test5(IntDoubleArrays i1,
+ IntDoubleArrays i2,
+ IntDoubleArrays i3)
+ returns (IntDoubleArrays o1,
+ IntDoubleArrays o2,
+ IntDoubleArrays o3);
+
+ sync Test6(IntDoubleArrays[] i1)
+ returns (IntDoubleArrays[] o1);
+
+ sync Test7_0(ActorWrapper a1)
+ returns (ActorWrapper o1);
+
+ sync Test7(Actors i1,
+ Actors i2,
+ Actors i3)
+ returns (Actors o1,
+ Actors o2,
+ Actors o3);
+
+ sync Test8(Actors[] i1)
+ returns (Actors[] o1);
+
+ sync Test9(Unions i1,
+ Unions i2,
+ Unions i3,
+ Unions i4)
+ returns (Unions o1,
+ Unions o2,
+ Unions o3,
+ Unions o4);
+
+ sync Test10(Unions[] i1)
+ returns (Unions[] o1);
+
+ sync Test11(SIntDouble i)
+ returns (SIntDouble o);
+
+ sync Test12(SIntDoubleArrays i)
+ returns (SIntDoubleArrays o);
+
+ sync Test13(SActors i)
+ returns (SActors o);
+
+ sync Test14(Structs i)
+ returns (Structs o);
+
+ sync Test15(WithStructs i1,
+ WithStructs i2,
+ WithStructs i3,
+ WithStructs i4,
+ WithStructs i5)
+ returns (WithStructs o1,
+ WithStructs o2,
+ WithStructs o3,
+ WithStructs o4,
+ WithStructs o5);
+
+ sync Test16(WithUnions i)
+ returns (WithUnions o);
+
+ sync Test17(Op[] ops);
+
+ // test that the ParamTraits<nsTArray>::Read() workaround for
+ // nsTArray's incorrect memmove() semantics works properly
+ // (nsIntRegion isn't memmove()able)
+ sync Test18(nsIntRegion[] ops);
+
+ sync Dummy(ShmemUnion su) returns (ShmemUnion rsu);
+
+state CONSTRUCTING:
+ send PTestDataStructuresSub goto CONSTRUCTING;
+ send Start goto TEST1;
+state TEST1: recv Test1 goto TEST2;
+state TEST2: recv Test2 goto TEST3;
+state TEST3: recv Test3 goto TEST4;
+state TEST4: recv Test4 goto TEST5;
+state TEST5: recv Test5 goto TEST6;
+state TEST6: recv Test6 goto TEST7;
+state TEST7: recv Test7 goto TEST8;
+state TEST8: recv Test8 goto TEST9;
+state TEST9: recv Test9 goto TEST10;
+state TEST10: recv Test10 goto TEST11;
+state TEST11: recv Test11 goto TEST12;
+state TEST12: recv Test12 goto TEST13;
+state TEST13: recv Test13 goto TEST14;
+state TEST14: recv Test14 goto TEST15;
+state TEST15: recv Test15 goto TEST16;
+state TEST16: recv Test16 goto TEST17;
+state TEST17: recv Test17 goto TEST18;
+state TEST18: recv Test18 goto DEAD;
+
+state DEAD:
+ recv __delete__;
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
+
diff --git a/ipc/ipdl/test/cxx/PTestDataStructuresCommon.ipdlh b/ipc/ipdl/test/cxx/PTestDataStructuresCommon.ipdlh
new file mode 100644
index 000000000..891b0c991
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestDataStructuresCommon.ipdlh
@@ -0,0 +1,107 @@
+include protocol PTestDataStructuresSub;
+
+using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
+using nsIntRegion from "nsRegion.h";
+
+namespace mozilla {
+namespace _foo {
+
+union IntDouble {
+ int;
+ double;
+};
+
+struct SIntDouble {
+ int i;
+ double d;
+};
+
+union IntDoubleArrays {
+ int;
+ int[];
+ double[];
+};
+
+struct SIntDoubleArrays {
+ int i;
+ int[] ai;
+ double[] ad;
+};
+
+struct ActorWrapper {
+ PTestDataStructuresSub actor;
+};
+
+union Actors {
+ int;
+ int[];
+ PTestDataStructuresSub[];
+};
+
+struct SActors {
+ int i;
+ int[] ai;
+ PTestDataStructuresSub[] ap;
+};
+
+union Unions {
+ int;
+ int[];
+ PTestDataStructuresSub[];
+ Actors[];
+};
+
+struct Structs {
+ int i;
+ int[] ai;
+ PTestDataStructuresSub[] ap;
+ SActors[] aa;
+};
+
+union WithStructs {
+ int;
+ int[];
+ PTestDataStructuresSub[];
+ SActors[];
+ Structs[];
+};
+
+struct WithUnions {
+ int i;
+ int[] ai;
+ PTestDataStructuresSub[] ap;
+ Actors[] aa;
+ Unions[] au;
+};
+
+struct CommonAttrs { bool dummy; };
+struct FooAttrs { int dummy; };
+struct BarAttrs { float dummy; };
+union SpecificAttrs {
+ FooAttrs;
+ BarAttrs;
+};
+struct Attrs {
+ CommonAttrs common;
+ SpecificAttrs specific;
+};
+struct SetAttrs {
+ PTestDataStructuresSub x;
+ Attrs attrs;
+};
+union Op { null_t; SetAttrs; };
+
+struct ShmemStruct {
+ int i;
+ Shmem mem;
+};
+
+union ShmemUnion {
+ int;
+ Shmem;
+};
+
+struct Empty { };
+
+} // namespace _foo
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestDataStructuresSub.ipdl b/ipc/ipdl/test/cxx/PTestDataStructuresSub.ipdl
new file mode 100644
index 000000000..7a4e87d83
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestDataStructuresSub.ipdl
@@ -0,0 +1,15 @@
+include PTestDataStructuresCommon;
+include protocol PTestDataStructures;
+
+namespace mozilla {
+namespace _ipdltest {
+
+sync protocol PTestDataStructuresSub {
+ manager PTestDataStructures;
+
+parent:
+ sync __delete__();
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestDemon.ipdl b/ipc/ipdl/test/cxx/PTestDemon.ipdl
new file mode 100644
index 000000000..25fa5d0f9
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestDemon.ipdl
@@ -0,0 +1,21 @@
+namespace mozilla {
+namespace _ipdltest {
+
+nested(upto inside_cpow) sync protocol PTestDemon
+{
+child:
+ async Start();
+
+both:
+ async AsyncMessage(int n);
+ nested(inside_sync) sync HiPrioSyncMessage();
+
+parent:
+ sync SyncMessage(int n);
+
+ nested(inside_cpow) async UrgentAsyncMessage(int n);
+ nested(inside_cpow) sync UrgentSyncMessage(int n);
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestDesc.ipdl b/ipc/ipdl/test/cxx/PTestDesc.ipdl
new file mode 100644
index 000000000..835c8c62d
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestDesc.ipdl
@@ -0,0 +1,31 @@
+include protocol PTestDescSub;
+include protocol PTestDescSubsub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+intr protocol PTestDesc {
+ manages PTestDescSub;
+child:
+ intr PTestDescSub(nullable PTestDescSubsub dummy);
+
+ async Test(PTestDescSubsub a);
+
+ async __delete__();
+
+parent:
+ async Ok(PTestDescSubsub a);
+
+
+state CONSTRUCT:
+ call PTestDescSub goto TEST;
+state TEST:
+ send Test goto ACK;
+state ACK:
+ recv Ok goto DEAD;
+state DEAD:
+ send __delete__;
+};
+
+}
+}
diff --git a/ipc/ipdl/test/cxx/PTestDescSub.ipdl b/ipc/ipdl/test/cxx/PTestDescSub.ipdl
new file mode 100644
index 000000000..67d4fe166
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestDescSub.ipdl
@@ -0,0 +1,18 @@
+include protocol PTestDesc;
+include protocol PTestDescSubsub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+intr protocol PTestDescSub {
+ manager PTestDesc;
+ manages PTestDescSubsub;
+
+child:
+ async __delete__();
+
+ intr PTestDescSubsub();
+};
+
+}
+}
diff --git a/ipc/ipdl/test/cxx/PTestDescSubsub.ipdl b/ipc/ipdl/test/cxx/PTestDescSubsub.ipdl
new file mode 100644
index 000000000..c449f2744
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestDescSubsub.ipdl
@@ -0,0 +1,15 @@
+
+include protocol PTestDescSub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+intr protocol PTestDescSubsub {
+ manager PTestDescSub;
+
+child:
+ intr __delete__();
+};
+
+}
+}
diff --git a/ipc/ipdl/test/cxx/PTestEndpointBridgeMain.ipdl b/ipc/ipdl/test/cxx/PTestEndpointBridgeMain.ipdl
new file mode 100644
index 000000000..e8026a0d0
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestEndpointBridgeMain.ipdl
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+
+include protocol PTestEndpointBridgeMainSub;
+include protocol PTestEndpointBridgeSub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+protocol PTestEndpointBridgeMain {
+ child spawns PTestEndpointBridgeSub;
+
+child:
+ async Start();
+
+parent:
+ async Bridged(Endpoint<PTestEndpointBridgeMainSubParent> endpoint);
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestEndpointBridgeMainSub.ipdl b/ipc/ipdl/test/cxx/PTestEndpointBridgeMainSub.ipdl
new file mode 100644
index 000000000..7364057ac
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestEndpointBridgeMainSub.ipdl
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+
+include protocol PTestEndpointBridgeMain;
+include protocol PTestEndpointBridgeSub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+// (Bridge protocols can have different semantics than the endpoints
+// they bridge)
+intr protocol PTestEndpointBridgeMainSub {
+child:
+ async Hi();
+ intr HiRpc();
+
+parent:
+ async Hello();
+ sync HelloSync();
+ intr HelloRpc();
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestEndpointBridgeSub.ipdl b/ipc/ipdl/test/cxx/PTestEndpointBridgeSub.ipdl
new file mode 100644
index 000000000..0bc09b70e
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestEndpointBridgeSub.ipdl
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+
+include protocol PTestEndpointBridgeMainSub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+protocol PTestEndpointBridgeSub {
+child:
+ async Ping();
+
+ async Bridged(Endpoint<PTestEndpointBridgeMainSubChild> endpoint);
+
+parent:
+ async BridgeEm();
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestEndpointOpens.ipdl b/ipc/ipdl/test/cxx/PTestEndpointOpens.ipdl
new file mode 100644
index 000000000..7be99ddd2
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestEndpointOpens.ipdl
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+include protocol PTestEndpointOpensOpened;
+
+namespace mozilla {
+namespace _ipdltest {
+
+protocol PTestEndpointOpens {
+child:
+ async Start();
+
+parent:
+ async StartSubprotocol(Endpoint<PTestEndpointOpensOpenedParent> endpoint);
+
+ async __delete__();
+};
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestEndpointOpensOpened.ipdl b/ipc/ipdl/test/cxx/PTestEndpointOpensOpened.ipdl
new file mode 100644
index 000000000..e14c0cb8a
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestEndpointOpensOpened.ipdl
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+namespace mozilla {
+namespace _ipdltest2 {
+
+// (Opens protocols can have different semantics than the endpoints
+// that opened them)
+intr protocol PTestEndpointOpensOpened {
+child:
+ async Hi();
+ intr HiRpc();
+
+parent:
+ async Hello();
+ sync HelloSync();
+ intr HelloRpc();
+ async __delete__();
+
+state START: recv Hello goto HI;
+state HI: send Hi goto HELLO_SYNC;
+state HELLO_SYNC: recv HelloSync goto HELLO_RPC;
+state HELLO_RPC: answer HelloRpc goto HI_RPC;
+state HI_RPC: call HiRpc goto DEAD;
+state DEAD:
+ recv __delete__;
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest2
diff --git a/ipc/ipdl/test/cxx/PTestFailedCtor.ipdl b/ipc/ipdl/test/cxx/PTestFailedCtor.ipdl
new file mode 100644
index 000000000..94b9da081
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestFailedCtor.ipdl
@@ -0,0 +1,19 @@
+include protocol PTestFailedCtorSub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+intr protocol PTestFailedCtor {
+ manages PTestFailedCtorSub;
+child:
+ intr PTestFailedCtorSub();
+ async __delete__();
+
+state CONSTRUCT:
+ call PTestFailedCtorSub goto DEAD;
+state DEAD:
+ send __delete__;
+};
+
+}
+}
diff --git a/ipc/ipdl/test/cxx/PTestFailedCtorSub.ipdl b/ipc/ipdl/test/cxx/PTestFailedCtorSub.ipdl
new file mode 100644
index 000000000..b1d18a05f
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestFailedCtorSub.ipdl
@@ -0,0 +1,18 @@
+include protocol PTestFailedCtor;
+include protocol PTestFailedCtorSubsub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+intr protocol PTestFailedCtorSub {
+ manager PTestFailedCtor;
+ manages PTestFailedCtorSubsub;
+
+parent:
+ async PTestFailedCtorSubsub();
+ sync Sync();
+ async __delete__();
+};
+
+}
+}
diff --git a/ipc/ipdl/test/cxx/PTestFailedCtorSubsub.ipdl b/ipc/ipdl/test/cxx/PTestFailedCtorSubsub.ipdl
new file mode 100644
index 000000000..654170d97
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestFailedCtorSubsub.ipdl
@@ -0,0 +1,15 @@
+
+include protocol PTestFailedCtorSub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+intr protocol PTestFailedCtorSubsub {
+ manager PTestFailedCtorSub;
+
+parent:
+ async __delete__();
+};
+
+}
+}
diff --git a/ipc/ipdl/test/cxx/PTestHandle.ipdl b/ipc/ipdl/test/cxx/PTestHandle.ipdl
new file mode 100644
index 000000000..aad92bae1
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestHandle.ipdl
@@ -0,0 +1,14 @@
+include protocol PTestJSON;
+
+namespace mozilla {
+namespace _ipdltest {
+
+protocol PTestHandle {
+ manager PTestJSON;
+
+child:
+ async __delete__();
+};
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestHangs.ipdl b/ipc/ipdl/test/cxx/PTestHangs.ipdl
new file mode 100644
index 000000000..b1b43fa75
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestHangs.ipdl
@@ -0,0 +1,40 @@
+
+namespace mozilla {
+namespace _ipdltest {
+
+intr protocol PTestHangs {
+both:
+ intr StackFrame();
+
+parent:
+ async Nonce();
+
+child:
+ async Start();
+ intr Hang();
+ async __delete__();
+
+
+state START:
+ send Start goto RACE;
+
+state RACE:
+ recv Nonce goto RACE1;
+ call StackFrame goto RACE2;
+state RACE1:
+ call StackFrame goto FRAME2;
+state RACE2:
+ recv Nonce goto FRAME2;
+
+// So as to test unwinding the RPC stack
+state FRAME2: answer StackFrame goto FRAME3;
+state FRAME3: call StackFrame goto FRAME4;
+state FRAME4: answer StackFrame goto HANG;
+state HANG: call Hang goto DEATH;
+
+state DEATH:
+ send __delete__;
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestHighestPrio.ipdl b/ipc/ipdl/test/cxx/PTestHighestPrio.ipdl
new file mode 100644
index 000000000..0192f59b2
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestHighestPrio.ipdl
@@ -0,0 +1,18 @@
+namespace mozilla {
+namespace _ipdltest {
+
+nested(upto inside_cpow) sync protocol PTestHighestPrio
+{
+parent:
+ nested(inside_cpow) async Msg1();
+ nested(inside_sync) sync Msg2();
+ nested(inside_cpow) async Msg3();
+ nested(inside_cpow) sync Msg4();
+
+child:
+ async Start();
+ nested(inside_sync) sync StartInner();
+};
+
+}
+}
diff --git a/ipc/ipdl/test/cxx/PTestIndirectProtocolParam.ipdlh b/ipc/ipdl/test/cxx/PTestIndirectProtocolParam.ipdlh
new file mode 100644
index 000000000..a81fcdee4
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestIndirectProtocolParam.ipdlh
@@ -0,0 +1,15 @@
+include protocol PTestIndirectProtocolParamSecond;
+
+namespace mozilla {
+namespace _ipdltest {
+
+struct IndirectParamStruct {
+ PTestIndirectProtocolParamSecond actor;
+};
+
+union IndirectParamUnion {
+ IndirectParamStruct;
+};
+
+}
+}
diff --git a/ipc/ipdl/test/cxx/PTestIndirectProtocolParamFirst.ipdl b/ipc/ipdl/test/cxx/PTestIndirectProtocolParamFirst.ipdl
new file mode 100644
index 000000000..228ec04e8
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestIndirectProtocolParamFirst.ipdl
@@ -0,0 +1,19 @@
+include protocol PTestIndirectProtocolParamManage;
+// FIXME/bug 792908 protocol PTestIndirectProtocolParamSecond is
+// already included in PTestIndirectProtocolParam.ipdlh
+include protocol PTestIndirectProtocolParamSecond;
+include PTestIndirectProtocolParam;
+
+namespace mozilla {
+namespace _ipdltest {
+
+sync protocol PTestIndirectProtocolParamFirst {
+ manager PTestIndirectProtocolParamManage;
+parent:
+ sync Test(IndirectParamUnion actor);
+both:
+ async __delete__();
+};
+
+}
+}
diff --git a/ipc/ipdl/test/cxx/PTestIndirectProtocolParamManage.ipdl b/ipc/ipdl/test/cxx/PTestIndirectProtocolParamManage.ipdl
new file mode 100644
index 000000000..db7c828a2
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestIndirectProtocolParamManage.ipdl
@@ -0,0 +1,17 @@
+include protocol PTestIndirectProtocolParamFirst;
+include protocol PTestIndirectProtocolParamSecond;
+
+namespace mozilla {
+namespace _ipdltest {
+
+sync protocol PTestIndirectProtocolParamManage {
+ manages PTestIndirectProtocolParamFirst;
+ manages PTestIndirectProtocolParamSecond;
+both:
+ async PTestIndirectProtocolParamFirst();
+ async PTestIndirectProtocolParamSecond();
+ async __delete__();
+};
+
+}
+}
diff --git a/ipc/ipdl/test/cxx/PTestIndirectProtocolParamSecond.ipdl b/ipc/ipdl/test/cxx/PTestIndirectProtocolParamSecond.ipdl
new file mode 100644
index 000000000..ed21f58f8
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestIndirectProtocolParamSecond.ipdl
@@ -0,0 +1,13 @@
+include protocol PTestIndirectProtocolParamManage;
+
+namespace mozilla {
+namespace _ipdltest {
+
+sync protocol PTestIndirectProtocolParamSecond {
+ manager PTestIndirectProtocolParamManage;
+both:
+ async __delete__();
+};
+
+}
+}
diff --git a/ipc/ipdl/test/cxx/PTestInterruptErrorCleanup.ipdl b/ipc/ipdl/test/cxx/PTestInterruptErrorCleanup.ipdl
new file mode 100644
index 000000000..95f933bba
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestInterruptErrorCleanup.ipdl
@@ -0,0 +1,11 @@
+namespace mozilla {
+namespace _ipdltest {
+
+intr protocol PTestInterruptErrorCleanup {
+child:
+ intr Error();
+ intr __delete__();
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestInterruptRaces.ipdl b/ipc/ipdl/test/cxx/PTestInterruptRaces.ipdl
new file mode 100644
index 000000000..bf71a251c
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestInterruptRaces.ipdl
@@ -0,0 +1,82 @@
+namespace mozilla {
+namespace _ipdltest {
+
+intr protocol PTestInterruptRaces {
+both:
+ intr Race() returns (bool hasReply);
+ intr StackFrame() returns ();
+ intr StackFrame3() returns ();
+
+parent:
+ sync StartRace();
+ intr Parent();
+ sync GetAnsweredParent() returns (bool answeredParent);
+
+child:
+ async Start();
+ async Wakeup();
+ async Wakeup3();
+ intr Child();
+ async __delete__();
+
+state START:
+ send Start goto TEST1;
+
+// First test: race while no other messages are on the Interrupt stack
+state TEST1:
+ recv StartRace goto RACE1;
+state RACE1:
+ call Race goto DUMMY1_1;
+ answer Race goto DUMMY1_2;
+state DUMMY1_1:
+ answer Race goto TEST2;
+state DUMMY1_2:
+ call Race goto TEST2;
+
+// Second test: race while other messages are on the Interrupt stack
+state TEST2:
+ call StackFrame goto MORESTACK;
+state MORESTACK:
+ answer StackFrame goto STARTRACE;
+state STARTRACE:
+ send Wakeup goto RACE2;
+state RACE2:
+ call Race goto DUMMY2_1;
+ answer Race goto DUMMY2_2;
+state DUMMY2_1:
+ answer Race goto TEST3;
+state DUMMY2_2:
+ call Race goto TEST3;
+
+// Third test: resolve race using custom policy
+state TEST3:
+ call StackFrame3 goto MORESTACK3;
+state MORESTACK3:
+ answer StackFrame3 goto STARTRACE3;
+state STARTRACE3:
+ send Wakeup3 goto RACE3;
+state RACE3:
+ call Child goto DUMMY3_1;
+ answer Parent goto DUMMY3_2;
+state DUMMY3_1:
+ // the parent receives this from the child in this state
+ recv GetAnsweredParent goto CHECK;
+ // this transition is never taken (if the custom race resolution
+ // works correctly)
+ answer Parent goto CHECK;
+state DUMMY3_2:
+ call Child goto CHECK;
+state CHECK:
+ // the child sends this from this state
+ recv GetAnsweredParent goto DYING;
+ // because of deferred processing, the parent receives the child's
+ // message here
+ answer Parent goto DYING;
+
+
+state DYING:
+ send __delete__;
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestInterruptShutdownRace.ipdl b/ipc/ipdl/test/cxx/PTestInterruptShutdownRace.ipdl
new file mode 100644
index 000000000..10b377487
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestInterruptShutdownRace.ipdl
@@ -0,0 +1,34 @@
+namespace mozilla {
+namespace _ipdltest {
+
+intr protocol PTestInterruptShutdownRace {
+parent:
+ sync StartDeath();
+ async Orphan();
+
+child:
+ async Start();
+ intr Exit();
+ async __delete__();
+
+state START:
+ send Start goto START_DEATH;
+
+state START_DEATH:
+ recv StartDeath goto EXITING;
+
+state EXITING:
+ recv Orphan goto QUITTING1;
+ call Exit goto QUITTING2;
+
+state QUITTING1:
+ call Exit goto DEAD;
+state QUITTING2:
+ recv Orphan goto DEAD;
+
+state DEAD:
+ send __delete__;
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestJSON.ipdl b/ipc/ipdl/test/cxx/PTestJSON.ipdl
new file mode 100644
index 000000000..6e8f3c1de
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestJSON.ipdl
@@ -0,0 +1,54 @@
+include protocol PTestHandle;
+
+using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
+using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
+
+namespace mozilla {
+namespace _ipdltest {
+
+union Key {
+// int;
+// double;
+ nsString;
+};
+
+struct KeyValue {
+ Key key;
+ JSONVariant value;
+};
+
+union JSONVariant {
+ void_t;
+ null_t;
+ bool;
+ int;
+ double;
+ nsString;
+ PTestHandle;
+ KeyValue[];
+ JSONVariant[];
+};
+
+sync protocol PTestJSON {
+ manages PTestHandle;
+
+child:
+ async Start();
+
+parent:
+ async PTestHandle();
+ sync Test(JSONVariant i)
+ returns (JSONVariant o);
+ async __delete__();
+
+state START:
+ send Start goto TEST;
+
+state TEST:
+ recv PTestHandle goto TEST;
+ recv Test goto TEST;
+ recv __delete__;
+};
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestLatency.ipdl b/ipc/ipdl/test/cxx/PTestLatency.ipdl
new file mode 100644
index 000000000..d0c9750d8
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestLatency.ipdl
@@ -0,0 +1,75 @@
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+intr protocol PTestLatency {
+
+child:
+ async __delete__();
+ async Ping();
+ async Ping5();
+ intr Rpc();
+ async Spam();
+ intr Synchro();
+ async CompressedSpam(uint32_t seqno) compress;
+ intr Synchro2() returns (uint32_t lastSeqno,
+ uint32_t numMessagesDispatched);
+
+parent:
+ async Pong();
+ async Pong5();
+
+state START:
+ // if the timing resolution is too low, abort the test
+ send __delete__;
+ // otherwise, kick off the ping/pong trials
+ send Ping goto PONG;
+
+ // Trial 1: single ping/pong latency
+state PING:
+ send Ping goto PONG;
+ send Ping5 goto PING4;
+
+state PONG:
+ recv Pong goto PING;
+
+ // Trial 2: "overlapped" ping/pong latency
+state PING5:
+ send Ping5 goto PING4;
+ call Rpc goto RPC;
+
+state PING4: send Ping5 goto PING3;
+state PING3: send Ping5 goto PING2;
+state PING2: send Ping5 goto PING1;
+state PING1: send Ping5 goto PONG1;
+
+state PONG1: recv Pong5 goto PONG2;
+state PONG2: recv Pong5 goto PONG3;
+state PONG3: recv Pong5 goto PONG4;
+state PONG4: recv Pong5 goto PONG5;
+state PONG5: recv Pong5 goto PING5;
+
+ // Trial 3: lotsa RPC
+state RPC:
+ call Rpc goto RPC;
+ send Spam goto SPAM;
+
+ // Trial 4: lots of sequential asyn messages, which tests pipelining
+state SPAM:
+ send Spam goto SPAM;
+ call Synchro goto COMPRESSED_SPAM;
+
+ // Trial 5: lots of async spam, but compressed to cut down on
+ // dispatch overhead
+state COMPRESSED_SPAM: // compressed spam, mmm
+ send CompressedSpam goto COMPRESSED_SPAM;
+ call Synchro2 goto DONE;
+
+state DONE:
+ send __delete__;
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestManyChildAllocs.ipdl b/ipc/ipdl/test/cxx/PTestManyChildAllocs.ipdl
new file mode 100644
index 000000000..767af85a2
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestManyChildAllocs.ipdl
@@ -0,0 +1,19 @@
+include protocol PTestManyChildAllocsSub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+protocol PTestManyChildAllocs {
+ manages PTestManyChildAllocsSub;
+
+child:
+ async Go(); // start allocating
+
+parent:
+ async Done();
+
+ async PTestManyChildAllocsSub();
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestManyChildAllocsSub.ipdl b/ipc/ipdl/test/cxx/PTestManyChildAllocsSub.ipdl
new file mode 100644
index 000000000..e3d9c98df
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestManyChildAllocsSub.ipdl
@@ -0,0 +1,19 @@
+include protocol PTestManyChildAllocs;
+
+namespace mozilla {
+namespace _ipdltest {
+
+protocol PTestManyChildAllocsSub {
+ manager PTestManyChildAllocs;
+
+child:
+ async __delete__();
+
+parent:
+ async Hello();
+
+ // empty
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestMultiMgrs.ipdl b/ipc/ipdl/test/cxx/PTestMultiMgrs.ipdl
new file mode 100644
index 000000000..6322bb1a3
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestMultiMgrs.ipdl
@@ -0,0 +1,34 @@
+include protocol PTestMultiMgrsLeft;
+include protocol PTestMultiMgrsRight;
+
+namespace mozilla {
+namespace _ipdltest {
+
+protocol PTestMultiMgrs {
+ manages PTestMultiMgrsLeft;
+ manages PTestMultiMgrsRight;
+
+parent:
+ async OK();
+
+child:
+ async PTestMultiMgrsLeft();
+ async PTestMultiMgrsRight();
+ async Check();
+ async __delete__();
+
+state START:
+ send PTestMultiMgrsLeft goto CONSTRUCT_RIGHT;
+state CONSTRUCT_RIGHT:
+ send PTestMultiMgrsRight goto CHILD_CHECK;
+state CHILD_CHECK:
+ send Check goto CHILD_ACK;
+state CHILD_ACK:
+ recv OK goto DONE;
+
+state DONE:
+ send __delete__;
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestMultiMgrsBottom.ipdl b/ipc/ipdl/test/cxx/PTestMultiMgrsBottom.ipdl
new file mode 100644
index 000000000..9c474f446
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestMultiMgrsBottom.ipdl
@@ -0,0 +1,18 @@
+include protocol PTestMultiMgrsLeft;
+include protocol PTestMultiMgrsRight;
+
+namespace mozilla {
+namespace _ipdltest {
+
+protocol PTestMultiMgrsBottom {
+ manager PTestMultiMgrsLeft or PTestMultiMgrsRight;
+
+child:
+ async __delete__();
+
+state DOA:
+ send __delete__;
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestMultiMgrsLeft.ipdl b/ipc/ipdl/test/cxx/PTestMultiMgrsLeft.ipdl
new file mode 100644
index 000000000..830176272
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestMultiMgrsLeft.ipdl
@@ -0,0 +1,24 @@
+include protocol PTestMultiMgrs;
+include protocol PTestMultiMgrsBottom;
+
+namespace mozilla {
+namespace _ipdltest {
+
+protocol PTestMultiMgrsLeft {
+ manager PTestMultiMgrs;
+
+ manages PTestMultiMgrsBottom;
+
+child:
+ async PTestMultiMgrsBottom();
+ async __delete__();
+
+state START:
+ send PTestMultiMgrsBottom goto DONE;
+
+state DONE:
+ send __delete__;
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestMultiMgrsRight.ipdl b/ipc/ipdl/test/cxx/PTestMultiMgrsRight.ipdl
new file mode 100644
index 000000000..36b0771b7
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestMultiMgrsRight.ipdl
@@ -0,0 +1,24 @@
+include protocol PTestMultiMgrs;
+include protocol PTestMultiMgrsBottom;
+
+namespace mozilla {
+namespace _ipdltest {
+
+protocol PTestMultiMgrsRight {
+ manager PTestMultiMgrs;
+
+ manages PTestMultiMgrsBottom;
+
+child:
+ async PTestMultiMgrsBottom();
+ async __delete__();
+
+state START:
+ send PTestMultiMgrsBottom goto DONE;
+
+state DONE:
+ send __delete__;
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestNestedLoops.ipdl b/ipc/ipdl/test/cxx/PTestNestedLoops.ipdl
new file mode 100644
index 000000000..f0f067e0b
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestNestedLoops.ipdl
@@ -0,0 +1,34 @@
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+intr protocol PTestNestedLoops {
+
+child:
+ async Start();
+ intr R();
+ async __delete__();
+
+parent:
+ async Nonce();
+
+
+state START:
+ send Start goto RACE;
+
+state RACE:
+ recv Nonce goto RACE1;
+ call R goto RACE2;
+state RACE1:
+ call R goto DEAD;
+state RACE2:
+ recv Nonce goto DEAD;
+
+state DEAD:
+ send __delete__;
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestOpens.ipdl b/ipc/ipdl/test/cxx/PTestOpens.ipdl
new file mode 100644
index 000000000..2717328fa
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestOpens.ipdl
@@ -0,0 +1,25 @@
+include protocol PTestOpensOpened;
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+protocol PTestOpens {
+ // This channel is opened and parked on a non-main thread
+ child opens PTestOpensOpened;
+
+child:
+ async Start();
+
+parent:
+ async __delete__();
+
+state START:
+ send Start goto DEAD;
+state DEAD:
+ recv __delete__;
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestOpensOpened.ipdl b/ipc/ipdl/test/cxx/PTestOpensOpened.ipdl
new file mode 100644
index 000000000..04b633634
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestOpensOpened.ipdl
@@ -0,0 +1,28 @@
+namespace mozilla {
+namespace _ipdltest2 {
+
+// (Opens protocols can have different semantics than the endpoints
+// that opened them)
+intr protocol PTestOpensOpened {
+child:
+ async Hi();
+ intr HiRpc();
+
+parent:
+ async Hello();
+ sync HelloSync();
+ intr HelloRpc();
+ async __delete__();
+
+state START: recv Hello goto HI;
+state HI: send Hi goto HELLO_SYNC;
+state HELLO_SYNC: recv HelloSync goto HELLO_RPC;
+state HELLO_RPC: answer HelloRpc goto HI_RPC;
+state HI_RPC: call HiRpc goto DEAD;
+state DEAD:
+ recv __delete__;
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest2
diff --git a/ipc/ipdl/test/cxx/PTestPriority.ipdl b/ipc/ipdl/test/cxx/PTestPriority.ipdl
new file mode 100644
index 000000000..edb98365b
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestPriority.ipdl
@@ -0,0 +1,14 @@
+namespace mozilla {
+namespace _ipdltest {
+
+sync protocol PTestPriority {
+parent:
+ prio(high) async Msg1();
+ prio(high) sync Msg2();
+
+child:
+ prio(high) async Msg3();
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestRPC.ipdl b/ipc/ipdl/test/cxx/PTestRPC.ipdl
new file mode 100644
index 000000000..f51ee2735
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestRPC.ipdl
@@ -0,0 +1,21 @@
+namespace mozilla {
+namespace _ipdltest {
+
+nested(upto inside_sync) sync protocol PTestRPC
+{
+parent:
+ nested(inside_sync) sync Test1_Start() returns (uint32_t result);
+ nested(inside_sync) sync Test1_InnerEvent() returns (uint32_t result);
+ async Test2_Start();
+ nested(inside_sync) sync Test2_OutOfOrder();
+
+child:
+ async Start();
+ nested(inside_sync) sync Test1_InnerQuery() returns (uint32_t result);
+ nested(inside_sync) sync Test1_NoReenter() returns (uint32_t result);
+ nested(inside_sync) sync Test2_FirstUrgent();
+ nested(inside_sync) sync Test2_SecondUrgent();
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestRaceDeadlock.ipdl b/ipc/ipdl/test/cxx/PTestRaceDeadlock.ipdl
new file mode 100644
index 000000000..1e4f57450
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestRaceDeadlock.ipdl
@@ -0,0 +1,20 @@
+namespace mozilla {
+namespace _ipdltest {
+
+intr protocol PTestRaceDeadlock {
+both:
+ async StartRace();
+
+parent:
+ intr Lose();
+
+child:
+ intr Win();
+ intr Rpc();
+ async __delete__();
+
+/* Tests that race resolution does not cause deadlocks */
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestRaceDeferral.ipdl b/ipc/ipdl/test/cxx/PTestRaceDeferral.ipdl
new file mode 100644
index 000000000..ada6df4c6
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestRaceDeferral.ipdl
@@ -0,0 +1,45 @@
+namespace mozilla {
+namespace _ipdltest {
+
+intr protocol PTestRaceDeferral {
+parent:
+ intr Lose();
+
+child:
+ async StartRace();
+ intr Win();
+ intr Rpc();
+ async __delete__();
+
+// Test that messages deferred due to race resolution are
+// re-considered when the winner makes later RPCs
+
+// IPDL's type system can't express this protocol because the race
+// resolution causes state to diverge for multiple steps, so we'll
+// leave it "stateless"
+/*
+state START:
+ send StartRace goto DEFER;
+state DEFER:
+ call Win goto PARENT;
+ answer Lose goto CHILD;
+
+state PARENT:
+ // 'Lose' is received here but deferred
+ call Rpc goto PARENT_LOSE;
+state PARENT_LOSE:
+ // Calling 'Rpc' undefers 'Lose', and it wins the "race" with 'Rpc'
+ answer Lose goto DONE;
+
+state CHILD:
+ call Win goto CHILD_RPC;
+state CHILD_RPC:
+ call Rpc goto DONE;
+
+state DONE:
+ send __delete__;
+*/
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestRacyInterruptReplies.ipdl b/ipc/ipdl/test/cxx/PTestRacyInterruptReplies.ipdl
new file mode 100644
index 000000000..56e700b85
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestRacyInterruptReplies.ipdl
@@ -0,0 +1,41 @@
+namespace mozilla {
+namespace _ipdltest {
+
+intr protocol PTestRacyInterruptReplies {
+child:
+ intr R_() returns (int replyNum);
+ async _A();
+ async ChildTest();
+ async __delete__();
+
+parent:
+ intr _R() returns (int replyNum);
+ async A_();
+
+state PARENT_START:
+ call R_ goto PARENT_S1;
+
+state PARENT_S1:
+ recv A_ goto PARENT_S2;
+
+state PARENT_S2:
+ call R_ goto CHILD_TEST;
+
+state CHILD_TEST:
+ send ChildTest goto CHILD_START;
+
+state CHILD_START:
+ answer _R goto CHILD_S1;
+
+state CHILD_S1:
+ send _A goto CHILD_S2;
+
+state CHILD_S2:
+ answer _R goto DYING;
+
+state DYING:
+ send __delete__;
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestRacyReentry.ipdl b/ipc/ipdl/test/cxx/PTestRacyReentry.ipdl
new file mode 100644
index 000000000..4dd5fe54f
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestRacyReentry.ipdl
@@ -0,0 +1,21 @@
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+intr protocol PTestRacyReentry {
+
+parent:
+ intr E();
+ async __delete__();
+
+child:
+ async Start();
+
+ async N();
+ intr H();
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestRacyUndefer.ipdl b/ipc/ipdl/test/cxx/PTestRacyUndefer.ipdl
new file mode 100644
index 000000000..8863d6179
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestRacyUndefer.ipdl
@@ -0,0 +1,28 @@
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+intr protocol PTestRacyUndefer {
+
+child:
+ async Start();
+
+ async AwakenSpam();
+ async AwakenRaceWinTwice();
+
+ intr Race();
+
+ async __delete__();
+
+parent:
+
+ intr Spam();
+ intr RaceWinTwice();
+
+ async Done();
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestSanity.ipdl b/ipc/ipdl/test/cxx/PTestSanity.ipdl
new file mode 100644
index 000000000..50249b741
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestSanity.ipdl
@@ -0,0 +1,28 @@
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+protocol PTestSanity {
+
+child:
+ async Ping(int zero, float zeroPtFive, int8_t dummy);
+ async __delete__();
+
+parent:
+ async Pong(int one, float zeroPtTwoFive, uint8_t dummy);
+
+
+state PING:
+ send Ping goto PONG;
+
+state PONG:
+ recv Pong goto DEAD;
+
+state DEAD:
+ send __delete__;
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestSelfManage.ipdl b/ipc/ipdl/test/cxx/PTestSelfManage.ipdl
new file mode 100644
index 000000000..69eb4f55c
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestSelfManage.ipdl
@@ -0,0 +1,22 @@
+include protocol PTestSelfManageRoot;
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+protocol PTestSelfManage {
+ manager PTestSelfManageRoot or PTestSelfManage;
+ manages PTestSelfManage;
+
+child:
+ async PTestSelfManage();
+ async __delete__();
+
+state LIVE:
+ send PTestSelfManage goto LIVE;
+ send __delete__;
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestSelfManageRoot.ipdl b/ipc/ipdl/test/cxx/PTestSelfManageRoot.ipdl
new file mode 100644
index 000000000..429f8bfc8
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestSelfManageRoot.ipdl
@@ -0,0 +1,23 @@
+include protocol PTestSelfManage;
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+protocol PTestSelfManageRoot {
+ manages PTestSelfManage;
+
+child:
+ async PTestSelfManage();
+ async __delete__();
+
+state LIVE:
+ send PTestSelfManage goto DEAD;
+
+state DEAD:
+ send __delete__;
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestShmem.ipdl b/ipc/ipdl/test/cxx/PTestShmem.ipdl
new file mode 100644
index 000000000..46a1958e2
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestShmem.ipdl
@@ -0,0 +1,22 @@
+namespace mozilla {
+namespace _ipdltest {
+
+protocol PTestShmem {
+child:
+ async Give(Shmem mem, Shmem unsafe, size_t expectedSize);
+
+parent:
+ async Take(Shmem mem, Shmem unsafe, size_t expectedSize);
+ async __delete__();
+
+
+state GIVING:
+ send Give goto TAKING;
+
+state TAKING:
+ recv Take goto TAKING;
+ recv __delete__;
+};
+
+}
+}
diff --git a/ipc/ipdl/test/cxx/PTestShutdown.ipdl b/ipc/ipdl/test/cxx/PTestShutdown.ipdl
new file mode 100644
index 000000000..e4d8d0dbe
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestShutdown.ipdl
@@ -0,0 +1,37 @@
+include protocol PTestShutdownSub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+intr protocol PTestShutdown {
+ manages PTestShutdownSub;
+
+child:
+ async Start();
+
+parent:
+ // NB: we test deletion and crashing only, not shutdown, because
+ // crashing is the same code path as shutdown, and other IPDL unit
+ // tests check shutdown semantics
+ async PTestShutdownSub(bool expectCrash);
+
+ // Used to synchronize between parent and child, to avoid races
+ // around flushing socket write queues
+ sync Sync();
+
+ async __delete__();
+
+
+state START:
+ send Start goto TESTING;
+
+state TESTING:
+ recv PTestShutdownSub goto TESTING;
+ recv Sync goto DYING;
+
+state DYING:
+ recv __delete__;
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestShutdownSub.ipdl b/ipc/ipdl/test/cxx/PTestShutdownSub.ipdl
new file mode 100644
index 000000000..39d45d9ed
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestShutdownSub.ipdl
@@ -0,0 +1,30 @@
+include protocol PTestShutdown;
+include protocol PTestShutdownSubsub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+intr protocol PTestShutdownSub {
+ manager PTestShutdown;
+ manages PTestShutdownSubsub;
+
+both:
+ intr StackFrame();
+
+parent:
+ async PTestShutdownSubsub(bool expectParentDeleted);
+ sync __delete__();
+
+state CREATING:
+ recv PTestShutdownSubsub goto CREATING;
+ answer StackFrame goto DUMMYFRAME;
+
+state DUMMYFRAME:
+ call StackFrame goto DEAD;
+
+state DEAD:
+ recv __delete__;
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestShutdownSubsub.ipdl b/ipc/ipdl/test/cxx/PTestShutdownSubsub.ipdl
new file mode 100644
index 000000000..91031ab41
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestShutdownSubsub.ipdl
@@ -0,0 +1,17 @@
+include protocol PTestShutdownSub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+sync protocol PTestShutdownSubsub {
+ manager PTestShutdownSub;
+
+parent:
+ sync __delete__();
+
+state LIVE:
+ recv __delete__;
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestStackHooks.ipdl b/ipc/ipdl/test/cxx/PTestStackHooks.ipdl
new file mode 100644
index 000000000..1f3af068f
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestStackHooks.ipdl
@@ -0,0 +1,56 @@
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+intr protocol PTestStackHooks {
+child:
+ async Start();
+
+ // These tests are more fruitful running child->parent, because
+ // children can send |sync| messages
+parent:
+ async Async();
+ sync Sync();
+ intr Rpc();
+
+both:
+ intr StackFrame();
+
+parent:
+ async __delete__();
+
+
+state START:
+ send Start goto TEST1;
+
+state TEST1:
+ recv Async goto TEST2;
+
+state TEST2:
+ recv Sync goto TEST3;
+
+state TEST3:
+ answer Rpc goto TEST4;
+
+state TEST4:
+ answer StackFrame goto TEST4_2;
+state TEST4_2:
+ call StackFrame goto TEST4_3;
+state TEST4_3:
+ recv Async goto TEST5;
+
+state TEST5:
+ answer StackFrame goto TEST5_2;
+state TEST5_2:
+ call StackFrame goto TEST5_3;
+state TEST5_3:
+ recv Sync goto DEAD;
+
+state DEAD:
+ recv __delete__;
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestSyncError.ipdl b/ipc/ipdl/test/cxx/PTestSyncError.ipdl
new file mode 100644
index 000000000..05b68ff35
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestSyncError.ipdl
@@ -0,0 +1,28 @@
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+sync protocol PTestSyncError {
+
+child:
+ async Start();
+
+parent:
+ sync Error();
+ async __delete__();
+
+
+state START:
+ send Start goto SYNC_ERROR;
+
+state SYNC_ERROR:
+ recv Error goto DEAD;
+
+state DEAD:
+ recv __delete__;
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestSyncHang.ipdl b/ipc/ipdl/test/cxx/PTestSyncHang.ipdl
new file mode 100644
index 000000000..eefcf2c5e
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestSyncHang.ipdl
@@ -0,0 +1,15 @@
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+protocol PTestSyncHang {
+
+child:
+ async __delete__();
+
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestSyncWakeup.ipdl b/ipc/ipdl/test/cxx/PTestSyncWakeup.ipdl
new file mode 100644
index 000000000..3d873cdf5
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestSyncWakeup.ipdl
@@ -0,0 +1,41 @@
+namespace mozilla {
+namespace _ipdltest {
+
+intr protocol PTestSyncWakeup {
+both:
+ intr StackFrame();
+
+child:
+ async Start();
+ async Note1();
+ async Note2();
+
+parent:
+ sync Sync1();
+ sync Sync2();
+ async __delete__();
+
+
+state START:
+ send Start goto TEST1;
+
+state TEST1:
+ recv Sync1 goto TEST1_P2;
+state TEST1_P2:
+ send Note1 goto TEST2;
+
+state TEST2:
+ answer StackFrame goto TEST2_P2;
+state TEST2_P2:
+ call StackFrame goto TEST2_P3;
+state TEST2_P3:
+ recv Sync2 goto TEST2_P4;
+state TEST2_P4:
+ send Note2 goto DONE;
+
+state DONE:
+ recv __delete__;
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestUrgency.ipdl b/ipc/ipdl/test/cxx/PTestUrgency.ipdl
new file mode 100644
index 000000000..79b479ca2
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestUrgency.ipdl
@@ -0,0 +1,19 @@
+namespace mozilla {
+namespace _ipdltest {
+
+nested(upto inside_cpow) sync protocol PTestUrgency
+{
+parent:
+ nested(inside_sync) sync Test1() returns (uint32_t result);
+ async Test2();
+ sync Test3() returns (uint32_t result);
+ sync FinalTest_Begin();
+
+child:
+ async Start();
+ nested(inside_sync) sync Reply1() returns (uint32_t result);
+ nested(inside_sync) sync Reply2() returns (uint32_t result);
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestUrgentHangs.ipdl b/ipc/ipdl/test/cxx/PTestUrgentHangs.ipdl
new file mode 100644
index 000000000..8ce8d1747
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestUrgentHangs.ipdl
@@ -0,0 +1,28 @@
+namespace mozilla {
+namespace _ipdltest {
+
+nested(upto inside_cpow) sync protocol PTestUrgentHangs
+{
+parent:
+ nested(inside_sync) sync Test1_2();
+
+ nested(inside_sync) sync TestInner();
+ nested(inside_cpow) sync TestInnerUrgent();
+
+child:
+ nested(inside_sync) sync Test1_1();
+ nested(inside_sync) sync Test1_3();
+
+ nested(inside_sync) sync Test2();
+
+ nested(inside_sync) sync Test3();
+
+ async Test4();
+ nested(inside_sync) sync Test4_1();
+
+ async Test5();
+ nested(inside_sync) sync Test5_1();
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/README.txt b/ipc/ipdl/test/cxx/README.txt
new file mode 100644
index 000000000..0fe6c0732
--- /dev/null
+++ b/ipc/ipdl/test/cxx/README.txt
@@ -0,0 +1,61 @@
+To add a new IPDL C++ unit test, you need to create (at least) the
+following files (for a test "TestFoo"):
+
+ - PTestFoo.ipdl, specifying the top-level protocol used for the test
+
+ - TestFoo.h, declaring the top-level parent/child actors used for
+ the test
+
+ - TestFoo.cpp, defining the top-level actors
+
+ - (make sure all are in the namespace mozilla::_ipdltest)
+
+Next
+
+ - add PTestFoo.ipdl to ipdl.mk
+
+ - append TestFoo to the variable IPDLTESTS in Makefile.in
+
+You must define three methods in your |TestFooParent| class:
+
+ - static methods |bool RunTestInProcesses()| and
+ |bool RunTestInThreads()|. These methods control whether
+ to execute the test using actors in separate processes and
+ threads respectively. Generally, both should return true.
+
+ - an instance method |void Main()|. The test harness wil first
+ initialize the processes or threads, create and open both actors,
+ and then kick off the test using |Main()|. Make sure you define
+ it.
+
+If your test passes its criteria, please call
+|MOZ_IPDL_TESTPASS("msg")| and "exit gracefully".
+
+If your tests fails, please call |MOZ_IPDL_TESTFAIL("msg")| and "exit
+ungracefully", preferably by abort()ing.
+
+
+If all goes well, running
+
+ make -C $OBJDIR/ipc/ipdl/test/cxx
+
+will update the file IPDLUnitTests.cpp (the test launcher), and your
+new code will be built automatically.
+
+
+You can launch your new test by invoking one of
+
+ make -C $OBJDIR/ipc/ipdl/test/cxx check-proc (test process-based tests)
+ make -C $OBJDIR/ipc/ipdl/test/cxx check-threads (test thread-based tests)
+ make -C $OBJDIR/ipc/ipdl/test/cxx check (tests both)
+
+If you want to launch only your test, run
+
+ cd $OBJDIR/dist/bin
+ ./run-mozilla.sh ./ipdlunittest TestFoo (test in two processes, if appl.)
+ ./run-mozilla.sh ./ipdlunittest thread:TestFoo (test in two threads, if appl.)
+
+
+For a bare-bones example of adding a test, take a look at
+PTestSanity.ipdl, TestSanity.h, TestSanity.cpp, and how "TestSanity"
+is included in ipdl.mk and Makefile.in.
diff --git a/ipc/ipdl/test/cxx/TestActorPunning.cpp b/ipc/ipdl/test/cxx/TestActorPunning.cpp
new file mode 100644
index 000000000..eb6fa412f
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestActorPunning.cpp
@@ -0,0 +1,128 @@
+#include "TestActorPunning.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+#include "mozilla/Unused.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+void
+TestActorPunningParent::Main()
+{
+ if (!SendStart())
+ fail("sending Start");
+}
+
+bool
+TestActorPunningParent::RecvPun(PTestActorPunningSubParent* a, const Bad& bad)
+{
+ if (a->SendBad())
+ fail("bad!");
+ fail("shouldn't have received this message in the first place");
+ return true;
+}
+
+PTestActorPunningPunnedParent*
+TestActorPunningParent::AllocPTestActorPunningPunnedParent()
+{
+ return new TestActorPunningPunnedParent();
+}
+
+bool
+TestActorPunningParent::DeallocPTestActorPunningPunnedParent(PTestActorPunningPunnedParent* a)
+{
+ delete a;
+ return true;
+}
+
+PTestActorPunningSubParent*
+TestActorPunningParent::AllocPTestActorPunningSubParent()
+{
+ return new TestActorPunningSubParent();
+}
+
+bool
+TestActorPunningParent::DeallocPTestActorPunningSubParent(PTestActorPunningSubParent* a)
+{
+ delete a;
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+PTestActorPunningPunnedChild*
+TestActorPunningChild::AllocPTestActorPunningPunnedChild()
+{
+ return new TestActorPunningPunnedChild();
+}
+
+bool
+TestActorPunningChild::DeallocPTestActorPunningPunnedChild(PTestActorPunningPunnedChild*)
+{
+ fail("should have died by now");
+ return true;
+}
+
+PTestActorPunningSubChild*
+TestActorPunningChild::AllocPTestActorPunningSubChild()
+{
+ return new TestActorPunningSubChild();
+}
+
+bool
+TestActorPunningChild::DeallocPTestActorPunningSubChild(PTestActorPunningSubChild*)
+{
+ fail("should have died by now");
+ return true;
+}
+
+bool
+TestActorPunningChild::RecvStart()
+{
+ SendPTestActorPunningSubConstructor();
+ SendPTestActorPunningPunnedConstructor();
+ PTestActorPunningSubChild* a = SendPTestActorPunningSubConstructor();
+ // We can't assert whether this succeeds or fails, due to race
+ // conditions.
+ SendPun(a, Bad());
+ return true;
+}
+
+bool
+TestActorPunningSubChild::RecvBad()
+{
+ fail("things are going really badly right now");
+ return true;
+}
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+namespace IPC {
+using namespace mozilla::_ipdltest;
+using namespace mozilla::ipc;
+
+/*static*/ void
+ParamTraits<Bad>::Write(Message* aMsg, const paramType& aParam)
+{
+ // Skip past the sentinel for the actor as well as the actor.
+ int32_t* ptr = aMsg->GetInt32PtrForTest(2 * sizeof(int32_t));
+ ActorHandle* ah = reinterpret_cast<ActorHandle*>(ptr);
+ if (ah->mId != -3)
+ fail("guessed wrong offset (value is %d, should be -3)", ah->mId);
+ ah->mId = -2;
+}
+
+/*static*/ bool
+ParamTraits<Bad>::Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+{
+ return true;
+}
+
+} // namespace IPC
+
diff --git a/ipc/ipdl/test/cxx/TestActorPunning.h b/ipc/ipdl/test/cxx/TestActorPunning.h
new file mode 100644
index 000000000..5ebf2e7f4
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestActorPunning.h
@@ -0,0 +1,110 @@
+#ifndef mozilla__ipdltest_TestActorPunning_h
+#define mozilla__ipdltest_TestActorPunning_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestActorPunningParent.h"
+#include "mozilla/_ipdltest/PTestActorPunningPunnedParent.h"
+#include "mozilla/_ipdltest/PTestActorPunningSubParent.h"
+#include "mozilla/_ipdltest/PTestActorPunningChild.h"
+#include "mozilla/_ipdltest/PTestActorPunningPunnedChild.h"
+#include "mozilla/_ipdltest/PTestActorPunningSubChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestActorPunningParent :
+ public PTestActorPunningParent
+{
+public:
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+
+protected:
+ PTestActorPunningPunnedParent* AllocPTestActorPunningPunnedParent() override;
+ bool DeallocPTestActorPunningPunnedParent(PTestActorPunningPunnedParent* a) override;
+
+ PTestActorPunningSubParent* AllocPTestActorPunningSubParent() override;
+ bool DeallocPTestActorPunningSubParent(PTestActorPunningSubParent* a) override;
+
+ virtual bool RecvPun(PTestActorPunningSubParent* a, const Bad& bad) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown == why)
+ fail("should have died from error!");
+ passed("ok");
+ QuitParent();
+ }
+};
+
+class TestActorPunningPunnedParent :
+ public PTestActorPunningPunnedParent
+{
+public:
+ TestActorPunningPunnedParent() {}
+ virtual ~TestActorPunningPunnedParent() {}
+protected:
+ virtual void ActorDestroy(ActorDestroyReason why) override {}
+};
+
+class TestActorPunningSubParent :
+ public PTestActorPunningSubParent
+{
+public:
+ TestActorPunningSubParent() {}
+ virtual ~TestActorPunningSubParent() {}
+protected:
+ virtual void ActorDestroy(ActorDestroyReason why) override {}
+};
+
+
+class TestActorPunningChild :
+ public PTestActorPunningChild
+{
+public:
+ TestActorPunningChild() {}
+ virtual ~TestActorPunningChild() {}
+
+protected:
+ PTestActorPunningPunnedChild* AllocPTestActorPunningPunnedChild() override;
+ bool DeallocPTestActorPunningPunnedChild(PTestActorPunningPunnedChild* a) override;
+
+ PTestActorPunningSubChild* AllocPTestActorPunningSubChild() override;
+ bool DeallocPTestActorPunningSubChild(PTestActorPunningSubChild* a) override;
+
+ virtual bool RecvStart() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ fail("should have been killed off!");
+ }
+};
+
+class TestActorPunningPunnedChild :
+ public PTestActorPunningPunnedChild
+{
+public:
+ TestActorPunningPunnedChild() {}
+ virtual ~TestActorPunningPunnedChild() {}
+};
+
+class TestActorPunningSubChild :
+ public PTestActorPunningSubChild
+{
+public:
+ TestActorPunningSubChild() {}
+ virtual ~TestActorPunningSubChild() {}
+
+ virtual bool RecvBad() override;
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestActorPunning_h
diff --git a/ipc/ipdl/test/cxx/TestBadActor.cpp b/ipc/ipdl/test/cxx/TestBadActor.cpp
new file mode 100644
index 000000000..bc1cf6f6d
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestBadActor.cpp
@@ -0,0 +1,51 @@
+#include "TestBadActor.h"
+#include "IPDLUnitTests.h"
+#include "mozilla/Unused.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+void
+TestBadActorParent::Main()
+{
+ // This test is designed to test a race condition where the child sends us
+ // a message on an actor that we've already destroyed. The child process
+ // should die, and the parent process should not abort.
+
+ PTestBadActorSubParent* child = SendPTestBadActorSubConstructor();
+ if (!child)
+ fail("Sending constructor");
+
+ Unused << child->Call__delete__(child);
+}
+
+PTestBadActorSubParent*
+TestBadActorParent::AllocPTestBadActorSubParent()
+{
+ return new TestBadActorSubParent();
+}
+
+bool
+TestBadActorSubParent::RecvPing()
+{
+ fail("Shouldn't have received ping.");
+ return false;
+}
+
+PTestBadActorSubChild*
+TestBadActorChild::AllocPTestBadActorSubChild()
+{
+ return new TestBadActorSubChild();
+}
+
+bool
+TestBadActorChild::RecvPTestBadActorSubConstructor(PTestBadActorSubChild* actor)
+{
+ if (!actor->SendPing()) {
+ fail("Couldn't send ping to an actor which supposedly isn't dead yet.");
+ }
+ return true;
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestBadActor.h b/ipc/ipdl/test/cxx/TestBadActor.h
new file mode 100644
index 000000000..9b1258ff6
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestBadActor.h
@@ -0,0 +1,91 @@
+#ifndef mozilla__ipdltest_TestBadActor_h
+#define mozilla__ipdltest_TestBadActor_h
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestBadActorParent.h"
+#include "mozilla/_ipdltest/PTestBadActorChild.h"
+
+#include "mozilla/_ipdltest/PTestBadActorSubParent.h"
+#include "mozilla/_ipdltest/PTestBadActorSubChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+class TestBadActorParent
+ : public PTestBadActorParent
+{
+public:
+ TestBadActorParent() { }
+ virtual ~TestBadActorParent() { }
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+
+protected:
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (AbnormalShutdown != why)
+ fail("unexpected destruction");
+ passed("ok");
+ QuitParent();
+ }
+
+ virtual PTestBadActorSubParent*
+ AllocPTestBadActorSubParent();
+
+ virtual bool
+ DeallocPTestBadActorSubParent(PTestBadActorSubParent* actor) {
+ delete actor;
+ return true;
+ }
+};
+
+class TestBadActorSubParent
+ : public PTestBadActorSubParent
+{
+public:
+ TestBadActorSubParent() { }
+ virtual ~TestBadActorSubParent() { }
+
+protected:
+ virtual void ActorDestroy(ActorDestroyReason why) override {}
+ virtual bool RecvPing();
+};
+
+class TestBadActorChild
+ : public PTestBadActorChild
+{
+public:
+ TestBadActorChild() { }
+ virtual ~TestBadActorChild() { }
+
+protected:
+ virtual PTestBadActorSubChild*
+ AllocPTestBadActorSubChild();
+
+ virtual bool
+ DeallocPTestBadActorSubChild(PTestBadActorSubChild* actor)
+ {
+ delete actor;
+ return true;
+ }
+
+ virtual bool
+ RecvPTestBadActorSubConstructor(PTestBadActorSubChild* actor);
+};
+
+class TestBadActorSubChild
+ : public PTestBadActorSubChild
+{
+public:
+ TestBadActorSubChild() { }
+ virtual ~TestBadActorSubChild() { }
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+#endif // mozilla__ipdltest_TestBadActor_h
diff --git a/ipc/ipdl/test/cxx/TestBridgeMain.cpp b/ipc/ipdl/test/cxx/TestBridgeMain.cpp
new file mode 100644
index 000000000..9b1590a46
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestBridgeMain.cpp
@@ -0,0 +1,227 @@
+#include "TestBridgeMain.h"
+
+#include "base/task.h"
+#include "IPDLUnitTests.h" // fail etc.
+#include "IPDLUnitTestSubprocess.h"
+
+#include "nsAutoPtr.h"
+
+using namespace std;
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+//-----------------------------------------------------------------------------
+// main process
+void
+TestBridgeMainParent::Main()
+{
+ if (!SendStart())
+ fail("sending Start");
+}
+
+PTestBridgeMainSubParent*
+TestBridgeMainParent::AllocPTestBridgeMainSubParent(Transport* transport,
+ ProcessId otherPid)
+{
+ nsAutoPtr<TestBridgeMainSubParent> a(new TestBridgeMainSubParent(transport));
+ if (!a->Open(transport, otherPid, XRE_GetIOMessageLoop(), ipc::ParentSide)) {
+ return nullptr;
+ }
+ return a.forget();
+}
+
+void
+TestBridgeMainParent::ActorDestroy(ActorDestroyReason why)
+{
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+}
+
+bool
+TestBridgeMainSubParent::RecvHello()
+{
+ return SendHi();
+}
+
+bool
+TestBridgeMainSubParent::RecvHelloSync()
+{
+ return true;
+}
+
+bool
+TestBridgeMainSubParent::AnswerHelloRpc()
+{
+ return CallHiRpc();
+}
+
+void
+TestBridgeMainSubParent::ActorDestroy(ActorDestroyReason why)
+{
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+
+ // ActorDestroy() is just a callback from IPDL-generated code,
+ // which needs the top-level actor (this) to stay alive a little
+ // longer so other things can be cleaned up.
+ MessageLoop::current()->PostTask(
+ do_AddRef(new DeleteTask<TestBridgeMainSubParent>(this)));
+}
+
+//-----------------------------------------------------------------------------
+// sub process --- child of main
+TestBridgeMainChild* gBridgeMainChild;
+
+TestBridgeMainChild::TestBridgeMainChild()
+ : mSubprocess(nullptr)
+{
+ gBridgeMainChild = this;
+}
+
+bool
+TestBridgeMainChild::RecvStart()
+{
+ vector<string> subsubArgs;
+ subsubArgs.push_back("TestBridgeSub");
+
+ mSubprocess = new IPDLUnitTestSubprocess();
+ if (!mSubprocess->SyncLaunch(subsubArgs))
+ fail("problem launching subprocess");
+
+ IPC::Channel* transport = mSubprocess->GetChannel();
+ if (!transport)
+ fail("no transport");
+
+ TestBridgeSubParent* bsp = new TestBridgeSubParent();
+ bsp->Open(transport, base::GetProcId(mSubprocess->GetChildProcessHandle()));
+
+ bsp->Main();
+ return true;
+}
+
+void
+TestBridgeMainChild::ActorDestroy(ActorDestroyReason why)
+{
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ // NB: this is kosher because QuitChild() joins with the IO thread
+ XRE_GetIOMessageLoop()->PostTask(
+ do_AddRef(new DeleteTask<IPDLUnitTestSubprocess>(mSubprocess)));
+ QuitChild();
+}
+
+void
+TestBridgeSubParent::Main()
+{
+ if (!SendPing())
+ fail("sending Ping");
+}
+
+bool
+TestBridgeSubParent::RecvBridgeEm()
+{
+ if (NS_FAILED(PTestBridgeMainSub::Bridge(gBridgeMainChild, this)))
+ fail("bridging Main and Sub");
+ return true;
+}
+
+void
+TestBridgeSubParent::ActorDestroy(ActorDestroyReason why)
+{
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ gBridgeMainChild->Close();
+
+ // ActorDestroy() is just a callback from IPDL-generated code,
+ // which needs the top-level actor (this) to stay alive a little
+ // longer so other things can be cleaned up.
+ MessageLoop::current()->PostTask(
+ do_AddRef(new DeleteTask<TestBridgeSubParent>(this)));
+}
+
+//-----------------------------------------------------------------------------
+// subsub process --- child of sub
+
+static TestBridgeSubChild* gBridgeSubChild;
+
+TestBridgeSubChild::TestBridgeSubChild()
+{
+ gBridgeSubChild = this;
+}
+
+bool
+TestBridgeSubChild::RecvPing()
+{
+ if (!SendBridgeEm())
+ fail("sending BridgeEm");
+ return true;
+}
+
+PTestBridgeMainSubChild*
+TestBridgeSubChild::AllocPTestBridgeMainSubChild(Transport* transport,
+ ProcessId otherPid)
+{
+ nsAutoPtr<TestBridgeMainSubChild> a(new TestBridgeMainSubChild(transport));
+ if (!a->Open(transport, otherPid, XRE_GetIOMessageLoop(), ipc::ChildSide)) {
+ return nullptr;
+ }
+
+ if (!a->SendHello())
+ fail("sending Hello");
+
+ return a.forget();
+}
+
+void
+TestBridgeSubChild::ActorDestroy(ActorDestroyReason why)
+{
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+}
+
+bool
+TestBridgeMainSubChild::RecvHi()
+{
+ if (!SendHelloSync())
+ fail("sending HelloSync");
+ if (!CallHelloRpc())
+ fail("calling HelloRpc");
+ if (!mGotHi)
+ fail("didn't answer HiRpc");
+
+ // Need to close the channel without message-processing frames on
+ // the C++ stack
+ MessageLoop::current()->PostTask(
+ NewNonOwningRunnableMethod(this, &TestBridgeMainSubChild::Close));
+ return true;
+}
+
+bool
+TestBridgeMainSubChild::AnswerHiRpc()
+{
+ mGotHi = true; // d00d
+ return true;
+}
+
+void
+TestBridgeMainSubChild::ActorDestroy(ActorDestroyReason why)
+{
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+
+ gBridgeSubChild->Close();
+
+ // ActorDestroy() is just a callback from IPDL-generated code,
+ // which needs the top-level actor (this) to stay alive a little
+ // longer so other things can be cleaned up.
+ MessageLoop::current()->PostTask(
+ do_AddRef(new DeleteTask<TestBridgeMainSubChild>(this)));
+}
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/TestBridgeMain.h b/ipc/ipdl/test/cxx/TestBridgeMain.h
new file mode 100644
index 000000000..a5c7c10cb
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestBridgeMain.h
@@ -0,0 +1,149 @@
+#ifndef mozilla__ipdltest_TestBridgeMain_h
+#define mozilla__ipdltest_TestBridgeMain_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestBridgeMainParent.h"
+#include "mozilla/_ipdltest/PTestBridgeMainChild.h"
+
+#include "mozilla/_ipdltest/PTestBridgeSubParent.h"
+#include "mozilla/_ipdltest/PTestBridgeSubChild.h"
+
+#include "mozilla/_ipdltest/PTestBridgeMainSubParent.h"
+#include "mozilla/_ipdltest/PTestBridgeMainSubChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// "Main" process
+//
+class TestBridgeMainParent :
+ public PTestBridgeMainParent
+{
+public:
+ TestBridgeMainParent() {}
+ virtual ~TestBridgeMainParent() {}
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+
+protected:
+ virtual PTestBridgeMainSubParent*
+ AllocPTestBridgeMainSubParent(Transport* transport,
+ ProcessId otherProcess) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+};
+
+class TestBridgeMainSubParent :
+ public PTestBridgeMainSubParent
+{
+public:
+ explicit TestBridgeMainSubParent(Transport* aTransport)
+ : mTransport(aTransport)
+ {}
+ virtual ~TestBridgeMainSubParent() {}
+
+protected:
+ virtual bool RecvHello() override;
+ virtual bool RecvHelloSync() override;
+ virtual bool AnswerHelloRpc() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ Transport* mTransport;
+};
+
+//-----------------------------------------------------------------------------
+// "Sub" process --- child of "main"
+//
+class TestBridgeSubParent;
+
+class TestBridgeMainChild :
+ public PTestBridgeMainChild
+{
+public:
+ TestBridgeMainChild();
+ virtual ~TestBridgeMainChild() {}
+
+protected:
+ virtual bool RecvStart() override;
+
+ virtual PTestBridgeMainSubChild*
+ AllocPTestBridgeMainSubChild(Transport* transport,
+ ProcessId otherProcess) override
+ {
+ // This shouldn't be called. It's just a byproduct of testing that
+ // the right code is generated for a bridged protocol that's also
+ // opened, but we only test bridging here.
+ MOZ_CRASH();
+ }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ IPDLUnitTestSubprocess* mSubprocess;
+};
+
+class TestBridgeSubParent :
+ public PTestBridgeSubParent
+{
+public:
+ TestBridgeSubParent() {}
+ virtual ~TestBridgeSubParent() {}
+
+ void Main();
+
+protected:
+ virtual bool RecvBridgeEm() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+};
+
+//-----------------------------------------------------------------------------
+// "Subsub" process --- child of "sub"
+//
+class TestBridgeSubChild :
+ public PTestBridgeSubChild
+{
+public:
+ TestBridgeSubChild();
+ virtual ~TestBridgeSubChild() {}
+
+protected:
+ virtual bool RecvPing() override;
+
+ virtual PTestBridgeMainSubChild*
+ AllocPTestBridgeMainSubChild(Transport* transport,
+ ProcessId otherProcess) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+};
+
+class TestBridgeMainSubChild :
+ public PTestBridgeMainSubChild
+{
+public:
+ explicit TestBridgeMainSubChild(Transport* aTransport)
+ : mGotHi(false)
+ , mTransport(aTransport)
+ {}
+ virtual ~TestBridgeMainSubChild() {}
+
+protected:
+ virtual bool RecvHi() override;
+ virtual bool AnswerHiRpc() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ bool mGotHi;
+ Transport* mTransport;
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestBridgeMain_h
diff --git a/ipc/ipdl/test/cxx/TestCancel.cpp b/ipc/ipdl/test/cxx/TestCancel.cpp
new file mode 100644
index 000000000..8f983b3de
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestCancel.cpp
@@ -0,0 +1,168 @@
+#include "TestCancel.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+TestCancelParent::TestCancelParent()
+{
+ MOZ_COUNT_CTOR(TestCancelParent);
+}
+
+TestCancelParent::~TestCancelParent()
+{
+ MOZ_COUNT_DTOR(TestCancelParent);
+}
+
+void
+TestCancelParent::Main()
+{
+ if (SendTest1_1())
+ fail("sending Test1_1");
+
+ uint32_t value = 0;
+ if (!SendCheckChild(&value))
+ fail("Test1 CheckChild");
+
+ if (value != 12)
+ fail("Test1 CheckChild reply");
+}
+
+bool
+TestCancelParent::RecvDone1()
+{
+ if (!SendStart2())
+ fail("sending Start2");
+
+ return true;
+}
+
+bool
+TestCancelParent::RecvTest2_1()
+{
+ if (SendTest2_2())
+ fail("sending Test2_2");
+
+ return true;
+}
+
+bool
+TestCancelParent::RecvStart3()
+{
+ if (SendTest3_1())
+ fail("sending Test3_1");
+
+ uint32_t value = 0;
+ if (!SendCheckChild(&value))
+ fail("Test1 CheckChild");
+
+ if (value != 12)
+ fail("Test1 CheckChild reply");
+
+ return true;
+}
+
+bool
+TestCancelParent::RecvTest3_2()
+{
+ GetIPCChannel()->CancelCurrentTransaction();
+ return true;
+}
+
+bool
+TestCancelParent::RecvDone()
+{
+ MessageLoop::current()->PostTask(
+ NewNonOwningRunnableMethod(this, &TestCancelParent::Close));
+ return true;
+}
+
+bool
+TestCancelParent::RecvCheckParent(uint32_t *reply)
+{
+ *reply = 12;
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+bool
+TestCancelChild::RecvTest1_1()
+{
+ GetIPCChannel()->CancelCurrentTransaction();
+
+ uint32_t value = 0;
+ if (!SendCheckParent(&value))
+ fail("Test1 CheckParent");
+
+ if (value != 12)
+ fail("Test1 CheckParent reply");
+
+ if (!SendDone1())
+ fail("Test1 CheckParent");
+
+ return true;
+}
+
+bool
+TestCancelChild::RecvStart2()
+{
+ if (!SendTest2_1())
+ fail("sending Test2_1");
+
+ if (!SendStart3())
+ fail("sending Start3");
+
+ return true;
+}
+
+bool
+TestCancelChild::RecvTest2_2()
+{
+ GetIPCChannel()->CancelCurrentTransaction();
+ return true;
+}
+
+bool
+TestCancelChild::RecvTest3_1()
+{
+ if (SendTest3_2())
+ fail("sending Test3_2");
+
+ uint32_t value = 0;
+ if (!SendCheckParent(&value))
+ fail("Test1 CheckParent");
+
+ if (value != 12)
+ fail("Test1 CheckParent reply");
+
+ if (!SendDone())
+ fail("sending Done");
+
+ return true;
+}
+
+bool
+TestCancelChild::RecvCheckChild(uint32_t *reply)
+{
+ *reply = 12;
+ return true;
+}
+
+TestCancelChild::TestCancelChild()
+{
+ MOZ_COUNT_CTOR(TestCancelChild);
+}
+
+TestCancelChild::~TestCancelChild()
+{
+ MOZ_COUNT_DTOR(TestCancelChild);
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestCancel.h b/ipc/ipdl/test/cxx/TestCancel.h
new file mode 100644
index 000000000..8cd51acd1
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestCancel.h
@@ -0,0 +1,66 @@
+#ifndef mozilla__ipdltest_TestCancel_h
+#define mozilla__ipdltest_TestCancel_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestCancelParent.h"
+#include "mozilla/_ipdltest/PTestCancelChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestCancelParent :
+ public PTestCancelParent
+{
+public:
+ TestCancelParent();
+ virtual ~TestCancelParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+
+ virtual bool RecvDone1() override;
+ virtual bool RecvTest2_1() override;
+ virtual bool RecvStart3() override;
+ virtual bool RecvTest3_2() override;
+ virtual bool RecvDone() override;
+
+ virtual bool RecvCheckParent(uint32_t *reply) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ passed("ok");
+ QuitParent();
+ }
+};
+
+
+class TestCancelChild :
+ public PTestCancelChild
+{
+public:
+ TestCancelChild();
+ virtual ~TestCancelChild();
+
+ virtual bool RecvTest1_1() override;
+ virtual bool RecvStart2() override;
+ virtual bool RecvTest2_2() override;
+ virtual bool RecvTest3_1() override;
+
+ virtual bool RecvCheckChild(uint32_t *reply) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ QuitChild();
+ }
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestCancel_h
diff --git a/ipc/ipdl/test/cxx/TestCrashCleanup.cpp b/ipc/ipdl/test/cxx/TestCrashCleanup.cpp
new file mode 100644
index 000000000..2f335daae
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestCrashCleanup.cpp
@@ -0,0 +1,116 @@
+#include "TestCrashCleanup.h"
+
+#include "base/task.h"
+#include "mozilla/CondVar.h"
+#include "mozilla/Mutex.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+#include "IPDLUnitTestSubprocess.h"
+
+using mozilla::CondVar;
+using mozilla::Mutex;
+using mozilla::MutexAutoLock;
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+namespace {
+
+// NB: this test does its own shutdown, rather than going through
+// QuitParent(), because it's testing degenerate edge cases
+
+void DeleteSubprocess(Mutex* mutex, CondVar* cvar)
+{
+ MutexAutoLock lock(*mutex);
+
+ delete gSubprocess;
+ gSubprocess = nullptr;
+
+ cvar->Notify();
+}
+
+void DeleteTheWorld()
+{
+ delete static_cast<TestCrashCleanupParent*>(gParentActor);
+ gParentActor = nullptr;
+
+ // needs to be synchronous to avoid affecting event ordering on
+ // the main thread
+ Mutex mutex("TestCrashCleanup.DeleteTheWorld.mutex");
+ CondVar cvar(mutex, "TestCrashCleanup.DeleteTheWorld.cvar");
+
+ MutexAutoLock lock(mutex);
+
+ XRE_GetIOMessageLoop()->PostTask(
+ NewRunnableFunction(DeleteSubprocess, &mutex, &cvar));
+
+ cvar.Wait();
+}
+
+void Done()
+{
+ static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
+ nsCOMPtr<nsIAppShell> appShell (do_GetService(kAppShellCID));
+ appShell->Exit();
+
+ passed(__FILE__);
+}
+
+} // namespace <anon>
+
+TestCrashCleanupParent::TestCrashCleanupParent() : mCleanedUp(false)
+{
+ MOZ_COUNT_CTOR(TestCrashCleanupParent);
+}
+
+TestCrashCleanupParent::~TestCrashCleanupParent()
+{
+ MOZ_COUNT_DTOR(TestCrashCleanupParent);
+
+ if (!mCleanedUp)
+ fail("should have been ActorDestroy()d!");
+}
+
+void
+TestCrashCleanupParent::Main()
+{
+ // NB: has to be enqueued before IO thread's error notification
+ MessageLoop::current()->PostTask(
+ NewRunnableFunction(DeleteTheWorld));
+
+ if (CallDIEDIEDIE())
+ fail("expected an error!");
+
+ Close();
+
+ MessageLoop::current()->PostTask(NewRunnableFunction(Done));
+}
+
+
+//-----------------------------------------------------------------------------
+// child
+
+TestCrashCleanupChild::TestCrashCleanupChild()
+{
+ MOZ_COUNT_CTOR(TestCrashCleanupChild);
+}
+
+TestCrashCleanupChild::~TestCrashCleanupChild()
+{
+ MOZ_COUNT_DTOR(TestCrashCleanupChild);
+}
+
+bool
+TestCrashCleanupChild::AnswerDIEDIEDIE()
+{
+ _exit(0);
+ NS_RUNTIMEABORT("unreached");
+ return false;
+}
+
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestCrashCleanup.h b/ipc/ipdl/test/cxx/TestCrashCleanup.h
new file mode 100644
index 000000000..b64b2c628
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestCrashCleanup.h
@@ -0,0 +1,58 @@
+#ifndef mozilla__ipdltest_TestCrashCleanup_h
+#define mozilla__ipdltest_TestCrashCleanup_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestCrashCleanupParent.h"
+#include "mozilla/_ipdltest/PTestCrashCleanupChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestCrashCleanupParent :
+ public PTestCrashCleanupParent
+{
+public:
+ TestCrashCleanupParent();
+ virtual ~TestCrashCleanupParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+
+protected:
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (AbnormalShutdown != why)
+ fail("unexpected destruction!");
+ mCleanedUp = true;
+ }
+
+ bool mCleanedUp;
+};
+
+
+class TestCrashCleanupChild :
+ public PTestCrashCleanupChild
+{
+public:
+ TestCrashCleanupChild();
+ virtual ~TestCrashCleanupChild();
+
+protected:
+ virtual bool AnswerDIEDIEDIE() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ fail("should have 'crashed'!");
+ }
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestCrashCleanup_h
diff --git a/ipc/ipdl/test/cxx/TestDataStructures.cpp b/ipc/ipdl/test/cxx/TestDataStructures.cpp
new file mode 100644
index 000000000..fe0d5918a
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestDataStructures.cpp
@@ -0,0 +1,984 @@
+#include "TestDataStructures.h"
+
+#include "mozilla/Unused.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+typedef InfallibleTArray<nsIntRegion> RegionArray;
+
+namespace mozilla {
+namespace _ipdltest {
+
+static const uint32_t nactors = 10;
+
+#define test_assert(_cond, _msg) \
+ if (!(_cond)) fail(_msg)
+
+template<typename T>
+static void
+assert_arrays_equal(const InfallibleTArray<T>& a, const InfallibleTArray<T>& b)
+{
+ test_assert(a == b, "arrays equal");
+}
+
+inline static TestDataStructuresSub&
+Cast(PTestDataStructuresSubParent* a)
+{
+ return *static_cast<TestDataStructuresSub*>(a);
+}
+
+inline static TestDataStructuresSub&
+Cast(PTestDataStructuresSubChild* a)
+{
+ return *static_cast<TestDataStructuresSub*>(a);
+}
+
+//-----------------------------------------------------------------------------
+// parent
+
+TestDataStructuresParent::TestDataStructuresParent()
+{
+ MOZ_COUNT_CTOR(TestDataStructuresParent);
+}
+
+TestDataStructuresParent::~TestDataStructuresParent()
+{
+ MOZ_COUNT_DTOR(TestDataStructuresParent);
+}
+
+void
+TestDataStructuresParent::Main()
+{
+ for (uint32_t i = 0; i < nactors; ++i)
+ if (!SendPTestDataStructuresSubConstructor(i))
+ fail("can't alloc actor");
+
+ if (!SendStart())
+ fail("can't send Start()");
+}
+
+bool
+TestDataStructuresParent::DeallocPTestDataStructuresSubParent(PTestDataStructuresSubParent* actor)
+{
+ test_assert(Cast(actor).mI == Cast(mKids[0]).mI,
+ "dtor sent to wrong actor");
+ mKids.RemoveElementAt(0);
+ delete actor;
+ if (mKids.Length() > 0)
+ return true;
+
+ return true;
+}
+
+bool TestDataStructuresParent::RecvTest1(
+ InfallibleTArray<int>&& ia,
+ InfallibleTArray<int>* oa)
+{
+ test_assert(5 == ia.Length(), "wrong length");
+ for (int i = 0; i < 5; ++i)
+ test_assert(i == ia[i], "wrong value");
+
+ *oa = ia;
+
+ return true;
+}
+
+bool TestDataStructuresParent::RecvTest2(
+ InfallibleTArray<PTestDataStructuresSubParent*>&& i1,
+ InfallibleTArray<PTestDataStructuresSubParent*>* o1)
+{
+ test_assert(nactors == i1.Length(), "wrong #actors");
+ for (uint32_t i = 0; i < i1.Length(); ++i)
+ test_assert(i == Cast(i1[i]).mI, "wrong mI value");
+ *o1 = i1;
+ return true;
+}
+
+bool TestDataStructuresParent::RecvTest3(
+ const IntDouble& i1,
+ const IntDouble& i2,
+ IntDouble* o1,
+ IntDouble* o2)
+{
+ test_assert(42 == i1.get_int(), "wrong value");
+ test_assert(4.0 == i2.get_double(), "wrong value");
+
+ *o1 = i1;
+ *o2 = i2;
+
+ return true;
+}
+
+bool TestDataStructuresParent::RecvTest4(
+ InfallibleTArray<IntDouble>&& i1,
+ InfallibleTArray<IntDouble>* o1)
+{
+ test_assert(4 == i1.Length(), "wrong length");
+ test_assert(1 == i1[0].get_int(), "wrong value");
+ test_assert(2.0 == i1[1].get_double(), "wrong value");
+ test_assert(3 == i1[2].get_int(), "wrong value");
+ test_assert(4.0 == i1[3].get_double(), "wrong value");
+
+ *o1 = i1;
+
+ return true;
+}
+
+bool TestDataStructuresParent::RecvTest5(
+ const IntDoubleArrays& i1,
+ const IntDoubleArrays& i2,
+ const IntDoubleArrays& i3,
+ IntDoubleArrays* o1,
+ IntDoubleArrays* o2,
+ IntDoubleArrays* o3)
+{
+ test_assert(42 == i1.get_int(), "wrong value");
+
+ const InfallibleTArray<int>& i2a = i2.get_ArrayOfint();
+ test_assert(3 == i2a.Length(), "wrong length");
+ test_assert(1 == i2a[0], "wrong value");
+ test_assert(2 == i2a[1], "wrong value");
+ test_assert(3 == i2a[2], "wrong value");
+
+ const InfallibleTArray<double>& i3a = i3.get_ArrayOfdouble();
+ test_assert(3 == i3a.Length(), "wrong length");
+ test_assert(1.0 == i3a[0], "wrong value");
+ test_assert(2.0 == i3a[1], "wrong value");
+ test_assert(3.0 == i3a[2], "wrong value");
+
+ *o1 = i1;
+ *o2 = i2a;
+ *o3 = i3a;
+
+ return true;
+}
+
+bool
+TestDataStructuresParent::RecvTest7_0(const ActorWrapper& i1,
+ ActorWrapper* o1)
+{
+ if (i1.actorChild() != nullptr)
+ fail("child side actor should always be null");
+
+ if (i1.actorParent() != mKids[0])
+ fail("should have got back same actor on parent side");
+
+ o1->actorParent() = mKids[0];
+ // malicious behavior
+ o1->actorChild() =
+ reinterpret_cast<PTestDataStructuresSubChild*>(uintptr_t(0xdeadbeef));
+ return true;
+}
+
+bool TestDataStructuresParent::RecvTest6(
+ InfallibleTArray<IntDoubleArrays>&& i1,
+ InfallibleTArray<IntDoubleArrays>* o1)
+{
+ test_assert(3 == i1.Length(), "wrong length");
+
+ IntDoubleArrays id1(i1[0]);
+ test_assert(42 == id1.get_int(), "wrong value");
+
+ InfallibleTArray<int> i2a(i1[1].get_ArrayOfint());
+ test_assert(3 == i2a.Length(), "wrong length");
+ test_assert(1 == i2a[0], "wrong value");
+ test_assert(2 == i2a[1], "wrong value");
+ test_assert(3 == i2a[2], "wrong value");
+
+ InfallibleTArray<double> i3a(i1[2].get_ArrayOfdouble());
+ test_assert(3 == i3a.Length(), "wrong length");
+ test_assert(1.0 == i3a[0], "wrong value");
+ test_assert(2.0 == i3a[1], "wrong value");
+ test_assert(3.0 == i3a[2], "wrong value");
+
+ o1->AppendElement(id1);
+ o1->AppendElement(IntDoubleArrays(i2a));
+ o1->AppendElement(IntDoubleArrays(i3a));
+
+ return true;
+}
+
+bool TestDataStructuresParent::RecvTest7(
+ const Actors& i1,
+ const Actors& i2,
+ const Actors& i3,
+ Actors* o1,
+ Actors* o2,
+ Actors* o3)
+{
+ test_assert(42 == i1.get_int(), "wrong value");
+
+ InfallibleTArray<int> i2a(i2.get_ArrayOfint());
+ test_assert(3 == i2a.Length(), "wrong length");
+ test_assert(1 == i2a[0], "wrong value");
+ test_assert(2 == i2a[1], "wrong value");
+ test_assert(3 == i2a[2], "wrong value");
+
+ assert_arrays_equal(mKids, i3.get_ArrayOfPTestDataStructuresSubParent());
+
+ *o1 = 42;
+ *o2 = i2a;
+ *o3 = mKids;
+
+ return true;
+}
+
+bool TestDataStructuresParent::RecvTest8(
+ InfallibleTArray<Actors>&& i1,
+ InfallibleTArray<Actors>* o1)
+{
+ test_assert(3 == i1.Length(), "wrong length");
+ test_assert(42 == i1[0].get_int(), "wrong value");
+
+ const InfallibleTArray<int>& i2a = i1[1].get_ArrayOfint();
+ test_assert(3 == i2a.Length(), "wrong length");
+ test_assert(1 == i2a[0], "wrong value");
+ test_assert(2 == i2a[1], "wrong value");
+ test_assert(3 == i2a[2], "wrong value");
+
+ assert_arrays_equal(mKids, i1[2].get_ArrayOfPTestDataStructuresSubParent());
+
+ *o1 = i1;
+
+ return true;
+}
+
+bool TestDataStructuresParent::RecvTest9(
+ const Unions& i1,
+ const Unions& i2,
+ const Unions& i3,
+ const Unions& i4,
+ Unions* o1,
+ Unions* o2,
+ Unions* o3,
+ Unions* o4)
+{
+ test_assert(42 == i1.get_int(), "wrong value");
+
+ const InfallibleTArray<int>& i2a = i2.get_ArrayOfint();
+ test_assert(3 == i2a.Length(), "wrong length");
+ test_assert(1 == i2a[0], "wrong value");
+ test_assert(2 == i2a[1], "wrong value");
+ test_assert(3 == i2a[2], "wrong value");
+
+ assert_arrays_equal(mKids, i3.get_ArrayOfPTestDataStructuresSubParent());
+
+ const InfallibleTArray<PTestDataStructuresSubParent*>& i4a =
+ i4.get_ArrayOfActors()[0].get_ArrayOfPTestDataStructuresSubParent();
+ assert_arrays_equal(mKids, i4a);
+
+ *o1 = i1;
+ *o2 = i2;
+ *o3 = i3;
+ *o4 = i4;
+
+ return true;
+}
+
+bool TestDataStructuresParent::RecvTest10(
+ InfallibleTArray<Unions>&& i1,
+ InfallibleTArray<Unions>* o1)
+{
+ test_assert(42 == i1[0].get_int(), "wrong value");
+
+ const InfallibleTArray<int>& i2a = i1[1].get_ArrayOfint();
+ test_assert(3 == i2a.Length(), "wrong length");
+ test_assert(1 == i2a[0], "wrong value");
+ test_assert(2 == i2a[1], "wrong value");
+ test_assert(3 == i2a[2], "wrong value");
+
+ assert_arrays_equal(mKids, i1[2].get_ArrayOfPTestDataStructuresSubParent());
+
+ const InfallibleTArray<PTestDataStructuresSubParent*>& i4a =
+ i1[3].get_ArrayOfActors()[0].get_ArrayOfPTestDataStructuresSubParent();
+ assert_arrays_equal(mKids, i4a);
+
+ *o1 = i1;
+
+ return true;
+}
+
+bool TestDataStructuresParent::RecvTest11(
+ const SIntDouble& i,
+ SIntDouble* o)
+{
+ test_assert(1 == i.i(), "wrong value");
+ test_assert(2.0 == i.d(), "wrong value");
+ *o = i;
+ return true;
+}
+
+bool TestDataStructuresParent::RecvTest12(
+ const SIntDoubleArrays& i,
+ SIntDoubleArrays* o)
+{
+ InfallibleTArray<int> ai;
+ ai.AppendElement(1);
+ ai.AppendElement(2);
+ ai.AppendElement(3);
+
+ InfallibleTArray<double> ad;
+ ad.AppendElement(.5);
+ ad.AppendElement(1.0);
+ ad.AppendElement(2.0);
+
+ test_assert(42 == i.i(), "wrong value");
+ assert_arrays_equal(ai, i.ai());
+ assert_arrays_equal(ad, i.ad());
+
+ *o = i;
+
+ return true;
+}
+
+bool TestDataStructuresParent::RecvTest13(
+ const SActors& i,
+ SActors* o)
+{
+ InfallibleTArray<int> ai;
+ ai.AppendElement(1); ai.AppendElement(2); ai.AppendElement(3);
+
+ test_assert(42 == i.i(), "wrong value");
+ assert_arrays_equal(ai, i.ai());
+ assert_arrays_equal(mKids, i.apParent());
+
+ *o = i;
+
+ return true;
+}
+
+bool TestDataStructuresParent::RecvTest14(
+ const Structs& i,
+ Structs* o)
+{
+ InfallibleTArray<int> ai;
+ ai.AppendElement(1); ai.AppendElement(2); ai.AppendElement(3);
+
+ test_assert(42 == i.i(), "wrong value");
+ assert_arrays_equal(ai, i.ai());
+ assert_arrays_equal(mKids, i.apParent());
+
+ const SActors& ia = i.aa()[0];
+ test_assert(42 == ia.i(), "wrong value");
+ assert_arrays_equal(ai, ia.ai());
+ assert_arrays_equal(mKids, ia.apParent());
+
+ *o = i;
+
+ return true;
+}
+
+bool TestDataStructuresParent::RecvTest15(
+ const WithStructs& i1,
+ const WithStructs& i2,
+ const WithStructs& i3,
+ const WithStructs& i4,
+ const WithStructs& i5,
+ WithStructs* o1,
+ WithStructs* o2,
+ WithStructs* o3,
+ WithStructs* o4,
+ WithStructs* o5)
+{
+ InfallibleTArray<int> ai;
+ ai.AppendElement(1); ai.AppendElement(2); ai.AppendElement(3);
+
+ test_assert(i1 == int(42), "wrong value");
+ assert_arrays_equal(i2.get_ArrayOfint(), ai);
+ assert_arrays_equal(i3.get_ArrayOfPTestDataStructuresSubParent(), mKids);
+
+ const SActors& ia = i4.get_ArrayOfSActors()[0];
+ test_assert(42 == ia.i(), "wrong value");
+ assert_arrays_equal(ai, ia.ai());
+ assert_arrays_equal(mKids, ia.apParent());
+
+ const Structs& is = i5.get_ArrayOfStructs()[0];
+ test_assert(42 == is.i(), "wrong value");
+ assert_arrays_equal(ai, is.ai());
+ assert_arrays_equal(mKids, is.apParent());
+
+ const SActors& isa = is.aa()[0];
+ test_assert(42 == isa.i(), "wrong value");
+ assert_arrays_equal(ai, isa.ai());
+ assert_arrays_equal(mKids, isa.apParent());
+
+ *o1 = i1;
+ *o2 = i2;
+ *o3 = i3;
+ *o4 = i4;
+ *o5 = i5;
+
+ return true;
+}
+
+bool TestDataStructuresParent::RecvTest16(
+ const WithUnions& i,
+ WithUnions* o)
+{
+ test_assert(i.i() == 42, "wrong value");
+
+ InfallibleTArray<int> ai;
+ ai.AppendElement(1); ai.AppendElement(2); ai.AppendElement(3);
+ assert_arrays_equal(ai, i.ai());
+
+ assert_arrays_equal(i.apParent(), mKids);
+
+ assert_arrays_equal(mKids, i.aa()[0].get_ArrayOfPTestDataStructuresSubParent());
+
+ const InfallibleTArray<Unions>& iau = i.au();
+ test_assert(iau[0] == 42, "wrong value");
+ assert_arrays_equal(ai, iau[1].get_ArrayOfint());
+ assert_arrays_equal(mKids, iau[2].get_ArrayOfPTestDataStructuresSubParent());
+ assert_arrays_equal(mKids,
+ iau[3].get_ArrayOfActors()[0]
+ .get_ArrayOfPTestDataStructuresSubParent());
+
+ *o = i;
+
+ return true;
+}
+
+bool TestDataStructuresParent::RecvTest17(InfallibleTArray<Op>&& sa)
+{
+ test_assert(sa.Length() == 1 && Op::TSetAttrs == sa[0].type(),
+ "wrong value");
+ return true;
+}
+
+bool TestDataStructuresParent::RecvTest18(RegionArray&& ra)
+{
+ for (RegionArray::index_type i = 0; i < ra.Length(); ++i) {
+ // if |ra| has been realloc()d and given a different allocator
+ // chunk, this loop will nondeterministically crash or iloop.
+ for (auto iter = ra[i].RectIter(); !iter.Done(); iter.Next()) {
+ Unused << iter.Get();
+ }
+ }
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+TestDataStructuresChild::TestDataStructuresChild()
+{
+ MOZ_COUNT_CTOR(TestDataStructuresChild);
+}
+
+TestDataStructuresChild::~TestDataStructuresChild()
+{
+ MOZ_COUNT_DTOR(TestDataStructuresChild);
+}
+
+bool
+TestDataStructuresChild::RecvStart()
+{
+ puts("[TestDataStructuresChild] starting");
+
+ Test1();
+ Test2();
+ Test3();
+ Test4();
+ Test5();
+ Test6();
+ Test7_0();
+ Test7();
+ Test8();
+ Test9();
+ Test10();
+ Test11();
+ Test12();
+ Test13();
+ Test14();
+ Test15();
+ Test16();
+ Test17();
+ if (OtherPid() != base::GetCurrentProcId()) {
+ //FIXME/bug 703317 allocation of nsIntRegion uses a global
+ //region pool which breaks threads
+ Test18();
+ }
+
+ for (uint32_t i = 0; i < nactors; ++i)
+ if (!PTestDataStructuresSubChild::Send__delete__(mKids[i]))
+ fail("can't send dtor");
+
+ Close();
+
+ return true;
+}
+
+void
+TestDataStructuresChild::Test1()
+{
+ InfallibleTArray<int> ia;
+
+ for (int i = 0; i < 5; ++i)
+ ia.AppendElement(i);
+
+ InfallibleTArray<int> oa;
+ if (!SendTest1(ia, &oa))
+ fail("can't send Test1");
+
+ assert_arrays_equal(ia, oa);
+
+ printf(" passed %s\n", __FUNCTION__);
+}
+
+void
+TestDataStructuresChild::Test2()
+{
+ InfallibleTArray<PTestDataStructuresSubChild*> oa;
+ if (!SendTest2(mKids, &oa))
+ fail("can't send Test2");
+ assert_arrays_equal(mKids, oa);
+
+ printf(" passed %s\n", __FUNCTION__);
+}
+
+void
+TestDataStructuresChild::Test3()
+{
+ int i1i = 42;
+ double i2d = 4.0;
+ IntDouble i1(i1i);
+ IntDouble i2(i2d);
+ IntDouble o1, o2;
+
+ SendTest3(i1, i2, &o1, &o2);
+
+ test_assert(i1i == o1.get_int(), "wrong value");
+ test_assert(i2d == o2.get_double(), "wrong value");
+
+ printf(" passed %s\n", __FUNCTION__);
+}
+
+void
+TestDataStructuresChild::Test4()
+{
+ InfallibleTArray<IntDouble> i1;
+ i1.AppendElement(IntDouble(int(1)));
+ i1.AppendElement(IntDouble(2.0));
+ i1.AppendElement(IntDouble(int(3)));
+ i1.AppendElement(IntDouble(4.0));
+
+ InfallibleTArray<IntDouble> o1;
+ if (!SendTest4(i1, &o1))
+ fail("can't send Test4");
+
+ // TODO Union::operator==()
+ test_assert(i1.Length() == o1.Length(), "wrong length");
+ test_assert(1 == o1[0].get_int(), "wrong value");
+ test_assert(2.0 == o1[1].get_double(), "wrong value");
+ test_assert(3 == o1[2].get_int(), "wrong value");
+ test_assert(4.0 == o1[3].get_double(), "wrong value");
+
+ printf(" passed %s\n", __FUNCTION__);
+}
+
+void
+TestDataStructuresChild::Test5()
+{
+ IntDoubleArrays i1(int(42));
+ InfallibleTArray<int> i2;
+ i2.AppendElement(1); i2.AppendElement(2); i2.AppendElement(3);
+ InfallibleTArray<double> i3;
+ i3.AppendElement(1.0); i3.AppendElement(2.0); i3.AppendElement(3.0);
+
+ IntDoubleArrays o1, o2, o3;
+ if (!SendTest5(i1, IntDoubleArrays(i2), IntDoubleArrays(i3),
+ &o1, &o2, &o3))
+ fail("can't send Test5");
+
+ test_assert(42 == o1.get_int(), "wrong value");
+ assert_arrays_equal(i2, o2.get_ArrayOfint());
+ assert_arrays_equal(i3, o3.get_ArrayOfdouble());
+
+ printf(" passed %s\n", __FUNCTION__);
+}
+
+void
+TestDataStructuresChild::Test6()
+{
+ IntDoubleArrays id1(int(42));
+ InfallibleTArray<int> id2;
+ id2.AppendElement(1); id2.AppendElement(2); id2.AppendElement(3);
+ InfallibleTArray<double> id3;
+ id3.AppendElement(1.0); id3.AppendElement(2.0); id3.AppendElement(3.0);
+
+ InfallibleTArray<IntDoubleArrays> i1;
+ i1.AppendElement(id1);
+ i1.AppendElement(IntDoubleArrays(id2));
+ i1.AppendElement(IntDoubleArrays(id3));
+
+ InfallibleTArray<IntDoubleArrays> o1;
+ if (!SendTest6(i1, &o1))
+ fail("can't send Test6");
+
+ test_assert(3 == o1.Length(), "wrong length");
+ IntDoubleArrays od1(o1[0]);
+ InfallibleTArray<int> od2(o1[1].get_ArrayOfint());
+ InfallibleTArray<double> od3(o1[2].get_ArrayOfdouble());
+
+ test_assert(42 == od1.get_int(), "wrong value");
+ assert_arrays_equal(id2, od2);
+ assert_arrays_equal(id3, od3);
+
+ printf(" passed %s\n", __FUNCTION__);
+}
+
+void
+TestDataStructuresChild::Test7_0()
+{
+ ActorWrapper iaw;
+ if (iaw.actorChild() != nullptr || iaw.actorParent() != nullptr)
+ fail("actor members should be null initially");
+
+ iaw.actorChild() = mKids[0];
+ if (iaw.actorParent() != nullptr)
+ fail("parent should be null on child side after set");
+
+ ActorWrapper oaw;
+ if (!SendTest7_0(iaw, &oaw))
+ fail("sending Test7_0");
+
+ if (oaw.actorParent() != nullptr)
+ fail("parent accessor on actor-struct members should always be null in child");
+
+ if (oaw.actorChild() != mKids[0])
+ fail("should have got back same child-side actor");
+}
+
+void
+TestDataStructuresChild::Test7()
+{
+ Actors i1(42);
+ InfallibleTArray<int> i2a;
+ i2a.AppendElement(1); i2a.AppendElement(2); i2a.AppendElement(3);
+
+ Actors o1, o2, o3;
+ if (!SendTest7(i1, Actors(i2a), Actors(mKids), &o1, &o2, &o3))
+ fail("can't send Test7");
+
+ test_assert(42 == o1.get_int(), "wrong value");
+ assert_arrays_equal(i2a, o2.get_ArrayOfint());
+ assert_arrays_equal(mKids, o3.get_ArrayOfPTestDataStructuresSubChild());
+
+ printf(" passed %s\n", __FUNCTION__);
+}
+
+void
+TestDataStructuresChild::Test8()
+{
+ Actors i1e(42);
+ InfallibleTArray<int> i2a;
+ i2a.AppendElement(1); i2a.AppendElement(2); i2a.AppendElement(3);
+
+ InfallibleTArray<Actors> i1;
+ i1.AppendElement(i1e);
+ i1.AppendElement(i2a);
+ i1.AppendElement(mKids);
+
+ InfallibleTArray<Actors> o1;
+ if (!SendTest8(i1, &o1))
+ fail("can't send Test8");
+
+ test_assert(3 == o1.Length(), "wrong length");
+ test_assert(42 == o1[0].get_int(), "wrong value");
+ assert_arrays_equal(i2a, o1[1].get_ArrayOfint());
+ assert_arrays_equal(mKids, o1[2].get_ArrayOfPTestDataStructuresSubChild());
+
+ printf(" passed %s\n", __FUNCTION__);
+}
+
+void
+TestDataStructuresChild::Test9()
+{
+ Unions i1(int(42));
+
+ InfallibleTArray<int> i2a;
+ i2a.AppendElement(1);
+ i2a.AppendElement(2);
+ i2a.AppendElement(3);
+
+ InfallibleTArray<Actors> i4a;
+ i4a.AppendElement(mKids);
+
+ Unions o1, o2, o3, o4;
+ if (!SendTest9(i1, Unions(i2a), Unions(mKids), Unions(i4a),
+ &o1, &o2, &o3, &o4))
+ fail("can't send Test9");
+
+ test_assert(42 == o1.get_int(), "wrong value");
+ assert_arrays_equal(i2a, o2.get_ArrayOfint());
+ assert_arrays_equal(mKids, o3.get_ArrayOfPTestDataStructuresSubChild());
+ assert_arrays_equal(
+ mKids,
+ o4.get_ArrayOfActors()[0].get_ArrayOfPTestDataStructuresSubChild());
+
+ printf(" passed %s\n", __FUNCTION__);
+}
+
+void
+TestDataStructuresChild::Test10()
+{
+ Unions i1a(int(42));
+
+ InfallibleTArray<int> i2a;
+ i2a.AppendElement(1);
+ i2a.AppendElement(2);
+ i2a.AppendElement(3);
+
+ InfallibleTArray<Actors> i4a;
+ i4a.AppendElement(mKids);
+
+ InfallibleTArray<Unions> i1;
+ i1.AppendElement(i1a);
+ i1.AppendElement(Unions(i2a));
+ i1.AppendElement(Unions(mKids));
+ i1.AppendElement(Unions(i4a));
+
+ InfallibleTArray<Unions> o1;
+ if (!SendTest10(i1, &o1))
+ fail("can't send Test10");
+
+ test_assert(4 == o1.Length(), "wrong length");
+ test_assert(42 == o1[0].get_int(), "wrong value");
+ assert_arrays_equal(i2a, o1[1].get_ArrayOfint());
+ assert_arrays_equal(mKids, o1[2].get_ArrayOfPTestDataStructuresSubChild());
+ assert_arrays_equal(
+ mKids,
+ o1[3].get_ArrayOfActors()[0].get_ArrayOfPTestDataStructuresSubChild());
+
+ printf(" passed %s\n", __FUNCTION__);
+}
+
+void
+TestDataStructuresChild::Test11()
+{
+ SIntDouble i(1, 2.0);
+ SIntDouble o;
+
+ if (!SendTest11(i, &o))
+ fail("sending Test11");
+
+ test_assert(1 == o.i() && 2.0 == o.d(), "wrong values");
+
+ printf(" passed %s\n", __FUNCTION__);
+}
+
+void
+TestDataStructuresChild::Test12()
+{
+ InfallibleTArray<int> ai;
+ ai.AppendElement(1); ai.AppendElement(2); ai.AppendElement(3);
+
+ InfallibleTArray<double> ad;
+ ad.AppendElement(.5); ad.AppendElement(1.0); ad.AppendElement(2.0);
+
+ SIntDoubleArrays i(42, ai, ad);
+ SIntDoubleArrays o;
+
+ if (!SendTest12(i, &o))
+ fail("sending Test12");
+
+ test_assert(42 == o.i(), "wrong value");
+ assert_arrays_equal(ai, o.ai());
+ assert_arrays_equal(ad, o.ad());
+
+ printf(" passed %s\n", __FUNCTION__);
+}
+
+void
+TestDataStructuresChild::Test13()
+{
+ InfallibleTArray<int> ai;
+ ai.AppendElement(1); ai.AppendElement(2); ai.AppendElement(3);
+
+ SActors i;
+ i.i() = 42;
+ i.ai() = ai;
+ i.apChild() = mKids;
+
+ SActors o;
+ if (!SendTest13(i, &o))
+ fail("can't send Test13");
+
+ test_assert(42 == o.i(), "wrong value");
+ assert_arrays_equal(ai, o.ai());
+ assert_arrays_equal(mKids, o.apChild());
+
+ printf(" passed %s\n", __FUNCTION__);
+}
+
+void
+TestDataStructuresChild::Test14()
+{
+ InfallibleTArray<int> ai;
+ ai.AppendElement(1); ai.AppendElement(2); ai.AppendElement(3);
+
+ SActors ia;
+ ia.i() = 42;
+ ia.ai() = ai;
+ ia.apChild() = mKids;
+ InfallibleTArray<SActors> aa; aa.AppendElement(ia);
+
+ Structs i;
+ i.i() = 42;
+ i.ai() = ai;
+ i.apChild() = mKids;
+ i.aa() = aa;
+
+ Structs o;
+ if (!SendTest14(i, &o))
+ fail("can't send Test14");
+
+ test_assert(42 == o.i(), "wrong value");
+ assert_arrays_equal(ai, o.ai());
+ assert_arrays_equal(mKids, o.apChild());
+
+ const SActors& os = o.aa()[0];
+ test_assert(42 == os.i(), "wrong value");
+ assert_arrays_equal(ai, os.ai());
+ assert_arrays_equal(mKids, os.apChild());
+
+ printf(" passed %s\n", __FUNCTION__);
+}
+
+void
+TestDataStructuresChild::Test15()
+{
+ InfallibleTArray<int> ai;
+ ai.AppendElement(1); ai.AppendElement(2); ai.AppendElement(3);
+
+ SActors ia;
+ ia.i() = 42;
+ ia.ai() = ai;
+ ia.apChild() = mKids;
+ InfallibleTArray<SActors> iaa; iaa.AppendElement(ia);
+
+ Structs is;
+ is.i() = 42;
+ is.ai() = ai;
+ is.apChild() = mKids;
+ is.aa() = iaa;
+ InfallibleTArray<Structs> isa; isa.AppendElement(is);
+
+ WithStructs o1, o2, o3, o4, o5;
+ if (!SendTest15(WithStructs(42),
+ WithStructs(ai),
+ WithStructs(mKids),
+ WithStructs(iaa),
+ WithStructs(isa),
+ &o1, &o2, &o3, &o4, &o5))
+ fail("sending Test15");
+
+ test_assert(o1 == int(42), "wrong value");
+ assert_arrays_equal(o2.get_ArrayOfint(), ai);
+ assert_arrays_equal(o3.get_ArrayOfPTestDataStructuresSubChild(), mKids);
+
+ const SActors& oa = o4.get_ArrayOfSActors()[0];
+ test_assert(42 == oa.i(), "wrong value");
+ assert_arrays_equal(ai, oa.ai());
+ assert_arrays_equal(mKids, oa.apChild());
+
+ const Structs& os = o5.get_ArrayOfStructs()[0];
+ test_assert(42 == os.i(), "wrong value");
+ assert_arrays_equal(ai, os.ai());
+ assert_arrays_equal(mKids, os.apChild());
+
+ const SActors& osa = os.aa()[0];
+ test_assert(42 == osa.i(), "wrong value");
+ assert_arrays_equal(ai, osa.ai());
+ assert_arrays_equal(mKids, osa.apChild());
+
+ printf(" passed %s\n", __FUNCTION__);
+}
+
+void
+TestDataStructuresChild::Test16()
+{
+ WithUnions i;
+
+ i.i() = 42;
+
+ InfallibleTArray<int> ai;
+ ai.AppendElement(1); ai.AppendElement(2); ai.AppendElement(3);
+ i.ai() = ai;
+
+ i.apChild() = mKids;
+
+ InfallibleTArray<Actors> iaa;
+ iaa.AppendElement(mKids);
+ i.aa() = iaa;
+
+ InfallibleTArray<Unions> iau;
+ iau.AppendElement(int(42));
+ iau.AppendElement(ai);
+ iau.AppendElement(mKids);
+ iau.AppendElement(iaa);
+ i.au() = iau;
+
+ WithUnions o;
+ if (!SendTest16(i, &o))
+ fail("sending Test16");
+
+ test_assert(42 == o.i(), "wrong value");
+ assert_arrays_equal(o.ai(), ai);
+ assert_arrays_equal(o.apChild(), mKids);
+
+ const Actors& oaa = o.aa()[0];
+ assert_arrays_equal(oaa.get_ArrayOfPTestDataStructuresSubChild(), mKids);
+
+ const InfallibleTArray<Unions>& oau = o.au();
+ test_assert(oau[0] == 42, "wrong value");
+ assert_arrays_equal(oau[1].get_ArrayOfint(), ai);
+ assert_arrays_equal(oau[2].get_ArrayOfPTestDataStructuresSubChild(),
+ mKids);
+ assert_arrays_equal(oau[3].get_ArrayOfActors()[0]
+ .get_ArrayOfPTestDataStructuresSubChild(),
+ mKids);
+
+ printf(" passed %s\n", __FUNCTION__);
+}
+
+void
+TestDataStructuresChild::Test17()
+{
+ Attrs attrs;
+ attrs.common() = CommonAttrs(true);
+ attrs.specific() = BarAttrs(1.0f);
+
+ InfallibleTArray<Op> ops;
+ ops.AppendElement(SetAttrs(nullptr, mKids[0], attrs));
+
+ if (!SendTest17(ops))
+ fail("sending Test17");
+
+ printf(" passed %s\n", __FUNCTION__);
+}
+
+void
+TestDataStructuresChild::Test18()
+{
+ const int nelements = 1000;
+ RegionArray ra;
+ // big enough to hopefully force a realloc to a different chunk of
+ // memory on the receiving side, if the workaround isn't working
+ // correctly. But SetCapacity() here because we don't want to
+ // crash on the sending side.
+ ra.SetCapacity(nelements);
+ for (int i = 0; i < nelements; ++i) {
+ nsIntRegion r;
+ r.Or(nsIntRect(0, 0, 10, 10), nsIntRect(10, 10, 10, 10));
+ ra.AppendElement(r);
+ }
+
+ if (!SendTest18(ra))
+ fail("sending Test18");
+
+ printf(" passed %s\n", __FUNCTION__);
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestDataStructures.h b/ipc/ipdl/test/cxx/TestDataStructures.h
new file mode 100644
index 000000000..f77cfa100
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestDataStructures.h
@@ -0,0 +1,234 @@
+#ifndef mozilla__ipdltest_TestDataStructures_h
+#define mozilla__ipdltest_TestDataStructures_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestDataStructuresParent.h"
+#include "mozilla/_ipdltest/PTestDataStructuresChild.h"
+
+#include "mozilla/_ipdltest/PTestDataStructuresSubParent.h"
+#include "mozilla/_ipdltest/PTestDataStructuresSubChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// Subprotocol actors
+
+class TestDataStructuresSub :
+ public PTestDataStructuresSubParent,
+ public PTestDataStructuresSubChild
+{
+public:
+ explicit TestDataStructuresSub(uint32_t i) : mI(i)
+ { }
+ virtual ~TestDataStructuresSub()
+ { }
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (Deletion != why)
+ fail("unexpected destruction!");
+ }
+ uint32_t mI;
+};
+
+//-----------------------------------------------------------------------------
+// Main actors
+
+class TestDataStructuresParent :
+ public PTestDataStructuresParent
+{
+public:
+ TestDataStructuresParent();
+ virtual ~TestDataStructuresParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+protected:
+ virtual PTestDataStructuresSubParent* AllocPTestDataStructuresSubParent(const int& i) override
+ {
+ PTestDataStructuresSubParent* actor = new TestDataStructuresSub(i);
+ mKids.AppendElement(actor);
+ return actor;
+ }
+
+ virtual bool DeallocPTestDataStructuresSubParent(PTestDataStructuresSubParent* actor) override;
+
+ virtual bool RecvTest1(
+ InfallibleTArray<int>&& i1,
+ InfallibleTArray<int>* o1) override;
+
+ virtual bool RecvTest2(
+ InfallibleTArray<PTestDataStructuresSubParent*>&& i1,
+ InfallibleTArray<PTestDataStructuresSubParent*>* o1) override;
+
+ virtual bool RecvTest3(
+ const IntDouble& i1,
+ const IntDouble& i2,
+ IntDouble* o1,
+ IntDouble* o2) override;
+
+ virtual bool RecvTest4(
+ InfallibleTArray<IntDouble>&& i1,
+ InfallibleTArray<IntDouble>* o1) override;
+
+ virtual bool RecvTest5(
+ const IntDoubleArrays& i1,
+ const IntDoubleArrays& i2,
+ const IntDoubleArrays& i3,
+ IntDoubleArrays* o1,
+ IntDoubleArrays* o2,
+ IntDoubleArrays* o3) override;
+
+ virtual bool RecvTest6(
+ InfallibleTArray<IntDoubleArrays>&& i1,
+ InfallibleTArray<IntDoubleArrays>* o1) override;
+
+
+ virtual bool RecvTest7_0(const ActorWrapper& i1,
+ ActorWrapper* o1) override;
+
+ virtual bool RecvTest7(
+ const Actors& i1,
+ const Actors& i2,
+ const Actors& i3,
+ Actors* o1,
+ Actors* o2,
+ Actors* o3) override;
+
+ virtual bool RecvTest8(
+ InfallibleTArray<Actors>&& i1,
+ InfallibleTArray<Actors>* o1) override;
+
+ virtual bool RecvTest9(
+ const Unions& i1,
+ const Unions& i2,
+ const Unions& i3,
+ const Unions& i4,
+ Unions* o1,
+ Unions* o2,
+ Unions* o3,
+ Unions* o4) override;
+
+ virtual bool RecvTest10(
+ InfallibleTArray<Unions>&& i1,
+ InfallibleTArray<Unions>* o1) override;
+
+ virtual bool RecvTest11(
+ const SIntDouble& i,
+ SIntDouble* o) override;
+
+ virtual bool RecvTest12(
+ const SIntDoubleArrays& i,
+ SIntDoubleArrays* o) override;
+
+ virtual bool RecvTest13(
+ const SActors& i,
+ SActors* o) override;
+
+ virtual bool RecvTest14(
+ const Structs& i,
+ Structs* o) override;
+
+ virtual bool RecvTest15(
+ const WithStructs& i1,
+ const WithStructs& i2,
+ const WithStructs& i3,
+ const WithStructs& i4,
+ const WithStructs& i5,
+ WithStructs* o1,
+ WithStructs* o2,
+ WithStructs* o3,
+ WithStructs* o4,
+ WithStructs* o5) override;
+
+ virtual bool RecvTest16(
+ const WithUnions& i,
+ WithUnions* o) override;
+
+ virtual bool RecvTest17(InfallibleTArray<Op>&& sa) override;
+
+ virtual bool RecvTest18(InfallibleTArray<nsIntRegion>&& ra) override;
+
+ virtual bool RecvDummy(const ShmemUnion& su, ShmemUnion* rsu) override
+ {
+ *rsu = su;
+ return true;
+ }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+
+private:
+ InfallibleTArray<PTestDataStructuresSubParent*> mKids;
+};
+
+
+class TestDataStructuresChild :
+ public PTestDataStructuresChild
+{
+public:
+ TestDataStructuresChild();
+ virtual ~TestDataStructuresChild();
+
+protected:
+ virtual PTestDataStructuresSubChild* AllocPTestDataStructuresSubChild(const int& i) override
+ {
+ PTestDataStructuresSubChild* actor = new TestDataStructuresSub(i);
+ mKids.AppendElement(actor);
+ return actor;
+ }
+
+ virtual bool DeallocPTestDataStructuresSubChild(PTestDataStructuresSubChild* actor) override
+ {
+ delete actor;
+ return true;
+ }
+
+ virtual bool RecvStart() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+
+private:
+ void Test1();
+ void Test2();
+ void Test3();
+ void Test4();
+ void Test5();
+ void Test6();
+ void Test7_0();
+ void Test7();
+ void Test8();
+ void Test9();
+ void Test10();
+ void Test11();
+ void Test12();
+ void Test13();
+ void Test14();
+ void Test15();
+ void Test16();
+ void Test17();
+ void Test18();
+
+ InfallibleTArray<PTestDataStructuresSubChild*> mKids;
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestDataStructures_h
diff --git a/ipc/ipdl/test/cxx/TestDemon.cpp b/ipc/ipdl/test/cxx/TestDemon.cpp
new file mode 100644
index 000000000..c349aafbe
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestDemon.cpp
@@ -0,0 +1,417 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=4 ts=4 et :
+ */
+#include "TestDemon.h"
+
+#include <stdlib.h>
+
+#include "IPDLUnitTests.h" // fail etc.
+#if defined(OS_POSIX)
+#include <sys/time.h>
+#include <unistd.h>
+#else
+#include <time.h>
+#include <windows.h>
+#endif
+
+namespace mozilla {
+namespace _ipdltest {
+
+const int kMaxStackHeight = 4;
+
+static LazyLogModule sLogModule("demon");
+
+#define DEMON_LOG(...) MOZ_LOG(sLogModule, LogLevel::Debug, (__VA_ARGS__))
+
+static int gStackHeight = 0;
+static bool gFlushStack = false;
+
+static int
+Choose(int count)
+{
+#if defined(OS_POSIX)
+ return random() % count;
+#else
+ return rand() % count;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// parent
+
+TestDemonParent::TestDemonParent()
+ : mDone(false),
+ mIncoming(),
+ mOutgoing()
+{
+ MOZ_COUNT_CTOR(TestDemonParent);
+}
+
+TestDemonParent::~TestDemonParent()
+{
+ MOZ_COUNT_DTOR(TestDemonParent);
+}
+
+void
+TestDemonParent::Main()
+{
+ if (!getenv("MOZ_TEST_IPC_DEMON")) {
+ QuitParent();
+ return;
+ }
+#if defined(OS_POSIX)
+ srandom(time(nullptr));
+#else
+ srand(time(nullptr));
+#endif
+
+ DEMON_LOG("Start demon");
+
+ if (!SendStart())
+ fail("sending Start");
+
+ RunUnlimitedSequence();
+}
+
+#ifdef DEBUG
+bool
+TestDemonParent::ShouldContinueFromReplyTimeout()
+{
+ return Choose(2) == 0;
+}
+
+bool
+TestDemonParent::ArtificialTimeout()
+{
+ return Choose(5) == 0;
+}
+
+void
+TestDemonParent::ArtificialSleep()
+{
+ if (Choose(2) == 0) {
+ // Sleep for anywhere from 0 to 100 milliseconds.
+ unsigned micros = Choose(100) * 1000;
+#ifdef OS_POSIX
+ usleep(micros);
+#else
+ Sleep(micros / 1000);
+#endif
+ }
+}
+#endif
+
+bool
+TestDemonParent::RecvAsyncMessage(const int& n)
+{
+ DEMON_LOG("Start RecvAsync [%d]", n);
+
+ MOZ_ASSERT(n == mIncoming[0]);
+ mIncoming[0]++;
+
+ RunLimitedSequence();
+
+ DEMON_LOG("End RecvAsync [%d]", n);
+ return true;
+}
+
+bool
+TestDemonParent::RecvHiPrioSyncMessage()
+{
+ DEMON_LOG("Start RecvHiPrioSyncMessage");
+ RunLimitedSequence();
+ DEMON_LOG("End RecvHiPrioSyncMessage");
+ return true;
+}
+
+bool
+TestDemonParent::RecvSyncMessage(const int& n)
+{
+ DEMON_LOG("Start RecvSync [%d]", n);
+
+ MOZ_ASSERT(n == mIncoming[0]);
+ mIncoming[0]++;
+
+ RunLimitedSequence(ASYNC_ONLY);
+
+ DEMON_LOG("End RecvSync [%d]", n);
+ return true;
+}
+
+bool
+TestDemonParent::RecvUrgentAsyncMessage(const int& n)
+{
+ DEMON_LOG("Start RecvUrgentAsyncMessage [%d]", n);
+
+ MOZ_ASSERT(n == mIncoming[2]);
+ mIncoming[2]++;
+
+ RunLimitedSequence(ASYNC_ONLY);
+
+ DEMON_LOG("End RecvUrgentAsyncMessage [%d]", n);
+ return true;
+}
+
+bool
+TestDemonParent::RecvUrgentSyncMessage(const int& n)
+{
+ DEMON_LOG("Start RecvUrgentSyncMessage [%d]", n);
+
+ MOZ_ASSERT(n == mIncoming[2]);
+ mIncoming[2]++;
+
+ RunLimitedSequence(ASYNC_ONLY);
+
+ DEMON_LOG("End RecvUrgentSyncMessage [%d]", n);
+ return true;
+}
+
+void
+TestDemonParent::RunUnlimitedSequence()
+{
+ if (mDone) {
+ return;
+ }
+
+ gFlushStack = false;
+ DoAction();
+
+ MessageLoop::current()->PostTask(NewNonOwningRunnableMethod(this, &TestDemonParent::RunUnlimitedSequence));
+}
+
+void
+TestDemonParent::RunLimitedSequence(int flags)
+{
+ if (gStackHeight >= kMaxStackHeight) {
+ return;
+ }
+ gStackHeight++;
+
+ int count = Choose(20);
+ for (int i = 0; i < count; i++) {
+ if (!DoAction(flags)) {
+ gFlushStack = true;
+ }
+ if (gFlushStack) {
+ gStackHeight--;
+ return;
+ }
+ }
+
+ gStackHeight--;
+}
+
+static bool
+AllowAsync(int outgoing, int incoming)
+{
+ return incoming >= outgoing - 5;
+}
+
+bool
+TestDemonParent::DoAction(int flags)
+{
+ if (flags & ASYNC_ONLY) {
+ if (AllowAsync(mOutgoing[0], mIncoming[0])) {
+ DEMON_LOG("SendAsyncMessage [%d]", mOutgoing[0]);
+ return SendAsyncMessage(mOutgoing[0]++);
+ } else {
+ return true;
+ }
+ } else {
+ switch (Choose(3)) {
+ case 0:
+ if (AllowAsync(mOutgoing[0], mIncoming[0])) {
+ DEMON_LOG("SendAsyncMessage [%d]", mOutgoing[0]);
+ return SendAsyncMessage(mOutgoing[0]++);
+ } else {
+ return true;
+ }
+
+ case 1: {
+ DEMON_LOG("Start SendHiPrioSyncMessage");
+ bool r = SendHiPrioSyncMessage();
+ DEMON_LOG("End SendHiPrioSyncMessage result=%d", r);
+ return r;
+ }
+
+ case 2:
+ DEMON_LOG("Cancel");
+ GetIPCChannel()->CancelCurrentTransaction();
+ return true;
+ }
+ }
+ MOZ_CRASH();
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+
+TestDemonChild::TestDemonChild()
+ : mIncoming(),
+ mOutgoing()
+{
+ MOZ_COUNT_CTOR(TestDemonChild);
+}
+
+TestDemonChild::~TestDemonChild()
+{
+ MOZ_COUNT_DTOR(TestDemonChild);
+}
+
+bool
+TestDemonChild::RecvStart()
+{
+#ifdef OS_POSIX
+ srandom(time(nullptr));
+#else
+ srand(time(nullptr));
+#endif
+
+ DEMON_LOG("RecvStart");
+
+ RunUnlimitedSequence();
+ return true;
+}
+
+#ifdef DEBUG
+void
+TestDemonChild::ArtificialSleep()
+{
+ if (Choose(2) == 0) {
+ // Sleep for anywhere from 0 to 100 milliseconds.
+ unsigned micros = Choose(100) * 1000;
+#ifdef OS_POSIX
+ usleep(micros);
+#else
+ Sleep(micros / 1000);
+#endif
+ }
+}
+#endif
+
+bool
+TestDemonChild::RecvAsyncMessage(const int& n)
+{
+ DEMON_LOG("Start RecvAsyncMessage [%d]", n);
+
+ MOZ_ASSERT(n == mIncoming[0]);
+ mIncoming[0]++;
+
+ RunLimitedSequence();
+
+ DEMON_LOG("End RecvAsyncMessage [%d]", n);
+ return true;
+}
+
+bool
+TestDemonChild::RecvHiPrioSyncMessage()
+{
+ DEMON_LOG("Start RecvHiPrioSyncMessage");
+ RunLimitedSequence();
+ DEMON_LOG("End RecvHiPrioSyncMessage");
+ return true;
+}
+
+void
+TestDemonChild::RunUnlimitedSequence()
+{
+ gFlushStack = false;
+ DoAction();
+
+ MessageLoop::current()->PostTask(NewNonOwningRunnableMethod(this, &TestDemonChild::RunUnlimitedSequence));
+}
+
+void
+TestDemonChild::RunLimitedSequence()
+{
+ if (gStackHeight >= kMaxStackHeight) {
+ return;
+ }
+ gStackHeight++;
+
+ int count = Choose(20);
+ for (int i = 0; i < count; i++) {
+ if (!DoAction()) {
+ gFlushStack = true;
+ }
+ if (gFlushStack) {
+ gStackHeight--;
+ return;
+ }
+ }
+
+ gStackHeight--;
+}
+
+bool
+TestDemonChild::DoAction()
+{
+ switch (Choose(6)) {
+ case 0:
+ if (AllowAsync(mOutgoing[0], mIncoming[0])) {
+ DEMON_LOG("SendAsyncMessage [%d]", mOutgoing[0]);
+ return SendAsyncMessage(mOutgoing[0]++);
+ } else {
+ return true;
+ }
+
+ case 1: {
+ DEMON_LOG("Start SendHiPrioSyncMessage");
+ bool r = SendHiPrioSyncMessage();
+ DEMON_LOG("End SendHiPrioSyncMessage result=%d", r);
+ return r;
+ }
+
+ case 2: {
+ DEMON_LOG("Start SendSyncMessage [%d]", mOutgoing[0]);
+ bool r = SendSyncMessage(mOutgoing[0]++);
+ switch (GetIPCChannel()->LastSendError()) {
+ case SyncSendError::PreviousTimeout:
+ case SyncSendError::SendingCPOWWhileDispatchingSync:
+ case SyncSendError::SendingCPOWWhileDispatchingUrgent:
+ case SyncSendError::NotConnectedBeforeSend:
+ case SyncSendError::CancelledBeforeSend:
+ mOutgoing[0]--;
+ break;
+ default:
+ break;
+ }
+ DEMON_LOG("End SendSyncMessage result=%d", r);
+ return r;
+ }
+
+ case 3:
+ DEMON_LOG("SendUrgentAsyncMessage [%d]", mOutgoing[2]);
+ return SendUrgentAsyncMessage(mOutgoing[2]++);
+
+ case 4: {
+ DEMON_LOG("Start SendUrgentSyncMessage [%d]", mOutgoing[2]);
+ bool r = SendUrgentSyncMessage(mOutgoing[2]++);
+ switch (GetIPCChannel()->LastSendError()) {
+ case SyncSendError::PreviousTimeout:
+ case SyncSendError::SendingCPOWWhileDispatchingSync:
+ case SyncSendError::SendingCPOWWhileDispatchingUrgent:
+ case SyncSendError::NotConnectedBeforeSend:
+ case SyncSendError::CancelledBeforeSend:
+ mOutgoing[2]--;
+ break;
+ default:
+ break;
+ }
+ DEMON_LOG("End SendUrgentSyncMessage result=%d", r);
+ return r;
+ }
+
+ case 5:
+ DEMON_LOG("Cancel");
+ GetIPCChannel()->CancelCurrentTransaction();
+ return true;
+ }
+ MOZ_CRASH();
+ return false;
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestDemon.h b/ipc/ipdl/test/cxx/TestDemon.h
new file mode 100644
index 000000000..d481039a6
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestDemon.h
@@ -0,0 +1,106 @@
+#ifndef mozilla__ipdltest_TestDemon_h
+#define mozilla__ipdltest_TestDemon_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestDemonParent.h"
+#include "mozilla/_ipdltest/PTestDemonChild.h"
+
+using namespace mozilla::ipc;
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestDemonParent :
+ public PTestDemonParent
+{
+public:
+ TestDemonParent();
+ virtual ~TestDemonParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+#ifdef DEBUG
+ bool ShouldContinueFromReplyTimeout() override;
+ bool ArtificialTimeout() override;
+
+ bool NeedArtificialSleep() override { return true; }
+ void ArtificialSleep() override;
+#endif
+
+ bool RecvAsyncMessage(const int& n) override;
+ bool RecvHiPrioSyncMessage() override;
+
+ bool RecvSyncMessage(const int& n) override;
+ bool RecvUrgentAsyncMessage(const int& n) override;
+ bool RecvUrgentSyncMessage(const int& n) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ mDone = true;
+ printf("Parent ActorDestroy\n");
+ passed("ok");
+ QuitParent();
+ }
+
+private:
+ bool mDone;
+ int mIncoming[3];
+ int mOutgoing[3];
+
+ enum {
+ ASYNC_ONLY = 1,
+ };
+
+ void RunUnlimitedSequence();
+ void RunLimitedSequence(int flags = 0);
+ bool DoAction(int flags = 0);
+};
+
+
+class TestDemonChild :
+ public PTestDemonChild
+{
+public:
+ TestDemonChild();
+ virtual ~TestDemonChild();
+
+ bool RecvStart() override;
+
+#ifdef DEBUG
+ bool NeedArtificialSleep() override { return true; }
+ void ArtificialSleep() override;
+#endif
+
+ bool RecvAsyncMessage(const int& n) override;
+ bool RecvHiPrioSyncMessage() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ _exit(0);
+ }
+
+ virtual void IntentionalCrash() override
+ {
+ _exit(0);
+ }
+
+private:
+ int mIncoming[3];
+ int mOutgoing[3];
+
+ void RunUnlimitedSequence();
+ void RunLimitedSequence();
+ bool DoAction();
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestDemon_h
diff --git a/ipc/ipdl/test/cxx/TestDesc.cpp b/ipc/ipdl/test/cxx/TestDesc.cpp
new file mode 100644
index 000000000..696e5a4c5
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestDesc.cpp
@@ -0,0 +1,105 @@
+#include "TestDesc.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+void
+TestDescParent::Main()
+{
+ PTestDescSubParent* p = CallPTestDescSubConstructor(0);
+ if (!p)
+ fail("can't allocate Sub");
+
+ PTestDescSubsubParent* pp = p->CallPTestDescSubsubConstructor();
+ if (!pp)
+ fail("can't allocate Subsub");
+
+ if (!SendTest(pp))
+ fail("can't send Subsub");
+}
+
+bool
+TestDescParent::RecvOk(PTestDescSubsubParent* a)
+{
+ if (!a)
+ fail("didn't receive Subsub");
+
+ if (!PTestDescSubsubParent::Call__delete__(a))
+ fail("deleting Subsub");
+
+ Close();
+
+ return true;
+}
+
+
+PTestDescSubParent*
+TestDescParent::AllocPTestDescSubParent(PTestDescSubsubParent* dummy) {
+ if (dummy)
+ fail("actor supposed to be null");
+ return new TestDescSubParent();
+}
+bool
+TestDescParent::DeallocPTestDescSubParent(PTestDescSubParent* actor)
+{
+ delete actor;
+ return true;
+}
+
+PTestDescSubsubParent*
+TestDescSubParent::AllocPTestDescSubsubParent()
+{
+ return new TestDescSubsubParent();
+}
+bool
+TestDescSubParent::DeallocPTestDescSubsubParent(PTestDescSubsubParent* actor)
+{
+ delete actor;
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// child
+
+bool
+TestDescChild::RecvTest(PTestDescSubsubChild* a)
+{
+ if (!a)
+ fail("didn't receive Subsub");
+ if (!SendOk(a))
+ fail("couldn't send Ok()");
+ return true;
+}
+
+PTestDescSubChild*
+TestDescChild::AllocPTestDescSubChild(PTestDescSubsubChild* dummy) {
+ if (dummy)
+ fail("actor supposed to be null");
+ return new TestDescSubChild();
+}
+bool
+TestDescChild::DeallocPTestDescSubChild(PTestDescSubChild* actor)
+{
+ delete actor;
+ return true;
+}
+
+PTestDescSubsubChild*
+TestDescSubChild::AllocPTestDescSubsubChild()
+{
+ return new TestDescSubsubChild();
+}
+bool
+TestDescSubChild::DeallocPTestDescSubsubChild(PTestDescSubsubChild* actor)
+{
+ delete actor;
+ return true;
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestDesc.h b/ipc/ipdl/test/cxx/TestDesc.h
new file mode 100644
index 000000000..876de7201
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestDesc.h
@@ -0,0 +1,128 @@
+#ifndef mozilla_ipdltest_TestDesc_h
+#define mozilla_ipdltest_TestDesc_h
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestDescParent.h"
+#include "mozilla/_ipdltest/PTestDescChild.h"
+
+#include "mozilla/_ipdltest/PTestDescSubParent.h"
+#include "mozilla/_ipdltest/PTestDescSubChild.h"
+
+#include "mozilla/_ipdltest/PTestDescSubsubParent.h"
+#include "mozilla/_ipdltest/PTestDescSubsubChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// Top-level
+//
+class TestDescParent :
+ public PTestDescParent
+{
+public:
+ TestDescParent() { }
+ virtual ~TestDescParent() { }
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+ virtual bool RecvOk(PTestDescSubsubParent* a) override;
+
+protected:
+ virtual PTestDescSubParent* AllocPTestDescSubParent(PTestDescSubsubParent*) override;
+ virtual bool DeallocPTestDescSubParent(PTestDescSubParent* actor) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+};
+
+
+class TestDescChild :
+ public PTestDescChild
+{
+public:
+ TestDescChild() { }
+ virtual ~TestDescChild() { }
+
+protected:
+ virtual PTestDescSubChild* AllocPTestDescSubChild(PTestDescSubsubChild*) override;
+
+ virtual bool DeallocPTestDescSubChild(PTestDescSubChild* actor) override;
+
+ virtual bool RecvTest(PTestDescSubsubChild* a) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+};
+
+
+//-----------------------------------------------------------------------------
+// First descendent
+//
+class TestDescSubParent :
+ public PTestDescSubParent
+{
+public:
+ TestDescSubParent() { }
+ virtual ~TestDescSubParent() { }
+
+protected:
+ virtual void ActorDestroy(ActorDestroyReason why) override {}
+ virtual PTestDescSubsubParent* AllocPTestDescSubsubParent() override;
+ virtual bool DeallocPTestDescSubsubParent(PTestDescSubsubParent* actor) override;
+};
+
+
+class TestDescSubChild :
+ public PTestDescSubChild
+{
+public:
+ TestDescSubChild() { }
+ virtual ~TestDescSubChild() { }
+
+protected:
+ virtual PTestDescSubsubChild* AllocPTestDescSubsubChild() override;
+ virtual bool DeallocPTestDescSubsubChild(PTestDescSubsubChild* actor) override;
+};
+
+
+//-----------------------------------------------------------------------------
+// Grand-descendent
+//
+class TestDescSubsubParent :
+ public PTestDescSubsubParent
+{
+public:
+ TestDescSubsubParent() { }
+ virtual ~TestDescSubsubParent() { }
+
+protected:
+ virtual void ActorDestroy(ActorDestroyReason why) override {}
+};
+
+class TestDescSubsubChild :
+ public PTestDescSubsubChild
+{
+public:
+ TestDescSubsubChild() { }
+ virtual ~TestDescSubsubChild() { }
+};
+
+
+}
+}
+
+#endif // ifndef mozilla_ipdltest_TestDesc_h
diff --git a/ipc/ipdl/test/cxx/TestEndpointBridgeMain.cpp b/ipc/ipdl/test/cxx/TestEndpointBridgeMain.cpp
new file mode 100644
index 000000000..1b646f425
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestEndpointBridgeMain.cpp
@@ -0,0 +1,257 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+
+#include "TestEndpointBridgeMain.h"
+
+#include "base/task.h"
+#include "IPDLUnitTests.h" // fail etc.
+#include "IPDLUnitTestSubprocess.h"
+
+using namespace std;
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+//-----------------------------------------------------------------------------
+// main process
+void
+TestEndpointBridgeMainParent::Main()
+{
+ if (!SendStart()) {
+ fail("sending Start");
+ }
+}
+
+bool
+TestEndpointBridgeMainParent::RecvBridged(Endpoint<PTestEndpointBridgeMainSubParent>&& endpoint)
+{
+ TestEndpointBridgeMainSubParent* a = new TestEndpointBridgeMainSubParent();
+ if (!endpoint.Bind(a)) {
+ fail("Bind failed");
+ }
+ return true;
+}
+
+void
+TestEndpointBridgeMainParent::ActorDestroy(ActorDestroyReason why)
+{
+ if (NormalShutdown != why) {
+ fail("unexpected destruction!");
+ }
+ passed("ok");
+ QuitParent();
+}
+
+bool
+TestEndpointBridgeMainSubParent::RecvHello()
+{
+ return SendHi();
+}
+
+bool
+TestEndpointBridgeMainSubParent::RecvHelloSync()
+{
+ return true;
+}
+
+bool
+TestEndpointBridgeMainSubParent::AnswerHelloRpc()
+{
+ return CallHiRpc();
+}
+
+void
+TestEndpointBridgeMainSubParent::ActorDestroy(ActorDestroyReason why)
+{
+ if (NormalShutdown != why) {
+ fail("unexpected destruction!");
+ }
+
+ // ActorDestroy() is just a callback from IPDL-generated code,
+ // which needs the top-level actor (this) to stay alive a little
+ // longer so other things can be cleaned up.
+ MessageLoop::current()->PostTask(
+ do_AddRef(new DeleteTask<TestEndpointBridgeMainSubParent>(this)));
+}
+
+//-----------------------------------------------------------------------------
+// sub process --- child of main
+TestEndpointBridgeMainChild* gEndpointBridgeMainChild;
+
+TestEndpointBridgeMainChild::TestEndpointBridgeMainChild()
+ : mSubprocess(nullptr)
+{
+ gEndpointBridgeMainChild = this;
+}
+
+bool
+TestEndpointBridgeMainChild::RecvStart()
+{
+ vector<string> subsubArgs;
+ subsubArgs.push_back("TestEndpointBridgeSub");
+
+ mSubprocess = new IPDLUnitTestSubprocess();
+ if (!mSubprocess->SyncLaunch(subsubArgs)) {
+ fail("problem launching subprocess");
+ }
+
+ IPC::Channel* transport = mSubprocess->GetChannel();
+ if (!transport) {
+ fail("no transport");
+ }
+
+ TestEndpointBridgeSubParent* bsp = new TestEndpointBridgeSubParent();
+ bsp->Open(transport, base::GetProcId(mSubprocess->GetChildProcessHandle()));
+
+ bsp->Main();
+ return true;
+}
+
+void
+TestEndpointBridgeMainChild::ActorDestroy(ActorDestroyReason why)
+{
+ if (NormalShutdown != why) {
+ fail("unexpected destruction!");
+ }
+ // NB: this is kosher because QuitChild() joins with the IO thread
+ XRE_GetIOMessageLoop()->PostTask(
+ do_AddRef(new DeleteTask<IPDLUnitTestSubprocess>(mSubprocess)));
+ QuitChild();
+}
+
+void
+TestEndpointBridgeSubParent::Main()
+{
+ if (!SendPing()) {
+ fail("sending Ping");
+ }
+}
+
+bool
+TestEndpointBridgeSubParent::RecvBridgeEm()
+{
+ Endpoint<PTestEndpointBridgeMainSubParent> parent;
+ Endpoint<PTestEndpointBridgeMainSubChild> child;
+ nsresult rv;
+ rv = PTestEndpointBridgeMainSub::CreateEndpoints(
+ gEndpointBridgeMainChild->OtherPid(), OtherPid(),
+ &parent, &child);
+ if (NS_FAILED(rv)) {
+ fail("opening PTestEndpointOpensOpened");
+ }
+
+ if (!gEndpointBridgeMainChild->SendBridged(mozilla::Move(parent))) {
+ fail("SendBridge failed for parent");
+ }
+ if (!SendBridged(mozilla::Move(child))) {
+ fail("SendBridge failed for child");
+ }
+
+ return true;
+}
+
+void
+TestEndpointBridgeSubParent::ActorDestroy(ActorDestroyReason why)
+{
+ if (NormalShutdown != why) {
+ fail("unexpected destruction!");
+ }
+ gEndpointBridgeMainChild->Close();
+
+ // ActorDestroy() is just a callback from IPDL-generated code,
+ // which needs the top-level actor (this) to stay alive a little
+ // longer so other things can be cleaned up.
+ MessageLoop::current()->PostTask(
+ do_AddRef(new DeleteTask<TestEndpointBridgeSubParent>(this)));
+}
+
+//-----------------------------------------------------------------------------
+// subsub process --- child of sub
+
+static TestEndpointBridgeSubChild* gBridgeSubChild;
+
+TestEndpointBridgeSubChild::TestEndpointBridgeSubChild()
+{
+ gBridgeSubChild = this;
+}
+
+bool
+TestEndpointBridgeSubChild::RecvPing()
+{
+ if (!SendBridgeEm()) {
+ fail("sending BridgeEm");
+ }
+ return true;
+}
+
+bool
+TestEndpointBridgeSubChild::RecvBridged(Endpoint<PTestEndpointBridgeMainSubChild>&& endpoint)
+{
+ TestEndpointBridgeMainSubChild* a = new TestEndpointBridgeMainSubChild();
+
+ if (!endpoint.Bind(a)) {
+ fail("failed to Bind");
+ }
+
+ if (!a->SendHello()) {
+ fail("sending Hello");
+ }
+
+ return true;
+}
+
+void
+TestEndpointBridgeSubChild::ActorDestroy(ActorDestroyReason why)
+{
+ if (NormalShutdown != why) {
+ fail("unexpected destruction!");
+ }
+ QuitChild();
+}
+
+bool
+TestEndpointBridgeMainSubChild::RecvHi()
+{
+ if (!SendHelloSync()) {
+ fail("sending HelloSync");
+ }
+ if (!CallHelloRpc()) {
+ fail("calling HelloRpc");
+ }
+ if (!mGotHi) {
+ fail("didn't answer HiRpc");
+ }
+
+ // Need to close the channel without message-processing frames on
+ // the C++ stack
+ MessageLoop::current()->PostTask(
+ NewNonOwningRunnableMethod(this, &TestEndpointBridgeMainSubChild::Close));
+ return true;
+}
+
+bool
+TestEndpointBridgeMainSubChild::AnswerHiRpc()
+{
+ mGotHi = true; // d00d
+ return true;
+}
+
+void
+TestEndpointBridgeMainSubChild::ActorDestroy(ActorDestroyReason why)
+{
+ if (NormalShutdown != why) {
+ fail("unexpected destruction!");
+ }
+
+ gBridgeSubChild->Close();
+
+ // ActorDestroy() is just a callback from IPDL-generated code,
+ // which needs the top-level actor (this) to stay alive a little
+ // longer so other things can be cleaned up.
+ MessageLoop::current()->PostTask(
+ do_AddRef(new DeleteTask<TestEndpointBridgeMainSubChild>(this)));
+}
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/TestEndpointBridgeMain.h b/ipc/ipdl/test/cxx/TestEndpointBridgeMain.h
new file mode 100644
index 000000000..a180ba8eb
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestEndpointBridgeMain.h
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+
+#ifndef mozilla__ipdltest_TestEndpointBridgeMain_h
+#define mozilla__ipdltest_TestEndpointBridgeMain_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestEndpointBridgeMainParent.h"
+#include "mozilla/_ipdltest/PTestEndpointBridgeMainChild.h"
+
+#include "mozilla/_ipdltest/PTestEndpointBridgeSubParent.h"
+#include "mozilla/_ipdltest/PTestEndpointBridgeSubChild.h"
+
+#include "mozilla/_ipdltest/PTestEndpointBridgeMainSubParent.h"
+#include "mozilla/_ipdltest/PTestEndpointBridgeMainSubChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// "Main" process
+//
+class TestEndpointBridgeMainParent :
+ public PTestEndpointBridgeMainParent
+{
+public:
+ TestEndpointBridgeMainParent() {}
+ virtual ~TestEndpointBridgeMainParent() {}
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+
+protected:
+ bool RecvBridged(mozilla::ipc::Endpoint<PTestEndpointBridgeMainSubParent>&& endpoint) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+};
+
+class TestEndpointBridgeMainSubParent :
+ public PTestEndpointBridgeMainSubParent
+{
+public:
+ explicit TestEndpointBridgeMainSubParent()
+ {}
+ virtual ~TestEndpointBridgeMainSubParent() {}
+
+protected:
+ virtual bool RecvHello() override;
+ virtual bool RecvHelloSync() override;
+ virtual bool AnswerHelloRpc() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+};
+
+//-----------------------------------------------------------------------------
+// "Sub" process --- child of "main"
+//
+class TestEndpointBridgeSubParent;
+
+class TestEndpointBridgeMainChild :
+ public PTestEndpointBridgeMainChild
+{
+public:
+ TestEndpointBridgeMainChild();
+ virtual ~TestEndpointBridgeMainChild() {}
+
+protected:
+ virtual bool RecvStart() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ IPDLUnitTestSubprocess* mSubprocess;
+};
+
+class TestEndpointBridgeSubParent :
+ public PTestEndpointBridgeSubParent
+{
+public:
+ TestEndpointBridgeSubParent() {}
+ virtual ~TestEndpointBridgeSubParent() {}
+
+ void Main();
+
+protected:
+ virtual bool RecvBridgeEm() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+};
+
+//-----------------------------------------------------------------------------
+// "Subsub" process --- child of "sub"
+//
+class TestEndpointBridgeSubChild :
+ public PTestEndpointBridgeSubChild
+{
+public:
+ TestEndpointBridgeSubChild();
+ virtual ~TestEndpointBridgeSubChild() {}
+
+protected:
+ virtual bool RecvPing() override;
+
+ bool RecvBridged(Endpoint<PTestEndpointBridgeMainSubChild>&& endpoint) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+};
+
+class TestEndpointBridgeMainSubChild :
+ public PTestEndpointBridgeMainSubChild
+{
+public:
+ explicit TestEndpointBridgeMainSubChild()
+ : mGotHi(false)
+ {}
+ virtual ~TestEndpointBridgeMainSubChild() {}
+
+protected:
+ virtual bool RecvHi() override;
+ virtual bool AnswerHiRpc() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ bool mGotHi;
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestEndpointBridgeMain_h
diff --git a/ipc/ipdl/test/cxx/TestEndpointOpens.cpp b/ipc/ipdl/test/cxx/TestEndpointOpens.cpp
new file mode 100644
index 000000000..820273ad7
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestEndpointOpens.cpp
@@ -0,0 +1,271 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+
+#include "base/task.h"
+#include "base/thread.h"
+
+#include "TestEndpointOpens.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+using namespace mozilla::ipc;
+
+using base::ProcessHandle;
+using base::Thread;
+
+namespace mozilla {
+// NB: this is generally bad style, but I am lazy.
+using namespace _ipdltest;
+using namespace _ipdltest2;
+
+static MessageLoop* gMainThread;
+
+static void
+AssertNotMainThread()
+{
+ if (!gMainThread) {
+ fail("gMainThread is not initialized");
+ }
+ if (MessageLoop::current() == gMainThread) {
+ fail("unexpectedly called on the main thread");
+ }
+}
+
+//-----------------------------------------------------------------------------
+// parent
+
+// Thread on which TestEndpointOpensOpenedParent runs
+static Thread* gParentThread;
+
+void
+TestEndpointOpensParent::Main()
+{
+ if (!SendStart()) {
+ fail("sending Start");
+ }
+}
+
+static void
+OpenParent(TestEndpointOpensOpenedParent* aParent,
+ Endpoint<PTestEndpointOpensOpenedParent>&& aEndpoint)
+{
+ AssertNotMainThread();
+
+ // Open the actor on the off-main thread to park it there.
+ // Messages will be delivered to this thread's message loop
+ // instead of the main thread's.
+ if (!aEndpoint.Bind(aParent)) {
+ fail("binding Parent");
+ }
+}
+
+bool
+TestEndpointOpensParent::RecvStartSubprotocol(
+ mozilla::ipc::Endpoint<PTestEndpointOpensOpenedParent>&& endpoint)
+{
+ gMainThread = MessageLoop::current();
+
+ gParentThread = new Thread("ParentThread");
+ if (!gParentThread->Start()) {
+ fail("starting parent thread");
+ }
+
+ TestEndpointOpensOpenedParent* a = new TestEndpointOpensOpenedParent();
+ gParentThread->message_loop()->PostTask(
+ NewRunnableFunction(OpenParent, a, mozilla::Move(endpoint)));
+
+ return true;
+}
+
+void
+TestEndpointOpensParent::ActorDestroy(ActorDestroyReason why)
+{
+ // Stops the thread and joins it
+ delete gParentThread;
+
+ if (NormalShutdown != why) {
+ fail("unexpected destruction A!");
+ }
+ passed("ok");
+ QuitParent();
+}
+
+bool
+TestEndpointOpensOpenedParent::RecvHello()
+{
+ AssertNotMainThread();
+ return SendHi();
+}
+
+bool
+TestEndpointOpensOpenedParent::RecvHelloSync()
+{
+ AssertNotMainThread();
+ return true;
+}
+
+bool
+TestEndpointOpensOpenedParent::AnswerHelloRpc()
+{
+ AssertNotMainThread();
+ return CallHiRpc();
+}
+
+static void
+ShutdownTestEndpointOpensOpenedParent(TestEndpointOpensOpenedParent* parent,
+ Transport* transport)
+{
+ delete parent;
+}
+
+void
+TestEndpointOpensOpenedParent::ActorDestroy(ActorDestroyReason why)
+{
+ AssertNotMainThread();
+
+ if (NormalShutdown != why) {
+ fail("unexpected destruction B!");
+ }
+
+ // ActorDestroy() is just a callback from IPDL-generated code,
+ // which needs the top-level actor (this) to stay alive a little
+ // longer so other things can be cleaned up.
+ gParentThread->message_loop()->PostTask(
+ NewRunnableFunction(ShutdownTestEndpointOpensOpenedParent,
+ this, GetTransport()));
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+static TestEndpointOpensChild* gOpensChild;
+// Thread on which TestEndpointOpensOpenedChild runs
+static Thread* gChildThread;
+
+TestEndpointOpensChild::TestEndpointOpensChild()
+{
+ gOpensChild = this;
+}
+
+static void
+OpenChild(TestEndpointOpensOpenedChild* aChild,
+ Endpoint<PTestEndpointOpensOpenedChild>&& endpoint)
+{
+ AssertNotMainThread();
+
+ // Open the actor on the off-main thread to park it there.
+ // Messages will be delivered to this thread's message loop
+ // instead of the main thread's.
+ if (!endpoint.Bind(aChild)) {
+ fail("binding child endpoint");
+ }
+
+ // Kick off the unit tests
+ if (!aChild->SendHello()) {
+ fail("sending Hello");
+ }
+}
+
+bool
+TestEndpointOpensChild::RecvStart()
+{
+ Endpoint<PTestEndpointOpensOpenedParent> parent;
+ Endpoint<PTestEndpointOpensOpenedChild> child;
+ nsresult rv;
+ rv = PTestEndpointOpensOpened::CreateEndpoints(OtherPid(), base::GetCurrentProcId(),
+ &parent, &child);
+ if (NS_FAILED(rv)) {
+ fail("opening PTestEndpointOpensOpened");
+ }
+
+ gMainThread = MessageLoop::current();
+
+ gChildThread = new Thread("ChildThread");
+ if (!gChildThread->Start()) {
+ fail("starting child thread");
+ }
+
+ TestEndpointOpensOpenedChild* a = new TestEndpointOpensOpenedChild();
+ gChildThread->message_loop()->PostTask(
+ NewRunnableFunction(OpenChild, a, mozilla::Move(child)));
+
+ if (!SendStartSubprotocol(parent)) {
+ fail("send StartSubprotocol");
+ }
+
+ return true;
+}
+
+void
+TestEndpointOpensChild::ActorDestroy(ActorDestroyReason why)
+{
+ // Stops the thread and joins it
+ delete gChildThread;
+
+ if (NormalShutdown != why) {
+ fail("unexpected destruction C!");
+ }
+ QuitChild();
+}
+
+bool
+TestEndpointOpensOpenedChild::RecvHi()
+{
+ AssertNotMainThread();
+
+ if (!SendHelloSync()) {
+ fail("sending HelloSync");
+ }
+ if (!CallHelloRpc()) {
+ fail("calling HelloRpc");
+ }
+ if (!mGotHi) {
+ fail("didn't answer HiRpc");
+ }
+
+ // Need to close the channel without message-processing frames on
+ // the C++ stack
+ MessageLoop::current()->PostTask(
+ NewNonOwningRunnableMethod(this, &TestEndpointOpensOpenedChild::Close));
+ return true;
+}
+
+bool
+TestEndpointOpensOpenedChild::AnswerHiRpc()
+{
+ AssertNotMainThread();
+
+ mGotHi = true; // d00d
+ return true;
+}
+
+static void
+ShutdownTestEndpointOpensOpenedChild(TestEndpointOpensOpenedChild* child,
+ Transport* transport)
+{
+ delete child;
+
+ // Kick off main-thread shutdown.
+ gMainThread->PostTask(
+ NewNonOwningRunnableMethod(gOpensChild, &TestEndpointOpensChild::Close));
+}
+
+void
+TestEndpointOpensOpenedChild::ActorDestroy(ActorDestroyReason why)
+{
+ AssertNotMainThread();
+
+ if (NormalShutdown != why) {
+ fail("unexpected destruction D!");
+ }
+
+ // ActorDestroy() is just a callback from IPDL-generated code,
+ // which needs the top-level actor (this) to stay alive a little
+ // longer so other things can be cleaned up. Defer shutdown to
+ // let cleanup finish.
+ gChildThread->message_loop()->PostTask(
+ NewRunnableFunction(ShutdownTestEndpointOpensOpenedChild,
+ this, GetTransport()));
+}
+
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestEndpointOpens.h b/ipc/ipdl/test/cxx/TestEndpointOpens.h
new file mode 100644
index 000000000..6bfaace2a
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestEndpointOpens.h
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+
+#ifndef mozilla__ipdltest_TestEndpointOpens_h
+#define mozilla__ipdltest_TestEndpointOpens_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestEndpointOpensParent.h"
+#include "mozilla/_ipdltest/PTestEndpointOpensChild.h"
+
+#include "mozilla/_ipdltest2/PTestEndpointOpensOpenedParent.h"
+#include "mozilla/_ipdltest2/PTestEndpointOpensOpenedChild.h"
+
+namespace mozilla {
+
+// parent process
+
+namespace _ipdltest {
+
+class TestEndpointOpensParent : public PTestEndpointOpensParent
+{
+public:
+ TestEndpointOpensParent() {}
+ virtual ~TestEndpointOpensParent() {}
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+
+protected:
+ virtual bool RecvStartSubprotocol(mozilla::ipc::Endpoint<PTestEndpointOpensOpenedParent>&& endpoint);
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+};
+
+} // namespace _ipdltest
+
+namespace _ipdltest2 {
+
+class TestEndpointOpensOpenedParent : public PTestEndpointOpensOpenedParent
+{
+public:
+ explicit TestEndpointOpensOpenedParent()
+ {}
+ virtual ~TestEndpointOpensOpenedParent() {}
+
+protected:
+ virtual bool RecvHello() override;
+ virtual bool RecvHelloSync() override;
+ virtual bool AnswerHelloRpc() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+};
+
+} // namespace _ipdltest2
+
+// child process
+
+namespace _ipdltest {
+
+class TestEndpointOpensChild : public PTestEndpointOpensChild
+{
+public:
+ TestEndpointOpensChild();
+ virtual ~TestEndpointOpensChild() {}
+
+protected:
+ virtual bool RecvStart() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+};
+
+} // namespace _ipdltest
+
+namespace _ipdltest2 {
+
+class TestEndpointOpensOpenedChild : public PTestEndpointOpensOpenedChild
+{
+public:
+ explicit TestEndpointOpensOpenedChild()
+ : mGotHi(false)
+ {}
+ virtual ~TestEndpointOpensOpenedChild() {}
+
+protected:
+ virtual bool RecvHi() override;
+ virtual bool AnswerHiRpc() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ bool mGotHi;
+};
+
+} // namespace _ipdltest2
+
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestEndpointOpens_h
diff --git a/ipc/ipdl/test/cxx/TestFailedCtor.cpp b/ipc/ipdl/test/cxx/TestFailedCtor.cpp
new file mode 100644
index 000000000..12ea8fdc1
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestFailedCtor.cpp
@@ -0,0 +1,137 @@
+#include "TestFailedCtor.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+void
+TestFailedCtorParent::Main()
+{
+ PTestFailedCtorSubParent* p = CallPTestFailedCtorSubConstructor();
+ if (p)
+ fail("expected ctor to fail");
+
+ Close();
+}
+
+PTestFailedCtorSubParent*
+TestFailedCtorParent::AllocPTestFailedCtorSubParent()
+{
+ return new TestFailedCtorSubParent();
+}
+bool
+TestFailedCtorParent::DeallocPTestFailedCtorSubParent(PTestFailedCtorSubParent* actor)
+{
+ delete actor;
+ return true;
+}
+
+PTestFailedCtorSubsubParent*
+TestFailedCtorSubParent::AllocPTestFailedCtorSubsubParent()
+{
+ TestFailedCtorSubsub* a = new TestFailedCtorSubsub();
+ if (!mOne) {
+ return mOne = a;
+ } else if (!mTwo) {
+ return mTwo = a;
+ } else if (!mThree) {
+ return mThree = a;
+ } else {
+ fail("unexpected Alloc()");
+ return nullptr;
+ }
+}
+bool
+TestFailedCtorSubParent::DeallocPTestFailedCtorSubsubParent(PTestFailedCtorSubsubParent* actor)
+{
+ static_cast<TestFailedCtorSubsub*>(actor)->mDealloced = true;
+ return true;
+}
+
+void
+TestFailedCtorSubParent::ActorDestroy(ActorDestroyReason why)
+{
+
+ if (mOne->mWhy != Deletion)
+ fail("Subsub one got wrong ActorDestroyReason");
+ if (mTwo->mWhy != AncestorDeletion)
+ fail("Subsub two got wrong ActorDestroyReason");
+ if (mThree->mWhy != AncestorDeletion)
+ fail("Subsub three got wrong ActorDestroyReason");
+
+ if (FailedConstructor != why)
+ fail("unexpected destruction!");
+}
+
+TestFailedCtorSubParent::~TestFailedCtorSubParent()
+{
+ if (!(mOne->mDealloced && mTwo->mDealloced && mThree->mDealloced))
+ fail("Not all subsubs were Dealloc'd");
+ delete mOne;
+ delete mTwo;
+ delete mThree;
+}
+
+
+//-----------------------------------------------------------------------------
+// child
+
+PTestFailedCtorSubChild*
+TestFailedCtorChild::AllocPTestFailedCtorSubChild()
+{
+ return new TestFailedCtorSubChild();
+}
+
+bool
+TestFailedCtorChild::AnswerPTestFailedCtorSubConstructor(PTestFailedCtorSubChild* actor)
+{
+ PTestFailedCtorSubsubChild* c1 = actor->SendPTestFailedCtorSubsubConstructor();
+ PTestFailedCtorSubsubChild::Send__delete__(c1);
+
+ if (!actor->SendPTestFailedCtorSubsubConstructor() ||
+ !actor->SendPTestFailedCtorSubsubConstructor() ||
+ !actor->SendSync())
+ fail("setting up test");
+
+ // This causes our process to die
+ return false;
+}
+
+bool
+TestFailedCtorChild::DeallocPTestFailedCtorSubChild(PTestFailedCtorSubChild* actor)
+{
+ delete actor;
+ return true;
+}
+
+void
+TestFailedCtorChild::ProcessingError(Result aCode, const char* aReason)
+{
+ if (OtherPid() != base::GetCurrentProcId()) // thread-mode
+ _exit(0);
+}
+
+PTestFailedCtorSubsubChild*
+TestFailedCtorSubChild::AllocPTestFailedCtorSubsubChild()
+{
+ return new TestFailedCtorSubsub();
+}
+
+bool
+TestFailedCtorSubChild::DeallocPTestFailedCtorSubsubChild(PTestFailedCtorSubsubChild* actor)
+{
+ delete actor;
+ return true;
+}
+
+void
+TestFailedCtorSubChild::ActorDestroy(ActorDestroyReason why)
+{
+}
+
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestFailedCtor.h b/ipc/ipdl/test/cxx/TestFailedCtor.h
new file mode 100644
index 000000000..97f9013cc
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestFailedCtor.h
@@ -0,0 +1,136 @@
+#ifndef mozilla_ipdltest_TestFailedCtor_h
+#define mozilla_ipdltest_TestFailedCtor_h
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestFailedCtorParent.h"
+#include "mozilla/_ipdltest/PTestFailedCtorChild.h"
+
+#include "mozilla/_ipdltest/PTestFailedCtorSubParent.h"
+#include "mozilla/_ipdltest/PTestFailedCtorSubChild.h"
+
+#include "mozilla/_ipdltest/PTestFailedCtorSubsubParent.h"
+#include "mozilla/_ipdltest/PTestFailedCtorSubsubChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// Top-level
+//
+class TestFailedCtorParent :
+ public PTestFailedCtorParent
+{
+public:
+ TestFailedCtorParent() { }
+ virtual ~TestFailedCtorParent() { }
+
+ static bool RunTestInProcesses() { return true; }
+
+ // FIXME/bug 703322 Disabled because child calls exit() to end
+ // test, not clear how to handle failed ctor in
+ // threaded mode.
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+
+protected:
+ virtual PTestFailedCtorSubParent* AllocPTestFailedCtorSubParent() override;
+ virtual bool DeallocPTestFailedCtorSubParent(PTestFailedCtorSubParent* actor) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (AbnormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+};
+
+
+class TestFailedCtorChild :
+ public PTestFailedCtorChild
+{
+public:
+ TestFailedCtorChild() { }
+ virtual ~TestFailedCtorChild() { }
+
+protected:
+ virtual PTestFailedCtorSubChild* AllocPTestFailedCtorSubChild() override;
+
+ virtual bool AnswerPTestFailedCtorSubConstructor(PTestFailedCtorSubChild* actor) override;
+
+ virtual bool DeallocPTestFailedCtorSubChild(PTestFailedCtorSubChild* actor) override;
+
+ virtual void ProcessingError(Result aCode, const char* aReason) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ fail("should have _exit()ed");
+ }
+};
+
+
+//-----------------------------------------------------------------------------
+// First descendent
+//
+class TestFailedCtorSubsub;
+
+class TestFailedCtorSubParent :
+ public PTestFailedCtorSubParent
+{
+public:
+ TestFailedCtorSubParent() : mOne(nullptr), mTwo(nullptr), mThree(nullptr) { }
+ virtual ~TestFailedCtorSubParent();
+
+protected:
+ virtual PTestFailedCtorSubsubParent* AllocPTestFailedCtorSubsubParent() override;
+
+ virtual bool DeallocPTestFailedCtorSubsubParent(PTestFailedCtorSubsubParent* actor) override;
+ virtual bool RecvSync() override { return true; }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ TestFailedCtorSubsub* mOne;
+ TestFailedCtorSubsub* mTwo;
+ TestFailedCtorSubsub* mThree;
+};
+
+
+class TestFailedCtorSubChild :
+ public PTestFailedCtorSubChild
+{
+public:
+ TestFailedCtorSubChild() { }
+ virtual ~TestFailedCtorSubChild() { }
+
+protected:
+ virtual PTestFailedCtorSubsubChild* AllocPTestFailedCtorSubsubChild() override;
+ virtual bool DeallocPTestFailedCtorSubsubChild(PTestFailedCtorSubsubChild* actor) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+};
+
+
+//-----------------------------------------------------------------------------
+// Grand-descendent
+//
+class TestFailedCtorSubsub :
+ public PTestFailedCtorSubsubParent,
+ public PTestFailedCtorSubsubChild
+{
+public:
+ TestFailedCtorSubsub() : mWhy(ActorDestroyReason(-1)), mDealloced(false) {}
+ virtual ~TestFailedCtorSubsub() {}
+
+ virtual void ActorDestroy(ActorDestroyReason why) override { mWhy = why; }
+
+ ActorDestroyReason mWhy;
+ bool mDealloced;
+};
+
+
+}
+}
+
+#endif // ifndef mozilla_ipdltest_TestFailedCtor_h
diff --git a/ipc/ipdl/test/cxx/TestHangs.cpp b/ipc/ipdl/test/cxx/TestHangs.cpp
new file mode 100644
index 000000000..b96823aee
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestHangs.cpp
@@ -0,0 +1,146 @@
+#include "base/process_util.h"
+
+#include "TestHangs.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+using base::KillProcess;
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+TestHangsParent::TestHangsParent() : mDetectedHang(false)
+{
+ MOZ_COUNT_CTOR(TestHangsParent);
+}
+
+TestHangsParent::~TestHangsParent()
+{
+ MOZ_COUNT_DTOR(TestHangsParent);
+}
+
+void
+TestHangsParent::Main()
+{
+ // Here we try to set things up to test the following sequence of events:
+ //
+ // - subprocess causes an OnMaybeDequeueOne() task to be posted to
+ // this thread
+ //
+ // - subprocess hangs just long enough for the hang timer to expire
+ //
+ // - hang-kill code in the parent starts running
+ //
+ // - subprocess replies to message while hang code runs
+ //
+ // - reply is processed in OnMaybeDequeueOne() before Close() has
+ // been called or the channel error notification has been posted
+
+ // this tells the subprocess to send us Nonce()
+ if (!SendStart())
+ fail("sending Start");
+
+ // now we sleep here for a while awaiting the Nonce() message from
+ // the child. since we're not blocked on anything, the IO thread
+ // will enqueue an OnMaybeDequeueOne() task to process that
+ // message
+ //
+ // NB: PR_Sleep is exactly what we want, only the current thread
+ // sleeping
+ PR_Sleep(5000);
+
+ // when we call into this, we'll pull the Nonce() message out of
+ // the mPending queue, but that doesn't matter ... the
+ // OnMaybeDequeueOne() event will remain
+ if (CallStackFrame() && mDetectedHang)
+ fail("should have timed out!");
+
+ // the Close() task in the queue will shut us down
+}
+
+bool
+TestHangsParent::ShouldContinueFromReplyTimeout()
+{
+ mDetectedHang = true;
+
+ // so we've detected a timeout after 2 ms ... now we cheat and
+ // sleep for a long time, to allow the subprocess's reply to come
+ // in
+
+ PR_Sleep(5000);
+
+ // reply should be here; we'll post a task to shut things down.
+ // This must be after OnMaybeDequeueOne() in the event queue.
+ MessageLoop::current()->PostTask(
+ NewNonOwningRunnableMethod(this, &TestHangsParent::CleanUp));
+
+ GetIPCChannel()->CloseWithTimeout();
+
+ return false;
+}
+
+bool
+TestHangsParent::AnswerStackFrame()
+{
+ if (PTestHangs::HANG != state()) {
+ if (CallStackFrame())
+ fail("should have timed out!");
+ }
+ else {
+ // minimum possible, 2 ms. We want to detecting a hang to race
+ // with the reply coming in, as reliably as possible
+ SetReplyTimeoutMs(2);
+
+ if (CallHang())
+ fail("should have timed out!");
+ }
+
+ return true;
+}
+
+void
+TestHangsParent::CleanUp()
+{
+ ipc::ScopedProcessHandle otherProcessHandle;
+ if (!base::OpenProcessHandle(OtherPid(), &otherProcessHandle.rwget())) {
+ fail("couldn't open child process");
+ } else {
+ if (!KillProcess(otherProcessHandle, 0, false)) {
+ fail("terminating child process");
+ }
+ }
+ Close();
+}
+
+
+//-----------------------------------------------------------------------------
+// child
+
+TestHangsChild::TestHangsChild()
+{
+ MOZ_COUNT_CTOR(TestHangsChild);
+}
+
+TestHangsChild::~TestHangsChild()
+{
+ MOZ_COUNT_DTOR(TestHangsChild);
+}
+
+bool
+TestHangsChild::AnswerHang()
+{
+ puts(" (child process is 'hanging' now)");
+
+ // just sleep until we're reasonably confident the 1ms hang
+ // detector fired in the parent process and it's sleeping in
+ // ShouldContinueFromReplyTimeout()
+ PR_Sleep(1000);
+
+ return true;
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestHangs.h b/ipc/ipdl/test/cxx/TestHangs.h
new file mode 100644
index 000000000..676215172
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestHangs.h
@@ -0,0 +1,87 @@
+#ifndef mozilla__ipdltest_TestHangs_h
+#define mozilla__ipdltest_TestHangs_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestHangsParent.h"
+#include "mozilla/_ipdltest/PTestHangsChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestHangsParent :
+ public PTestHangsParent
+{
+public:
+ TestHangsParent();
+ virtual ~TestHangsParent();
+
+ static bool RunTestInProcesses() { return true; }
+
+ // FIXME/bug 703320 Disabled because parent kills child proc, not
+ // clear how that should work in threads.
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+
+protected:
+ virtual bool ShouldContinueFromReplyTimeout() override;
+
+ virtual bool RecvNonce() {
+ return true;
+ }
+
+ virtual bool AnswerStackFrame() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (AbnormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+
+ void CleanUp();
+
+ bool mDetectedHang;
+};
+
+
+class TestHangsChild :
+ public PTestHangsChild
+{
+public:
+ TestHangsChild();
+ virtual ~TestHangsChild();
+
+protected:
+ virtual bool RecvStart() override {
+ if (!SendNonce())
+ fail("sending Nonce");
+ return true;
+ }
+
+ virtual bool AnswerStackFrame() override
+ {
+ if (CallStackFrame())
+ fail("should have failed");
+ return true;
+ }
+
+ virtual bool AnswerHang() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (AbnormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestHangs_h
diff --git a/ipc/ipdl/test/cxx/TestHighestPrio.cpp b/ipc/ipdl/test/cxx/TestHighestPrio.cpp
new file mode 100644
index 000000000..524943de4
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestHighestPrio.cpp
@@ -0,0 +1,118 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: sw=4 ts=4 et :
+ */
+/* 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 "TestHighestPrio.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+#if defined(OS_POSIX)
+#include <unistd.h>
+#else
+#include <windows.h>
+#endif
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+TestHighestPrioParent::TestHighestPrioParent()
+ : msg_num_(0)
+{
+ MOZ_COUNT_CTOR(TestHighestPrioParent);
+}
+
+TestHighestPrioParent::~TestHighestPrioParent()
+{
+ MOZ_COUNT_DTOR(TestHighestPrioParent);
+}
+
+void
+TestHighestPrioParent::Main()
+{
+ if (!SendStart())
+ fail("sending Start");
+}
+
+bool
+TestHighestPrioParent::RecvMsg1()
+{
+ MOZ_ASSERT(msg_num_ == 0);
+ msg_num_ = 1;
+ return true;
+}
+
+bool
+TestHighestPrioParent::RecvMsg2()
+{
+
+ MOZ_ASSERT(msg_num_ == 1);
+ msg_num_ = 2;
+
+ if (!SendStartInner())
+ fail("sending StartInner");
+
+ return true;
+}
+
+bool
+TestHighestPrioParent::RecvMsg3()
+{
+ MOZ_ASSERT(msg_num_ == 2);
+ msg_num_ = 3;
+ return true;
+}
+
+bool
+TestHighestPrioParent::RecvMsg4()
+{
+ MOZ_ASSERT(msg_num_ == 3);
+ msg_num_ = 4;
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+
+TestHighestPrioChild::TestHighestPrioChild()
+{
+ MOZ_COUNT_CTOR(TestHighestPrioChild);
+}
+
+TestHighestPrioChild::~TestHighestPrioChild()
+{
+ MOZ_COUNT_DTOR(TestHighestPrioChild);
+}
+
+bool
+TestHighestPrioChild::RecvStart()
+{
+ if (!SendMsg1())
+ fail("sending Msg1");
+
+ if (!SendMsg2())
+ fail("sending Msg2");
+
+ Close();
+ return true;
+}
+
+bool
+TestHighestPrioChild::RecvStartInner()
+{
+ if (!SendMsg3())
+ fail("sending Msg3");
+
+ if (!SendMsg4())
+ fail("sending Msg4");
+
+ return true;
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestHighestPrio.h b/ipc/ipdl/test/cxx/TestHighestPrio.h
new file mode 100644
index 000000000..e6dc8c427
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestHighestPrio.h
@@ -0,0 +1,68 @@
+#ifndef mozilla__ipdltest_TestHighestPrio_h
+#define mozilla__ipdltest_TestHighestPrio_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestHighestPrioParent.h"
+#include "mozilla/_ipdltest/PTestHighestPrioChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestHighestPrioParent :
+ public PTestHighestPrioParent
+{
+public:
+ TestHighestPrioParent();
+ virtual ~TestHighestPrioParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+
+ bool RecvMsg1() override;
+ bool RecvMsg2() override;
+ bool RecvMsg3() override;
+ bool RecvMsg4() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ if (msg_num_ != 4)
+ fail("missed IPC call");
+ passed("ok");
+ QuitParent();
+ }
+
+private:
+ int msg_num_;
+};
+
+
+class TestHighestPrioChild :
+ public PTestHighestPrioChild
+{
+public:
+ TestHighestPrioChild();
+ virtual ~TestHighestPrioChild();
+
+ bool RecvStart() override;
+ bool RecvStartInner() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestHighestPrio_h
diff --git a/ipc/ipdl/test/cxx/TestInterruptErrorCleanup.cpp b/ipc/ipdl/test/cxx/TestInterruptErrorCleanup.cpp
new file mode 100644
index 000000000..21a5bc592
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestInterruptErrorCleanup.cpp
@@ -0,0 +1,155 @@
+#include "TestInterruptErrorCleanup.h"
+
+#include "base/task.h"
+#include "mozilla/CondVar.h"
+#include "mozilla/Mutex.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+#include "IPDLUnitTestSubprocess.h"
+
+using mozilla::CondVar;
+using mozilla::Mutex;
+using mozilla::MutexAutoLock;
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+namespace {
+
+// NB: this test does its own shutdown, rather than going through
+// QuitParent(), because it's testing degenerate edge cases
+
+void DeleteSubprocess(Mutex* mutex, CondVar* cvar)
+{
+ MutexAutoLock lock(*mutex);
+
+ delete gSubprocess;
+ gSubprocess = nullptr;
+
+ cvar->Notify();
+}
+
+void DeleteTheWorld()
+{
+ delete static_cast<TestInterruptErrorCleanupParent*>(gParentActor);
+ gParentActor = nullptr;
+
+ // needs to be synchronous to avoid affecting event ordering on
+ // the main thread
+ Mutex mutex("TestInterruptErrorCleanup.DeleteTheWorld.mutex");
+ CondVar cvar(mutex, "TestInterruptErrorCleanup.DeleteTheWorld.cvar");
+
+ MutexAutoLock lock(mutex);
+
+ XRE_GetIOMessageLoop()->PostTask(
+ NewRunnableFunction(DeleteSubprocess, &mutex, &cvar));
+
+ cvar.Wait();
+}
+
+void Done()
+{
+ static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
+ nsCOMPtr<nsIAppShell> appShell (do_GetService(kAppShellCID));
+ appShell->Exit();
+
+ passed(__FILE__);
+}
+
+} // namespace <anon>
+
+TestInterruptErrorCleanupParent::TestInterruptErrorCleanupParent()
+ : mGotProcessingError(false)
+{
+ MOZ_COUNT_CTOR(TestInterruptErrorCleanupParent);
+}
+
+TestInterruptErrorCleanupParent::~TestInterruptErrorCleanupParent()
+{
+ MOZ_COUNT_DTOR(TestInterruptErrorCleanupParent);
+}
+
+void
+TestInterruptErrorCleanupParent::Main()
+{
+ // This test models the following sequence of events
+ //
+ // (1) Parent: Interrupt out-call
+ // (2) Child: crash
+ // --[Parent-only hereafter]--
+ // (3) Interrupt out-call return false
+ // (4) Close()
+ // --[event loop]--
+ // (5) delete parentActor
+ // (6) delete childProcess
+ // --[event loop]--
+ // (7) Channel::OnError notification
+ // --[event loop]--
+ // (8) Done, quit
+ //
+ // See bug 535298 and friends; this seqeunce of events captures
+ // three differnent potential errors
+ // - Close()-after-error (semantic error previously)
+ // - use-after-free of parentActor
+ // - use-after-free of channel
+ //
+ // Because of legacy constraints related to nsNPAPI* code, we need
+ // to ensure that this sequence of events can occur without
+ // errors/crashes.
+
+ MessageLoop::current()->PostTask(
+ NewRunnableFunction(DeleteTheWorld));
+
+ // it's a failure if this *succeeds*
+ if (CallError())
+ fail("expected an error!");
+
+ if (!mGotProcessingError)
+ fail("expected a ProcessingError() notification");
+
+ // it's OK to Close() a channel after an error, because nsNPAPI*
+ // wants to do this
+ Close();
+
+ // we know that this event *must* be after the MaybeError
+ // notification enqueued by AsyncChannel, because that event is
+ // enqueued within the same mutex that ends up signaling the
+ // wakeup-on-error of |CallError()| above
+ MessageLoop::current()->PostTask(NewRunnableFunction(Done));
+}
+
+void
+TestInterruptErrorCleanupParent::ProcessingError(Result aCode, const char* aReason)
+{
+ if (aCode != MsgDropped)
+ fail("unexpected processing error");
+ mGotProcessingError = true;
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+TestInterruptErrorCleanupChild::TestInterruptErrorCleanupChild()
+{
+ MOZ_COUNT_CTOR(TestInterruptErrorCleanupChild);
+}
+
+TestInterruptErrorCleanupChild::~TestInterruptErrorCleanupChild()
+{
+ MOZ_COUNT_DTOR(TestInterruptErrorCleanupChild);
+}
+
+bool
+TestInterruptErrorCleanupChild::AnswerError()
+{
+ _exit(0);
+ NS_RUNTIMEABORT("unreached");
+ return false;
+}
+
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestInterruptErrorCleanup.h b/ipc/ipdl/test/cxx/TestInterruptErrorCleanup.h
new file mode 100644
index 000000000..bd45b2c98
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestInterruptErrorCleanup.h
@@ -0,0 +1,60 @@
+#ifndef mozilla__ipdltest_TestInterruptErrorCleanup_h
+#define mozilla__ipdltest_TestInterruptErrorCleanup_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestInterruptErrorCleanupParent.h"
+#include "mozilla/_ipdltest/PTestInterruptErrorCleanupChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestInterruptErrorCleanupParent :
+ public PTestInterruptErrorCleanupParent
+{
+public:
+ TestInterruptErrorCleanupParent();
+ virtual ~TestInterruptErrorCleanupParent();
+
+ static bool RunTestInProcesses() { return true; }
+ // FIXME/bug 703323 Could work if modified
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+
+protected:
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (AbnormalShutdown != why)
+ fail("unexpected destruction!");
+ }
+
+ virtual void ProcessingError(Result aCode, const char* aReason) override;
+
+ bool mGotProcessingError;
+};
+
+
+class TestInterruptErrorCleanupChild :
+ public PTestInterruptErrorCleanupChild
+{
+public:
+ TestInterruptErrorCleanupChild();
+ virtual ~TestInterruptErrorCleanupChild();
+
+protected:
+ virtual bool AnswerError() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ fail("should have 'crashed'!");
+ }
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestInterruptErrorCleanup_h
diff --git a/ipc/ipdl/test/cxx/TestInterruptRaces.cpp b/ipc/ipdl/test/cxx/TestInterruptRaces.cpp
new file mode 100644
index 000000000..6986506bb
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestInterruptRaces.cpp
@@ -0,0 +1,220 @@
+#include "TestInterruptRaces.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+using mozilla::ipc::MessageChannel;
+
+namespace mozilla {
+namespace _ipdltest {
+
+ipc::RacyInterruptPolicy
+MediateRace(const MessageChannel::MessageInfo& parent,
+ const MessageChannel::MessageInfo& child)
+{
+ return (PTestInterruptRaces::Msg_Child__ID == parent.type()) ?
+ ipc::RIPParentWins : ipc::RIPChildWins;
+}
+
+//-----------------------------------------------------------------------------
+// parent
+void
+TestInterruptRacesParent::Main()
+{
+ if (!SendStart())
+ fail("sending Start()");
+}
+
+bool
+TestInterruptRacesParent::RecvStartRace()
+{
+ MessageLoop::current()->PostTask(
+ NewNonOwningRunnableMethod(this, &TestInterruptRacesParent::OnRaceTime));
+ return true;
+}
+
+void
+TestInterruptRacesParent::OnRaceTime()
+{
+ if (!CallRace(&mChildHasReply))
+ fail("problem calling Race()");
+
+ if (!mChildHasReply)
+ fail("child should have got a reply already");
+
+ mHasReply = true;
+
+ MessageLoop::current()->PostTask(
+ NewNonOwningRunnableMethod(this, &TestInterruptRacesParent::Test2));
+}
+
+bool
+TestInterruptRacesParent::AnswerRace(bool* hasReply)
+{
+ if (mHasReply)
+ fail("apparently the parent won the Interrupt race!");
+ *hasReply = hasReply;
+ return true;
+}
+
+void
+TestInterruptRacesParent::Test2()
+{
+ puts(" passed");
+ puts("Test 2");
+
+ mHasReply = false;
+ mChildHasReply = false;
+
+ if (!CallStackFrame())
+ fail("can't set up a stack frame");
+
+ puts(" passed");
+
+ MessageLoop::current()->PostTask(
+ NewNonOwningRunnableMethod(this, &TestInterruptRacesParent::Test3));
+}
+
+bool
+TestInterruptRacesParent::AnswerStackFrame()
+{
+ if (!SendWakeup())
+ fail("can't wake up the child");
+
+ if (!CallRace(&mChildHasReply))
+ fail("can't set up race condition");
+ mHasReply = true;
+
+ if (!mChildHasReply)
+ fail("child should have got a reply already");
+
+ return true;
+}
+
+void
+TestInterruptRacesParent::Test3()
+{
+ puts("Test 3");
+
+ if (!CallStackFrame3())
+ fail("can't set up a stack frame");
+
+ puts(" passed");
+
+ Close();
+}
+
+bool
+TestInterruptRacesParent::AnswerStackFrame3()
+{
+ if (!SendWakeup3())
+ fail("can't wake up the child");
+
+ if (!CallChild())
+ fail("can't set up race condition");
+
+ return true;
+}
+
+bool
+TestInterruptRacesParent::AnswerParent()
+{
+ mAnsweredParent = true;
+ return true;
+}
+
+bool
+TestInterruptRacesParent::RecvGetAnsweredParent(bool* answeredParent)
+{
+ *answeredParent = mAnsweredParent;
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// child
+bool
+TestInterruptRacesChild::RecvStart()
+{
+ puts("Test 1");
+
+ if (!SendStartRace())
+ fail("problem sending StartRace()");
+
+ bool dontcare;
+ if (!CallRace(&dontcare))
+ fail("problem calling Race()");
+
+ mHasReply = true;
+ return true;
+}
+
+bool
+TestInterruptRacesChild::AnswerRace(bool* hasReply)
+{
+ if (!mHasReply)
+ fail("apparently the child lost the Interrupt race!");
+
+ *hasReply = mHasReply;
+
+ return true;
+}
+
+bool
+TestInterruptRacesChild::AnswerStackFrame()
+{
+ // reset for the second test
+ mHasReply = false;
+
+ if (!CallStackFrame())
+ fail("can't set up stack frame");
+
+ if (!mHasReply)
+ fail("should have had reply by now");
+
+ return true;
+}
+
+bool
+TestInterruptRacesChild::RecvWakeup()
+{
+ bool dontcare;
+ if (!CallRace(&dontcare))
+ fail("can't set up race condition");
+
+ mHasReply = true;
+ return true;
+}
+
+bool
+TestInterruptRacesChild::AnswerStackFrame3()
+{
+ if (!CallStackFrame3())
+ fail("can't set up stack frame");
+ return true;
+}
+
+bool
+TestInterruptRacesChild::RecvWakeup3()
+{
+ if (!CallParent())
+ fail("can't set up race condition");
+ return true;
+}
+
+bool
+TestInterruptRacesChild::AnswerChild()
+{
+ bool parentAnsweredParent;
+ // the parent is supposed to win the race, which means its
+ // message, Child(), is supposed to be processed before the
+ // child's message, Parent()
+ if (!SendGetAnsweredParent(&parentAnsweredParent))
+ fail("sending GetAnsweredParent");
+
+ if (parentAnsweredParent)
+ fail("parent was supposed to win the race!");
+
+ return true;
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestInterruptRaces.h b/ipc/ipdl/test/cxx/TestInterruptRaces.h
new file mode 100644
index 000000000..f83c49fd1
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestInterruptRaces.h
@@ -0,0 +1,131 @@
+#ifndef mozilla__ipdltest_TestInterruptRaces_h
+#define mozilla__ipdltest_TestInterruptRaces_h
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestInterruptRacesParent.h"
+#include "mozilla/_ipdltest/PTestInterruptRacesChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+mozilla::ipc::RacyInterruptPolicy
+MediateRace(const mozilla::ipc::MessageChannel::MessageInfo& parent,
+ const mozilla::ipc::MessageChannel::MessageInfo& child);
+
+class TestInterruptRacesParent :
+ public PTestInterruptRacesParent
+{
+public:
+ TestInterruptRacesParent() : mHasReply(false),
+ mChildHasReply(false),
+ mAnsweredParent(false)
+ { }
+ virtual ~TestInterruptRacesParent() { }
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+protected:
+ virtual bool
+ RecvStartRace() override;
+
+ virtual bool
+ AnswerRace(bool* hasRace) override;
+
+ virtual bool
+ AnswerStackFrame() override;
+
+ virtual bool
+ AnswerStackFrame3() override;
+
+ virtual bool
+ AnswerParent() override;
+
+ virtual bool
+ RecvGetAnsweredParent(bool* answeredParent) override;
+
+ virtual mozilla::ipc::RacyInterruptPolicy
+ MediateInterruptRace(const MessageInfo& parent,
+ const MessageInfo& child) override
+ {
+ return MediateRace(parent, child);
+ }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ if (!(mHasReply && mChildHasReply))
+ fail("both sides should have replies!");
+ passed("ok");
+ QuitParent();
+ }
+
+private:
+ void OnRaceTime();
+
+ void Test2();
+ void Test3();
+
+ bool mHasReply;
+ bool mChildHasReply;
+ bool mAnsweredParent;
+};
+
+
+class TestInterruptRacesChild :
+ public PTestInterruptRacesChild
+{
+public:
+ TestInterruptRacesChild() : mHasReply(false) { }
+ virtual ~TestInterruptRacesChild() { }
+
+protected:
+ virtual bool
+ RecvStart() override;
+
+ virtual bool
+ AnswerRace(bool* hasRace) override;
+
+ virtual bool
+ AnswerStackFrame() override;
+
+ virtual bool
+ AnswerStackFrame3() override;
+
+ virtual bool
+ RecvWakeup() override;
+
+ virtual bool
+ RecvWakeup3() override;
+
+ virtual bool
+ AnswerChild() override;
+
+ virtual mozilla::ipc::RacyInterruptPolicy
+ MediateInterruptRace(const MessageInfo& parent,
+ const MessageInfo& child) override
+ {
+ return MediateRace(parent, child);
+ }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+
+private:
+ bool mHasReply;
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestInterruptRaces_h
diff --git a/ipc/ipdl/test/cxx/TestInterruptShutdownRace.cpp b/ipc/ipdl/test/cxx/TestInterruptShutdownRace.cpp
new file mode 100644
index 000000000..38f7993da
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestInterruptShutdownRace.cpp
@@ -0,0 +1,134 @@
+#include "TestInterruptShutdownRace.h"
+
+#include "base/task.h"
+#include "IPDLUnitTests.h" // fail etc.
+#include "IPDLUnitTestSubprocess.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+namespace {
+
+// NB: this test does its own shutdown, rather than going through
+// QuitParent(), because it's testing degenerate edge cases
+
+void DeleteSubprocess()
+{
+ delete gSubprocess;
+ gSubprocess = nullptr;
+}
+
+void Done()
+{
+ passed(__FILE__);
+ QuitParent();
+}
+
+} // namespace <anon>
+
+TestInterruptShutdownRaceParent::TestInterruptShutdownRaceParent()
+{
+ MOZ_COUNT_CTOR(TestInterruptShutdownRaceParent);
+}
+
+TestInterruptShutdownRaceParent::~TestInterruptShutdownRaceParent()
+{
+ MOZ_COUNT_DTOR(TestInterruptShutdownRaceParent);
+}
+
+void
+TestInterruptShutdownRaceParent::Main()
+{
+ if (!SendStart())
+ fail("sending Start");
+}
+
+bool
+TestInterruptShutdownRaceParent::RecvStartDeath()
+{
+ // this will be ordered before the OnMaybeDequeueOne event of
+ // Orphan in the queue
+ MessageLoop::current()->PostTask(
+ NewNonOwningRunnableMethod(this,
+ &TestInterruptShutdownRaceParent::StartShuttingDown));
+ return true;
+}
+
+void
+TestInterruptShutdownRaceParent::StartShuttingDown()
+{
+ // NB: we sleep here to try and avoid receiving the Orphan message
+ // while waiting for the CallExit() reply. if we fail at that, it
+ // will cause the test to pass spuriously, because there won't be
+ // an OnMaybeDequeueOne task for Orphan
+ PR_Sleep(2000);
+
+ if (CallExit())
+ fail("connection was supposed to be interrupted");
+
+ Close();
+
+ delete static_cast<TestInterruptShutdownRaceParent*>(gParentActor);
+ gParentActor = nullptr;
+
+ XRE_GetIOMessageLoop()->PostTask(NewRunnableFunction(DeleteSubprocess));
+
+ // this is ordered after the OnMaybeDequeueOne event in the queue
+ MessageLoop::current()->PostTask(NewRunnableFunction(Done));
+
+ // |this| has been deleted, be mindful
+}
+
+bool
+TestInterruptShutdownRaceParent::RecvOrphan()
+{
+ // it would be nice to fail() here, but we'll process this message
+ // while waiting for the reply CallExit(). The OnMaybeDequeueOne
+ // task will still be in the queue, it just wouldn't have had any
+ // work to do, if we hadn't deleted ourself
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+TestInterruptShutdownRaceChild::TestInterruptShutdownRaceChild()
+{
+ MOZ_COUNT_CTOR(TestInterruptShutdownRaceChild);
+}
+
+TestInterruptShutdownRaceChild::~TestInterruptShutdownRaceChild()
+{
+ MOZ_COUNT_DTOR(TestInterruptShutdownRaceChild);
+}
+
+bool
+TestInterruptShutdownRaceChild::RecvStart()
+{
+ if (!SendStartDeath())
+ fail("sending StartDeath");
+
+ // See comment in StartShuttingDown(): we want to send Orphan()
+ // while the parent is in its PR_Sleep()
+ PR_Sleep(1000);
+
+ if (!SendOrphan())
+ fail("sending Orphan");
+
+ return true;
+}
+
+bool
+TestInterruptShutdownRaceChild::AnswerExit()
+{
+ _exit(0);
+ NS_RUNTIMEABORT("unreached");
+ return false;
+}
+
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestInterruptShutdownRace.h b/ipc/ipdl/test/cxx/TestInterruptShutdownRace.h
new file mode 100644
index 000000000..c291b14a6
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestInterruptShutdownRace.h
@@ -0,0 +1,64 @@
+#ifndef mozilla__ipdltest_TestInterruptShutdownRace_h
+#define mozilla__ipdltest_TestInterruptShutdownRace_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestInterruptShutdownRaceParent.h"
+#include "mozilla/_ipdltest/PTestInterruptShutdownRaceChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestInterruptShutdownRaceParent :
+ public PTestInterruptShutdownRaceParent
+{
+public:
+ TestInterruptShutdownRaceParent();
+ virtual ~TestInterruptShutdownRaceParent();
+
+ static bool RunTestInProcesses() { return true; }
+ // FIXME/bug 703323 Could work if modified
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+
+ virtual bool RecvStartDeath() override;
+
+ virtual bool RecvOrphan() override;
+
+protected:
+ void StartShuttingDown();
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (AbnormalShutdown != why)
+ fail("unexpected destruction!");
+ }
+};
+
+
+class TestInterruptShutdownRaceChild :
+ public PTestInterruptShutdownRaceChild
+{
+public:
+ TestInterruptShutdownRaceChild();
+ virtual ~TestInterruptShutdownRaceChild();
+
+protected:
+ virtual bool RecvStart() override;
+
+ virtual bool AnswerExit() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ fail("should have 'crashed'!");
+ }
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestInterruptShutdownRace_h
diff --git a/ipc/ipdl/test/cxx/TestJSON.cpp b/ipc/ipdl/test/cxx/TestJSON.cpp
new file mode 100644
index 000000000..ea742b705
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestJSON.cpp
@@ -0,0 +1,129 @@
+#include "TestJSON.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+#define test_assert(_cond, _msg) \
+ if (!(_cond)) fail(_msg)
+
+namespace mozilla {
+namespace _ipdltest {
+
+static nsString
+String(const char* const str)
+{
+ return NS_ConvertUTF8toUTF16(str);
+}
+
+static void
+Array123(InfallibleTArray<JSONVariant>& a123)
+{
+ a123.AppendElement(1); a123.AppendElement(2); a123.AppendElement(3);
+
+ test_assert(a123 == a123, "operator== is broken");
+}
+
+template<class HandleT>
+JSONVariant
+MakeTestVariant(HandleT* handle)
+{
+ // In JS syntax:
+ //
+ // return [
+ // undefined, null, true, 1.25, "test string",
+ // handle,
+ // [ 1, 2, 3 ],
+ // { "undefined" : undefined,
+ // "null" : null,
+ // "true" : true,
+ // "1.25" : 1.25,
+ // "string" : "string"
+ // "handle" : handle,
+ // "array" : [ 1, 2, 3 ]
+ // }
+ // ]
+ //
+ InfallibleTArray<JSONVariant> outer;
+
+ outer.AppendElement(void_t());
+ outer.AppendElement(null_t());
+ outer.AppendElement(true);
+ outer.AppendElement(1.25);
+ outer.AppendElement(String("test string"));
+
+ outer.AppendElement(handle);
+
+ InfallibleTArray<JSONVariant> tmp;
+ Array123(tmp);
+ outer.AppendElement(tmp);
+
+ InfallibleTArray<KeyValue> obj;
+ obj.AppendElement(KeyValue(String("undefined"), void_t()));
+ obj.AppendElement(KeyValue(String("null"), null_t()));
+ obj.AppendElement(KeyValue(String("true"), true));
+ obj.AppendElement(KeyValue(String("1.25"), 1.25));
+ obj.AppendElement(KeyValue(String("string"), String("value")));
+ obj.AppendElement(KeyValue(String("handle"), handle));
+ InfallibleTArray<JSONVariant> tmp2;
+ Array123(tmp2);
+ obj.AppendElement(KeyValue(String("array"), tmp2));
+
+ outer.AppendElement(obj);
+
+ test_assert(outer == outer, "operator== is broken");
+
+ return JSONVariant(outer);
+}
+
+//-----------------------------------------------------------------------------
+// parent
+
+void
+TestJSONParent::Main()
+{
+ if (!SendStart())
+ fail("sending Start");
+}
+
+
+bool
+TestJSONParent::RecvTest(const JSONVariant& i,
+ JSONVariant* o)
+{
+ test_assert(i == i, "operator== is broken");
+ test_assert(i == MakeTestVariant(mKid), "inparam mangled en route");
+
+ *o = i;
+
+ test_assert(i == *o, "operator= is broken");
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// child
+
+bool
+TestJSONChild::RecvStart()
+{
+ if (!SendPTestHandleConstructor())
+ fail("sending Handle ctor");
+
+ JSONVariant i(MakeTestVariant(mKid));
+ test_assert(i == i, "operator== is broken");
+ test_assert(i == MakeTestVariant(mKid), "copy ctor is broken");
+
+ JSONVariant o;
+ if (!SendTest(i, &o))
+ fail("sending Test");
+
+ test_assert(i == o, "round-trip mangled input data");
+ test_assert(o == MakeTestVariant(mKid), "outparam mangled en route");
+
+ Close();
+ return true;
+}
+
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestJSON.h b/ipc/ipdl/test/cxx/TestJSON.h
new file mode 100644
index 000000000..55d6e041b
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestJSON.h
@@ -0,0 +1,111 @@
+#ifndef mozilla__ipdltest_TestJSON_h
+#define mozilla__ipdltest_TestJSON_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestJSONParent.h"
+#include "mozilla/_ipdltest/PTestJSONChild.h"
+
+#include "mozilla/_ipdltest/PTestHandleParent.h"
+#include "mozilla/_ipdltest/PTestHandleChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+class TestHandleParent :
+ public PTestHandleParent
+{
+public:
+ TestHandleParent() { }
+ virtual ~TestHandleParent() { }
+
+protected:
+ virtual void ActorDestroy(ActorDestroyReason why) override {}
+};
+
+class TestJSONParent :
+ public PTestJSONParent
+{
+public:
+ TestJSONParent() { }
+ virtual ~TestJSONParent() { }
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+protected:
+ virtual bool
+ RecvTest(const JSONVariant& i,
+ JSONVariant* o) override;
+
+ virtual PTestHandleParent* AllocPTestHandleParent() override
+ {
+ return mKid = new TestHandleParent();
+ }
+
+ virtual bool DeallocPTestHandleParent(PTestHandleParent* actor) override
+ {
+ delete actor;
+ return true;
+ }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+
+ PTestHandleParent* mKid;
+};
+
+
+class TestHandleChild :
+ public PTestHandleChild
+{
+public:
+ TestHandleChild() { }
+ virtual ~TestHandleChild() { }
+};
+
+class TestJSONChild :
+ public PTestJSONChild
+{
+public:
+ TestJSONChild() { }
+ virtual ~TestJSONChild() { }
+
+protected:
+ virtual bool
+ RecvStart() override;
+
+ virtual PTestHandleChild* AllocPTestHandleChild() override
+ {
+ return mKid = new TestHandleChild();
+ }
+
+ virtual bool DeallocPTestHandleChild(PTestHandleChild* actor) override
+ {
+ delete actor;
+ return true;
+ }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+
+ PTestHandleChild* mKid;
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestJSON_h
diff --git a/ipc/ipdl/test/cxx/TestLatency.cpp b/ipc/ipdl/test/cxx/TestLatency.cpp
new file mode 100644
index 000000000..094622b82
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestLatency.cpp
@@ -0,0 +1,258 @@
+#include "TestLatency.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+// A ping/pong trial takes O(100us) or more, so if we don't have 10us
+// resolution or better, the results will not be terribly useful
+static const double kTimingResolutionCutoff = 0.00001; // 10us
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+TestLatencyParent::TestLatencyParent() :
+ mStart(),
+ mPPTimeTotal(),
+ mPP5TimeTotal(),
+ mRpcTimeTotal(),
+ mPPTrialsToGo(NR_TRIALS),
+ mPP5TrialsToGo(NR_TRIALS),
+ mNumChildProcessedCompressedSpams(0)
+{
+ MOZ_COUNT_CTOR(TestLatencyParent);
+}
+
+TestLatencyParent::~TestLatencyParent()
+{
+ MOZ_COUNT_DTOR(TestLatencyParent);
+}
+
+void
+TestLatencyParent::Main()
+{
+ TimeDuration resolution = TimeDuration::Resolution();
+ if (resolution.ToSeconds() > kTimingResolutionCutoff) {
+ puts(" (skipping TestLatency, timing resolution is too poor)");
+ Close();
+ return;
+ }
+
+ printf(" timing resolution: %g seconds\n",
+ resolution.ToSecondsSigDigits());
+
+ if (mozilla::ipc::LoggingEnabled())
+ NS_RUNTIMEABORT("you really don't want to log all IPC messages during this test, trust me");
+
+ PingPongTrial();
+}
+
+void
+TestLatencyParent::PingPongTrial()
+{
+ mStart = TimeStamp::Now();
+ if (!SendPing())
+ fail("sending Ping()");
+}
+
+void
+TestLatencyParent::Ping5Pong5Trial()
+{
+ mStart = TimeStamp::Now();
+
+ if (!SendPing5() ||
+ !SendPing5() ||
+ !SendPing5() ||
+ !SendPing5() ||
+ !SendPing5())
+ fail("sending Ping5()");
+}
+
+bool
+TestLatencyParent::RecvPong()
+{
+ TimeDuration thisTrial = (TimeStamp::Now() - mStart);
+ mPPTimeTotal += thisTrial;
+
+ if (0 == (mPPTrialsToGo % 1000))
+ printf(" PP trial %d: %g\n",
+ mPPTrialsToGo, thisTrial.ToSecondsSigDigits());
+
+ if (--mPPTrialsToGo > 0)
+ PingPongTrial();
+ else
+ Ping5Pong5Trial();
+ return true;
+}
+
+bool
+TestLatencyParent::RecvPong5()
+{
+ if (PTestLatency::PING5 != state())
+ return true;
+
+ TimeDuration thisTrial = (TimeStamp::Now() - mStart);
+ mPP5TimeTotal += thisTrial;
+
+ if (0 == (mPP5TrialsToGo % 1000))
+ printf(" PP5 trial %d: %g\n",
+ mPP5TrialsToGo, thisTrial.ToSecondsSigDigits());
+
+ if (0 < --mPP5TrialsToGo)
+ Ping5Pong5Trial();
+ else
+ RpcTrials();
+
+ return true;
+}
+
+void
+TestLatencyParent::RpcTrials()
+{
+ TimeStamp start = TimeStamp::Now();
+ for (int i = 0; i < NR_TRIALS; ++i) {
+ if (!CallRpc())
+ fail("can't call Rpc()");
+ if (0 == (i % 1000))
+ printf(" Rpc trial %d\n", i);
+ }
+ mRpcTimeTotal = (TimeStamp::Now() - start);
+
+ SpamTrial();
+}
+
+void
+TestLatencyParent::SpamTrial()
+{
+ TimeStamp start = TimeStamp::Now();
+ for (int i = 0; i < NR_SPAMS - 1; ++i) {
+ if (!SendSpam())
+ fail("sending Spam()");
+ if (0 == (i % 10000))
+ printf(" Spam trial %d\n", i);
+ }
+
+ // Synchronize with the child process to ensure all messages have
+ // been processed. This adds the overhead of a reply message from
+ // child-->here, but should be insignificant compared to >>
+ // NR_SPAMS.
+ if (!CallSynchro())
+ fail("calling Synchro()");
+
+ mSpamTimeTotal = (TimeStamp::Now() - start);
+
+ CompressedSpamTrial();
+}
+
+void
+TestLatencyParent::CompressedSpamTrial()
+{
+ for (int i = 0; i < NR_SPAMS; ++i) {
+ if (!SendCompressedSpam(i + 1))
+ fail("sending CompressedSpam()");
+ if (0 == (i % 10000))
+ printf(" CompressedSpam trial %d\n", i);
+ }
+
+ uint32_t lastSeqno;
+ if (!CallSynchro2(&lastSeqno, &mNumChildProcessedCompressedSpams))
+ fail("calling Synchro2()");
+
+ if (lastSeqno != NR_SPAMS)
+ fail("last seqno was %u, expected %u", lastSeqno, NR_SPAMS);
+
+ // NB: since this is testing an optimization, it's somewhat bogus.
+ // Need to make a warning if it actually intermittently fails in
+ // practice, which is doubtful.
+ if (!(mNumChildProcessedCompressedSpams < NR_SPAMS))
+ fail("Didn't compress any messages?");
+
+ Exit();
+}
+
+void
+TestLatencyParent::Exit()
+{
+ Close();
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+TestLatencyChild::TestLatencyChild()
+ : mLastSeqno(0)
+ , mNumProcessedCompressedSpams(0)
+{
+ MOZ_COUNT_CTOR(TestLatencyChild);
+}
+
+TestLatencyChild::~TestLatencyChild()
+{
+ MOZ_COUNT_DTOR(TestLatencyChild);
+}
+
+bool
+TestLatencyChild::RecvPing()
+{
+ SendPong();
+ return true;
+}
+
+bool
+TestLatencyChild::RecvPing5()
+{
+ if (PTestLatency::PONG1 != state())
+ return true;
+
+ if (!SendPong5() ||
+ !SendPong5() ||
+ !SendPong5() ||
+ !SendPong5() ||
+ !SendPong5())
+ fail("sending Pong5()");
+
+ return true;
+}
+
+bool
+TestLatencyChild::AnswerRpc()
+{
+ return true;
+}
+
+bool
+TestLatencyChild::RecvSpam()
+{
+ // no-op
+ return true;
+}
+
+bool
+TestLatencyChild::AnswerSynchro()
+{
+ return true;
+}
+
+bool
+TestLatencyChild::RecvCompressedSpam(const uint32_t& seqno)
+{
+ if (seqno <= mLastSeqno)
+ fail("compressed seqnos must monotonically increase");
+
+ mLastSeqno = seqno;
+ ++mNumProcessedCompressedSpams;
+ return true;
+}
+
+bool
+TestLatencyChild::AnswerSynchro2(uint32_t* lastSeqno,
+ uint32_t* numMessagesDispatched)
+{
+ *lastSeqno = mLastSeqno;
+ *numMessagesDispatched = mNumProcessedCompressedSpams;
+ return true;
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestLatency.h b/ipc/ipdl/test/cxx/TestLatency.h
new file mode 100644
index 000000000..d179213db
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestLatency.h
@@ -0,0 +1,111 @@
+#ifndef mozilla__ipdltest_TestLatency_h
+#define mozilla__ipdltest_TestLatency_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestLatencyParent.h"
+#include "mozilla/_ipdltest/PTestLatencyChild.h"
+
+#include "mozilla/TimeStamp.h"
+
+#define NR_TRIALS 10000
+#define NR_SPAMS 25000
+
+namespace mozilla {
+namespace _ipdltest {
+
+class TestLatencyParent :
+ public PTestLatencyParent
+{
+private:
+ typedef mozilla::TimeStamp TimeStamp;
+ typedef mozilla::TimeDuration TimeDuration;
+
+public:
+ TestLatencyParent();
+ virtual ~TestLatencyParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+protected:
+ virtual bool RecvPong() override;
+ virtual bool RecvPong5() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+
+ passed("\n"
+ " average #ping-pong/sec: %g\n"
+ " average #ping5-pong5/sec: %g\n"
+ " average #RPC call-answer/sec: %g\n"
+ " average #spams/sec: %g\n"
+ " pct. spams compressed away: %g\n",
+ double(NR_TRIALS) / mPPTimeTotal.ToSecondsSigDigits(),
+ double(NR_TRIALS) / mPP5TimeTotal.ToSecondsSigDigits(),
+ double(NR_TRIALS) / mRpcTimeTotal.ToSecondsSigDigits(),
+ double(NR_SPAMS) / mSpamTimeTotal.ToSecondsSigDigits(),
+ 100.0 * (double(NR_SPAMS - mNumChildProcessedCompressedSpams) /
+ double(NR_SPAMS)));
+
+ QuitParent();
+ }
+
+private:
+ void PingPongTrial();
+ void Ping5Pong5Trial();
+ void RpcTrials();
+ void SpamTrial();
+ void CompressedSpamTrial();
+ void Exit();
+
+ TimeStamp mStart;
+ TimeDuration mPPTimeTotal;
+ TimeDuration mPP5TimeTotal;
+ TimeDuration mRpcTimeTotal;
+ TimeDuration mSpamTimeTotal;
+
+ int mPPTrialsToGo;
+ int mPP5TrialsToGo;
+ uint32_t mNumChildProcessedCompressedSpams;
+};
+
+
+class TestLatencyChild :
+ public PTestLatencyChild
+{
+public:
+ TestLatencyChild();
+ virtual ~TestLatencyChild();
+
+protected:
+ virtual bool RecvPing() override;
+ virtual bool RecvPing5() override;
+ virtual bool AnswerRpc() override;
+ virtual bool RecvSpam() override;
+ virtual bool AnswerSynchro() override;
+ virtual bool RecvCompressedSpam(const uint32_t& seqno) override;
+ virtual bool AnswerSynchro2(uint32_t* lastSeqno,
+ uint32_t* numMessagesDispatched) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+
+ uint32_t mLastSeqno;
+ uint32_t mNumProcessedCompressedSpams;
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestLatency_h
diff --git a/ipc/ipdl/test/cxx/TestManyChildAllocs.cpp b/ipc/ipdl/test/cxx/TestManyChildAllocs.cpp
new file mode 100644
index 000000000..7dee30e63
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestManyChildAllocs.cpp
@@ -0,0 +1,104 @@
+#include "TestManyChildAllocs.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+
+#define NALLOCS 10
+
+namespace mozilla {
+namespace _ipdltest {
+
+// parent code
+
+TestManyChildAllocsParent::TestManyChildAllocsParent()
+{
+ MOZ_COUNT_CTOR(TestManyChildAllocsParent);
+}
+
+TestManyChildAllocsParent::~TestManyChildAllocsParent()
+{
+ MOZ_COUNT_DTOR(TestManyChildAllocsParent);
+}
+
+void
+TestManyChildAllocsParent::Main()
+{
+ if (!SendGo())
+ fail("can't send Go()");
+}
+
+bool
+TestManyChildAllocsParent::RecvDone()
+{
+ // explicitly *not* cleaning up, so we can sanity-check IPDL's
+ // auto-shutdown/cleanup handling
+ Close();
+
+ return true;
+}
+
+bool
+TestManyChildAllocsParent::DeallocPTestManyChildAllocsSubParent(
+ PTestManyChildAllocsSubParent* __a)
+{
+ delete __a; return true;
+}
+
+PTestManyChildAllocsSubParent*
+TestManyChildAllocsParent::AllocPTestManyChildAllocsSubParent()
+{
+ return new TestManyChildAllocsSubParent();
+}
+
+
+// child code
+
+TestManyChildAllocsChild::TestManyChildAllocsChild()
+{
+ MOZ_COUNT_CTOR(TestManyChildAllocsChild);
+}
+
+TestManyChildAllocsChild::~TestManyChildAllocsChild()
+{
+ MOZ_COUNT_DTOR(TestManyChildAllocsChild);
+}
+
+bool TestManyChildAllocsChild::RecvGo()
+{
+ for (int i = 0; i < NALLOCS; ++i) {
+ PTestManyChildAllocsSubChild* child =
+ SendPTestManyChildAllocsSubConstructor();
+
+ if (!child)
+ fail("can't send ctor()");
+
+ if (!child->SendHello())
+ fail("can't send Hello()");
+ }
+
+ size_t len = ManagedPTestManyChildAllocsSubChild().Count();
+ if (NALLOCS != len)
+ fail("expected %lu kids, got %lu", NALLOCS, len);
+
+ if (!SendDone())
+ fail("can't send Done()");
+
+ return true;
+}
+
+bool
+TestManyChildAllocsChild::DeallocPTestManyChildAllocsSubChild(
+ PTestManyChildAllocsSubChild* __a)
+{
+ delete __a; return true;
+}
+
+PTestManyChildAllocsSubChild*
+TestManyChildAllocsChild::AllocPTestManyChildAllocsSubChild()
+{
+ return new TestManyChildAllocsSubChild();
+}
+
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestManyChildAllocs.h b/ipc/ipdl/test/cxx/TestManyChildAllocs.h
new file mode 100644
index 000000000..d29a35cb8
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestManyChildAllocs.h
@@ -0,0 +1,94 @@
+#ifndef mozilla__ipdltest_TestManyChildAllocs_h
+#define mozilla__ipdltest_TestManyChildAllocs_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestManyChildAllocsParent.h"
+#include "mozilla/_ipdltest/PTestManyChildAllocsChild.h"
+
+#include "mozilla/_ipdltest/PTestManyChildAllocsSubParent.h"
+#include "mozilla/_ipdltest/PTestManyChildAllocsSubChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+// top-level protocol
+
+class TestManyChildAllocsParent :
+ public PTestManyChildAllocsParent
+{
+public:
+ TestManyChildAllocsParent();
+ virtual ~TestManyChildAllocsParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+protected:
+ virtual bool RecvDone() override;
+ virtual bool DeallocPTestManyChildAllocsSubParent(PTestManyChildAllocsSubParent* __a) override;
+ virtual PTestManyChildAllocsSubParent* AllocPTestManyChildAllocsSubParent() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+};
+
+
+class TestManyChildAllocsChild :
+ public PTestManyChildAllocsChild
+{
+public:
+ TestManyChildAllocsChild();
+ virtual ~TestManyChildAllocsChild();
+
+protected:
+ virtual bool RecvGo() override;
+ virtual bool DeallocPTestManyChildAllocsSubChild(PTestManyChildAllocsSubChild* __a) override;
+ virtual PTestManyChildAllocsSubChild* AllocPTestManyChildAllocsSubChild() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+};
+
+
+// do-nothing sub-protocol actors
+
+class TestManyChildAllocsSubParent :
+ public PTestManyChildAllocsSubParent
+{
+public:
+ TestManyChildAllocsSubParent() { }
+ virtual ~TestManyChildAllocsSubParent() { }
+
+protected:
+ virtual void ActorDestroy(ActorDestroyReason why) override {}
+ virtual bool RecvHello() override { return true; }
+};
+
+
+class TestManyChildAllocsSubChild :
+ public PTestManyChildAllocsSubChild
+{
+public:
+ TestManyChildAllocsSubChild() { }
+ virtual ~TestManyChildAllocsSubChild() { }
+};
+
+
+
+} // namepsace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestManyChildAllocs_h
diff --git a/ipc/ipdl/test/cxx/TestMultiMgrs.cpp b/ipc/ipdl/test/cxx/TestMultiMgrs.cpp
new file mode 100644
index 000000000..422b3fea3
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestMultiMgrs.cpp
@@ -0,0 +1,104 @@
+#include "TestMultiMgrs.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+#include "mozilla/ipc/ProtocolUtils.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+void
+TestMultiMgrsParent::Main()
+{
+ TestMultiMgrsLeftParent* leftie = new TestMultiMgrsLeftParent();
+ if (!SendPTestMultiMgrsLeftConstructor(leftie))
+ fail("error sending ctor");
+
+ TestMultiMgrsRightParent* rightie = new TestMultiMgrsRightParent();
+ if (!SendPTestMultiMgrsRightConstructor(rightie))
+ fail("error sending ctor");
+
+ TestMultiMgrsBottomParent* bottomL = new TestMultiMgrsBottomParent();
+ if (!leftie->SendPTestMultiMgrsBottomConstructor(bottomL))
+ fail("error sending ctor");
+
+ TestMultiMgrsBottomParent* bottomR = new TestMultiMgrsBottomParent();
+ if (!rightie->SendPTestMultiMgrsBottomConstructor(bottomR))
+ fail("error sending ctor");
+
+ if (!leftie->HasChild(bottomL))
+ fail("leftie didn't have a child it was supposed to!");
+ if (leftie->HasChild(bottomR))
+ fail("leftie had rightie's child!");
+
+ if (!rightie->HasChild(bottomR))
+ fail("rightie didn't have a child it was supposed to!");
+ if (rightie->HasChild(bottomL))
+ fail("rightie had rightie's child!");
+
+ if (!SendCheck())
+ fail("couldn't kick off the child-side check");
+}
+
+bool
+TestMultiMgrsParent::RecvOK()
+{
+ Close();
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+bool
+TestMultiMgrsLeftChild::RecvPTestMultiMgrsBottomConstructor(
+ PTestMultiMgrsBottomChild* actor)
+{
+ static_cast<TestMultiMgrsChild*>(Manager())->mBottomL = actor;
+ return true;
+}
+
+bool
+TestMultiMgrsRightChild::RecvPTestMultiMgrsBottomConstructor(
+ PTestMultiMgrsBottomChild* actor)
+{
+ static_cast<TestMultiMgrsChild*>(Manager())->mBottomR = actor;
+ return true;
+}
+
+bool
+TestMultiMgrsChild::RecvCheck()
+{
+ if (1 != ManagedPTestMultiMgrsLeftChild().Count())
+ fail("where's leftie?");
+ if (1 != ManagedPTestMultiMgrsRightChild().Count())
+ fail("where's rightie?");
+
+ TestMultiMgrsLeftChild* leftie =
+ static_cast<TestMultiMgrsLeftChild*>(
+ LoneManagedOrNullAsserts(ManagedPTestMultiMgrsLeftChild()));
+ TestMultiMgrsRightChild* rightie =
+ static_cast<TestMultiMgrsRightChild*>(
+ LoneManagedOrNullAsserts(ManagedPTestMultiMgrsRightChild()));
+
+ if (!leftie->HasChild(mBottomL))
+ fail("leftie didn't have a child it was supposed to!");
+ if (leftie->HasChild(mBottomR))
+ fail("leftie had rightie's child!");
+
+ if (!rightie->HasChild(mBottomR))
+ fail("rightie didn't have a child it was supposed to!");
+ if (rightie->HasChild(mBottomL))
+ fail("rightie had leftie's child!");
+
+ if (!SendOK())
+ fail("couldn't send OK()");
+
+ return true;
+}
+
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestMultiMgrs.h b/ipc/ipdl/test/cxx/TestMultiMgrs.h
new file mode 100644
index 000000000..3dfab7430
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestMultiMgrs.h
@@ -0,0 +1,250 @@
+#ifndef mozilla__ipdltest_TestMultiMgrs_h
+#define mozilla__ipdltest_TestMultiMgrs_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestMultiMgrsParent.h"
+#include "mozilla/_ipdltest/PTestMultiMgrsChild.h"
+#include "mozilla/_ipdltest/PTestMultiMgrsBottomParent.h"
+#include "mozilla/_ipdltest/PTestMultiMgrsBottomChild.h"
+#include "mozilla/_ipdltest/PTestMultiMgrsLeftParent.h"
+#include "mozilla/_ipdltest/PTestMultiMgrsLeftChild.h"
+#include "mozilla/_ipdltest/PTestMultiMgrsRightParent.h"
+#include "mozilla/_ipdltest/PTestMultiMgrsRightChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// Parent side
+//
+
+class TestMultiMgrsBottomParent :
+ public PTestMultiMgrsBottomParent
+{
+public:
+ TestMultiMgrsBottomParent() { }
+ virtual ~TestMultiMgrsBottomParent() { }
+
+protected:
+ virtual void ActorDestroy(ActorDestroyReason why) override {}
+};
+
+class TestMultiMgrsLeftParent :
+ public PTestMultiMgrsLeftParent
+{
+public:
+ TestMultiMgrsLeftParent() { }
+ virtual ~TestMultiMgrsLeftParent() { }
+
+ bool HasChild(TestMultiMgrsBottomParent* c)
+ {
+ return ManagedPTestMultiMgrsBottomParent().Contains(c);
+ }
+
+protected:
+ virtual void ActorDestroy(ActorDestroyReason why) override {}
+
+ virtual PTestMultiMgrsBottomParent* AllocPTestMultiMgrsBottomParent() override
+ {
+ return new TestMultiMgrsBottomParent();
+ }
+
+ virtual bool DeallocPTestMultiMgrsBottomParent(PTestMultiMgrsBottomParent* actor) override
+ {
+ delete actor;
+ return true;
+ }
+};
+
+class TestMultiMgrsRightParent :
+ public PTestMultiMgrsRightParent
+{
+public:
+ TestMultiMgrsRightParent() { }
+ virtual ~TestMultiMgrsRightParent() { }
+
+ bool HasChild(TestMultiMgrsBottomParent* c)
+ {
+ return ManagedPTestMultiMgrsBottomParent().Contains(c);
+ }
+
+protected:
+ virtual void ActorDestroy(ActorDestroyReason why) override {}
+
+ virtual PTestMultiMgrsBottomParent* AllocPTestMultiMgrsBottomParent() override
+ {
+ return new TestMultiMgrsBottomParent();
+ }
+
+ virtual bool DeallocPTestMultiMgrsBottomParent(PTestMultiMgrsBottomParent* actor) override
+ {
+ delete actor;
+ return true;
+ }
+};
+
+class TestMultiMgrsParent :
+ public PTestMultiMgrsParent
+{
+public:
+ TestMultiMgrsParent() { }
+ virtual ~TestMultiMgrsParent() { }
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+protected:
+ virtual bool RecvOK() override;
+
+ virtual PTestMultiMgrsLeftParent* AllocPTestMultiMgrsLeftParent() override
+ {
+ return new TestMultiMgrsLeftParent();
+ }
+
+ virtual bool DeallocPTestMultiMgrsLeftParent(PTestMultiMgrsLeftParent* actor) override
+ {
+ delete actor;
+ return true;
+ }
+
+ virtual PTestMultiMgrsRightParent* AllocPTestMultiMgrsRightParent() override
+ {
+ return new TestMultiMgrsRightParent();
+ }
+
+ virtual bool DeallocPTestMultiMgrsRightParent(PTestMultiMgrsRightParent* actor) override
+ {
+ delete actor;
+ return true;
+ }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+};
+
+//-----------------------------------------------------------------------------
+// Child side
+//
+
+class TestMultiMgrsBottomChild :
+ public PTestMultiMgrsBottomChild
+{
+public:
+ TestMultiMgrsBottomChild() { }
+ virtual ~TestMultiMgrsBottomChild() { }
+};
+
+class TestMultiMgrsLeftChild :
+ public PTestMultiMgrsLeftChild
+{
+public:
+ TestMultiMgrsLeftChild() { }
+ virtual ~TestMultiMgrsLeftChild() { }
+
+ bool HasChild(PTestMultiMgrsBottomChild* c)
+ {
+ return ManagedPTestMultiMgrsBottomChild().Contains(c);
+ }
+
+protected:
+ virtual bool RecvPTestMultiMgrsBottomConstructor(PTestMultiMgrsBottomChild* actor) override;
+
+ virtual PTestMultiMgrsBottomChild* AllocPTestMultiMgrsBottomChild() override
+ {
+ return new TestMultiMgrsBottomChild();
+ }
+
+ virtual bool DeallocPTestMultiMgrsBottomChild(PTestMultiMgrsBottomChild* actor) override
+ {
+ delete actor;
+ return true;
+ }
+};
+
+class TestMultiMgrsRightChild :
+ public PTestMultiMgrsRightChild
+{
+public:
+ TestMultiMgrsRightChild() { }
+ virtual ~TestMultiMgrsRightChild() { }
+
+ bool HasChild(PTestMultiMgrsBottomChild* c)
+ {
+ return ManagedPTestMultiMgrsBottomChild().Contains(c);
+ }
+
+protected:
+ virtual bool RecvPTestMultiMgrsBottomConstructor(PTestMultiMgrsBottomChild* actor) override;
+
+ virtual PTestMultiMgrsBottomChild* AllocPTestMultiMgrsBottomChild() override
+ {
+ return new TestMultiMgrsBottomChild();
+ }
+
+ virtual bool DeallocPTestMultiMgrsBottomChild(PTestMultiMgrsBottomChild* actor) override
+ {
+ delete actor;
+ return true;
+ }
+};
+
+class TestMultiMgrsChild :
+ public PTestMultiMgrsChild
+{
+public:
+ TestMultiMgrsChild() { }
+ virtual ~TestMultiMgrsChild() { }
+
+ void Main();
+
+ PTestMultiMgrsBottomChild* mBottomL;
+ PTestMultiMgrsBottomChild* mBottomR;
+
+protected:
+ virtual bool RecvCheck() override;
+
+ virtual PTestMultiMgrsLeftChild* AllocPTestMultiMgrsLeftChild() override
+ {
+ return new TestMultiMgrsLeftChild();
+ }
+
+ virtual bool DeallocPTestMultiMgrsLeftChild(PTestMultiMgrsLeftChild* actor) override
+ {
+ delete actor;
+ return true;
+ }
+
+ virtual PTestMultiMgrsRightChild* AllocPTestMultiMgrsRightChild() override
+ {
+ return new TestMultiMgrsRightChild();
+ }
+
+ virtual bool DeallocPTestMultiMgrsRightChild(PTestMultiMgrsRightChild* actor) override
+ {
+ delete actor;
+ return true;
+ }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitChild();
+ }
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestMultiMgrs_h
diff --git a/ipc/ipdl/test/cxx/TestNestedLoops.cpp b/ipc/ipdl/test/cxx/TestNestedLoops.cpp
new file mode 100644
index 000000000..0e0ba496d
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestNestedLoops.cpp
@@ -0,0 +1,98 @@
+#include "base/basictypes.h"
+
+#include "nsThreadUtils.h"
+
+#include "TestNestedLoops.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+TestNestedLoopsParent::TestNestedLoopsParent() : mBreakNestedLoop(false)
+{
+ MOZ_COUNT_CTOR(TestNestedLoopsParent);
+}
+
+TestNestedLoopsParent::~TestNestedLoopsParent()
+{
+ MOZ_COUNT_DTOR(TestNestedLoopsParent);
+}
+
+void
+TestNestedLoopsParent::Main()
+{
+ if (!SendStart())
+ fail("sending Start");
+
+ // sigh ... spin for a while to let Nonce arrive
+ puts(" (sleeping to wait for nonce ... sorry)");
+ PR_Sleep(5000);
+
+ // while waiting for the reply to R, we'll receive Nonce
+ if (!CallR())
+ fail("calling R");
+
+ Close();
+}
+
+bool
+TestNestedLoopsParent::RecvNonce()
+{
+ // if we have an OnMaybeDequeueOne waiting for us (we may not, due
+ // to the inherent race condition in this test, then this event
+ // must be ordered after it in the queue
+ MessageLoop::current()->PostTask(
+ NewNonOwningRunnableMethod(this, &TestNestedLoopsParent::BreakNestedLoop));
+
+ // sigh ... spin for a while to let the reply to R arrive
+ puts(" (sleeping to wait for reply to R ... sorry)");
+ PR_Sleep(5000);
+
+ // sigh ... we have no idea when code might do this
+ do {
+ if (!NS_ProcessNextEvent(nullptr, false))
+ fail("expected at least one pending event");
+ } while (!mBreakNestedLoop);
+
+ return true;
+}
+
+void
+TestNestedLoopsParent::BreakNestedLoop()
+{
+ mBreakNestedLoop = true;
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+TestNestedLoopsChild::TestNestedLoopsChild()
+{
+ MOZ_COUNT_CTOR(TestNestedLoopsChild);
+}
+
+TestNestedLoopsChild::~TestNestedLoopsChild()
+{
+ MOZ_COUNT_DTOR(TestNestedLoopsChild);
+}
+
+bool
+TestNestedLoopsChild::RecvStart()
+{
+ if (!SendNonce())
+ fail("sending Nonce");
+ return true;
+}
+
+bool
+TestNestedLoopsChild::AnswerR()
+{
+ return true;
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestNestedLoops.h b/ipc/ipdl/test/cxx/TestNestedLoops.h
new file mode 100644
index 000000000..a91258a43
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestNestedLoops.h
@@ -0,0 +1,67 @@
+#ifndef mozilla__ipdltest_TestNestedLoops_h
+#define mozilla__ipdltest_TestNestedLoops_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestNestedLoopsParent.h"
+#include "mozilla/_ipdltest/PTestNestedLoopsChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestNestedLoopsParent :
+ public PTestNestedLoopsParent
+{
+public:
+ TestNestedLoopsParent();
+ virtual ~TestNestedLoopsParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+protected:
+ virtual bool RecvNonce() override;
+
+ void BreakNestedLoop();
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+
+ bool mBreakNestedLoop;
+};
+
+
+class TestNestedLoopsChild :
+ public PTestNestedLoopsChild
+{
+public:
+ TestNestedLoopsChild();
+ virtual ~TestNestedLoopsChild();
+
+protected:
+ virtual bool RecvStart() override;
+
+ virtual bool AnswerR() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestNestedLoops_h
diff --git a/ipc/ipdl/test/cxx/TestOpens.cpp b/ipc/ipdl/test/cxx/TestOpens.cpp
new file mode 100644
index 000000000..cb1ed00b5
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestOpens.cpp
@@ -0,0 +1,251 @@
+#include "base/task.h"
+#include "base/thread.h"
+
+#include "TestOpens.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+using namespace mozilla::ipc;
+
+using base::ProcessHandle;
+using base::Thread;
+
+namespace mozilla {
+// NB: this is generally bad style, but I am lazy.
+using namespace _ipdltest;
+using namespace _ipdltest2;
+
+static MessageLoop* gMainThread;
+
+static void
+AssertNotMainThread()
+{
+ if (!gMainThread)
+ fail("gMainThread is not initialized");
+ if (MessageLoop::current() == gMainThread)
+ fail("unexpectedly called on the main thread");
+}
+
+//-----------------------------------------------------------------------------
+// parent
+
+// Thread on which TestOpensOpenedParent runs
+static Thread* gParentThread;
+
+void
+TestOpensParent::Main()
+{
+ if (!SendStart())
+ fail("sending Start");
+}
+
+static void
+OpenParent(TestOpensOpenedParent* aParent,
+ Transport* aTransport, base::ProcessId aOtherPid)
+{
+ AssertNotMainThread();
+
+ // Open the actor on the off-main thread to park it there.
+ // Messages will be delivered to this thread's message loop
+ // instead of the main thread's.
+ if (!aParent->Open(aTransport, aOtherPid,
+ XRE_GetIOMessageLoop(), ipc::ParentSide))
+ fail("opening Parent");
+}
+
+PTestOpensOpenedParent*
+TestOpensParent::AllocPTestOpensOpenedParent(Transport* transport,
+ ProcessId otherPid)
+{
+ gMainThread = MessageLoop::current();
+
+ gParentThread = new Thread("ParentThread");
+ if (!gParentThread->Start())
+ fail("starting parent thread");
+
+ TestOpensOpenedParent* a = new TestOpensOpenedParent(transport);
+ gParentThread->message_loop()->PostTask(
+ NewRunnableFunction(OpenParent, a, transport, otherPid));
+
+ return a;
+}
+
+void
+TestOpensParent::ActorDestroy(ActorDestroyReason why)
+{
+ // Stops the thread and joins it
+ delete gParentThread;
+
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+}
+
+bool
+TestOpensOpenedParent::RecvHello()
+{
+ AssertNotMainThread();
+ return SendHi();
+}
+
+bool
+TestOpensOpenedParent::RecvHelloSync()
+{
+ AssertNotMainThread();
+ return true;
+}
+
+bool
+TestOpensOpenedParent::AnswerHelloRpc()
+{
+ AssertNotMainThread();
+ return CallHiRpc();
+}
+
+static void
+ShutdownTestOpensOpenedParent(TestOpensOpenedParent* parent,
+ Transport* transport)
+{
+ delete parent;
+}
+
+void
+TestOpensOpenedParent::ActorDestroy(ActorDestroyReason why)
+{
+ AssertNotMainThread();
+
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+
+ // ActorDestroy() is just a callback from IPDL-generated code,
+ // which needs the top-level actor (this) to stay alive a little
+ // longer so other things can be cleaned up.
+ gParentThread->message_loop()->PostTask(
+ NewRunnableFunction(ShutdownTestOpensOpenedParent,
+ this, mTransport));
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+static TestOpensChild* gOpensChild;
+// Thread on which TestOpensOpenedChild runs
+static Thread* gChildThread;
+
+TestOpensChild::TestOpensChild()
+{
+ gOpensChild = this;
+}
+
+bool
+TestOpensChild::RecvStart()
+{
+ if (!PTestOpensOpened::Open(this))
+ fail("opening PTestOpensOpened");
+ return true;
+}
+
+static void
+OpenChild(TestOpensOpenedChild* aChild,
+ Transport* aTransport, base::ProcessId aOtherPid)
+{
+ AssertNotMainThread();
+
+ // Open the actor on the off-main thread to park it there.
+ // Messages will be delivered to this thread's message loop
+ // instead of the main thread's.
+ if (!aChild->Open(aTransport, aOtherPid,
+ XRE_GetIOMessageLoop(), ipc::ChildSide))
+ fail("opening Child");
+
+ // Kick off the unit tests
+ if (!aChild->SendHello())
+ fail("sending Hello");
+}
+
+PTestOpensOpenedChild*
+TestOpensChild::AllocPTestOpensOpenedChild(Transport* transport,
+ ProcessId otherPid)
+{
+ gMainThread = MessageLoop::current();
+
+ gChildThread = new Thread("ChildThread");
+ if (!gChildThread->Start())
+ fail("starting child thread");
+
+ TestOpensOpenedChild* a = new TestOpensOpenedChild(transport);
+ gChildThread->message_loop()->PostTask(
+ NewRunnableFunction(OpenChild, a, transport, otherPid));
+
+ return a;
+}
+
+void
+TestOpensChild::ActorDestroy(ActorDestroyReason why)
+{
+ // Stops the thread and joins it
+ delete gChildThread;
+
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+}
+
+bool
+TestOpensOpenedChild::RecvHi()
+{
+ AssertNotMainThread();
+
+ if (!SendHelloSync())
+ fail("sending HelloSync");
+ if (!CallHelloRpc())
+ fail("calling HelloRpc");
+ if (!mGotHi)
+ fail("didn't answer HiRpc");
+
+ // Need to close the channel without message-processing frames on
+ // the C++ stack
+ MessageLoop::current()->PostTask(
+ NewNonOwningRunnableMethod(this, &TestOpensOpenedChild::Close));
+ return true;
+}
+
+bool
+TestOpensOpenedChild::AnswerHiRpc()
+{
+ AssertNotMainThread();
+
+ mGotHi = true; // d00d
+ return true;
+}
+
+static void
+ShutdownTestOpensOpenedChild(TestOpensOpenedChild* child,
+ Transport* transport)
+{
+ delete child;
+
+ // Kick off main-thread shutdown.
+ gMainThread->PostTask(
+ NewNonOwningRunnableMethod(gOpensChild, &TestOpensChild::Close));
+}
+
+void
+TestOpensOpenedChild::ActorDestroy(ActorDestroyReason why)
+{
+ AssertNotMainThread();
+
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+
+ // ActorDestroy() is just a callback from IPDL-generated code,
+ // which needs the top-level actor (this) to stay alive a little
+ // longer so other things can be cleaned up. Defer shutdown to
+ // let cleanup finish.
+ gChildThread->message_loop()->PostTask(
+ NewRunnableFunction(ShutdownTestOpensOpenedChild,
+ this, mTransport));
+}
+
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestOpens.h b/ipc/ipdl/test/cxx/TestOpens.h
new file mode 100644
index 000000000..b83645a82
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestOpens.h
@@ -0,0 +1,107 @@
+#ifndef mozilla__ipdltest_TestOpens_h
+#define mozilla__ipdltest_TestOpens_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestOpensParent.h"
+#include "mozilla/_ipdltest/PTestOpensChild.h"
+
+#include "mozilla/_ipdltest2/PTestOpensOpenedParent.h"
+#include "mozilla/_ipdltest2/PTestOpensOpenedChild.h"
+
+namespace mozilla {
+
+// parent process
+
+namespace _ipdltest {
+
+class TestOpensParent : public PTestOpensParent
+{
+public:
+ TestOpensParent() {}
+ virtual ~TestOpensParent() {}
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+
+protected:
+ virtual PTestOpensOpenedParent*
+ AllocPTestOpensOpenedParent(Transport* transport, ProcessId otherProcess) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+};
+
+} // namespace _ipdltest
+
+namespace _ipdltest2 {
+
+class TestOpensOpenedParent : public PTestOpensOpenedParent
+{
+public:
+ explicit TestOpensOpenedParent(Transport* aTransport)
+ : mTransport(aTransport)
+ {}
+ virtual ~TestOpensOpenedParent() {}
+
+protected:
+ virtual bool RecvHello() override;
+ virtual bool RecvHelloSync() override;
+ virtual bool AnswerHelloRpc() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ Transport* mTransport;
+};
+
+} // namespace _ipdltest2
+
+// child process
+
+namespace _ipdltest {
+
+class TestOpensChild : public PTestOpensChild
+{
+public:
+ TestOpensChild();
+ virtual ~TestOpensChild() {}
+
+protected:
+ virtual bool RecvStart() override;
+
+ virtual PTestOpensOpenedChild*
+ AllocPTestOpensOpenedChild(Transport* transport, ProcessId otherProcess) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+};
+
+} // namespace _ipdltest
+
+namespace _ipdltest2 {
+
+class TestOpensOpenedChild : public PTestOpensOpenedChild
+{
+public:
+ explicit TestOpensOpenedChild(Transport* aTransport)
+ : mGotHi(false)
+ , mTransport(aTransport)
+ {}
+ virtual ~TestOpensOpenedChild() {}
+
+protected:
+ virtual bool RecvHi() override;
+ virtual bool AnswerHiRpc() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ bool mGotHi;
+ Transport* mTransport;
+};
+
+} // namespace _ipdltest2
+
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestOpens_h
diff --git a/ipc/ipdl/test/cxx/TestRPC.cpp b/ipc/ipdl/test/cxx/TestRPC.cpp
new file mode 100644
index 000000000..2dfc51d33
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestRPC.cpp
@@ -0,0 +1,152 @@
+#include "TestRPC.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+#if defined(OS_POSIX)
+#include <unistd.h>
+#else
+#include <windows.h>
+#endif
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+TestRPCParent::TestRPCParent()
+ : reentered_(false),
+ resolved_first_cpow_(false)
+{
+ MOZ_COUNT_CTOR(TestRPCParent);
+}
+
+TestRPCParent::~TestRPCParent()
+{
+ MOZ_COUNT_DTOR(TestRPCParent);
+}
+
+void
+TestRPCParent::Main()
+{
+ if (!SendStart())
+ fail("sending Start");
+}
+
+bool
+TestRPCParent::RecvTest1_Start(uint32_t* aResult)
+{
+ uint32_t result;
+ if (!SendTest1_InnerQuery(&result))
+ fail("SendTest1_InnerQuery");
+ if (result != 300)
+ fail("Wrong result (expected 300)");
+
+ *aResult = 100;
+ return true;
+}
+
+bool
+TestRPCParent::RecvTest1_InnerEvent(uint32_t* aResult)
+{
+ uint32_t result;
+ if (!SendTest1_NoReenter(&result))
+ fail("SendTest1_NoReenter");
+ if (result != 400)
+ fail("Wrong result (expected 400)");
+
+ *aResult = 200;
+ return true;
+}
+
+bool
+TestRPCParent::RecvTest2_Start()
+{
+ // Send a CPOW. During this time, we must NOT process the RPC message, as
+ // we could start receiving CPOW replies out-of-order.
+ if (!SendTest2_FirstUrgent())
+ fail("SendTest2_FirstUrgent");
+
+ MOZ_ASSERT(!reentered_);
+ resolved_first_cpow_ = true;
+ return true;
+}
+
+bool
+TestRPCParent::RecvTest2_OutOfOrder()
+{
+ // Send a CPOW. If this RPC call was initiated while waiting for the first
+ // CPOW to resolve, replies will be processed out of order, and we'll crash.
+ if (!SendTest2_SecondUrgent())
+ fail("SendTest2_SecondUrgent");
+
+ reentered_ = true;
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+
+TestRPCChild::TestRPCChild()
+{
+ MOZ_COUNT_CTOR(TestRPCChild);
+}
+
+TestRPCChild::~TestRPCChild()
+{
+ MOZ_COUNT_DTOR(TestRPCChild);
+}
+
+bool
+TestRPCChild::RecvStart()
+{
+ uint32_t result;
+ if (!SendTest1_Start(&result))
+ fail("SendTest1_Start");
+ if (result != 100)
+ fail("Wrong result (expected 100)");
+
+ if (!SendTest2_Start())
+ fail("SendTest2_Start");
+
+ if (!SendTest2_OutOfOrder())
+ fail("SendTest2_OutOfOrder");
+
+ Close();
+ return true;
+}
+
+bool
+TestRPCChild::RecvTest1_InnerQuery(uint32_t* aResult)
+{
+ uint32_t result;
+ if (!SendTest1_InnerEvent(&result))
+ fail("SendTest1_InnerEvent");
+ if (result != 200)
+ fail("Wrong result (expected 200)");
+
+ *aResult = 300;
+ return true;
+}
+
+bool
+TestRPCChild::RecvTest1_NoReenter(uint32_t* aResult)
+{
+ *aResult = 400;
+ return true;
+}
+
+bool
+TestRPCChild::RecvTest2_FirstUrgent()
+{
+ return true;
+}
+
+bool
+TestRPCChild::RecvTest2_SecondUrgent()
+{
+ return true;
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestRPC.h b/ipc/ipdl/test/cxx/TestRPC.h
new file mode 100644
index 000000000..0a11b3e76
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestRPC.h
@@ -0,0 +1,74 @@
+#ifndef mozilla__ipdltest_TestRPC_h
+#define mozilla__ipdltest_TestRPC_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestRPCParent.h"
+#include "mozilla/_ipdltest/PTestRPCChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestRPCParent :
+ public PTestRPCParent
+{
+public:
+ TestRPCParent();
+ virtual ~TestRPCParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+
+ bool RecvTest1_Start(uint32_t* aResult) override;
+ bool RecvTest1_InnerEvent(uint32_t* aResult) override;
+ bool RecvTest2_Start() override;
+ bool RecvTest2_OutOfOrder() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ if (!reentered_)
+ fail("never processed raced RPC call!");
+ if (!resolved_first_cpow_)
+ fail("never resolved first CPOW!");
+ passed("ok");
+ QuitParent();
+ }
+
+private:
+ bool reentered_;
+ bool resolved_first_cpow_;
+};
+
+
+class TestRPCChild :
+ public PTestRPCChild
+{
+public:
+ TestRPCChild();
+ virtual ~TestRPCChild();
+
+ bool RecvStart() override;
+ bool RecvTest1_InnerQuery(uint32_t* aResult) override;
+ bool RecvTest1_NoReenter(uint32_t* aResult) override;
+ bool RecvTest2_FirstUrgent() override;
+ bool RecvTest2_SecondUrgent() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestRPC_h
diff --git a/ipc/ipdl/test/cxx/TestRaceDeadlock.cpp b/ipc/ipdl/test/cxx/TestRaceDeadlock.cpp
new file mode 100644
index 000000000..7402f30d3
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestRaceDeadlock.cpp
@@ -0,0 +1,131 @@
+#include "TestRaceDeadlock.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+// #define TEST_TIMEOUT 5000
+
+using namespace mozilla::ipc;
+typedef mozilla::ipc::MessageChannel::Message Message;
+typedef mozilla::ipc::MessageChannel::MessageInfo MessageInfo;
+
+namespace mozilla {
+namespace _ipdltest {
+
+static RacyInterruptPolicy
+MediateRace(const MessageInfo& parent, const MessageInfo& child)
+{
+ return (PTestRaceDeadlock::Msg_Win__ID == parent.type()) ?
+ RIPParentWins : RIPChildWins;
+}
+
+//-----------------------------------------------------------------------------
+// parent
+
+TestRaceDeadlockParent::TestRaceDeadlockParent()
+{
+ MOZ_COUNT_CTOR(TestRaceDeadlockParent);
+}
+
+TestRaceDeadlockParent::~TestRaceDeadlockParent()
+{
+ MOZ_COUNT_DTOR(TestRaceDeadlockParent);
+}
+
+void
+TestRaceDeadlockParent::Main()
+{
+ Test1();
+
+ Close();
+}
+
+bool
+TestRaceDeadlockParent::ShouldContinueFromReplyTimeout()
+{
+ fail("This test should not hang");
+ GetIPCChannel()->CloseWithTimeout();
+ return false;
+}
+
+void
+TestRaceDeadlockParent::Test1()
+{
+#if defined(TEST_TIMEOUT)
+ SetReplyTimeoutMs(TEST_TIMEOUT);
+#endif
+ if (!SendStartRace()) {
+ fail("sending StartRace");
+ }
+ if (!CallRpc()) {
+ fail("calling Rpc");
+ }
+}
+
+bool
+TestRaceDeadlockParent::AnswerLose()
+{
+ return true;
+}
+
+RacyInterruptPolicy
+TestRaceDeadlockParent::MediateInterruptRace(const MessageInfo& parent,
+ const MessageInfo& child)
+{
+ return MediateRace(parent, child);
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+TestRaceDeadlockChild::TestRaceDeadlockChild()
+{
+ MOZ_COUNT_CTOR(TestRaceDeadlockChild);
+}
+
+TestRaceDeadlockChild::~TestRaceDeadlockChild()
+{
+ MOZ_COUNT_DTOR(TestRaceDeadlockChild);
+}
+
+bool
+TestRaceDeadlockParent::RecvStartRace()
+{
+ if (!CallWin()) {
+ fail("calling Win");
+ }
+ return true;
+}
+
+bool
+TestRaceDeadlockChild::RecvStartRace()
+{
+ if (!SendStartRace()) {
+ fail("calling SendStartRace");
+ }
+ if (!CallLose()) {
+ fail("calling Lose");
+ }
+ return true;
+}
+
+bool
+TestRaceDeadlockChild::AnswerWin()
+{
+ return true;
+}
+
+bool
+TestRaceDeadlockChild::AnswerRpc()
+{
+ return true;
+}
+
+RacyInterruptPolicy
+TestRaceDeadlockChild::MediateInterruptRace(const MessageInfo& parent,
+ const MessageInfo& child)
+{
+ return MediateRace(parent, child);
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestRaceDeadlock.h b/ipc/ipdl/test/cxx/TestRaceDeadlock.h
new file mode 100644
index 000000000..7723c7629
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestRaceDeadlock.h
@@ -0,0 +1,77 @@
+#ifndef mozilla__ipdltest_TestRaceDeadlock_h
+#define mozilla__ipdltest_TestRaceDeadlock_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestRaceDeadlockParent.h"
+#include "mozilla/_ipdltest/PTestRaceDeadlockChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+class TestRaceDeadlockParent :
+ public PTestRaceDeadlockParent
+{
+public:
+ TestRaceDeadlockParent();
+ virtual ~TestRaceDeadlockParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+protected:
+ virtual bool ShouldContinueFromReplyTimeout() override;
+
+ void Test1();
+
+ virtual bool RecvStartRace() override;
+ virtual bool AnswerLose() override;
+
+ virtual mozilla::ipc::RacyInterruptPolicy
+ MediateInterruptRace(const MessageInfo& parent,
+ const MessageInfo& child) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+};
+
+
+class TestRaceDeadlockChild :
+ public PTestRaceDeadlockChild
+{
+public:
+ TestRaceDeadlockChild();
+ virtual ~TestRaceDeadlockChild();
+
+protected:
+ virtual bool RecvStartRace() override;
+
+ virtual bool AnswerWin() override;
+
+ virtual bool AnswerRpc() override;
+
+ virtual mozilla::ipc::RacyInterruptPolicy
+ MediateInterruptRace(const MessageInfo& parent,
+ const MessageInfo& child) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestRaceDeadlock_h
diff --git a/ipc/ipdl/test/cxx/TestRaceDeferral.cpp b/ipc/ipdl/test/cxx/TestRaceDeferral.cpp
new file mode 100644
index 000000000..b62d0e2f3
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestRaceDeferral.cpp
@@ -0,0 +1,118 @@
+#include "TestRaceDeferral.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+using namespace mozilla::ipc;
+typedef mozilla::ipc::MessageChannel::Message Message;
+typedef mozilla::ipc::MessageChannel::MessageInfo MessageInfo;
+
+namespace mozilla {
+namespace _ipdltest {
+
+static RacyInterruptPolicy
+MediateRace(const MessageInfo& parent, const MessageInfo& child)
+{
+ return (PTestRaceDeferral::Msg_Win__ID == parent.type()) ?
+ RIPParentWins : RIPChildWins;
+}
+
+//-----------------------------------------------------------------------------
+// parent
+
+TestRaceDeferralParent::TestRaceDeferralParent()
+ : mProcessedLose(false)
+{
+ MOZ_COUNT_CTOR(TestRaceDeferralParent);
+}
+
+TestRaceDeferralParent::~TestRaceDeferralParent()
+{
+ MOZ_COUNT_DTOR(TestRaceDeferralParent);
+
+ if (!mProcessedLose)
+ fail("never processed Lose");
+}
+
+void
+TestRaceDeferralParent::Main()
+{
+ Test1();
+
+ Close();
+}
+
+void
+TestRaceDeferralParent::Test1()
+{
+ if (!SendStartRace())
+ fail("sending StartRace");
+
+ if (!CallWin())
+ fail("calling Win");
+ if (mProcessedLose)
+ fail("Lose didn't lose");
+
+ if (!CallRpc())
+ fail("calling Rpc");
+ if (!mProcessedLose)
+ fail("didn't resolve Rpc vs. Lose 'race' correctly");
+}
+
+bool
+TestRaceDeferralParent::AnswerLose()
+{
+ if (mProcessedLose)
+ fail("processed Lose twice");
+ mProcessedLose = true;
+ return true;
+}
+
+RacyInterruptPolicy
+TestRaceDeferralParent::MediateInterruptRace(const MessageInfo& parent,
+ const MessageInfo& child)
+{
+ return MediateRace(parent, child);
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+TestRaceDeferralChild::TestRaceDeferralChild()
+{
+ MOZ_COUNT_CTOR(TestRaceDeferralChild);
+}
+
+TestRaceDeferralChild::~TestRaceDeferralChild()
+{
+ MOZ_COUNT_DTOR(TestRaceDeferralChild);
+}
+
+bool
+TestRaceDeferralChild::RecvStartRace()
+{
+ if (!CallLose())
+ fail("calling Lose");
+ return true;
+}
+
+bool
+TestRaceDeferralChild::AnswerWin()
+{
+ return true;
+}
+
+bool
+TestRaceDeferralChild::AnswerRpc()
+{
+ return true;
+}
+
+RacyInterruptPolicy
+TestRaceDeferralChild::MediateInterruptRace(const MessageInfo& parent,
+ const MessageInfo& child)
+{
+ return MediateRace(parent, child);
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestRaceDeferral.h b/ipc/ipdl/test/cxx/TestRaceDeferral.h
new file mode 100644
index 000000000..e465cf77c
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestRaceDeferral.h
@@ -0,0 +1,76 @@
+#ifndef mozilla__ipdltest_TestRaceDeferral_h
+#define mozilla__ipdltest_TestRaceDeferral_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestRaceDeferralParent.h"
+#include "mozilla/_ipdltest/PTestRaceDeferralChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+class TestRaceDeferralParent :
+ public PTestRaceDeferralParent
+{
+public:
+ TestRaceDeferralParent();
+ virtual ~TestRaceDeferralParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+protected:
+ void Test1();
+
+ virtual bool AnswerLose() override;
+
+ virtual mozilla::ipc::RacyInterruptPolicy
+ MediateInterruptRace(const MessageInfo& parent,
+ const MessageInfo& child) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+
+ bool mProcessedLose;
+};
+
+
+class TestRaceDeferralChild :
+ public PTestRaceDeferralChild
+{
+public:
+ TestRaceDeferralChild();
+ virtual ~TestRaceDeferralChild();
+
+protected:
+ virtual bool RecvStartRace() override;
+
+ virtual bool AnswerWin() override;
+
+ virtual bool AnswerRpc() override;
+
+ virtual mozilla::ipc::RacyInterruptPolicy
+ MediateInterruptRace(const MessageInfo& parent,
+ const MessageInfo& child) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestRaceDeferral_h
diff --git a/ipc/ipdl/test/cxx/TestRacyInterruptReplies.cpp b/ipc/ipdl/test/cxx/TestRacyInterruptReplies.cpp
new file mode 100644
index 000000000..513b84f73
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestRacyInterruptReplies.cpp
@@ -0,0 +1,119 @@
+#include "TestRacyInterruptReplies.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+TestRacyInterruptRepliesParent::TestRacyInterruptRepliesParent() : mReplyNum(0)
+{
+ MOZ_COUNT_CTOR(TestRacyInterruptRepliesParent);
+}
+
+TestRacyInterruptRepliesParent::~TestRacyInterruptRepliesParent()
+{
+ MOZ_COUNT_DTOR(TestRacyInterruptRepliesParent);
+}
+
+void
+TestRacyInterruptRepliesParent::Main()
+{
+ int replyNum = -1;
+ if (!CallR_(&replyNum))
+ fail("calling R()");
+
+ if (1 != replyNum)
+ fail("this should have been the first reply to R()");
+
+ if (!SendChildTest())
+ fail("sending ChildStart");
+}
+
+bool
+TestRacyInterruptRepliesParent::RecvA_()
+{
+ int replyNum = -1;
+ // this R() call races with the reply being generated by the other
+ // side to the R() call from Main(). This is a pretty nasty edge
+ // case for which one could argue we're breaking in-order message
+ // delivery, since this side will process the second reply to R()
+ // before the first.
+ if (!CallR_(&replyNum))
+ fail("calling R()");
+
+ if (2 != replyNum)
+ fail("this should have been the second reply to R()");
+
+ return true;
+}
+
+bool
+TestRacyInterruptRepliesParent::Answer_R(int* replyNum)
+{
+ *replyNum = ++mReplyNum;
+
+ if (1 == *replyNum)
+ if (!Send_A())
+ fail("sending _A()");
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+TestRacyInterruptRepliesChild::TestRacyInterruptRepliesChild() : mReplyNum(0)
+{
+ MOZ_COUNT_CTOR(TestRacyInterruptRepliesChild);
+}
+
+TestRacyInterruptRepliesChild::~TestRacyInterruptRepliesChild()
+{
+ MOZ_COUNT_DTOR(TestRacyInterruptRepliesChild);
+}
+
+bool
+TestRacyInterruptRepliesChild::AnswerR_(int* replyNum)
+{
+ *replyNum = ++mReplyNum;
+
+ if (1 == *replyNum)
+ SendA_();
+
+ return true;
+}
+
+bool
+TestRacyInterruptRepliesChild::RecvChildTest()
+{
+ int replyNum = -1;
+ if (!Call_R(&replyNum))
+ fail("calling R()");
+
+ if (1 != replyNum)
+ fail("this should have been the first reply to R()");
+
+ Close();
+
+ return true;
+}
+
+bool
+TestRacyInterruptRepliesChild::Recv_A()
+{
+ int replyNum = -1;
+
+ if (!Call_R(&replyNum))
+ fail("calling _R()");
+
+ if (2 != replyNum)
+ fail("this should have been the second reply to R()");
+
+ return true;
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestRacyInterruptReplies.h b/ipc/ipdl/test/cxx/TestRacyInterruptReplies.h
new file mode 100644
index 000000000..94f514188
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestRacyInterruptReplies.h
@@ -0,0 +1,73 @@
+#ifndef mozilla__ipdltest_TestRacyInterruptReplies_h
+#define mozilla__ipdltest_TestRacyInterruptReplies_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestRacyInterruptRepliesParent.h"
+#include "mozilla/_ipdltest/PTestRacyInterruptRepliesChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestRacyInterruptRepliesParent :
+ public PTestRacyInterruptRepliesParent
+{
+public:
+ TestRacyInterruptRepliesParent();
+ virtual ~TestRacyInterruptRepliesParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+protected:
+ virtual bool RecvA_() override;
+
+ virtual bool Answer_R(int* replyNum) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+
+private:
+ int mReplyNum;
+};
+
+
+class TestRacyInterruptRepliesChild :
+ public PTestRacyInterruptRepliesChild
+{
+public:
+ TestRacyInterruptRepliesChild();
+ virtual ~TestRacyInterruptRepliesChild();
+
+protected:
+ virtual bool AnswerR_(int* replyNum) override;
+
+ virtual bool RecvChildTest() override;
+
+ virtual bool Recv_A() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+
+private:
+ int mReplyNum;
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestRacyInterruptReplies_h
diff --git a/ipc/ipdl/test/cxx/TestRacyReentry.cpp b/ipc/ipdl/test/cxx/TestRacyReentry.cpp
new file mode 100644
index 000000000..00996cc39
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestRacyReentry.cpp
@@ -0,0 +1,84 @@
+#include "TestRacyReentry.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+TestRacyReentryParent::TestRacyReentryParent() : mRecvdE(false)
+{
+ MOZ_COUNT_CTOR(TestRacyReentryParent);
+}
+
+TestRacyReentryParent::~TestRacyReentryParent()
+{
+ MOZ_COUNT_DTOR(TestRacyReentryParent);
+}
+
+void
+TestRacyReentryParent::Main()
+{
+ if (!SendStart())
+ fail("sending Start");
+
+ if (!SendN())
+ fail("sending N");
+}
+
+bool
+TestRacyReentryParent::AnswerE()
+{
+ if (!mRecvdE) {
+ mRecvdE = true;
+ return true;
+ }
+
+ if (!CallH())
+ fail("calling H");
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+TestRacyReentryChild::TestRacyReentryChild()
+{
+ MOZ_COUNT_CTOR(TestRacyReentryChild);
+}
+
+TestRacyReentryChild::~TestRacyReentryChild()
+{
+ MOZ_COUNT_DTOR(TestRacyReentryChild);
+}
+
+bool
+TestRacyReentryChild::RecvStart()
+{
+ if (!CallE())
+ fail("calling E");
+
+ Close();
+
+ return true;
+}
+
+bool
+TestRacyReentryChild::RecvN()
+{
+ if (!CallE())
+ fail("calling E");
+ return true;
+}
+
+bool
+TestRacyReentryChild::AnswerH()
+{
+ return true;
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestRacyReentry.h b/ipc/ipdl/test/cxx/TestRacyReentry.h
new file mode 100644
index 000000000..f496ef7bf
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestRacyReentry.h
@@ -0,0 +1,67 @@
+#ifndef mozilla__ipdltest_TestRacyReentry_h
+#define mozilla__ipdltest_TestRacyReentry_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestRacyReentryParent.h"
+#include "mozilla/_ipdltest/PTestRacyReentryChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestRacyReentryParent :
+ public PTestRacyReentryParent
+{
+public:
+ TestRacyReentryParent();
+ virtual ~TestRacyReentryParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+protected:
+ virtual bool AnswerE() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+
+ bool mRecvdE;
+};
+
+
+class TestRacyReentryChild :
+ public PTestRacyReentryChild
+{
+public:
+ TestRacyReentryChild();
+ virtual ~TestRacyReentryChild();
+
+protected:
+ virtual bool RecvStart() override;
+
+ virtual bool RecvN() override;
+
+ virtual bool AnswerH() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestRacyReentry_h
diff --git a/ipc/ipdl/test/cxx/TestRacyUndefer.cpp b/ipc/ipdl/test/cxx/TestRacyUndefer.cpp
new file mode 100644
index 000000000..8bdd804cc
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestRacyUndefer.cpp
@@ -0,0 +1,115 @@
+#include "base/basictypes.h"
+
+#include "TestRacyUndefer.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+TestRacyUndeferParent::TestRacyUndeferParent()
+{
+ MOZ_COUNT_CTOR(TestRacyUndeferParent);
+}
+
+TestRacyUndeferParent::~TestRacyUndeferParent()
+{
+ MOZ_COUNT_DTOR(TestRacyUndeferParent);
+}
+
+void
+TestRacyUndeferParent::Main()
+{
+ if (!SendStart())
+ fail("sending Start");
+}
+
+bool
+TestRacyUndeferParent::AnswerSpam()
+{
+ static bool spammed = false;
+ static bool raced = false;
+ if (!spammed) {
+ spammed = true;
+
+ if (!SendAwakenSpam())
+ fail("sending AwakenSpam");
+ }
+ else if (!raced) {
+ raced = true;
+
+ if (!SendAwakenRaceWinTwice())
+ fail("sending WinRaceTwice");
+
+ if (!CallRace())
+ fail("calling Race1");
+ }
+ return true;
+}
+
+bool
+TestRacyUndeferParent::AnswerRaceWinTwice()
+{
+ return true;
+}
+
+bool
+TestRacyUndeferParent::RecvDone()
+{
+ Close();
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// child
+
+TestRacyUndeferChild::TestRacyUndeferChild()
+{
+ MOZ_COUNT_CTOR(TestRacyUndeferChild);
+}
+
+TestRacyUndeferChild::~TestRacyUndeferChild()
+{
+ MOZ_COUNT_DTOR(TestRacyUndeferChild);
+}
+
+bool
+TestRacyUndeferChild::RecvStart()
+{
+ if (!CallSpam())
+ fail("calling Spam");
+
+ if (!SendDone())
+ fail("sending Done");
+
+ return true;
+}
+
+bool
+TestRacyUndeferChild::RecvAwakenSpam()
+{
+ if (!CallSpam())
+ fail("calling Spam");
+ return true;
+}
+
+bool
+TestRacyUndeferChild::RecvAwakenRaceWinTwice()
+{
+ if (!CallRaceWinTwice())
+ fail("calling RaceWinTwice");
+ return true;
+}
+
+bool
+TestRacyUndeferChild::AnswerRace()
+{
+ return true;
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestRacyUndefer.h b/ipc/ipdl/test/cxx/TestRacyUndefer.h
new file mode 100644
index 000000000..eee06a86c
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestRacyUndefer.h
@@ -0,0 +1,70 @@
+#ifndef mozilla__ipdltest_TestRacyUndefer_h
+#define mozilla__ipdltest_TestRacyUndefer_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestRacyUndeferParent.h"
+#include "mozilla/_ipdltest/PTestRacyUndeferChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestRacyUndeferParent :
+ public PTestRacyUndeferParent
+{
+public:
+ TestRacyUndeferParent();
+ virtual ~TestRacyUndeferParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+protected:
+ virtual bool AnswerSpam() override;
+
+ virtual bool AnswerRaceWinTwice() override;
+
+ virtual bool RecvDone() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+};
+
+
+class TestRacyUndeferChild :
+ public PTestRacyUndeferChild
+{
+public:
+ TestRacyUndeferChild();
+ virtual ~TestRacyUndeferChild();
+
+protected:
+ virtual bool RecvStart() override;
+
+ virtual bool RecvAwakenSpam() override;
+ virtual bool RecvAwakenRaceWinTwice() override;
+
+ virtual bool AnswerRace() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestRacyUndefer_h
diff --git a/ipc/ipdl/test/cxx/TestSanity.cpp b/ipc/ipdl/test/cxx/TestSanity.cpp
new file mode 100644
index 000000000..64ffceed6
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestSanity.cpp
@@ -0,0 +1,75 @@
+#include "TestSanity.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+TestSanityParent::TestSanityParent()
+{
+ MOZ_COUNT_CTOR(TestSanityParent);
+}
+
+TestSanityParent::~TestSanityParent()
+{
+ MOZ_COUNT_DTOR(TestSanityParent);
+}
+
+void
+TestSanityParent::Main()
+{
+ if (!SendPing(0, 0.5f, 0))
+ fail("sending Ping");
+}
+
+
+bool
+TestSanityParent::RecvPong(const int& one, const float& zeroPtTwoFive,
+ const uint8_t&/*unused*/)
+{
+ if (1 != one)
+ fail("invalid argument `%d', should have been `1'", one);
+
+ if (0.25f != zeroPtTwoFive)
+ fail("invalid argument `%g', should have been `0.25'", zeroPtTwoFive);
+
+ Close();
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// child
+
+TestSanityChild::TestSanityChild()
+{
+ MOZ_COUNT_CTOR(TestSanityChild);
+}
+
+TestSanityChild::~TestSanityChild()
+{
+ MOZ_COUNT_DTOR(TestSanityChild);
+}
+
+bool
+TestSanityChild::RecvPing(const int& zero, const float& zeroPtFive,
+ const int8_t&/*unused*/)
+{
+ if (0 != zero)
+ fail("invalid argument `%d', should have been `0'", zero);
+
+ if (0.5f != zeroPtFive)
+ fail("invalid argument `%g', should have been `0.5'", zeroPtFive);
+
+ if (!SendPong(1, 0.25f, 0))
+ fail("sending Pong");
+ return true;
+}
+
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestSanity.h b/ipc/ipdl/test/cxx/TestSanity.h
new file mode 100644
index 000000000..6e5b8cb59
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestSanity.h
@@ -0,0 +1,63 @@
+#ifndef mozilla__ipdltest_TestSanity_h
+#define mozilla__ipdltest_TestSanity_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestSanityParent.h"
+#include "mozilla/_ipdltest/PTestSanityChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestSanityParent :
+ public PTestSanityParent
+{
+public:
+ TestSanityParent();
+ virtual ~TestSanityParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+protected:
+ virtual bool RecvPong(const int& one, const float& zeroPtTwoFive,
+ const uint8_t& dummy) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+};
+
+
+class TestSanityChild :
+ public PTestSanityChild
+{
+public:
+ TestSanityChild();
+ virtual ~TestSanityChild();
+
+protected:
+ virtual bool RecvPing(const int& zero, const float& zeroPtFive,
+ const int8_t& dummy) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestSanity_h
diff --git a/ipc/ipdl/test/cxx/TestSelfManageRoot.cpp b/ipc/ipdl/test/cxx/TestSelfManageRoot.cpp
new file mode 100644
index 000000000..e8b2330ba
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestSelfManageRoot.cpp
@@ -0,0 +1,63 @@
+#include "TestSelfManageRoot.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+#define ASSERT(c) \
+ do { \
+ if (!(c)) \
+ fail(#c); \
+ } while (0)
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+void
+TestSelfManageRootParent::Main()
+{
+ TestSelfManageParent* a =
+ static_cast<TestSelfManageParent*>(SendPTestSelfManageConstructor());
+ if (!a)
+ fail("constructing PTestSelfManage");
+
+ ASSERT(1 == ManagedPTestSelfManageParent().Count());
+
+ TestSelfManageParent* aa =
+ static_cast<TestSelfManageParent*>(a->SendPTestSelfManageConstructor());
+ if (!aa)
+ fail("constructing PTestSelfManage");
+
+ ASSERT(1 == ManagedPTestSelfManageParent().Count() &&
+ 1 == a->ManagedPTestSelfManageParent().Count());
+
+ if (!PTestSelfManageParent::Send__delete__(aa))
+ fail("destroying PTestSelfManage");
+ ASSERT(Deletion == aa->mWhy &&
+ 1 == ManagedPTestSelfManageParent().Count() &&
+ 0 == a->ManagedPTestSelfManageParent().Count());
+ delete aa;
+
+ aa =
+ static_cast<TestSelfManageParent*>(a->SendPTestSelfManageConstructor());
+ if (!aa)
+ fail("constructing PTestSelfManage");
+
+ ASSERT(1 == ManagedPTestSelfManageParent().Count() &&
+ 1 == a->ManagedPTestSelfManageParent().Count());
+
+ if (!PTestSelfManageParent::Send__delete__(a))
+ fail("destroying PTestSelfManage");
+ ASSERT(Deletion == a->mWhy &&
+ AncestorDeletion == aa->mWhy &&
+ 0 == ManagedPTestSelfManageParent().Count() &&
+ 0 == a->ManagedPTestSelfManageParent().Count());
+ delete a;
+ delete aa;
+
+ Close();
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestSelfManageRoot.h b/ipc/ipdl/test/cxx/TestSelfManageRoot.h
new file mode 100644
index 000000000..2a94fa392
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestSelfManageRoot.h
@@ -0,0 +1,141 @@
+#ifndef mozilla__ipdltest_TestSelfManageRoot_h
+#define mozilla__ipdltest_TestSelfManageRoot_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestSelfManageRootParent.h"
+#include "mozilla/_ipdltest/PTestSelfManageRootChild.h"
+#include "mozilla/_ipdltest/PTestSelfManageParent.h"
+#include "mozilla/_ipdltest/PTestSelfManageChild.h"
+
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// Parent side
+
+class TestSelfManageParent :
+ public PTestSelfManageParent
+{
+public:
+ TestSelfManageParent() {
+ MOZ_COUNT_CTOR(TestSelfManageParent);
+ }
+ virtual ~TestSelfManageParent() {
+ MOZ_COUNT_DTOR(TestSelfManageParent);
+ }
+
+ ActorDestroyReason mWhy;
+
+protected:
+ virtual PTestSelfManageParent* AllocPTestSelfManageParent() override {
+ return new TestSelfManageParent();
+ }
+
+ virtual bool DeallocPTestSelfManageParent(PTestSelfManageParent* a) override {
+ return true;
+ }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override {
+ mWhy = why;
+ }
+};
+
+class TestSelfManageRootParent :
+ public PTestSelfManageRootParent
+{
+public:
+ TestSelfManageRootParent() {
+ MOZ_COUNT_CTOR(TestSelfManageRootParent);
+ }
+ virtual ~TestSelfManageRootParent() {
+ MOZ_COUNT_DTOR(TestSelfManageRootParent);
+ }
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+protected:
+ virtual PTestSelfManageParent* AllocPTestSelfManageParent() override {
+ return new TestSelfManageParent();
+ }
+
+ virtual bool DeallocPTestSelfManageParent(PTestSelfManageParent* a) override {
+ return true;
+ }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+};
+
+//-----------------------------------------------------------------------------
+// Child side
+
+class TestSelfManageChild :
+ public PTestSelfManageChild
+{
+public:
+ TestSelfManageChild() {
+ MOZ_COUNT_CTOR(TestSelfManageChild);
+ }
+ virtual ~TestSelfManageChild() {
+ MOZ_COUNT_DTOR(TestSelfManageChild);
+ }
+
+protected:
+ virtual PTestSelfManageChild* AllocPTestSelfManageChild() override {
+ return new TestSelfManageChild();
+ }
+
+ virtual bool DeallocPTestSelfManageChild(PTestSelfManageChild* a) override {
+ delete a;
+ return true;
+ }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override { }
+};
+
+class TestSelfManageRootChild :
+ public PTestSelfManageRootChild
+{
+public:
+ TestSelfManageRootChild() {
+ MOZ_COUNT_CTOR(TestSelfManageRootChild);
+ }
+ virtual ~TestSelfManageRootChild() {
+ MOZ_COUNT_DTOR(TestSelfManageRootChild);
+ }
+
+ void Main();
+
+protected:
+ virtual PTestSelfManageChild* AllocPTestSelfManageChild() override {
+ return new TestSelfManageChild();
+ }
+
+ virtual bool DeallocPTestSelfManageChild(PTestSelfManageChild* a) override {
+ delete a;
+ return true;
+ }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestSelfManageRoot_h
diff --git a/ipc/ipdl/test/cxx/TestShmem.cpp b/ipc/ipdl/test/cxx/TestShmem.cpp
new file mode 100644
index 000000000..1ff62bc3a
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestShmem.cpp
@@ -0,0 +1,115 @@
+#include "TestShmem.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// Parent
+
+void
+TestShmemParent::Main()
+{
+ Shmem mem;
+ Shmem unsafe;
+
+ size_t size = 12345;
+ if (!AllocShmem(size, SharedMemory::TYPE_BASIC, &mem))
+ fail("can't alloc shmem");
+ if (!AllocUnsafeShmem(size, SharedMemory::TYPE_BASIC, &unsafe))
+ fail("can't alloc shmem");
+
+ if (mem.Size<char>() != size)
+ fail("shmem is wrong size: expected %lu, got %lu",
+ size, mem.Size<char>());
+ if (unsafe.Size<char>() != size)
+ fail("shmem is wrong size: expected %lu, got %lu",
+ size, unsafe.Size<char>());
+
+ char* ptr = mem.get<char>();
+ memcpy(ptr, "Hello!", sizeof("Hello!"));
+
+ char* unsafeptr = unsafe.get<char>();
+ memcpy(unsafeptr, "Hello!", sizeof("Hello!"));
+
+ Shmem unsafecopy = unsafe;
+ if (!SendGive(mem, unsafe, size))
+ fail("can't send Give()");
+
+ // uncomment the following line for a (nondeterministic) surprise!
+ //char c1 = *ptr; (void)c1;
+
+ // uncomment the following line for a deterministic surprise!
+ //char c2 = *mem.get<char>(); (void)c2;
+
+ // unsafe shmem gets rid of those checks
+ char uc1 = *unsafeptr; (void)uc1;
+ char uc2 = *unsafecopy.get<char>(); (void)uc2;
+}
+
+
+bool
+TestShmemParent::RecvTake(Shmem&& mem, Shmem&& unsafe,
+ const size_t& expectedSize)
+{
+ if (mem.Size<char>() != expectedSize)
+ fail("expected shmem size %lu, but it has size %lu",
+ expectedSize, mem.Size<char>());
+ if (unsafe.Size<char>() != expectedSize)
+ fail("expected shmem size %lu, but it has size %lu",
+ expectedSize, unsafe.Size<char>());
+
+ if (strcmp(mem.get<char>(), "And yourself!"))
+ fail("expected message was not written");
+ if (strcmp(unsafe.get<char>(), "And yourself!"))
+ fail("expected message was not written");
+
+ if (!DeallocShmem(mem))
+ fail("DeallocShmem");
+ if (!DeallocShmem(unsafe))
+ fail("DeallocShmem");
+
+ Close();
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Child
+
+bool
+TestShmemChild::RecvGive(Shmem&& mem, Shmem&& unsafe, const size_t& expectedSize)
+{
+ if (mem.Size<char>() != expectedSize)
+ fail("expected shmem size %lu, but it has size %lu",
+ expectedSize, mem.Size<char>());
+ if (unsafe.Size<char>() != expectedSize)
+ fail("expected shmem size %lu, but it has size %lu",
+ expectedSize, unsafe.Size<char>());
+
+ if (strcmp(mem.get<char>(), "Hello!"))
+ fail("expected message was not written");
+ if (strcmp(unsafe.get<char>(), "Hello!"))
+ fail("expected message was not written");
+
+ char* unsafeptr = unsafe.get<char>();
+
+ memcpy(mem.get<char>(), "And yourself!", sizeof("And yourself!"));
+ memcpy(unsafeptr, "And yourself!", sizeof("And yourself!"));
+
+ Shmem unsafecopy = unsafe;
+ if (!SendTake(mem, unsafe, expectedSize))
+ fail("can't send Take()");
+
+ // these checks also shouldn't fail in the child
+ char uc1 = *unsafeptr; (void)uc1;
+ char uc2 = *unsafecopy.get<char>(); (void)uc2;
+
+ return true;
+}
+
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestShmem.h b/ipc/ipdl/test/cxx/TestShmem.h
new file mode 100644
index 000000000..d583fe978
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestShmem.h
@@ -0,0 +1,66 @@
+#ifndef mozilla__ipdltest_TestShmem_h
+#define mozilla__ipdltest_TestShmem_h
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestShmemParent.h"
+#include "mozilla/_ipdltest/PTestShmemChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestShmemParent :
+ public PTestShmemParent
+{
+public:
+ TestShmemParent() { }
+ virtual ~TestShmemParent() { }
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+protected:
+ virtual bool RecvTake(
+ Shmem&& mem,
+ Shmem&& unsafe,
+ const size_t& expectedSize) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+};
+
+
+class TestShmemChild :
+ public PTestShmemChild
+{
+public:
+ TestShmemChild() { }
+ virtual ~TestShmemChild() { }
+
+protected:
+ virtual bool RecvGive(
+ Shmem&& mem,
+ Shmem&& unsafe,
+ const size_t& expectedSize) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+#endif // ifndef mozilla__ipdltest_TestShmem_h
diff --git a/ipc/ipdl/test/cxx/TestShutdown.cpp b/ipc/ipdl/test/cxx/TestShutdown.cpp
new file mode 100644
index 000000000..95a242bff
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestShutdown.cpp
@@ -0,0 +1,235 @@
+#include "TestShutdown.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// Parent side
+void
+TestShutdownParent::Main()
+{
+ if (!SendStart())
+ fail("sending Start()");
+}
+
+void
+TestShutdownParent::ActorDestroy(ActorDestroyReason why)
+{
+ if (AbnormalShutdown != why)
+ fail("should have ended test with crash!");
+
+ passed("ok");
+
+ QuitParent();
+}
+
+void
+TestShutdownSubParent::ActorDestroy(ActorDestroyReason why)
+{
+ if (Manager()->ManagedPTestShutdownSubParent().Count() == 0)
+ fail("manager should still have managees!");
+
+ if (mExpectCrash && AbnormalShutdown != why)
+ fail("expected crash!");
+ else if (!mExpectCrash && AbnormalShutdown == why)
+ fail("wasn't expecting crash!");
+
+ if (mExpectCrash && 0 == ManagedPTestShutdownSubsubParent().Count())
+ fail("expected to *still* have kids");
+}
+
+void
+TestShutdownSubsubParent::ActorDestroy(ActorDestroyReason why)
+{
+ if (Manager()->ManagedPTestShutdownSubsubParent().Count() == 0)
+ fail("manager should still have managees!");
+
+ if (mExpectParentDeleted && AncestorDeletion != why)
+ fail("expected ParentDeleted == why");
+ else if (!mExpectParentDeleted && AncestorDeletion == why)
+ fail("wasn't expecting parent delete");
+}
+
+//-----------------------------------------------------------------------------
+// Child side
+
+bool
+TestShutdownChild::RecvStart()
+{
+ // test 1: alloc some actors and subactors, delete in
+ // managee-before-manager order
+ {
+ bool expectCrash = false, expectParentDeleted = false;
+
+ PTestShutdownSubChild* c1 =
+ SendPTestShutdownSubConstructor(expectCrash);
+ if (!c1)
+ fail("problem sending ctor");
+
+ PTestShutdownSubChild* c2 =
+ SendPTestShutdownSubConstructor(expectCrash);
+ if (!c2)
+ fail("problem sending ctor");
+
+ PTestShutdownSubsubChild* c1s1 =
+ c1->SendPTestShutdownSubsubConstructor(expectParentDeleted);
+ if (!c1s1)
+ fail("problem sending ctor");
+ PTestShutdownSubsubChild* c1s2 =
+ c1->SendPTestShutdownSubsubConstructor(expectParentDeleted);
+ if (!c1s2)
+ fail("problem sending ctor");
+
+ PTestShutdownSubsubChild* c2s1 =
+ c2->SendPTestShutdownSubsubConstructor(expectParentDeleted);
+ if (!c2s1)
+ fail("problem sending ctor");
+ PTestShutdownSubsubChild* c2s2 =
+ c2->SendPTestShutdownSubsubConstructor(expectParentDeleted);
+ if (!c2s2)
+ fail("problem sending ctor");
+
+ if (!PTestShutdownSubsubChild::Send__delete__(c1s1))
+ fail("problem sending dtor");
+ if (!PTestShutdownSubsubChild::Send__delete__(c1s2))
+ fail("problem sending dtor");
+ if (!PTestShutdownSubsubChild::Send__delete__(c2s1))
+ fail("problem sending dtor");
+ if (!PTestShutdownSubsubChild::Send__delete__(c2s2))
+ fail("problem sending dtor");
+
+ if (!c1->CallStackFrame())
+ fail("problem creating dummy stack frame");
+ if (!c2->CallStackFrame())
+ fail("problem creating dummy stack frame");
+ }
+
+ // test 2: alloc some actors and subactors, delete managers first
+ {
+ bool expectCrash = false, expectParentDeleted = true;
+
+ PTestShutdownSubChild* c1 =
+ SendPTestShutdownSubConstructor(expectCrash);
+ if (!c1)
+ fail("problem sending ctor");
+
+ PTestShutdownSubChild* c2 =
+ SendPTestShutdownSubConstructor(expectCrash);
+ if (!c2)
+ fail("problem sending ctor");
+
+ PTestShutdownSubsubChild* c1s1 =
+ c1->SendPTestShutdownSubsubConstructor(expectParentDeleted);
+ if (!c1s1)
+ fail("problem sending ctor");
+ PTestShutdownSubsubChild* c1s2 =
+ c1->SendPTestShutdownSubsubConstructor(expectParentDeleted);
+ if (!c1s2)
+ fail("problem sending ctor");
+
+ PTestShutdownSubsubChild* c2s1 =
+ c2->SendPTestShutdownSubsubConstructor(expectParentDeleted);
+ if (!c2s1)
+ fail("problem sending ctor");
+ PTestShutdownSubsubChild* c2s2 =
+ c2->SendPTestShutdownSubsubConstructor(expectParentDeleted);
+ if (!c2s2)
+ fail("problem sending ctor");
+
+ // delete parents without deleting kids
+ if (!c1->CallStackFrame())
+ fail("problem creating dummy stack frame");
+ if (!c2->CallStackFrame())
+ fail("problem creating dummy stack frame");
+ }
+
+ // test 3: alloc some actors and subactors, then crash
+ {
+ bool expectCrash = true, expectParentDeleted = false;
+
+ PTestShutdownSubChild* c1 =
+ SendPTestShutdownSubConstructor(expectCrash);
+ if (!c1)
+ fail("problem sending ctor");
+
+ PTestShutdownSubChild* c2 =
+ SendPTestShutdownSubConstructor(expectCrash);
+ if (!c2)
+ fail("problem sending ctor");
+
+ PTestShutdownSubsubChild* c1s1 =
+ c1->SendPTestShutdownSubsubConstructor(expectParentDeleted);
+ if (!c1s1)
+ fail("problem sending ctor");
+ PTestShutdownSubsubChild* c1s2 =
+ c1->SendPTestShutdownSubsubConstructor(expectParentDeleted);
+ if (!c1s2)
+ fail("problem sending ctor");
+
+ PTestShutdownSubsubChild* c2s1 =
+ c2->SendPTestShutdownSubsubConstructor(expectParentDeleted);
+ if (!c2s1)
+ fail("problem sending ctor");
+ PTestShutdownSubsubChild* c2s2 =
+ c2->SendPTestShutdownSubsubConstructor(expectParentDeleted);
+ if (!c2s2)
+ fail("problem sending ctor");
+
+ // make sure the ctors have been processed by the other side;
+ // the write end of the socket may temporarily be unwriteable
+ if (!SendSync())
+ fail("can't synchronize with parent");
+
+ // "crash", but without tripping tinderbox assert/abort
+ // detectors
+ _exit(0);
+ }
+}
+
+void
+TestShutdownChild::ActorDestroy(ActorDestroyReason why)
+{
+ fail("hey wait ... we should have crashed!");
+}
+
+bool
+TestShutdownSubChild::AnswerStackFrame()
+{
+ if (!PTestShutdownSubChild::Send__delete__(this))
+ fail("problem sending dtor");
+
+ // WATCH OUT! |this| has just deleted
+
+ return true;
+}
+
+void
+TestShutdownSubChild::ActorDestroy(ActorDestroyReason why)
+{
+ if (Manager()->ManagedPTestShutdownSubChild().Count() == 0)
+ fail("manager should still have managees!");
+
+ if (mExpectCrash && AbnormalShutdown != why)
+ fail("expected crash!");
+ else if (!mExpectCrash && AbnormalShutdown == why)
+ fail("wasn't expecting crash!");
+
+ if (mExpectCrash && 0 == ManagedPTestShutdownSubsubChild().Count())
+ fail("expected to *still* have kids");
+}
+
+void
+TestShutdownSubsubChild::ActorDestroy(ActorDestroyReason why)
+{
+ if (Manager()->ManagedPTestShutdownSubsubChild().Count() == 0)
+ fail("manager should still have managees!");
+
+ if (mExpectParentDeleted && AncestorDeletion != why)
+ fail("expected ParentDeleted == why");
+ else if (!mExpectParentDeleted && AncestorDeletion == why)
+ fail("wasn't expecting parent delete");
+}
+
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestShutdown.h b/ipc/ipdl/test/cxx/TestShutdown.h
new file mode 100644
index 000000000..c79cbb8fc
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestShutdown.h
@@ -0,0 +1,225 @@
+#ifndef mozilla__ipdltest_TestShutdown_h
+#define mozilla__ipdltest_TestShutdown_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestShutdownParent.h"
+#include "mozilla/_ipdltest/PTestShutdownChild.h"
+
+#include "mozilla/_ipdltest/PTestShutdownSubParent.h"
+#include "mozilla/_ipdltest/PTestShutdownSubChild.h"
+
+#include "mozilla/_ipdltest/PTestShutdownSubsubParent.h"
+#include "mozilla/_ipdltest/PTestShutdownSubsubChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// Parent side
+
+class TestShutdownSubsubParent :
+ public PTestShutdownSubsubParent
+{
+public:
+ explicit TestShutdownSubsubParent(bool expectParentDeleted) :
+ mExpectParentDeleted(expectParentDeleted)
+ {
+ }
+
+ virtual ~TestShutdownSubsubParent()
+ {
+ }
+
+protected:
+ virtual void
+ ActorDestroy(ActorDestroyReason why) override;
+
+private:
+ bool mExpectParentDeleted;
+};
+
+
+class TestShutdownSubParent :
+ public PTestShutdownSubParent
+{
+public:
+ explicit TestShutdownSubParent(bool expectCrash) :
+ mExpectCrash(expectCrash),
+ mDeletedCount(0)
+ {
+ }
+
+ virtual ~TestShutdownSubParent()
+ {
+ if (2 != mDeletedCount)
+ fail("managees outliving manager!");
+ }
+
+protected:
+ virtual bool
+ AnswerStackFrame() override
+ {
+ return CallStackFrame();
+ }
+
+ virtual PTestShutdownSubsubParent*
+ AllocPTestShutdownSubsubParent(const bool& expectParentDelete) override
+ {
+ return new TestShutdownSubsubParent(expectParentDelete);
+ }
+
+ virtual bool
+ DeallocPTestShutdownSubsubParent(PTestShutdownSubsubParent* actor) override
+ {
+ delete actor;
+ ++mDeletedCount;
+ return true;
+ }
+
+ virtual void
+ ActorDestroy(ActorDestroyReason why) override;
+
+private:
+ bool mExpectCrash;
+ int mDeletedCount;
+};
+
+
+class TestShutdownParent :
+ public PTestShutdownParent
+{
+public:
+ TestShutdownParent()
+ {
+ }
+ virtual ~TestShutdownParent()
+ {
+ }
+
+ static bool RunTestInProcesses() { return true; }
+ // FIXME/bug 703323 Could work if modified
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+
+protected:
+ virtual bool RecvSync() override { return true; }
+
+ virtual PTestShutdownSubParent*
+ AllocPTestShutdownSubParent(const bool& expectCrash) override
+ {
+ return new TestShutdownSubParent(expectCrash);
+ }
+
+ virtual bool
+ DeallocPTestShutdownSubParent(PTestShutdownSubParent* actor) override
+ {
+ delete actor;
+ return true;
+ }
+
+ virtual void
+ ActorDestroy(ActorDestroyReason why) override;
+};
+
+
+//-----------------------------------------------------------------------------
+// Child side
+
+class TestShutdownSubsubChild :
+ public PTestShutdownSubsubChild
+{
+public:
+ explicit TestShutdownSubsubChild(bool expectParentDeleted) :
+ mExpectParentDeleted(expectParentDeleted)
+ {
+ }
+ virtual ~TestShutdownSubsubChild()
+ {
+ }
+
+protected:
+ virtual void
+ ActorDestroy(ActorDestroyReason why) override;
+
+private:
+ bool mExpectParentDeleted;
+};
+
+
+class TestShutdownSubChild :
+ public PTestShutdownSubChild
+{
+public:
+ explicit TestShutdownSubChild(bool expectCrash) : mExpectCrash(expectCrash)
+ {
+ }
+
+ virtual ~TestShutdownSubChild()
+ {
+ }
+
+protected:
+ virtual bool AnswerStackFrame() override;
+
+ virtual PTestShutdownSubsubChild*
+ AllocPTestShutdownSubsubChild(const bool& expectParentDelete) override
+ {
+ return new TestShutdownSubsubChild(expectParentDelete);
+ }
+
+ virtual bool
+ DeallocPTestShutdownSubsubChild(PTestShutdownSubsubChild* actor) override
+ {
+ delete actor;
+ return true;
+ }
+
+ virtual void
+ ActorDestroy(ActorDestroyReason why) override;
+
+private:
+ bool mExpectCrash;
+};
+
+
+class TestShutdownChild :
+ public PTestShutdownChild
+{
+public:
+ TestShutdownChild()
+ {
+ }
+ virtual ~TestShutdownChild()
+ {
+ }
+
+protected:
+ virtual bool
+ RecvStart();
+
+ virtual PTestShutdownSubChild*
+ AllocPTestShutdownSubChild(
+ const bool& expectCrash) override
+ {
+ return new TestShutdownSubChild(expectCrash);
+ }
+
+ virtual bool
+ DeallocPTestShutdownSubChild(PTestShutdownSubChild* actor) override
+ {
+ delete actor;
+ return true;
+ }
+
+ virtual void
+ ActorDestroy(ActorDestroyReason why) override;
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestShutdown_h
diff --git a/ipc/ipdl/test/cxx/TestStackHooks.cpp b/ipc/ipdl/test/cxx/TestStackHooks.cpp
new file mode 100644
index 000000000..b4181985c
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestStackHooks.cpp
@@ -0,0 +1,168 @@
+#include "TestStackHooks.h"
+
+#include "base/task.h"
+#include "IPDLUnitTests.h" // fail etc.
+
+
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+TestStackHooksParent::TestStackHooksParent() :
+ mOnStack(false), mIncallDepth(0)
+{
+ MOZ_COUNT_CTOR(TestStackHooksParent);
+}
+
+TestStackHooksParent::~TestStackHooksParent()
+{
+ MOZ_COUNT_DTOR(TestStackHooksParent);
+}
+
+void
+TestStackHooksParent::Main()
+{
+ if (!SendStart())
+ fail("sending Start()");
+}
+
+
+bool
+TestStackHooksParent::AnswerStackFrame()
+{
+ if (!mOnStack)
+ fail("not on C++ stack?!");
+
+ if (!CallStackFrame())
+ fail("calling StackFrame()");
+
+ if (!mOnStack)
+ fail("not on C++ stack?!");
+
+ if (1 != mIncallDepth)
+ fail("missed EnteredCall or ExitedCall hook");
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+TestStackHooksChild::TestStackHooksChild() :
+ mOnStack(false),
+ mEntered(0),
+ mExited(0),
+ mIncallDepth(0)
+{
+ MOZ_COUNT_CTOR(TestStackHooksChild);
+}
+
+TestStackHooksChild::~TestStackHooksChild()
+{
+ MOZ_COUNT_DTOR(TestStackHooksChild);
+}
+
+namespace {
+void RunTestsFn() {
+ static_cast<TestStackHooksChild*>(gChildActor)->RunTests();
+}
+}
+
+bool
+TestStackHooksChild::RecvStart()
+{
+ if (!mOnStack)
+ fail("missed stack notification");
+
+ if (0 != mIncallDepth)
+ fail("EnteredCall/ExitedCall malfunction");
+
+ // kick off tests from a runnable so that we can start with
+ // MessageChannel code on the C++ stack
+ MessageLoop::current()->PostTask(NewRunnableFunction(RunTestsFn));
+
+ return true;
+}
+
+bool
+TestStackHooksChild::AnswerStackFrame()
+{
+ if (!mOnStack)
+ fail("missed stack notification");
+
+ if (1 != mIncallDepth)
+ fail("missed EnteredCall or ExitedCall hook");
+
+ if (PTestStackHooks::TEST4_3 == state()) {
+ if (!SendAsync())
+ fail("sending Async()");
+ }
+ else if (PTestStackHooks::TEST5_3 == state()) {
+ if (!SendSync())
+ fail("sending Sync()");
+ }
+ else {
+ fail("unexpected state");
+ }
+
+ if (!mOnStack)
+ fail("bad stack exit notification");
+
+ return true;
+}
+
+void
+TestStackHooksChild::RunTests()
+{
+ // 1 because of RecvStart()
+ if (1 != mEntered)
+ fail("missed stack notification");
+ if (mOnStack)
+ fail("spurious stack notification");
+ if (0 != mIncallDepth)
+ fail("EnteredCall/ExitedCall malfunction");
+
+ if (!SendAsync())
+ fail("sending Async()");
+ if (mOnStack)
+ fail("spurious stack notification");
+ if (0 != mIncallDepth)
+ fail("EnteredCall/ExitedCall malfunction");
+ if (2 != mEntered)
+ fail("missed stack notification");
+
+ if (!SendSync())
+ fail("sending Sync()");
+ if (mOnStack)
+ fail("spurious stack notification");
+ if (0 != mIncallDepth)
+ fail("EnteredCall/ExitedCall malfunction");
+ if (3 != mEntered)
+ fail("missed stack notification");
+
+ if (!CallRpc())
+ fail("calling RPC()");
+ if (mOnStack)
+ fail("spurious stack notification");
+ if (0 != mIncallDepth)
+ fail("EnteredCall/ExitedCall malfunction");
+ if (4 != mEntered)
+ fail("missed stack notification");
+
+ if (!CallStackFrame())
+ fail("calling StackFrame()");
+ if (mOnStack)
+ fail("spurious stack notification");
+ if (0 != mIncallDepth)
+ fail("EnteredCall/ExitedCall malfunction");
+ if (5 != mEntered)
+ fail("missed stack notification");
+
+ Close();
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestStackHooks.h b/ipc/ipdl/test/cxx/TestStackHooks.h
new file mode 100644
index 000000000..c26d5f937
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestStackHooks.h
@@ -0,0 +1,130 @@
+#ifndef mozilla__ipdltest_TestStackHooks_h
+#define mozilla__ipdltest_TestStackHooks_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestStackHooksParent.h"
+#include "mozilla/_ipdltest/PTestStackHooksChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestStackHooksParent :
+ public PTestStackHooksParent
+{
+public:
+ TestStackHooksParent();
+ virtual ~TestStackHooksParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+protected:
+ virtual bool RecvAsync() override {
+ if (!mOnStack)
+ fail("not on C++ stack?!");
+ return true;
+ }
+
+ virtual bool RecvSync() override {
+ if (!mOnStack)
+ fail("not on C++ stack?!");
+ return true;
+ }
+
+ virtual bool AnswerRpc() override {
+ if (!mOnStack)
+ fail("not on C++ stack?!");
+ return true;
+ }
+
+ virtual bool AnswerStackFrame() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+
+ virtual void EnteredCxxStack() override {
+ mOnStack = true;
+ }
+ virtual void ExitedCxxStack() override {
+ mOnStack = false;
+ }
+
+ virtual void EnteredCall() override {
+ ++mIncallDepth;
+ }
+ virtual void ExitedCall() override {
+ --mIncallDepth;
+ }
+
+private:
+ bool mOnStack;
+ int mIncallDepth;
+};
+
+
+class TestStackHooksChild :
+ public PTestStackHooksChild
+{
+public:
+ TestStackHooksChild();
+ virtual ~TestStackHooksChild();
+
+ void RunTests();
+
+protected:
+ virtual bool RecvStart() override;
+
+ virtual bool AnswerStackFrame() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+
+ if (mEntered != mExited)
+ fail("unbalanced enter/exit notifications");
+
+ if (mOnStack)
+ fail("computing mOnStack went awry; should have failed above assertion");
+
+ QuitChild();
+ }
+
+ virtual void EnteredCxxStack() override {
+ ++mEntered;
+ mOnStack = true;
+ }
+ virtual void ExitedCxxStack() override {
+ ++mExited;
+ mOnStack = false;
+ }
+
+ virtual void EnteredCall() override {
+ ++mIncallDepth;
+ }
+ virtual void ExitedCall() override {
+ --mIncallDepth;
+ }
+
+private:
+ bool mOnStack;
+ int mEntered;
+ int mExited;
+ int mIncallDepth;
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestStackHooks_h
diff --git a/ipc/ipdl/test/cxx/TestSyncError.cpp b/ipc/ipdl/test/cxx/TestSyncError.cpp
new file mode 100644
index 000000000..b9d8ec938
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestSyncError.cpp
@@ -0,0 +1,61 @@
+#include "TestSyncError.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+TestSyncErrorParent::TestSyncErrorParent()
+{
+ MOZ_COUNT_CTOR(TestSyncErrorParent);
+}
+
+TestSyncErrorParent::~TestSyncErrorParent()
+{
+ MOZ_COUNT_DTOR(TestSyncErrorParent);
+}
+
+void
+TestSyncErrorParent::Main()
+{
+ if (!SendStart())
+ fail("sending Start");
+}
+
+bool
+TestSyncErrorParent::RecvError()
+{
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// child
+
+TestSyncErrorChild::TestSyncErrorChild()
+{
+ MOZ_COUNT_CTOR(TestSyncErrorChild);
+}
+
+TestSyncErrorChild::~TestSyncErrorChild()
+{
+ MOZ_COUNT_DTOR(TestSyncErrorChild);
+}
+
+bool
+TestSyncErrorChild::RecvStart()
+{
+ if (SendError())
+ fail("Error() should have return false");
+
+ Close();
+
+ return true;
+}
+
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestSyncError.h b/ipc/ipdl/test/cxx/TestSyncError.h
new file mode 100644
index 000000000..c39402a87
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestSyncError.h
@@ -0,0 +1,71 @@
+#ifndef mozilla__ipdltest_TestSyncError_h
+#define mozilla__ipdltest_TestSyncError_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestSyncErrorParent.h"
+#include "mozilla/_ipdltest/PTestSyncErrorChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestSyncErrorParent :
+ public PTestSyncErrorParent
+{
+public:
+ TestSyncErrorParent();
+ virtual ~TestSyncErrorParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+protected:
+ virtual bool RecvError() override;
+
+ virtual void ProcessingError(Result aCode, const char* aReason) override
+ {
+ // Ignore errors
+ }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+};
+
+
+class TestSyncErrorChild :
+ public PTestSyncErrorChild
+{
+public:
+ TestSyncErrorChild();
+ virtual ~TestSyncErrorChild();
+
+protected:
+ virtual bool RecvStart() override;
+
+ virtual void ProcessingError(Result aCode, const char* aReason) override
+ {
+ // Ignore errors
+ }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestSyncError_h
diff --git a/ipc/ipdl/test/cxx/TestSyncHang.cpp b/ipc/ipdl/test/cxx/TestSyncHang.cpp
new file mode 100644
index 000000000..62b9ae723
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestSyncHang.cpp
@@ -0,0 +1,70 @@
+#include "TestSyncHang.h"
+#include "base/task.h"
+#include "mozilla/ipc/GeckoChildProcessHost.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+using std::vector;
+using std::string;
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+mozilla::ipc::GeckoChildProcessHost* gSyncHangSubprocess;
+
+TestSyncHangParent::TestSyncHangParent()
+{
+ MOZ_COUNT_CTOR(TestSyncHangParent);
+}
+
+TestSyncHangParent::~TestSyncHangParent()
+{
+ MOZ_COUNT_DTOR(TestSyncHangParent);
+}
+
+void
+DeleteSyncHangSubprocess(MessageLoop* uiLoop)
+{
+ delete gSyncHangSubprocess;
+}
+
+void
+DeferredSyncHangParentShutdown()
+{
+ // ping to DeleteSubprocess
+ XRE_GetIOMessageLoop()->PostTask(
+ NewRunnableFunction(DeleteSyncHangSubprocess, MessageLoop::current()));
+}
+
+void
+TestSyncHangParent::Main()
+{
+ vector<string> args;
+ args.push_back("fake/path");
+ gSyncHangSubprocess = new mozilla::ipc::GeckoChildProcessHost(GeckoProcessType_Plugin);
+ bool launched = gSyncHangSubprocess->SyncLaunch(args, 2);
+ if (launched)
+ fail("Calling SyncLaunch with an invalid path should return false");
+
+ MessageLoop::current()->PostTask(NewRunnableFunction(DeferredSyncHangParentShutdown));
+ Close();
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+TestSyncHangChild::TestSyncHangChild()
+{
+ MOZ_COUNT_CTOR(TestSyncHangChild);
+}
+
+TestSyncHangChild::~TestSyncHangChild()
+{
+ MOZ_COUNT_DTOR(TestSyncHangChild);
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestSyncHang.h b/ipc/ipdl/test/cxx/TestSyncHang.h
new file mode 100644
index 000000000..87533d661
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestSyncHang.h
@@ -0,0 +1,58 @@
+#ifndef mozilla__ipdltest_TestSyncHang_h
+#define mozilla__ipdltest_TestSyncHang_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestSyncHangParent.h"
+#include "mozilla/_ipdltest/PTestSyncHangChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestSyncHangParent :
+ public PTestSyncHangParent
+{
+public:
+ TestSyncHangParent();
+ virtual ~TestSyncHangParent();
+
+ static bool RunTestInProcesses() { return true; }
+ // FIXME/bug 703323 Could work if modified
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+
+protected:
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+};
+
+
+class TestSyncHangChild :
+ public PTestSyncHangChild
+{
+public:
+ TestSyncHangChild();
+ virtual ~TestSyncHangChild();
+
+protected:
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestSyncHang_h
diff --git a/ipc/ipdl/test/cxx/TestSyncWakeup.cpp b/ipc/ipdl/test/cxx/TestSyncWakeup.cpp
new file mode 100644
index 000000000..cce0ca34f
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestSyncWakeup.cpp
@@ -0,0 +1,134 @@
+#if defined(OS_POSIX)
+#include <unistd.h> // sleep()
+#endif
+
+#include "TestSyncWakeup.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+TestSyncWakeupParent::TestSyncWakeupParent()
+{
+ MOZ_COUNT_CTOR(TestSyncWakeupParent);
+}
+
+TestSyncWakeupParent::~TestSyncWakeupParent()
+{
+ MOZ_COUNT_DTOR(TestSyncWakeupParent);
+}
+
+void
+TestSyncWakeupParent::Main()
+{
+ if (!SendStart())
+ fail("sending Start()");
+}
+
+bool
+TestSyncWakeupParent::AnswerStackFrame()
+{
+ if (!CallStackFrame())
+ fail("calling StackFrame()");
+ return true;
+}
+
+bool
+TestSyncWakeupParent::RecvSync1()
+{
+ if (!SendNote1())
+ fail("sending Note1()");
+
+ // XXX ugh ... need to ensure that the async message and sync
+ // reply come in "far enough" apart that this test doesn't pass on
+ // accident
+#if defined(OS_POSIX)
+ // NB: can't use PR_Sleep (i.e. Sleep() on windows) because it's
+ // only spec'd to block the current thread, not the current
+ // process. We need the IO thread to sleep as well.
+ puts(" (sleeping for 5 seconds. sorry!)");
+ sleep(5);
+#endif
+
+ return true;
+}
+
+bool
+TestSyncWakeupParent::RecvSync2()
+{
+ if (!SendNote2())
+ fail("sending Note2()");
+
+#if defined(OS_POSIX)
+ // see above
+ sleep(5);
+ puts(" (sleeping for 5 seconds. sorry!)");
+#endif
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+TestSyncWakeupChild::TestSyncWakeupChild() : mDone(false)
+{
+ MOZ_COUNT_CTOR(TestSyncWakeupChild);
+}
+
+TestSyncWakeupChild::~TestSyncWakeupChild()
+{
+ MOZ_COUNT_DTOR(TestSyncWakeupChild);
+}
+
+bool
+TestSyncWakeupChild::RecvStart()
+{
+ // First test: the parent fires back an async message while
+ // replying to a sync one
+ if (!SendSync1())
+ fail("sending Sync()");
+
+ // drop back into the event loop to get Note1(), then kick off the
+ // second test
+ return true;
+}
+
+bool
+TestSyncWakeupChild::RecvNote1()
+{
+ // Second test: the parent fires back an async message while
+ // replying to a sync one, with a frame on the RPC stack
+ if (!CallStackFrame())
+ fail("calling StackFrame()");
+
+ if (!mDone)
+ fail("should have received Note2()!");
+
+ Close();
+
+ return true;
+}
+
+bool
+TestSyncWakeupChild::AnswerStackFrame()
+{
+ if (!SendSync2())
+ fail("sending Sync()");
+
+ return true;
+}
+
+bool
+TestSyncWakeupChild::RecvNote2()
+{
+ mDone = true;
+ return true;
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestSyncWakeup.h b/ipc/ipdl/test/cxx/TestSyncWakeup.h
new file mode 100644
index 000000000..dac3f4312
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestSyncWakeup.h
@@ -0,0 +1,74 @@
+#ifndef mozilla__ipdltest_TestSyncWakeup_h
+#define mozilla__ipdltest_TestSyncWakeup_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestSyncWakeupParent.h"
+#include "mozilla/_ipdltest/PTestSyncWakeupChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestSyncWakeupParent :
+ public PTestSyncWakeupParent
+{
+public:
+ TestSyncWakeupParent();
+ virtual ~TestSyncWakeupParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+protected:
+ virtual bool AnswerStackFrame() override;
+
+ virtual bool RecvSync1() override;
+
+ virtual bool RecvSync2() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+};
+
+
+class TestSyncWakeupChild :
+ public PTestSyncWakeupChild
+{
+public:
+ TestSyncWakeupChild();
+ virtual ~TestSyncWakeupChild();
+
+protected:
+ virtual bool RecvStart() override;
+
+ virtual bool RecvNote1() override;
+
+ virtual bool AnswerStackFrame() override;
+
+ virtual bool RecvNote2() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+
+private:
+ bool mDone;
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestSyncWakeup_h
diff --git a/ipc/ipdl/test/cxx/TestUrgency.cpp b/ipc/ipdl/test/cxx/TestUrgency.cpp
new file mode 100644
index 000000000..b9b05bcea
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestUrgency.cpp
@@ -0,0 +1,162 @@
+#include "TestUrgency.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+#if defined(OS_POSIX)
+#include <unistd.h>
+#else
+#include <windows.h>
+#endif
+
+namespace mozilla {
+namespace _ipdltest {
+
+#if defined(OS_POSIX)
+static void Sleep(int ms)
+{
+ sleep(ms / 1000);
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// parent
+
+TestUrgencyParent::TestUrgencyParent()
+ : inreply_(false)
+{
+ MOZ_COUNT_CTOR(TestUrgencyParent);
+}
+
+TestUrgencyParent::~TestUrgencyParent()
+{
+ MOZ_COUNT_DTOR(TestUrgencyParent);
+}
+
+void
+TestUrgencyParent::Main()
+{
+ if (!SendStart())
+ fail("sending Start");
+}
+
+bool
+TestUrgencyParent::RecvTest1(uint32_t *value)
+{
+ if (!SendReply1(value))
+ fail("sending Reply1");
+ if (*value != 99)
+ fail("bad value");
+ return true;
+}
+
+bool
+TestUrgencyParent::RecvTest2()
+{
+ uint32_t value;
+ inreply_ = true;
+ if (!SendReply2(&value))
+ fail("sending Reply2");
+ inreply_ = false;
+ if (value != 500)
+ fail("bad value");
+ return true;
+}
+
+bool
+TestUrgencyParent::RecvTest3(uint32_t *value)
+{
+ if (inreply_)
+ fail("nested non-urgent on top of urgent rpc");
+ *value = 1000;
+ return true;
+}
+
+bool
+TestUrgencyParent::RecvFinalTest_Begin()
+{
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+enum {
+ kFirstTestBegin = 1,
+ kFirstTestGotReply,
+ kSecondTestBegin,
+ kSecondTestGotReply,
+};
+
+bool
+TestUrgencyChild::RecvStart()
+{
+ uint32_t result;
+
+ // Send a synchronous message, expect to get an urgent message while
+ // blocked.
+ test_ = kFirstTestBegin;
+ if (!SendTest1(&result))
+ fail("calling SendTest1");
+ if (result != 99)
+ fail("bad result in RecvStart");
+ if (test_ != kFirstTestGotReply)
+ fail("never received urgent message");
+
+ // Initiate the next test by sending an asynchronous message, then becoming
+ // blocked. This tests that the urgent message is still delivered properly,
+ // and that the parent does not try to service the sync
+ test_ = kSecondTestBegin;
+ if (!SendTest2())
+ fail("calling SendTest2");
+ if (!SendTest3(&result))
+ fail("calling SendTest3");
+ if (test_ != kSecondTestGotReply)
+ fail("never received urgent message #2");
+ if (result != 1000)
+ fail("wrong value from test3");
+
+ if (!SendFinalTest_Begin())
+ fail("Final test should have succeeded");
+
+ Close();
+
+ return true;
+}
+
+bool
+TestUrgencyChild::RecvReply1(uint32_t *reply)
+{
+ if (test_ != kFirstTestBegin)
+ fail("wrong test # in RecvReply1");
+
+ *reply = 99;
+ test_ = kFirstTestGotReply;
+ return true;
+}
+
+bool
+TestUrgencyChild::RecvReply2(uint32_t *reply)
+{
+ if (test_ != kSecondTestBegin)
+ fail("wrong test # in RecvReply2");
+
+ // sleep for 5 seconds so the parent process tries to deliver more messages.
+ Sleep(5000);
+
+ *reply = 500;
+ test_ = kSecondTestGotReply;
+ return true;
+}
+
+TestUrgencyChild::TestUrgencyChild()
+ : test_(0)
+{
+ MOZ_COUNT_CTOR(TestUrgencyChild);
+}
+
+TestUrgencyChild::~TestUrgencyChild()
+{
+ MOZ_COUNT_DTOR(TestUrgencyChild);
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestUrgency.h b/ipc/ipdl/test/cxx/TestUrgency.h
new file mode 100644
index 000000000..07a31b1c1
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestUrgency.h
@@ -0,0 +1,72 @@
+#ifndef mozilla__ipdltest_TestUrgency_h
+#define mozilla__ipdltest_TestUrgency_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestUrgencyParent.h"
+#include "mozilla/_ipdltest/PTestUrgencyChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestUrgencyParent :
+ public PTestUrgencyParent
+{
+public:
+ TestUrgencyParent();
+ virtual ~TestUrgencyParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+
+ bool RecvTest1(uint32_t *value);
+ bool RecvTest2();
+ bool RecvTest3(uint32_t *value);
+ bool RecvTest4_Begin();
+ bool RecvTest4_NestedSync();
+ bool RecvFinalTest_Begin();
+
+ bool ShouldContinueFromReplyTimeout() override
+ {
+ return false;
+ }
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ passed("ok");
+ QuitParent();
+ }
+
+private:
+ bool inreply_;
+};
+
+
+class TestUrgencyChild :
+ public PTestUrgencyChild
+{
+public:
+ TestUrgencyChild();
+ virtual ~TestUrgencyChild();
+
+ bool RecvStart();
+ bool RecvReply1(uint32_t *reply);
+ bool RecvReply2(uint32_t *reply);
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ QuitChild();
+ }
+
+private:
+ uint32_t test_;
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestUrgency_h
diff --git a/ipc/ipdl/test/cxx/TestUrgentHangs.cpp b/ipc/ipdl/test/cxx/TestUrgentHangs.cpp
new file mode 100644
index 000000000..b798ae18d
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestUrgentHangs.cpp
@@ -0,0 +1,217 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: sw=4 ts=4 et :
+ */
+#include "TestUrgentHangs.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+#include "prthread.h"
+#if defined(OS_POSIX)
+#include <unistd.h>
+#else
+#include <windows.h>
+#endif
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+TestUrgentHangsParent::TestUrgentHangsParent()
+ : mInnerCount(0),
+ mInnerUrgentCount(0)
+{
+ MOZ_COUNT_CTOR(TestUrgentHangsParent);
+}
+
+TestUrgentHangsParent::~TestUrgentHangsParent()
+{
+ MOZ_COUNT_DTOR(TestUrgentHangsParent);
+}
+
+void
+TestUrgentHangsParent::Main()
+{
+ SetReplyTimeoutMs(1000);
+
+ // Should succeed despite the nested sleep call because the content process
+ // responded to the transaction.
+ if (!SendTest1_1())
+ fail("sending Test1_1");
+
+ // Fails with a timeout.
+ if (SendTest2())
+ fail("sending Test2");
+
+ // Also fails since we haven't gotten a response for Test2 yet.
+ if (SendTest3())
+ fail("sending Test3");
+
+ // Do a second round of testing once the reply to Test2 comes back.
+ MessageLoop::current()->PostDelayedTask(
+ NewNonOwningRunnableMethod(this, &TestUrgentHangsParent::SecondStage),
+ 3000);
+}
+
+void
+TestUrgentHangsParent::SecondStage()
+{
+ // Send an async message that waits 2 seconds and then sends a sync message
+ // (which should be processed).
+ if (!SendTest4())
+ fail("sending Test4");
+
+ // Send a sync message that will time out because the child is waiting
+ // inside RecvTest4.
+ if (SendTest4_1())
+ fail("sending Test4_1");
+
+ MessageLoop::current()->PostDelayedTask(
+ NewNonOwningRunnableMethod(this, &TestUrgentHangsParent::ThirdStage),
+ 3000);
+}
+
+void
+TestUrgentHangsParent::ThirdStage()
+{
+ // The third stage does the same thing as the second stage except that the
+ // child sends an urgent message to us. In this case, we actually answer
+ // that message unconditionally.
+
+ // Send an async message that waits 2 seconds and then sends a sync message
+ // (which should be processed).
+ if (!SendTest5())
+ fail("sending Test5");
+
+ // Send a sync message that will time out because the child is waiting
+ // inside RecvTest5.
+ if (SendTest5_1())
+ fail("sending Test5_1");
+
+ // Close the channel after the child finishes its work in RecvTest5.
+ MessageLoop::current()->PostDelayedTask(
+ NewNonOwningRunnableMethod(this, &TestUrgentHangsParent::Close),
+ 3000);
+}
+
+bool
+TestUrgentHangsParent::RecvTest1_2()
+{
+ if (!SendTest1_3())
+ fail("sending Test1_3");
+ return true;
+}
+
+bool
+TestUrgentHangsParent::RecvTestInner()
+{
+ mInnerCount++;
+ return true;
+}
+
+bool
+TestUrgentHangsParent::RecvTestInnerUrgent()
+{
+ mInnerUrgentCount++;
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+bool
+TestUrgentHangsChild::RecvTest1_1()
+{
+ if (!SendTest1_2())
+ fail("sending Test1_2");
+
+ return true;
+}
+
+bool
+TestUrgentHangsChild::RecvTest1_3()
+{
+ PR_Sleep(PR_SecondsToInterval(2));
+
+ return true;
+}
+
+bool
+TestUrgentHangsChild::RecvTest2()
+{
+ PR_Sleep(PR_SecondsToInterval(2));
+
+ // Should fail because of the timeout.
+ if (SendTestInner())
+ fail("sending TestInner");
+
+ return true;
+}
+
+bool
+TestUrgentHangsChild::RecvTest3()
+{
+ fail("RecvTest3 should never be called");
+ return true;
+}
+
+bool
+TestUrgentHangsChild::RecvTest4()
+{
+ PR_Sleep(PR_SecondsToInterval(2));
+
+ // This won't fail because we should handle Test4_1 here before actually
+ // sending TestInner to the parent.
+ if (!SendTestInner())
+ fail("sending TestInner");
+
+ return true;
+}
+
+bool
+TestUrgentHangsChild::RecvTest4_1()
+{
+ // This should fail because Test4_1 timed out and hasn't gotten a response
+ // yet.
+ if (SendTestInner())
+ fail("sending TestInner");
+
+ return true;
+}
+
+bool
+TestUrgentHangsChild::RecvTest5()
+{
+ PR_Sleep(PR_SecondsToInterval(2));
+
+ // This message will actually be handled by the parent even though it's in
+ // the timeout state.
+ if (!SendTestInnerUrgent())
+ fail("sending TestInner");
+
+ return true;
+}
+
+bool
+TestUrgentHangsChild::RecvTest5_1()
+{
+ // This message will actually be handled by the parent even though it's in
+ // the timeout state.
+ if (!SendTestInnerUrgent())
+ fail("sending TestInner");
+
+ return true;
+}
+
+TestUrgentHangsChild::TestUrgentHangsChild()
+{
+ MOZ_COUNT_CTOR(TestUrgentHangsChild);
+}
+
+TestUrgentHangsChild::~TestUrgentHangsChild()
+{
+ MOZ_COUNT_DTOR(TestUrgentHangsChild);
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestUrgentHangs.h b/ipc/ipdl/test/cxx/TestUrgentHangs.h
new file mode 100644
index 000000000..bbe63d11e
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestUrgentHangs.h
@@ -0,0 +1,79 @@
+#ifndef mozilla__ipdltest_TestUrgentHangs_h
+#define mozilla__ipdltest_TestUrgentHangs_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestUrgentHangsParent.h"
+#include "mozilla/_ipdltest/PTestUrgentHangsChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestUrgentHangsParent :
+ public PTestUrgentHangsParent
+{
+public:
+ TestUrgentHangsParent();
+ virtual ~TestUrgentHangsParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+ void SecondStage();
+ void ThirdStage();
+
+ bool RecvTest1_2();
+ bool RecvTestInner();
+ bool RecvTestInnerUrgent();
+
+ bool ShouldContinueFromReplyTimeout() override
+ {
+ return false;
+ }
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (mInnerCount != 1) {
+ fail("wrong mInnerCount");
+ }
+ if (mInnerUrgentCount != 2) {
+ fail("wrong mInnerUrgentCount");
+ }
+ passed("ok");
+ QuitParent();
+ }
+
+private:
+ size_t mInnerCount, mInnerUrgentCount;
+};
+
+
+class TestUrgentHangsChild :
+ public PTestUrgentHangsChild
+{
+public:
+ TestUrgentHangsChild();
+ virtual ~TestUrgentHangsChild();
+
+ bool RecvTest1_1();
+ bool RecvTest1_3();
+ bool RecvTest2();
+ bool RecvTest3();
+ bool RecvTest4();
+ bool RecvTest4_1();
+ bool RecvTest5();
+ bool RecvTest5_1();
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ QuitChild();
+ }
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestUrgentHangs_h
diff --git a/ipc/ipdl/test/cxx/app/Makefile.in b/ipc/ipdl/test/cxx/app/Makefile.in
new file mode 100644
index 000000000..3738d44d3
--- /dev/null
+++ b/ipc/ipdl/test/cxx/app/Makefile.in
@@ -0,0 +1,5 @@
+# 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/.
+
+NSDISTMODE = copy
diff --git a/ipc/ipdl/test/cxx/app/TestIPDL.cpp b/ipc/ipdl/test/cxx/app/TestIPDL.cpp
new file mode 100644
index 000000000..a34cca080
--- /dev/null
+++ b/ipc/ipdl/test/cxx/app/TestIPDL.cpp
@@ -0,0 +1,20 @@
+/* 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 "nsXULAppAPI.h"
+
+#if defined(XP_WIN)
+#include <windows.h>
+#include "nsWindowsWMain.cpp"
+#endif
+
+int
+main(int argc, char** argv)
+{
+ // the first argument specifies which IPDL test case/suite to load
+ if (argc < 2)
+ return 1;
+
+ return XRE_RunIPDLTest(argc, argv);
+}
diff --git a/ipc/ipdl/test/cxx/app/moz.build b/ipc/ipdl/test/cxx/app/moz.build
new file mode 100644
index 000000000..d464e78a6
--- /dev/null
+++ b/ipc/ipdl/test/cxx/app/moz.build
@@ -0,0 +1,20 @@
+# -*- 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/.
+
+GeckoProgram('ipdlunittest', linkage='dependent')
+
+SOURCES += [
+ 'TestIPDL.cpp',
+]
+include('/ipc/chromium/chromium-config.mozbuild')
+
+LOCAL_INCLUDES += [
+ '/toolkit/xre',
+ '/xpcom/base',
+]
+
+if CONFIG['_MSC_VER']:
+ WIN32_EXE_LDFLAGS += ['-ENTRY:wmainCRTStartup']
diff --git a/ipc/ipdl/test/cxx/genIPDLUnitTests.py b/ipc/ipdl/test/cxx/genIPDLUnitTests.py
new file mode 100644
index 000000000..24026451b
--- /dev/null
+++ b/ipc/ipdl/test/cxx/genIPDLUnitTests.py
@@ -0,0 +1,141 @@
+# 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/.
+
+import string, sys
+
+def usage():
+ print >>sys.stderr, """
+%s template_file -t unit_tests... -e extra_protocols...
+
+ TEMPLATE_FILE is used to generate to generate the unit-tester .cpp
+ UNIT_TESTS are the top-level protocols defining unit tests
+ EXTRA_PROTOCOLS are top-level protocols for subprocesses that can be
+ spawned in tests but are not unit tests in and of
+ themselves
+"""% (sys.argv[0])
+ sys.exit(1)
+
+def main(argv):
+ template = argv[1]
+
+ if argv[2] != '-t': usage()
+ i = 3
+ unittests = []
+ while argv[i] != '-e':
+ unittests.append(argv[i])
+ i += 1
+
+ extras = argv[(i+1):]
+
+ includes = '\n'.join([
+ '#include "%s.h"'% (t) for t in unittests ])
+
+
+ enum_values = '\n'.join([
+ ' %s,'% (t) for t in unittests+extras ])
+ last_enum = unittests[-1]
+
+
+ string_to_enums = '\n'.join([
+ ''' else if (!strcmp(aString, "%s"))
+ return %s;'''% (t, t) for t in unittests+extras ])
+
+ enum_to_strings = '\n'.join([
+ ''' case %s:
+ return "%s";'''%(t, t) for t in unittests+extras ])
+
+ parent_delete_cases = '\n'.join([
+''' case %s: {
+ delete reinterpret_cast<%sParent*>(gParentActor);
+ return;
+ }
+'''% (t, t) for t in unittests ])
+
+ parent_enabled_cases_proc = '\n'.join([
+''' case %s: {
+ if (!%sParent::RunTestInProcesses()) {
+ passed("N/A to proc");
+ DeferredParentShutdown();
+ return;
+ }
+ break;
+ }
+''' % (t, t) for t in unittests ])
+
+ parent_main_cases_proc = '\n'.join([
+''' case %s: {
+ %sParent** parent =
+ reinterpret_cast<%sParent**>(&gParentActor);
+ *parent = new %sParent();
+ (*parent)->Open(transport, child);
+ return (*parent)->Main();
+ }
+'''% (t, t, t, t) for t in unittests ])
+
+ parent_enabled_cases_thread = '\n'.join([
+''' case %s: {
+ if (!%sParent::RunTestInThreads()) {
+ passed("N/A to threads");
+ DeferredParentShutdown();
+ return;
+ }
+ break;
+ }
+''' % (t, t) for t in unittests ])
+
+ parent_main_cases_thread = '\n'.join([
+''' case %s: {
+ %sParent** parent =
+ reinterpret_cast<%sParent**>(&gParentActor);
+ *parent = new %sParent();
+
+ %sChild** child =
+ reinterpret_cast<%sChild**>(&gChildActor);
+ *child = new %sChild();
+
+ ::mozilla::ipc::MessageChannel *childChannel = (*child)->GetIPCChannel();
+ ::mozilla::ipc::Side parentSide =
+ ::mozilla::ipc::ParentSide;
+
+ (*parent)->Open(childChannel, childMessageLoop, parentSide);
+ return (*parent)->Main();
+ }
+'''% (t, t, t, t, t, t, t) for t in unittests ])
+
+ child_delete_cases = '\n'.join([
+''' case %s: {
+ delete reinterpret_cast<%sChild*>(gChildActor);
+ return;
+ }
+'''% (t, t) for t in unittests+extras ])
+
+
+ child_init_cases = '\n'.join([
+''' case %s: {
+ %sChild** child =
+ reinterpret_cast<%sChild**>(&gChildActor);
+ *child = new %sChild();
+ (*child)->Open(transport, parentPid, worker);
+ return;
+ }
+'''% (t, t, t, t) for t in unittests+extras ])
+
+ templatefile = open(template, 'r')
+ sys.stdout.write(
+ string.Template(templatefile.read()).substitute(
+ INCLUDES=includes,
+ ENUM_VALUES=enum_values, LAST_ENUM=last_enum,
+ STRING_TO_ENUMS=string_to_enums,
+ ENUM_TO_STRINGS=enum_to_strings,
+ PARENT_DELETE_CASES=parent_delete_cases,
+ PARENT_ENABLED_CASES_PROC=parent_enabled_cases_proc,
+ PARENT_MAIN_CASES_PROC=parent_main_cases_proc,
+ PARENT_ENABLED_CASES_THREAD=parent_enabled_cases_thread,
+ PARENT_MAIN_CASES_THREAD=parent_main_cases_thread,
+ CHILD_DELETE_CASES=child_delete_cases,
+ CHILD_INIT_CASES=child_init_cases))
+ templatefile.close()
+
+if __name__ == '__main__':
+ main(sys.argv)
diff --git a/ipc/ipdl/test/cxx/moz.build b/ipc/ipdl/test/cxx/moz.build
new file mode 100644
index 000000000..c87455e02
--- /dev/null
+++ b/ipc/ipdl/test/cxx/moz.build
@@ -0,0 +1,134 @@
+# -*- 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 += ['app']
+
+EXPORTS.mozilla._ipdltest += [
+ 'IPDLUnitTestProcessChild.h',
+ 'IPDLUnitTests.h',
+ 'IPDLUnitTestTypes.h',
+ 'IPDLUnitTestUtils.h',
+]
+
+SOURCES += [
+ 'TestActorPunning.cpp',
+ 'TestBadActor.cpp',
+ 'TestBridgeMain.cpp',
+ 'TestCancel.cpp',
+ 'TestCrashCleanup.cpp',
+ 'TestDataStructures.cpp',
+ 'TestDemon.cpp',
+ 'TestDesc.cpp',
+ 'TestEndpointBridgeMain.cpp',
+ 'TestEndpointOpens.cpp',
+ 'TestFailedCtor.cpp',
+ 'TestHangs.cpp',
+ 'TestHighestPrio.cpp',
+ 'TestInterruptErrorCleanup.cpp',
+ 'TestInterruptRaces.cpp',
+ 'TestInterruptShutdownRace.cpp',
+ 'TestJSON.cpp',
+ 'TestLatency.cpp',
+ 'TestManyChildAllocs.cpp',
+ 'TestMultiMgrs.cpp',
+ 'TestNestedLoops.cpp',
+ 'TestOpens.cpp',
+ 'TestRaceDeadlock.cpp',
+ 'TestRaceDeferral.cpp',
+ 'TestRacyInterruptReplies.cpp',
+ 'TestRacyReentry.cpp',
+ 'TestRacyUndefer.cpp',
+ 'TestRPC.cpp',
+ 'TestSanity.cpp',
+ 'TestSelfManageRoot.cpp',
+ 'TestShmem.cpp',
+ 'TestShutdown.cpp',
+ 'TestStackHooks.cpp',
+ 'TestSyncError.cpp',
+ 'TestSyncHang.cpp',
+ 'TestSyncWakeup.cpp',
+ 'TestUrgency.cpp',
+ 'TestUrgentHangs.cpp',
+]
+
+SOURCES += [
+ '!IPDLUnitTests.cpp',
+ 'IPDLUnitTestProcessChild.cpp',
+ 'IPDLUnitTestSubprocess.cpp',
+]
+
+IPDL_SOURCES += [
+ 'PTestActorPunning.ipdl',
+ 'PTestActorPunningPunned.ipdl',
+ 'PTestActorPunningSub.ipdl',
+ 'PTestBadActor.ipdl',
+ 'PTestBadActorSub.ipdl',
+ 'PTestBridgeMain.ipdl',
+ 'PTestBridgeMainSub.ipdl',
+ 'PTestBridgeSub.ipdl',
+ 'PTestCancel.ipdl',
+ 'PTestCrashCleanup.ipdl',
+ 'PTestDataStructures.ipdl',
+ 'PTestDataStructuresCommon.ipdlh',
+ 'PTestDataStructuresSub.ipdl',
+ 'PTestDemon.ipdl',
+ 'PTestDesc.ipdl',
+ 'PTestDescSub.ipdl',
+ 'PTestDescSubsub.ipdl',
+ 'PTestEndpointBridgeMain.ipdl',
+ 'PTestEndpointBridgeMainSub.ipdl',
+ 'PTestEndpointBridgeSub.ipdl',
+ 'PTestEndpointOpens.ipdl',
+ 'PTestEndpointOpensOpened.ipdl',
+ 'PTestFailedCtor.ipdl',
+ 'PTestFailedCtorSub.ipdl',
+ 'PTestFailedCtorSubsub.ipdl',
+ 'PTestHandle.ipdl',
+ 'PTestHangs.ipdl',
+ 'PTestHighestPrio.ipdl',
+ 'PTestIndirectProtocolParam.ipdlh',
+ 'PTestIndirectProtocolParamFirst.ipdl',
+ 'PTestIndirectProtocolParamManage.ipdl',
+ 'PTestIndirectProtocolParamSecond.ipdl',
+ 'PTestInterruptErrorCleanup.ipdl',
+ 'PTestInterruptRaces.ipdl',
+ 'PTestInterruptShutdownRace.ipdl',
+ 'PTestJSON.ipdl',
+ 'PTestLatency.ipdl',
+ 'PTestManyChildAllocs.ipdl',
+ 'PTestManyChildAllocsSub.ipdl',
+ 'PTestMultiMgrs.ipdl',
+ 'PTestMultiMgrsBottom.ipdl',
+ 'PTestMultiMgrsLeft.ipdl',
+ 'PTestMultiMgrsRight.ipdl',
+ 'PTestNestedLoops.ipdl',
+ 'PTestOpens.ipdl',
+ 'PTestOpensOpened.ipdl',
+ 'PTestPriority.ipdl',
+ 'PTestRaceDeadlock.ipdl',
+ 'PTestRaceDeferral.ipdl',
+ 'PTestRacyInterruptReplies.ipdl',
+ 'PTestRacyReentry.ipdl',
+ 'PTestRacyUndefer.ipdl',
+ 'PTestRPC.ipdl',
+ 'PTestSanity.ipdl',
+ 'PTestSelfManage.ipdl',
+ 'PTestSelfManageRoot.ipdl',
+ 'PTestShmem.ipdl',
+ 'PTestShutdown.ipdl',
+ 'PTestShutdownSub.ipdl',
+ 'PTestShutdownSubsub.ipdl',
+ 'PTestStackHooks.ipdl',
+ 'PTestSyncError.ipdl',
+ 'PTestSyncHang.ipdl',
+ 'PTestSyncWakeup.ipdl',
+ 'PTestUrgency.ipdl',
+ 'PTestUrgentHangs.ipdl',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
diff --git a/ipc/ipdl/test/ipdl/IPDLCompile.py b/ipc/ipdl/test/ipdl/IPDLCompile.py
new file mode 100644
index 000000000..e9f9d42b0
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/IPDLCompile.py
@@ -0,0 +1,75 @@
+import copy, re, os, subprocess, sys, tempfile
+
+# We test the compiler indirectly, rather than reaching into the ipdl/
+# module, to make the testing framework as general as possible.
+
+class IPDLCompile:
+ def __init__(self, specfilename, ipdlargv=[ 'python', 'ipdl.py' ]):
+ self.argv = copy.deepcopy(ipdlargv)
+ self.specfilename = specfilename
+ self.stdout = None
+ self.stderr = None
+ self.returncode = None
+
+
+ def run(self):
+ '''Run |self.specstring| through the IPDL compiler.'''
+ assert self.returncode is None
+
+ tmpoutdir = tempfile.mkdtemp(prefix='ipdl_unit_test')
+
+ try:
+ self.argv.extend([
+ '-d', tmpoutdir,
+ self.specfilename
+ ])
+
+ proc = subprocess.Popen(args=self.argv,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ self.stdout, self.stderr = proc.communicate()
+
+ self.returncode = proc.returncode
+ assert self.returncode is not None
+
+ finally:
+ for root, dirs, files in os.walk(tmpoutdir, topdown=0):
+ for name in files:
+ os.remove(os.path.join(root, name))
+ for name in dirs:
+ os.rmdir(os.path.join(root, name))
+ os.rmdir(tmpoutdir)
+
+ if proc.returncode is None:
+ proc.kill()
+
+
+ def completed(self):
+ return (self.returncode is not None
+ and isinstance(self.stdout, str)
+ and isinstance(self.stderr, str))
+
+
+ def error(self):
+ '''Return True iff compiling self.specstring resulted in an
+IPDL compiler error.'''
+ assert self.completed()
+
+ return None is not re.search(r'error:', self.stderr)
+
+
+ def exception(self):
+ '''Return True iff compiling self.specstring resulted in a Python
+exception being raised.'''
+ assert self.completed()
+
+ return None is not re.search(r'Traceback (most recent call last):',
+ self.stderr)
+
+ def ok(self):
+ '''Return True iff compiling self.specstring was successful.'''
+ assert self.completed()
+
+ return (not self.exception()
+ and not self.error()
+ and (0 == self.returncode))
diff --git a/ipc/ipdl/test/ipdl/Makefile.in b/ipc/ipdl/test/ipdl/Makefile.in
new file mode 100644
index 000000000..71981c616
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/Makefile.in
@@ -0,0 +1,17 @@
+# 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 $(topsrcdir)/config/rules.mk
+
+OKTESTS := $(wildcard $(srcdir)/ok/*.ipdl) $(wildcard $(srcdir)/ok/*.ipdlh)
+ERRORTESTS := $(wildcard $(srcdir)/error/*.ipdl) $(wildcard $(srcdir)/error/*.ipdlh)
+
+check::
+ @$(PYTHON) $(srcdir)/runtests.py \
+ $(srcdir)/ok $(srcdir)/error \
+ $(PYTHON) $(topsrcdir)/config/pythonpath.py \
+ $(PLY_INCLUDE) \
+ $(topsrcdir)/ipc/ipdl/ipdl.py \
+ OKTESTS $(OKTESTS) \
+ ERRORTESTS $(ERRORTESTS)
diff --git a/ipc/ipdl/test/ipdl/error/DeleteRace.ipdl b/ipc/ipdl/test/ipdl/error/DeleteRace.ipdl
new file mode 100644
index 000000000..992bbabdf
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/DeleteRace.ipdl
@@ -0,0 +1,14 @@
+// XXX kind of a gray area whether |__delete__| should be a part of the
+// top-level protocol. but if it's ever not, this test will break and
+// we'll notice, right?
+protocol DeleteRace {
+parent:
+ async M1();
+
+child:
+ async __delete__();
+
+state START:
+ send __delete__;
+ recv M1 goto START;
+};
diff --git a/ipc/ipdl/test/ipdl/error/Nullable.ipdl b/ipc/ipdl/test/ipdl/error/Nullable.ipdl
new file mode 100644
index 000000000..cb7097f0e
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/Nullable.ipdl
@@ -0,0 +1,4 @@
+protocol Nullable {
+child:
+ async Msg(nullable int i);
+};
diff --git a/ipc/ipdl/test/ipdl/error/Nullable2.ipdl b/ipc/ipdl/test/ipdl/error/Nullable2.ipdl
new file mode 100644
index 000000000..d572951ae
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/Nullable2.ipdl
@@ -0,0 +1,8 @@
+union Union {
+ nullable int;
+};
+
+protocol Nullable2 {
+child:
+ async Msg(Union i);
+};
diff --git a/ipc/ipdl/test/ipdl/error/actorparam_badState.ipdl b/ipc/ipdl/test/ipdl/error/actorparam_badState.ipdl
new file mode 100644
index 000000000..93b8ba218
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/actorparam_badState.ipdl
@@ -0,0 +1,10 @@
+protocol actorparam_badState {
+
+child:
+ async Msg(actorparam_badState:FARGEL p);
+ async __delete__();
+
+state S1:
+ send Msg goto S1;
+ send __delete__;
+};
diff --git a/ipc/ipdl/test/ipdl/error/array_Recursive.ipdl b/ipc/ipdl/test/ipdl/error/array_Recursive.ipdl
new file mode 100644
index 000000000..898307193
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/array_Recursive.ipdl
@@ -0,0 +1,3 @@
+protocol array_Recursive {
+child: Msg(int[][] aa);
+};
diff --git a/ipc/ipdl/test/ipdl/error/badProtocolInclude.ipdl b/ipc/ipdl/test/ipdl/error/badProtocolInclude.ipdl
new file mode 100644
index 000000000..cf310f34f
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/badProtocolInclude.ipdl
@@ -0,0 +1,7 @@
+include protocol IDONTEXIST;
+
+// error: nonexistent protocol ^^^
+
+protocol badProtocolInclude {
+child: Msg();
+};
diff --git a/ipc/ipdl/test/ipdl/error/bridgesNonexistent.ipdl b/ipc/ipdl/test/ipdl/error/bridgesNonexistent.ipdl
new file mode 100644
index 000000000..313de0fd1
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/bridgesNonexistent.ipdl
@@ -0,0 +1,6 @@
+protocol bridgesNonexistent {
+ bridges Leftie, Rightie;
+
+child: __delete__();
+state DEAD: send __delete__;
+};
diff --git a/ipc/ipdl/test/ipdl/error/bridgesSubprotocol.ipdl b/ipc/ipdl/test/ipdl/error/bridgesSubprotocol.ipdl
new file mode 100644
index 000000000..a07c295d9
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/bridgesSubprotocol.ipdl
@@ -0,0 +1,13 @@
+include protocol subprotocolBridges;
+
+protocol bridgesSubprotocol {
+ bridges subprotocolBridges, subprotocolBridges;
+
+ manages subprotocolBridges;
+
+child:
+ subprotocolBridges();
+ async __delete__();
+
+state DEAD: send __delete__;
+};
diff --git a/ipc/ipdl/test/ipdl/error/compressCtor.ipdl b/ipc/ipdl/test/ipdl/error/compressCtor.ipdl
new file mode 100644
index 000000000..f1ea880b1
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/compressCtor.ipdl
@@ -0,0 +1,8 @@
+include protocol compressCtorManagee;
+
+intr protocol compressCtor {
+ manages compressCtorManagee;
+
+parent:
+ async compressCtorManagee() compress;
+};
diff --git a/ipc/ipdl/test/ipdl/error/compressCtorManagee.ipdl b/ipc/ipdl/test/ipdl/error/compressCtorManagee.ipdl
new file mode 100644
index 000000000..3d0c66c21
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/compressCtorManagee.ipdl
@@ -0,0 +1,8 @@
+include protocol compressCtor;
+
+intr protocol compressCtorManagee {
+ manager compressCtor;
+
+child:
+ async __delete__() compress;
+};
diff --git a/ipc/ipdl/test/ipdl/error/conflictProtocolMsg.ipdl b/ipc/ipdl/test/ipdl/error/conflictProtocolMsg.ipdl
new file mode 100644
index 000000000..9090c9395
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/conflictProtocolMsg.ipdl
@@ -0,0 +1,7 @@
+protocol conflictProtocolMsg {
+
+ // it's an error to re-use the protocol name as a message ID; these
+ // are reserved
+child: conflictProtocolMsg();
+
+};
diff --git a/ipc/ipdl/test/ipdl/error/cyclecheck_Child.ipdl b/ipc/ipdl/test/ipdl/error/cyclecheck_Child.ipdl
new file mode 100644
index 000000000..2e1b93299
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/cyclecheck_Child.ipdl
@@ -0,0 +1,12 @@
+include protocol cyclecheck_Parent;
+include protocol cyclecheck_Grandchild;
+
+protocol cyclecheck_Child {
+ manager cyclecheck_Parent;
+ manages cyclecheck_Grandchild;
+
+child:
+ cyclecheck_Grandchild();
+ async __delete__();
+};
+
diff --git a/ipc/ipdl/test/ipdl/error/cyclecheck_Grandchild.ipdl b/ipc/ipdl/test/ipdl/error/cyclecheck_Grandchild.ipdl
new file mode 100644
index 000000000..d91949de4
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/cyclecheck_Grandchild.ipdl
@@ -0,0 +1,12 @@
+include protocol cyclecheck_Child;
+include protocol cyclecheck_Parent;
+
+protocol cyclecheck_Grandchild {
+ manager cyclecheck_Child;
+ manages cyclecheck_Parent;
+
+child:
+ cyclecheck_Parent();
+ async __delete__();
+};
+
diff --git a/ipc/ipdl/test/ipdl/error/cyclecheck_Parent.ipdl b/ipc/ipdl/test/ipdl/error/cyclecheck_Parent.ipdl
new file mode 100644
index 000000000..666d44c54
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/cyclecheck_Parent.ipdl
@@ -0,0 +1,10 @@
+include protocol cyclecheck_Child;
+
+protocol cyclecheck_Parent {
+ manages cyclecheck_Child;
+
+child:
+ cyclecheck_Child();
+ async __delete__();
+};
+
diff --git a/ipc/ipdl/test/ipdl/error/dtorReserved.ipdl b/ipc/ipdl/test/ipdl/error/dtorReserved.ipdl
new file mode 100644
index 000000000..c59987b9c
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/dtorReserved.ipdl
@@ -0,0 +1,8 @@
+protocol dtorReserved {
+
+ // it's an error to use old-style dtor syntax
+
+child:
+ ~SomeMsg();
+
+};
diff --git a/ipc/ipdl/test/ipdl/error/empty.ipdl b/ipc/ipdl/test/ipdl/error/empty.ipdl
new file mode 100644
index 000000000..8b1378917
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/empty.ipdl
@@ -0,0 +1 @@
+
diff --git a/ipc/ipdl/test/ipdl/error/intrMessageCompress.ipdl b/ipc/ipdl/test/ipdl/error/intrMessageCompress.ipdl
new file mode 100644
index 000000000..dc0cdd800
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/intrMessageCompress.ipdl
@@ -0,0 +1,6 @@
+intr protocol intrMessageCompress {
+parent:
+ intr foo() compress;
+child:
+ intr bar() compress;
+};
diff --git a/ipc/ipdl/test/ipdl/error/lex1.ipdl b/ipc/ipdl/test/ipdl/error/lex1.ipdl
new file mode 100644
index 000000000..0ab9e1e4f
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/lex1.ipdl
@@ -0,0 +1 @@
+slkdjfl*&^*
diff --git a/ipc/ipdl/test/ipdl/error/manageSelfToplevel.ipdl b/ipc/ipdl/test/ipdl/error/manageSelfToplevel.ipdl
new file mode 100644
index 000000000..d70889126
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/manageSelfToplevel.ipdl
@@ -0,0 +1,8 @@
+protocol manageSelfToplevel {
+ manager manageSelfToplevel;
+ manages manageSelfToplevel;
+
+child:
+ manageSelfToplevel();
+ async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/error/managedNoCtor.ipdl b/ipc/ipdl/test/ipdl/error/managedNoCtor.ipdl
new file mode 100644
index 000000000..ddc04a005
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/managedNoCtor.ipdl
@@ -0,0 +1,7 @@
+include protocol managerNoCtor;
+
+protocol managedNoCtor {
+ manager managerNoCtor;
+ // empty
+child: __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/error/managedNoDtor.ipdl b/ipc/ipdl/test/ipdl/error/managedNoDtor.ipdl
new file mode 100644
index 000000000..8d98018ee
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/managedNoDtor.ipdl
@@ -0,0 +1,6 @@
+include protocol managerNoDtor;
+
+protocol managedNoDtor {
+ manager managerNoDtor;
+ // empty
+};
diff --git a/ipc/ipdl/test/ipdl/error/managerNoCtor.ipdl b/ipc/ipdl/test/ipdl/error/managerNoCtor.ipdl
new file mode 100644
index 000000000..e39b89009
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/managerNoCtor.ipdl
@@ -0,0 +1,8 @@
+include protocol managedNoCtor;
+
+protocol managerNoCtor {
+ manages managedNoCtor;
+
+parent:
+ // error: no ctor defined
+};
diff --git a/ipc/ipdl/test/ipdl/error/managerNoDtor.ipdl b/ipc/ipdl/test/ipdl/error/managerNoDtor.ipdl
new file mode 100644
index 000000000..72e95149f
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/managerNoDtor.ipdl
@@ -0,0 +1,9 @@
+include protocol managedNoDtor;
+
+protocol managerNoDtor {
+ manages managedNoDtor;
+
+parent:
+ managedNoDtor();
+ // error: no ctor defined
+};
diff --git a/ipc/ipdl/test/ipdl/error/messageNoDirection.ipdl b/ipc/ipdl/test/ipdl/error/messageNoDirection.ipdl
new file mode 100644
index 000000000..a3d9585d4
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/messageNoDirection.ipdl
@@ -0,0 +1,3 @@
+protocol messageNoDirection {
+ async NoDirection();
+};
diff --git a/ipc/ipdl/test/ipdl/error/multimanDupMgrs.ipdl b/ipc/ipdl/test/ipdl/error/multimanDupMgrs.ipdl
new file mode 100644
index 000000000..eb89f77d8
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/multimanDupMgrs.ipdl
@@ -0,0 +1,8 @@
+include protocol multimanDupMgrsMgr;
+
+protocol multimanDupMgrs {
+ manager multimanDupMgrsMgr or multimanDupMgrsMgr;
+
+child:
+ async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/error/multimanDupMgrsMgr.ipdl b/ipc/ipdl/test/ipdl/error/multimanDupMgrsMgr.ipdl
new file mode 100644
index 000000000..be8dfbc83
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/multimanDupMgrsMgr.ipdl
@@ -0,0 +1,9 @@
+include protocol multimanDupMgrs;
+
+protocol multimanDupMgrsMgr {
+ manages multimanDupMgrs;
+
+child:
+ multimanDupMgrs();
+ async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/error/multimanNonexistentMgrs.ipdl b/ipc/ipdl/test/ipdl/error/multimanNonexistentMgrs.ipdl
new file mode 100644
index 000000000..dd4ba254c
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/multimanNonexistentMgrs.ipdl
@@ -0,0 +1,7 @@
+protocol multimanNonexistentManagers {
+ manager Starsky or Hutch;
+
+child:
+ async Dummy();
+ async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/error/mutualRecStruct.ipdl b/ipc/ipdl/test/ipdl/error/mutualRecStruct.ipdl
new file mode 100644
index 000000000..fd4ee4d18
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/mutualRecStruct.ipdl
@@ -0,0 +1,20 @@
+struct X {
+ int i;
+ Y[] y;
+};
+
+struct Y {
+ X x;
+ Z z;
+};
+
+struct Z {
+ double d;
+ X x;
+};
+
+protocol mutualRecStruct {
+child:
+ async Test(X x, Y y, Z z);
+ async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/error/mutualRecStructUnion.ipdl b/ipc/ipdl/test/ipdl/error/mutualRecStructUnion.ipdl
new file mode 100644
index 000000000..f475952d1
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/mutualRecStructUnion.ipdl
@@ -0,0 +1,20 @@
+struct X {
+ int i;
+ Y[] y;
+};
+
+union Y {
+ X;
+ Z;
+};
+
+struct Z {
+ double d;
+ X x;
+};
+
+protocol mutualRecStructUnion {
+child:
+ async Test(X x, Y y, Z z);
+ async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/error/noEmptyToplevel.ipdl b/ipc/ipdl/test/ipdl/error/noEmptyToplevel.ipdl
new file mode 100644
index 000000000..08f13694e
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/noEmptyToplevel.ipdl
@@ -0,0 +1,5 @@
+protocol noEmptyToplevel {
+
+ // it's an error for top-level protocols to be empty
+
+};
diff --git a/ipc/ipdl/test/ipdl/error/noProtocolInHeader.ipdlh b/ipc/ipdl/test/ipdl/error/noProtocolInHeader.ipdlh
new file mode 100644
index 000000000..33571c415
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/noProtocolInHeader.ipdlh
@@ -0,0 +1,4 @@
+protocol noProtocolInHeader {
+child:
+ __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/error/oldIncludeSyntax.ipdl b/ipc/ipdl/test/ipdl/error/oldIncludeSyntax.ipdl
new file mode 100644
index 000000000..571c017c3
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/oldIncludeSyntax.ipdl
@@ -0,0 +1,5 @@
+include protocol "Foo.ipdl";
+
+protocol oldIncludeSyntax {
+
+};
diff --git a/ipc/ipdl/test/ipdl/error/opensNonexistent.ipdl b/ipc/ipdl/test/ipdl/error/opensNonexistent.ipdl
new file mode 100644
index 000000000..521f69ace
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/opensNonexistent.ipdl
@@ -0,0 +1,6 @@
+protocol opensNonexistent {
+ parent opens Unicorn;
+
+child: __delete__();
+state DEAD: send __delete__;
+};
diff --git a/ipc/ipdl/test/ipdl/error/opensSubprotocol.ipdl b/ipc/ipdl/test/ipdl/error/opensSubprotocol.ipdl
new file mode 100644
index 000000000..af884a636
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/opensSubprotocol.ipdl
@@ -0,0 +1,13 @@
+include protocol subprotocolOpens;
+
+protocol opensSubprotocol {
+ child opens subprotocolOpens;
+
+ manages subprotocolOpens;
+
+child:
+ subprotocolOpens();
+ async __delete__();
+
+state DEAD: send __delete__;
+};
diff --git a/ipc/ipdl/test/ipdl/error/parser.ipdl b/ipc/ipdl/test/ipdl/error/parser.ipdl
new file mode 100644
index 000000000..f3f273a02
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/parser.ipdl
@@ -0,0 +1 @@
+protocol parser {
diff --git a/ipc/ipdl/test/ipdl/error/race_MultiOut.ipdl b/ipc/ipdl/test/ipdl/error/race_MultiOut.ipdl
new file mode 100644
index 000000000..263d87f59
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/race_MultiOut.ipdl
@@ -0,0 +1,20 @@
+protocol race_MultiOut {
+child: M1();
+parent: M2();
+
+state S1:
+ send M1 goto S2;
+ recv M2 goto S3;
+
+state S2:
+ recv M2 goto S4 or S5;
+
+state S3:
+ send M1 goto S4 or S5;
+
+state S4:
+ send M1 goto S4;
+
+state S5:
+ recv M2 goto S5;
+};
diff --git a/ipc/ipdl/test/ipdl/error/race_OverlappingMultiOut.ipdl b/ipc/ipdl/test/ipdl/error/race_OverlappingMultiOut.ipdl
new file mode 100644
index 000000000..8aa831842
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/race_OverlappingMultiOut.ipdl
@@ -0,0 +1,35 @@
+protocol race_OverlappingMultiOut {
+child:
+ async Msg1();
+ async Msg1_();
+ async __delete__(); suppressUndeleteableError();
+
+parent:
+ async Msg2();
+ async Msg2_();
+
+
+start state _:
+ send __delete__;
+ send suppressUndeleteableError goto S10;
+
+ // *** ERROR: send/recv of Msg1/Msg2 in state S10 goes to overlapping
+ // sets { S11, S12 }, { S12, S13 } and thus can't be unidirectional
+state S10:
+ send Msg1 goto S11 or S12;
+ recv Msg2 goto S12 or S13;
+
+state S11:
+ recv Msg2 goto S14;
+
+state S12:
+ send Msg1 goto S14;
+ recv Msg2 goto S14;
+
+state S13:
+ send Msg1 goto S14;
+
+state S14:
+ send Msg1 goto S14;
+ recv Msg2 goto S14;
+};
diff --git a/ipc/ipdl/test/ipdl/error/race_ToDiffStates.ipdl b/ipc/ipdl/test/ipdl/error/race_ToDiffStates.ipdl
new file mode 100644
index 000000000..db431b146
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/race_ToDiffStates.ipdl
@@ -0,0 +1,20 @@
+protocol race_ToDiffStates {
+child: M1();
+parent: M2();
+
+state S1:
+ send M1 goto S2;
+ recv M2 goto S3;
+
+state S2:
+ recv M2 goto S4;
+
+state S3:
+ send M1 goto S5;
+
+state S4:
+ send M1 goto S4;
+
+state S5:
+ recv M2 goto S5;
+};
diff --git a/ipc/ipdl/test/ipdl/error/race_ToError.ipdl b/ipc/ipdl/test/ipdl/error/race_ToError.ipdl
new file mode 100644
index 000000000..66951fa39
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/race_ToError.ipdl
@@ -0,0 +1,14 @@
+protocol race_ToError {
+child: M1();
+parent: M2();
+
+state S1:
+ send M1 goto S2;
+ recv M2 goto S3;
+
+state S2:
+ send M1 goto S2;
+
+state S3:
+ recv M2 goto S3;
+};
diff --git a/ipc/ipdl/test/ipdl/error/race_ViolateSameDirection.ipdl b/ipc/ipdl/test/ipdl/error/race_ViolateSameDirection.ipdl
new file mode 100644
index 000000000..a1f964ec7
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/race_ViolateSameDirection.ipdl
@@ -0,0 +1,30 @@
+protocol race_ViolateSameDirection {
+child:
+ async Msg1();
+ async Msg1_();
+ async __delete__();
+ async suppressUndeleteableError();
+parent:
+ async Msg2();
+ async Msg2_();
+
+ // *** ERROR: state S7 doesn't have all-same-direction
+start state _:
+ send __delete__;
+ send suppressUndeleteableError goto S6;
+
+state S6:
+ send Msg1 goto S7;
+ recv Msg2 goto S8;
+
+state S7:
+ recv Msg2 goto S9;
+ send Msg1 goto S9;
+
+state S8:
+ send Msg1 goto S9;
+
+state S9:
+ send Msg1 goto S9;
+ recv Msg2 goto S9;
+};
diff --git a/ipc/ipdl/test/ipdl/error/redeclMessage.ipdl b/ipc/ipdl/test/ipdl/error/redeclMessage.ipdl
new file mode 100644
index 000000000..5b2cdcf70
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/redeclMessage.ipdl
@@ -0,0 +1,9 @@
+protocol redeclMessage {
+
+ // can't declare two messages with the same name
+
+child:
+ async Msg();
+ async Msg();
+
+};
diff --git a/ipc/ipdl/test/ipdl/error/redeclParamReturn.ipdl b/ipc/ipdl/test/ipdl/error/redeclParamReturn.ipdl
new file mode 100644
index 000000000..0bc2f135c
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/redeclParamReturn.ipdl
@@ -0,0 +1,7 @@
+sync protocol redeclParamReturn {
+
+ // it's an error to name a parameter with the same id as a return
+
+parent: Msg(int f) returns (bool f);
+
+};
diff --git a/ipc/ipdl/test/ipdl/error/redefState.ipdl b/ipc/ipdl/test/ipdl/error/redefState.ipdl
new file mode 100644
index 000000000..b9da7212b
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/redefState.ipdl
@@ -0,0 +1,14 @@
+protocol redefState {
+
+ // error: redefining state in state machine
+child:
+ async Msg();
+ async __delete__();
+
+state S1: send Msg goto S1;
+
+state S1: send Msg goto S1;
+
+start state _:
+ send __delete__;
+};
diff --git a/ipc/ipdl/test/ipdl/error/repeatedOutState.ipdl b/ipc/ipdl/test/ipdl/error/repeatedOutState.ipdl
new file mode 100644
index 000000000..461f7dbff
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/repeatedOutState.ipdl
@@ -0,0 +1,12 @@
+protocol repeatedOutState {
+child: Msg(); __delete__();
+
+ // error: S2 repeated in multi-out set
+
+state S1:
+ send Msg goto S2 or S2 or S4;
+
+state S2: send Msg goto S2;
+state S3: send Msg goto S3;
+state S4: send Mesg goto S4; send __delete__;
+};
diff --git a/ipc/ipdl/test/ipdl/error/rpcParentToChild.ipdl b/ipc/ipdl/test/ipdl/error/rpcParentToChild.ipdl
new file mode 100644
index 000000000..fb6cd122b
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/rpcParentToChild.ipdl
@@ -0,0 +1,6 @@
+intr protocol rpcParentToChild {
+
+ // can't declare rpc parent-to-child messages
+child: rpc Msg();
+
+};
diff --git a/ipc/ipdl/test/ipdl/error/shmem.ipdl b/ipc/ipdl/test/ipdl/error/shmem.ipdl
new file mode 100644
index 000000000..f9046fb4c
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/shmem.ipdl
@@ -0,0 +1,5 @@
+using class mozilla::ipc::Shmem from "mozilla/ipc/Shmem.h"; // redeclaration
+
+protocol shmem {
+child: Msg(Shmem s);
+};
diff --git a/ipc/ipdl/test/ipdl/error/shmem_access_union.ipdl b/ipc/ipdl/test/ipdl/error/shmem_access_union.ipdl
new file mode 100644
index 000000000..2ed162368
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/shmem_access_union.ipdl
@@ -0,0 +1,8 @@
+union Union {
+ [-r-w|+r+w] Shmem;
+};
+
+protocol shmem_access_union {
+child:
+ async Msg(Union u);
+};
diff --git a/ipc/ipdl/test/ipdl/error/spawnsNonexistent.ipdl b/ipc/ipdl/test/ipdl/error/spawnsNonexistent.ipdl
new file mode 100644
index 000000000..1bc4919bf
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/spawnsNonexistent.ipdl
@@ -0,0 +1,6 @@
+protocol spawnsNonexistent {
+ parent spawns Nonexistent;
+
+child: __delete__();
+state DEAD: send __delete__;
+};
diff --git a/ipc/ipdl/test/ipdl/error/spawnsSubprotocol.ipdl b/ipc/ipdl/test/ipdl/error/spawnsSubprotocol.ipdl
new file mode 100644
index 000000000..fc056a254
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/spawnsSubprotocol.ipdl
@@ -0,0 +1,13 @@
+include protocol subprotocolSpawns;
+
+protocol spawnsSubprotocol {
+ parent spawns subprotocolSpawns;
+
+ manages subprotocolSpawns;
+
+child:
+ subprotocolSpawns();
+ async __delete__();
+
+state DEAD: send __delete__;
+};
diff --git a/ipc/ipdl/test/ipdl/error/structRedecl.ipdl b/ipc/ipdl/test/ipdl/error/structRedecl.ipdl
new file mode 100644
index 000000000..a2e6f5c76
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/structRedecl.ipdl
@@ -0,0 +1,8 @@
+struct Redecl {
+ int a;
+ double a;
+};
+
+protocol structRedecl {
+child: __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/error/structUnknownField.ipdl b/ipc/ipdl/test/ipdl/error/structUnknownField.ipdl
new file mode 100644
index 000000000..1d5fe8896
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/structUnknownField.ipdl
@@ -0,0 +1,7 @@
+struct S {
+ Foobers i;
+};
+
+protocol structUnknownField {
+child: __delete__(S s);
+};
diff --git a/ipc/ipdl/test/ipdl/error/subprotocolBridges.ipdl b/ipc/ipdl/test/ipdl/error/subprotocolBridges.ipdl
new file mode 100644
index 000000000..68cf94547
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/subprotocolBridges.ipdl
@@ -0,0 +1,10 @@
+include protocol bridgesSubprotocol;
+
+protocol subprotocolBridges {
+ bridges bridgesSubprotocol, bridgesSubprotocol;
+
+ manager bridgesSubprotocol;
+
+child: __delete__();
+state DEAD: send __delete__;
+};
diff --git a/ipc/ipdl/test/ipdl/error/subprotocolOpens.ipdl b/ipc/ipdl/test/ipdl/error/subprotocolOpens.ipdl
new file mode 100644
index 000000000..1d6a5851d
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/subprotocolOpens.ipdl
@@ -0,0 +1,10 @@
+include protocol opensSubprotocol;
+
+protocol subprotocolOpens {
+ parent opens opensSubprotocol;
+
+ manager opensSubprotocol;
+
+child: __delete__();
+state DEAD: send __delete__;
+};
diff --git a/ipc/ipdl/test/ipdl/error/subprotocolSpawns.ipdl b/ipc/ipdl/test/ipdl/error/subprotocolSpawns.ipdl
new file mode 100644
index 000000000..e3caab624
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/subprotocolSpawns.ipdl
@@ -0,0 +1,10 @@
+include protocol spawnsSubprotocol;
+
+protocol subprotocolSpawns {
+ parent spawns spawnsSubprotocol;
+
+ manager spawnsSubprotocol;
+
+child: __delete__();
+state DEAD: send __delete__;
+};
diff --git a/ipc/ipdl/test/ipdl/error/syncMessageCompress.ipdl b/ipc/ipdl/test/ipdl/error/syncMessageCompress.ipdl
new file mode 100644
index 000000000..73c92664e
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/syncMessageCompress.ipdl
@@ -0,0 +1,4 @@
+sync protocol syncMessageCompress {
+parent:
+ sync foo() compress;
+};
diff --git a/ipc/ipdl/test/ipdl/error/syncParentToChild.ipdl b/ipc/ipdl/test/ipdl/error/syncParentToChild.ipdl
new file mode 100644
index 000000000..f0d298fe5
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/syncParentToChild.ipdl
@@ -0,0 +1,6 @@
+intr protocol syncParentToChild {
+
+ // can't declare sync parent-to-child messages
+child: sync Msg();
+
+};
diff --git a/ipc/ipdl/test/ipdl/error/tooWeakInterruptAsync.ipdl b/ipc/ipdl/test/ipdl/error/tooWeakInterruptAsync.ipdl
new file mode 100644
index 000000000..a0ee9a47c
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/tooWeakInterruptAsync.ipdl
@@ -0,0 +1,7 @@
+protocol tooWeakRPCAsync {
+
+ // it's an error to declare an async protocol with an intr message
+
+parent: intr Msg();
+
+};
diff --git a/ipc/ipdl/test/ipdl/error/tooWeakRpcSync.ipdl b/ipc/ipdl/test/ipdl/error/tooWeakRpcSync.ipdl
new file mode 100644
index 000000000..2d368de63
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/tooWeakRpcSync.ipdl
@@ -0,0 +1,6 @@
+sync protocol tooWeakRpcSync {
+
+ // it's an error to declare a sync protocol with an rpc message
+parent:
+ rpc Msg();
+};
diff --git a/ipc/ipdl/test/ipdl/error/tooWeakSyncAsync.ipdl b/ipc/ipdl/test/ipdl/error/tooWeakSyncAsync.ipdl
new file mode 100644
index 000000000..2448a0cd9
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/tooWeakSyncAsync.ipdl
@@ -0,0 +1,7 @@
+protocol tooWeakSyncAsync {
+
+ // it's an error to declare an async protocol with a sync message
+
+parent: sync Msg();
+
+};
diff --git a/ipc/ipdl/test/ipdl/error/trans_WrongDirection.ipdl b/ipc/ipdl/test/ipdl/error/trans_WrongDirection.ipdl
new file mode 100644
index 000000000..53c2fd952
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/trans_WrongDirection.ipdl
@@ -0,0 +1,12 @@
+protocol trans_WrongDirection {
+
+child:
+ async Msg();
+ async __delete__();
+
+state S1:
+ recv Msg goto S1;
+
+start state _:
+ send __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/error/trans_WrongDirection2.ipdl b/ipc/ipdl/test/ipdl/error/trans_WrongDirection2.ipdl
new file mode 100644
index 000000000..c15dfa5af
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/trans_WrongDirection2.ipdl
@@ -0,0 +1,12 @@
+protocol trans_WrongDirection2 {
+
+parent:
+ async Msg();
+ async __delete__();
+
+state S1:
+ send Msg goto S1;
+
+start state _:
+ recv __delete__;
+};
diff --git a/ipc/ipdl/test/ipdl/error/trans_WrongDirection3.ipdl b/ipc/ipdl/test/ipdl/error/trans_WrongDirection3.ipdl
new file mode 100644
index 000000000..80621ecd1
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/trans_WrongDirection3.ipdl
@@ -0,0 +1,12 @@
+sync protocol trans_WrongDirection3 {
+
+parent:
+ sync Msg();
+ async __delete__();
+
+state S1:
+ send Msg goto S1;
+
+start state _:
+ recv __delete__;
+};
diff --git a/ipc/ipdl/test/ipdl/error/trans_WrongDirection4.ipdl b/ipc/ipdl/test/ipdl/error/trans_WrongDirection4.ipdl
new file mode 100644
index 000000000..c27ad331d
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/trans_WrongDirection4.ipdl
@@ -0,0 +1,12 @@
+intr protocol trans_WrongDirection4 {
+
+child:
+ intr Msg();
+ async __delete__();
+
+state S1:
+ answer Msg goto S1;
+
+start state _:
+ send __delete__;
+};
diff --git a/ipc/ipdl/test/ipdl/error/trans_WrongDirection5.ipdl b/ipc/ipdl/test/ipdl/error/trans_WrongDirection5.ipdl
new file mode 100644
index 000000000..7997649c8
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/trans_WrongDirection5.ipdl
@@ -0,0 +1,12 @@
+intr protocol trans_WrongDirection5 {
+
+parent:
+ intr Msg();
+ async __delete__()
+
+state S1:
+ call Msg goto S1;
+
+start state_:
+ recv __delete__;
+};
diff --git a/ipc/ipdl/test/ipdl/error/trans_WrongName.ipdl b/ipc/ipdl/test/ipdl/error/trans_WrongName.ipdl
new file mode 100644
index 000000000..a944d5879
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/trans_WrongName.ipdl
@@ -0,0 +1,10 @@
+protocol trans_WrongName {
+
+child:
+ async Msg();
+ async __delete__();
+
+state S1:
+ call Msg goto S1;
+ send __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/error/trans_WrongName2.ipdl b/ipc/ipdl/test/ipdl/error/trans_WrongName2.ipdl
new file mode 100644
index 000000000..ece935b7b
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/trans_WrongName2.ipdl
@@ -0,0 +1,10 @@
+protocol trans_WrongName2 {
+
+parent:
+ async Msg();
+ async __delete__();
+
+state S1:
+ answer Msg goto S1;
+ recv __delete__;
+};
diff --git a/ipc/ipdl/test/ipdl/error/trans_WrongName3.ipdl b/ipc/ipdl/test/ipdl/error/trans_WrongName3.ipdl
new file mode 100644
index 000000000..80c54383f
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/trans_WrongName3.ipdl
@@ -0,0 +1,10 @@
+sync protocol trans_WrongName3 {
+
+parent:
+ sync Msg();
+ async __delete__();
+
+state S1:
+ answer Msg goto S1;
+ recv __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/error/trans_WrongName4.ipdl b/ipc/ipdl/test/ipdl/error/trans_WrongName4.ipdl
new file mode 100644
index 000000000..f8a9746e4
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/trans_WrongName4.ipdl
@@ -0,0 +1,10 @@
+intr protocol trans_WrongName4 {
+
+child:
+ intr Msg();
+ async __delete__();
+
+state S1:
+ send Msg goto S1;
+ send __delete__
+};
diff --git a/ipc/ipdl/test/ipdl/error/trans_WrongName5.ipdl b/ipc/ipdl/test/ipdl/error/trans_WrongName5.ipdl
new file mode 100644
index 000000000..a793905d2
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/trans_WrongName5.ipdl
@@ -0,0 +1,10 @@
+intr protocol trans_WrongName5 {
+
+parent:
+ intr Msg();
+ async __delete__();
+
+state S1:
+ recv Msg goto S1;
+ recv __delete__;
+};
diff --git a/ipc/ipdl/test/ipdl/error/twoprotocols.ipdl b/ipc/ipdl/test/ipdl/error/twoprotocols.ipdl
new file mode 100644
index 000000000..8d5cb79fc
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/twoprotocols.ipdl
@@ -0,0 +1,9 @@
+// it's an error to define two protocols in the same file
+
+protocol p1 {
+child: Msg();
+};
+
+protocol p2 {
+child: Msg();
+};
diff --git a/ipc/ipdl/test/ipdl/error/undeclParamType.ipdl b/ipc/ipdl/test/ipdl/error/undeclParamType.ipdl
new file mode 100644
index 000000000..25aa1af4f
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/undeclParamType.ipdl
@@ -0,0 +1,5 @@
+protocol undeclParamType {
+
+child: Msg(FARGLEGARGLE p);
+
+};
diff --git a/ipc/ipdl/test/ipdl/error/undeclProtocol.ipdl b/ipc/ipdl/test/ipdl/error/undeclProtocol.ipdl
new file mode 100644
index 000000000..9462e0463
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/undeclProtocol.ipdl
@@ -0,0 +1,6 @@
+protocol undeclProtocol {
+ manages undeclared;
+
+child:
+ undeclared();
+};
diff --git a/ipc/ipdl/test/ipdl/error/undeclReturnType.ipdl b/ipc/ipdl/test/ipdl/error/undeclReturnType.ipdl
new file mode 100644
index 000000000..b4bee83d1
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/undeclReturnType.ipdl
@@ -0,0 +1,5 @@
+sync protocol undeclReturnType {
+
+child: sync Msg() returns (FARGLEGARGLE r);
+
+};
diff --git a/ipc/ipdl/test/ipdl/error/undefMutualRecStruct.ipdl b/ipc/ipdl/test/ipdl/error/undefMutualRecStruct.ipdl
new file mode 100644
index 000000000..4dd89647f
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/undefMutualRecStruct.ipdl
@@ -0,0 +1,7 @@
+struct X { Y y; };
+struct Y { Z z; };
+struct Z { X x; };
+
+protocol undefMutualRecStruct {
+child: __delete__(X x);
+};
diff --git a/ipc/ipdl/test/ipdl/error/undefMutualRecStructUnion.ipdl b/ipc/ipdl/test/ipdl/error/undefMutualRecStructUnion.ipdl
new file mode 100644
index 000000000..f9177b371
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/undefMutualRecStructUnion.ipdl
@@ -0,0 +1,7 @@
+struct X { Y y; };
+union Y { Z; };
+struct Z { X x; };
+
+protocol undefMutualRecStructUnion {
+child: __delete__(X x);
+};
diff --git a/ipc/ipdl/test/ipdl/error/undefMutualRecUnion.ipdl b/ipc/ipdl/test/ipdl/error/undefMutualRecUnion.ipdl
new file mode 100644
index 000000000..be64fc040
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/undefMutualRecUnion.ipdl
@@ -0,0 +1,7 @@
+union X { Y; };
+union Y { Z; };
+union Z { X; };
+
+protocol undefMutualRecUnion {
+child: __delete__(X x);
+};
diff --git a/ipc/ipdl/test/ipdl/error/undefSelfRecStruct.ipdl b/ipc/ipdl/test/ipdl/error/undefSelfRecStruct.ipdl
new file mode 100644
index 000000000..92f47164b
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/undefSelfRecStruct.ipdl
@@ -0,0 +1,5 @@
+struct X { X x; };
+
+protocol undefSelfRecStruct {
+child: __delete__(X x);
+};
diff --git a/ipc/ipdl/test/ipdl/error/undefSelfRecUnion.ipdl b/ipc/ipdl/test/ipdl/error/undefSelfRecUnion.ipdl
new file mode 100644
index 000000000..24faf3130
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/undefSelfRecUnion.ipdl
@@ -0,0 +1,5 @@
+union X { X; };
+
+protocol undefSelfRecUnion {
+child: __delete__(X x);
+};
diff --git a/ipc/ipdl/test/ipdl/error/unreachedDelete.ipdl b/ipc/ipdl/test/ipdl/error/unreachedDelete.ipdl
new file mode 100644
index 000000000..8af41b55c
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/unreachedDelete.ipdl
@@ -0,0 +1,5 @@
+protocol unreachedDelete {
+child: M1(); __delete__();
+
+state S1: send M1 goto S1;
+};
diff --git a/ipc/ipdl/test/ipdl/error/unreachedDeleteMultiStart.ipdl b/ipc/ipdl/test/ipdl/error/unreachedDeleteMultiStart.ipdl
new file mode 100644
index 000000000..80e28a26d
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/unreachedDeleteMultiStart.ipdl
@@ -0,0 +1,10 @@
+protocol unreachedDeleteMultiStart {
+child:
+ async M1(); async M2(); async __delete__();
+
+start state S1: send M1 goto S2;
+state S2: send __delete__;
+
+start state S3: send M2 goto S4;
+state S4: send M1 goto S3;
+};
diff --git a/ipc/ipdl/test/ipdl/moz.build b/ipc/ipdl/test/ipdl/moz.build
new file mode 100644
index 000000000..28919c271
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/moz.build
@@ -0,0 +1,6 @@
+# -*- 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/.
+
diff --git a/ipc/ipdl/test/ipdl/ok/Delete.ipdl b/ipc/ipdl/test/ipdl/ok/Delete.ipdl
new file mode 100644
index 000000000..311d82537
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/Delete.ipdl
@@ -0,0 +1,8 @@
+include protocol DeleteSub;
+
+sync protocol Delete {
+ manages DeleteSub;
+
+child:
+ async DeleteSub();
+};
diff --git a/ipc/ipdl/test/ipdl/ok/DeleteSub.ipdl b/ipc/ipdl/test/ipdl/ok/DeleteSub.ipdl
new file mode 100644
index 000000000..45dd2a827
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/DeleteSub.ipdl
@@ -0,0 +1,12 @@
+include protocol Delete;
+
+sync protocol DeleteSub {
+ manager Delete;
+
+parent:
+ sync __delete__(int x) returns (double d);
+
+state START:
+ recv __delete__;
+};
+
diff --git a/ipc/ipdl/test/ipdl/ok/Nullable.ipdl b/ipc/ipdl/test/ipdl/ok/Nullable.ipdl
new file mode 100644
index 000000000..6a81e125d
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/Nullable.ipdl
@@ -0,0 +1,11 @@
+union Union {
+ nullable Nullable;
+ nullable Nullable[];
+};
+
+protocol Nullable {
+child:
+ async Msg(nullable Nullable n);
+ async Msg2(nullable Nullable[] N);
+ async Msg3(Union u);
+};
diff --git a/ipc/ipdl/test/ipdl/ok/Struct.ipdl b/ipc/ipdl/test/ipdl/ok/Struct.ipdl
new file mode 100644
index 000000000..da3ca055a
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/Struct.ipdl
@@ -0,0 +1,10 @@
+struct S {
+ int i;
+ double d;
+};
+
+sync protocol Struct {
+parent:
+ sync test(S s) returns (S ss);
+ async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/ok/actorparam.ipdl b/ipc/ipdl/test/ipdl/ok/actorparam.ipdl
new file mode 100644
index 000000000..1a3e7b972
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/actorparam.ipdl
@@ -0,0 +1,5 @@
+protocol actorparam {
+
+child: Msg(actorparam p);
+
+};
diff --git a/ipc/ipdl/test/ipdl/ok/actorparam_state.ipdl b/ipc/ipdl/test/ipdl/ok/actorparam_state.ipdl
new file mode 100644
index 000000000..5a3109f5f
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/actorparam_state.ipdl
@@ -0,0 +1,10 @@
+protocol actorparam_state {
+
+child:
+ async Msg(actorparam_state:S1 p);
+ async __delete__();
+
+state S1:
+ send Msg goto S1;
+ send __delete__;
+};
diff --git a/ipc/ipdl/test/ipdl/ok/actorreturn.ipdl b/ipc/ipdl/test/ipdl/ok/actorreturn.ipdl
new file mode 100644
index 000000000..9642ca854
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/actorreturn.ipdl
@@ -0,0 +1,6 @@
+sync protocol actorreturn {
+
+parent:
+ sync Msg(actorreturn p) returns (actorreturn r);
+
+};
diff --git a/ipc/ipdl/test/ipdl/ok/array_Basic.ipdl b/ipc/ipdl/test/ipdl/ok/array_Basic.ipdl
new file mode 100644
index 000000000..391917708
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/array_Basic.ipdl
@@ -0,0 +1,4 @@
+protocol array_Basic {
+child:
+ async Msg(int[] array);
+};
diff --git a/ipc/ipdl/test/ipdl/ok/array_OfActors.ipdl b/ipc/ipdl/test/ipdl/ok/array_OfActors.ipdl
new file mode 100644
index 000000000..8d0c7fb6a
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/array_OfActors.ipdl
@@ -0,0 +1,10 @@
+include protocol array_OfActorsSub;
+
+protocol array_OfActors {
+ manages array_OfActorsSub;
+
+child:
+ async Msg(array_OfActorsSub[] p);
+
+ array_OfActorsSub();
+};
diff --git a/ipc/ipdl/test/ipdl/ok/array_OfActorsSub.ipdl b/ipc/ipdl/test/ipdl/ok/array_OfActorsSub.ipdl
new file mode 100644
index 000000000..3b765511f
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/array_OfActorsSub.ipdl
@@ -0,0 +1,7 @@
+include protocol array_OfActors;
+
+protocol array_OfActorsSub {
+ manager array_OfActors;
+
+child: __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/ok/array_Union.ipdl b/ipc/ipdl/test/ipdl/ok/array_Union.ipdl
new file mode 100644
index 000000000..64d8adbd7
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/array_Union.ipdl
@@ -0,0 +1,10 @@
+union Union {
+ int[];
+ int;
+ double;
+};
+
+sync protocol array_Union {
+parent:
+ sync Msg(Union u, Union[] au) returns (Union r);
+};
diff --git a/ipc/ipdl/test/ipdl/ok/builtins.ipdl b/ipc/ipdl/test/ipdl/ok/builtins.ipdl
new file mode 100644
index 000000000..e407c5ed2
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/builtins.ipdl
@@ -0,0 +1,21 @@
+protocol builtins {
+
+ // sanity-check that "essential" builtins are being declared
+
+child: Msg(bool b,
+ char c,
+ int i,
+ long l,
+
+ float f,
+ double d,
+
+ int8_t i8t,
+ uint8_t u8t,
+ int16_t i16t,
+ uint16_t u16t,
+ int32_t i32t,
+ uint32_t u32t,
+ int64_t i64t,
+ uint64_t u64t);
+};
diff --git a/ipc/ipdl/test/ipdl/ok/compositor.ipdl b/ipc/ipdl/test/ipdl/ok/compositor.ipdl
new file mode 100644
index 000000000..3f1b16335
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/compositor.ipdl
@@ -0,0 +1,11 @@
+include protocol content;
+
+sync protocol compositor {
+ bridges compositor, content;
+
+child:
+ async __delete__();
+
+state DEAD:
+ send __delete__;
+};
diff --git a/ipc/ipdl/test/ipdl/ok/content.ipdl b/ipc/ipdl/test/ipdl/ok/content.ipdl
new file mode 100644
index 000000000..655998bac
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/content.ipdl
@@ -0,0 +1,17 @@
+include protocol compositor;
+include protocol jetpack;
+include protocol media;
+include protocol plugin;
+
+sync protocol content {
+ parent spawns compositor as parent;
+ parent spawns jetpack;
+ child spawns plugin;
+ child opens media;
+
+child:
+ async __delete__();
+
+state DEAD:
+ send __delete__;
+};
diff --git a/ipc/ipdl/test/ipdl/ok/empty.ipdl b/ipc/ipdl/test/ipdl/ok/empty.ipdl
new file mode 100644
index 000000000..73d512586
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/empty.ipdl
@@ -0,0 +1,3 @@
+protocol empty {
+child: Msg();
+};
diff --git a/ipc/ipdl/test/ipdl/ok/emptyStruct.ipdl b/ipc/ipdl/test/ipdl/ok/emptyStruct.ipdl
new file mode 100644
index 000000000..f6be5eb86
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/emptyStruct.ipdl
@@ -0,0 +1,3 @@
+struct empty { };
+
+protocol emptyStruct { child: __delete__(); };
diff --git a/ipc/ipdl/test/ipdl/ok/header.ipdlh b/ipc/ipdl/test/ipdl/ok/header.ipdlh
new file mode 100644
index 000000000..fc3f8c827
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/header.ipdlh
@@ -0,0 +1,15 @@
+using foo from "foo.h";
+using bar::baz from "foo.h";
+
+struct Outer { };
+
+namespace a { struct Inner1 { }; }
+
+namespace b { struct Inner2 { }; }
+
+namespace c {
+union X {
+ int32_t;
+ float;
+};
+}
diff --git a/ipc/ipdl/test/ipdl/ok/headerProto.ipdl b/ipc/ipdl/test/ipdl/ok/headerProto.ipdl
new file mode 100644
index 000000000..0bccc3834
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/headerProto.ipdl
@@ -0,0 +1,10 @@
+include header;
+
+namespace c {
+
+protocol headerProto {
+child:
+ async __delete__(foo a, baz b, Inner1 c, Inner2 d, X x);
+};
+
+}
diff --git a/ipc/ipdl/test/ipdl/ok/intrProtocol.ipdl b/ipc/ipdl/test/ipdl/ok/intrProtocol.ipdl
new file mode 100644
index 000000000..1c9327f01
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/intrProtocol.ipdl
@@ -0,0 +1,13 @@
+intr protocol intrProtocol {
+
+ // sanity check of Interrupt protocols
+child:
+ async AsyncMsg();
+
+parent:
+ sync SyncMsg(int i) returns (int r);
+
+both:
+ intr InterruptMsg(int x) returns (int y);
+
+};
diff --git a/ipc/ipdl/test/ipdl/ok/jetpack.ipdl b/ipc/ipdl/test/ipdl/ok/jetpack.ipdl
new file mode 100644
index 000000000..7cdb5c329
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/jetpack.ipdl
@@ -0,0 +1,7 @@
+sync protocol jetpack {
+child:
+ async __delete__();
+
+state DEAD:
+ send __delete__;
+};
diff --git a/ipc/ipdl/test/ipdl/ok/jetpackContent.ipdl b/ipc/ipdl/test/ipdl/ok/jetpackContent.ipdl
new file mode 100644
index 000000000..1e19c541f
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/jetpackContent.ipdl
@@ -0,0 +1,12 @@
+include protocol content;
+include protocol jetpack;
+
+intr protocol jetpackContent {
+ bridges jetpack, content;
+
+child:
+ async __delete__();
+
+state DEAD:
+ send __delete__;
+};
diff --git a/ipc/ipdl/test/ipdl/ok/manageSelf.ipdl b/ipc/ipdl/test/ipdl/ok/manageSelf.ipdl
new file mode 100644
index 000000000..b90ad7237
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/manageSelf.ipdl
@@ -0,0 +1,10 @@
+include protocol manageSelf_Toplevel;
+
+protocol manageSelf {
+ manager manageSelf_Toplevel or manageSelf;
+ manages manageSelf;
+
+child:
+ manageSelf();
+ async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/ok/manageSelf_Toplevel.ipdl b/ipc/ipdl/test/ipdl/ok/manageSelf_Toplevel.ipdl
new file mode 100644
index 000000000..8788338f3
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/manageSelf_Toplevel.ipdl
@@ -0,0 +1,9 @@
+include protocol manageSelf;
+
+protocol manageSelf_Toplevel {
+ manages manageSelf;
+
+child:
+ manageSelf();
+ async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/ok/managedProtocol.ipdl b/ipc/ipdl/test/ipdl/ok/managedProtocol.ipdl
new file mode 100644
index 000000000..f59ab6651
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/managedProtocol.ipdl
@@ -0,0 +1,8 @@
+include protocol managerProtocol;
+
+protocol managedProtocol {
+ manager managerProtocol;
+
+child:
+ async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/ok/managerProtocol.ipdl b/ipc/ipdl/test/ipdl/ok/managerProtocol.ipdl
new file mode 100644
index 000000000..fc1e57ada
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/managerProtocol.ipdl
@@ -0,0 +1,13 @@
+include protocol managedProtocol;
+
+// sanity check of managed/manager protocols
+
+protocol managerProtocol {
+ manages managedProtocol;
+
+parent:
+ managedProtocol(int i);
+
+state CREATING:
+ recv managedProtocol goto CREATING;
+};
diff --git a/ipc/ipdl/test/ipdl/ok/media.ipdl b/ipc/ipdl/test/ipdl/ok/media.ipdl
new file mode 100644
index 000000000..032f7df6f
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/media.ipdl
@@ -0,0 +1,7 @@
+sync protocol media {
+child:
+ async __delete__();
+
+state DEAD:
+ send __delete__;
+};
diff --git a/ipc/ipdl/test/ipdl/ok/messageCompress.ipdl b/ipc/ipdl/test/ipdl/ok/messageCompress.ipdl
new file mode 100644
index 000000000..dd563fa3b
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/messageCompress.ipdl
@@ -0,0 +1,4 @@
+intr protocol messageCompress {
+child:
+ async foo() compress;
+};
diff --git a/ipc/ipdl/test/ipdl/ok/messageVerify.ipdl b/ipc/ipdl/test/ipdl/ok/messageVerify.ipdl
new file mode 100644
index 000000000..0de9ef4ad
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/messageVerify.ipdl
@@ -0,0 +1,14 @@
+include protocol messageVerifyTopLevel;
+
+intr protocol messageVerify {
+ manager messageVerifyTopLevel;
+
+parent:
+ sync __delete__(uint32_t x) returns (double rv1) verify;
+ async msg1() verify;
+ async msg2(uint32_t aParam1) verify;
+ sync msg3()
+ returns (uint32_t rv1) verify;
+ sync msg4(uint32_t aParam1)
+ returns (uint32_t rv1) verify;
+};
diff --git a/ipc/ipdl/test/ipdl/ok/messageVerifyTopLevel.ipdl b/ipc/ipdl/test/ipdl/ok/messageVerifyTopLevel.ipdl
new file mode 100644
index 000000000..14ff22111
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/messageVerifyTopLevel.ipdl
@@ -0,0 +1,7 @@
+include protocol messageVerify;
+
+intr protocol messageVerifyTopLevel{
+ manages messageVerify;
+ parent:
+ sync messageVerify(uint32_t aParam1) returns (double rv1) verify;
+};
diff --git a/ipc/ipdl/test/ipdl/ok/multiManaged.ipdl b/ipc/ipdl/test/ipdl/ok/multiManaged.ipdl
new file mode 100644
index 000000000..c845d5086
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/multiManaged.ipdl
@@ -0,0 +1,9 @@
+include protocol multiManager1;
+include protocol multiManager2;
+
+protocol multiManaged {
+ manager multiManager1 or multiManager2;
+
+child:
+ async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/ok/multiManager1.ipdl b/ipc/ipdl/test/ipdl/ok/multiManager1.ipdl
new file mode 100644
index 000000000..0507a28cd
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/multiManager1.ipdl
@@ -0,0 +1,8 @@
+include protocol multiManaged;
+
+protocol multiManager1 {
+ manages multiManaged;
+
+child:
+ multiManaged();
+};
diff --git a/ipc/ipdl/test/ipdl/ok/multiManager2.ipdl b/ipc/ipdl/test/ipdl/ok/multiManager2.ipdl
new file mode 100644
index 000000000..0cad3f206
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/multiManager2.ipdl
@@ -0,0 +1,8 @@
+include protocol multiManaged;
+
+protocol multiManager2 {
+ manages multiManaged;
+
+child:
+ multiManaged();
+};
diff --git a/ipc/ipdl/test/ipdl/ok/multiOutStates.ipdl b/ipc/ipdl/test/ipdl/ok/multiOutStates.ipdl
new file mode 100644
index 000000000..d3554f9e7
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/multiOutStates.ipdl
@@ -0,0 +1,12 @@
+protocol multiOutStates {
+child: Msg();
+
+ // sanity check that multi-out-states are being processed correctly
+
+state S1:
+ send Msg goto S2 or S3 or S4;
+
+state S2: send Msg goto S2;
+state S3: send Msg goto S3;
+state S4: send Msg goto S4;
+};
diff --git a/ipc/ipdl/test/ipdl/ok/multiStartState.ipdl b/ipc/ipdl/test/ipdl/ok/multiStartState.ipdl
new file mode 100644
index 000000000..bbeb016ac
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/multiStartState.ipdl
@@ -0,0 +1,11 @@
+protocol multiStartState {
+
+child: Msg(); __delete__();
+
+start state S1:
+ send Msg goto S1; send __delete__;
+
+start state S2:
+ send Msg goto S2; send __delete__;
+
+};
diff --git a/ipc/ipdl/test/ipdl/ok/multipleUsingCxxTypes.ipdl b/ipc/ipdl/test/ipdl/ok/multipleUsingCxxTypes.ipdl
new file mode 100644
index 000000000..045d3fa6e
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/multipleUsingCxxTypes.ipdl
@@ -0,0 +1,7 @@
+using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
+using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
+
+protocol multipleUsingCxxTypes {
+child:
+ async Msg(void_t foo);
+};
diff --git a/ipc/ipdl/test/ipdl/ok/mutualRecStructUnion.ipdl b/ipc/ipdl/test/ipdl/ok/mutualRecStructUnion.ipdl
new file mode 100644
index 000000000..0891d3e78
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/mutualRecStructUnion.ipdl
@@ -0,0 +1,21 @@
+struct X {
+ int i;
+ Y[] y;
+};
+
+union Y {
+ double;
+ X;
+ Z;
+};
+
+struct Z {
+ X x;
+ Y y;
+};
+
+protocol mutualRecStructUnion {
+child:
+ async Test(X x, Y y, Z z);
+ async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/ok/mutualRecUnion.ipdl b/ipc/ipdl/test/ipdl/ok/mutualRecUnion.ipdl
new file mode 100644
index 000000000..fcd364920
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/mutualRecUnion.ipdl
@@ -0,0 +1,20 @@
+union X {
+ int;
+ Y[];
+};
+
+union Y {
+ X;
+ Z;
+};
+
+union Z {
+ double;
+ X;
+};
+
+protocol mutualRecUnion {
+child:
+ async Test(X x, Y y, Z z);
+ async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/ok/namespace_Basic.ipdl b/ipc/ipdl/test/ipdl/ok/namespace_Basic.ipdl
new file mode 100644
index 000000000..4eee5ffd1
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/namespace_Basic.ipdl
@@ -0,0 +1,12 @@
+namespace basic {
+
+// sanity check of namespaced protocols
+
+protocol namespace_Basic {
+
+child:
+ async Msg();
+
+};
+
+} // namespace basic
diff --git a/ipc/ipdl/test/ipdl/ok/noRedeclCrossMessage.ipdl b/ipc/ipdl/test/ipdl/ok/noRedeclCrossMessage.ipdl
new file mode 100644
index 000000000..578ac82d8
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/noRedeclCrossMessage.ipdl
@@ -0,0 +1,9 @@
+protocol noRedeclCrossMessage {
+
+ // each message has its own scope for param/return names
+
+child:
+ async Msg1(int f);
+ async Msg2(int f);
+
+};
diff --git a/ipc/ipdl/test/ipdl/ok/plugin.ipdl b/ipc/ipdl/test/ipdl/ok/plugin.ipdl
new file mode 100644
index 000000000..b1e2d24a9
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/plugin.ipdl
@@ -0,0 +1,7 @@
+intr protocol plugin {
+child:
+ async __delete__();
+
+state DEAD:
+ send __delete__;
+};
diff --git a/ipc/ipdl/test/ipdl/ok/race_DiamondRule1.ipdl b/ipc/ipdl/test/ipdl/ok/race_DiamondRule1.ipdl
new file mode 100644
index 000000000..cc1a8b16a
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/race_DiamondRule1.ipdl
@@ -0,0 +1,27 @@
+protocol race_DiamondRule1 {
+
+child:
+ async Msg1();
+ async Msg1_();
+parent:
+ async Msg2();
+ async Msg2_();
+
+ // OK: this state machine is one of the simplest that follows the
+ // Diamond Rule
+
+start state S1:
+ send Msg1 goto S2;
+ recv Msg2 goto S3;
+
+state S2:
+ recv Msg2 goto S4;
+ recv Msg2_ goto S2;
+
+state S3:
+ send Msg1 goto S4;
+ send Msg1_ goto S3;
+
+state S4:
+ send Msg1 goto S4;
+};
diff --git a/ipc/ipdl/test/ipdl/ok/race_KitchenSink.ipdl b/ipc/ipdl/test/ipdl/ok/race_KitchenSink.ipdl
new file mode 100644
index 000000000..55d674ada
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/race_KitchenSink.ipdl
@@ -0,0 +1,61 @@
+protocol race_KitchenSink {
+child:
+ async Msg1();
+ async Msg1_();
+parent:
+ async Msg2();
+ async Msg2_();
+
+
+ // concatenation of a few other state machines, should be OK
+
+start state S1:
+ send Msg1 goto S2;
+ recv Msg2 goto S3;
+
+state S2:
+ recv Msg2 goto S4;
+ recv Msg2_ goto S2;
+
+state S3:
+ send Msg1 goto S4;
+ send Msg1_ goto S3;
+
+state S4:
+ send Msg1 goto S4;
+
+
+
+start state S5:
+ send Msg1 goto S5;
+ recv Msg2 goto S5;
+
+
+
+start state S15:
+ send Msg1 goto S16 or S17;
+ recv Msg2 goto S18 or S19;
+
+state S16:
+ recv Msg2 goto S20;
+ recv Msg2_ goto S18;
+
+state S17:
+ recv Msg2 goto S20;
+ recv Msg2_ goto S15;
+
+state S18:
+ send Msg1 goto S20;
+ send Msg1_ goto S15;
+
+state S19:
+ send Msg1 goto S20;
+ send Msg1_ goto S16;
+
+state S20:
+ send Msg1 goto S20;
+ send Msg1_ goto S20;
+ recv Msg2 goto S20;
+ recv Msg2_ goto S20;
+
+};
diff --git a/ipc/ipdl/test/ipdl/ok/race_MultiOut.ipdl b/ipc/ipdl/test/ipdl/ok/race_MultiOut.ipdl
new file mode 100644
index 000000000..80cffd91c
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/race_MultiOut.ipdl
@@ -0,0 +1,35 @@
+protocol race_MultiOut {
+child:
+ async Msg1();
+ async Msg1_();
+parent:
+ async Msg2();
+ async Msg2_();
+
+start state S15:
+ send Msg1 goto S16 or S17;
+ recv Msg2 goto S18 or S19;
+
+state S16:
+ recv Msg2 goto S20;
+ recv Msg2_ goto S18;
+
+state S17:
+ recv Msg2 goto S20;
+ recv Msg2_ goto S15;
+
+state S18:
+ send Msg1 goto S20;
+ send Msg1_ goto S15;
+
+state S19:
+ send Msg1 goto S20;
+ send Msg1_ goto S16;
+
+state S20:
+ send Msg1 goto S20;
+ send Msg1_ goto S20;
+ recv Msg2 goto S20;
+ recv Msg2_ goto S20;
+
+};
diff --git a/ipc/ipdl/test/ipdl/ok/race_Stateless.ipdl b/ipc/ipdl/test/ipdl/ok/race_Stateless.ipdl
new file mode 100644
index 000000000..6cffe1e7c
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/race_Stateless.ipdl
@@ -0,0 +1,16 @@
+protocol race_Stateless {
+// manages Child;
+
+child:
+ async Msg1();
+ async Msg1_();
+parent:
+ async Msg2();
+ async Msg2_();
+
+
+ // OK: this is trivial stateless protocol, so race-free "by definition"
+start state S5:
+ send Msg1 goto S5;
+ recv Msg2 goto S5;
+};
diff --git a/ipc/ipdl/test/ipdl/ok/selfRecUnion.ipdl b/ipc/ipdl/test/ipdl/ok/selfRecUnion.ipdl
new file mode 100644
index 000000000..70f04602c
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/selfRecUnion.ipdl
@@ -0,0 +1,11 @@
+union R {
+ int;
+ double;
+ R;
+};
+
+protocol selfRecUnion {
+child:
+ async Test(R r);
+ async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/ok/shmem.ipdl b/ipc/ipdl/test/ipdl/ok/shmem.ipdl
new file mode 100644
index 000000000..8bc44010b
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/shmem.ipdl
@@ -0,0 +1,13 @@
+union Foo {
+ int;
+ Shmem;
+};
+
+intr protocol shmem {
+parent:
+ async Msg(Shmem s, Foo f);
+ sync SyncMsg(Shmem s, Foo f)
+ returns (Shmem t, Foo g);
+ intr InterruptMsg(Shmem s, Foo f)
+ returns (Shmem t, Foo g);
+};
diff --git a/ipc/ipdl/test/ipdl/ok/syncProtocol.ipdl b/ipc/ipdl/test/ipdl/ok/syncProtocol.ipdl
new file mode 100644
index 000000000..cfe7760e6
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/syncProtocol.ipdl
@@ -0,0 +1,11 @@
+sync protocol syncProtocol {
+
+ // sanity check of sync protocols
+
+child:
+ async AsyncMsg();
+
+parent:
+ sync SyncMsg() returns (int i);
+
+};
diff --git a/ipc/ipdl/test/ipdl/ok/threeDirections.ipdl b/ipc/ipdl/test/ipdl/ok/threeDirections.ipdl
new file mode 100644
index 000000000..c06f9d56f
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/threeDirections.ipdl
@@ -0,0 +1,13 @@
+protocol threeDirections {
+
+ // sanity check that the three direction specifiers are being accepted
+child:
+ async ChildMsg();
+
+parent:
+ async ParentMsg();
+
+both:
+ async BothMsg();
+
+};
diff --git a/ipc/ipdl/test/ipdl/ok/union_Basic.ipdl b/ipc/ipdl/test/ipdl/ok/union_Basic.ipdl
new file mode 100644
index 000000000..ef5b1cbfe
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/union_Basic.ipdl
@@ -0,0 +1,11 @@
+union Basic {
+ int;
+ double;
+};
+
+sync protocol union_Basic {
+
+parent:
+ sync Msg(Basic p) returns (Basic r);
+
+};
diff --git a/ipc/ipdl/test/ipdl/ok/union_Namespaced.ipdl b/ipc/ipdl/test/ipdl/ok/union_Namespaced.ipdl
new file mode 100644
index 000000000..02f86f9de
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/union_Namespaced.ipdl
@@ -0,0 +1,18 @@
+namespace kitties {
+
+union Socks {
+ int;
+ double;
+};
+
+} // namespace kitties
+
+
+namespace puppies {
+
+protocol union_Namespaced {
+child:
+ async Msg(Socks s);
+};
+
+} // namespace puppies
diff --git a/ipc/ipdl/test/ipdl/runtests.py b/ipc/ipdl/test/ipdl/runtests.py
new file mode 100644
index 000000000..93a73729e
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/runtests.py
@@ -0,0 +1,75 @@
+import os, unittest
+
+from IPDLCompile import IPDLCompile
+
+
+class IPDLTestCase(unittest.TestCase):
+ def __init__(self, ipdlargv, filename):
+ unittest.TestCase.__init__(self, 'test')
+ self.filename = filename
+ self.compile = IPDLCompile(filename, ipdlargv)
+
+ def test(self):
+ self.compile.run()
+ self.assertFalse(self.compile.exception(), self.mkFailMsg())
+ self.checkPassed()
+
+ def mkFailMsg(self):
+ return '''
+### Command: %s
+### stderr:
+%s'''% (' '.join(self.compile.argv), self.compile.stderr)
+
+ def shortDescription(self):
+ return '%s test of "%s"'% (self.__class__.__name__, self.filename)
+
+
+class OkTestCase(IPDLTestCase):
+ '''An invocation of the IPDL compiler on a valid specification.
+The IPDL compiler should not produce errors or exceptions.'''
+
+ def __init__(self, ipdlargv, filename):
+ IPDLTestCase.__init__(self, ipdlargv, filename)
+
+ def checkPassed(self):
+ self.assertTrue(self.compile.ok(), self.mkFailMsg())
+
+
+class ErrorTestCase(IPDLTestCase):
+ '''An invocation of the IPDL compiler on an *invalid* specification.
+The IPDL compiler *should* produce errors but not exceptions.'''
+
+ def __init__(self, ipdlargv, filename):
+ IPDLTestCase.__init__(self, ipdlargv, filename)
+
+ def checkPassed(self):
+ self.assertTrue(self.compile.error(), self.mkFailMsg())
+
+
+if __name__ == '__main__':
+ import sys
+
+ okdir = sys.argv[1]
+ assert os.path.isdir(okdir)
+ errordir = sys.argv[2]
+ assert os.path.isdir(errordir)
+
+ ipdlargv = [ ]
+ oksuite = unittest.TestSuite()
+ errorsuite = unittest.TestSuite()
+
+ oktests, errortests = 0, 0
+ for arg in sys.argv[3:]:
+ if errortests:
+ errorsuite.addTest(ErrorTestCase(ipdlargv+ [ '-I', errordir ],
+ arg))
+ elif oktests:
+ if 'ERRORTESTS' == arg: errortests = 1; continue
+ oksuite.addTest(OkTestCase(ipdlargv+ [ '-I', okdir ],
+ arg))
+ else:
+ if 'OKTESTS' == arg: oktests = 1; continue
+ ipdlargv.append(arg)
+
+ (unittest.TextTestRunner()).run(
+ unittest.TestSuite([ oksuite, errorsuite ]))
diff --git a/ipc/ipdl/test/moz.build b/ipc/ipdl/test/moz.build
new file mode 100644
index 000000000..5d5caacf8
--- /dev/null
+++ b/ipc/ipdl/test/moz.build
@@ -0,0 +1,12 @@
+# -*- 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/.
+
+# we ignore MOZ_IPDL_TESTS for the IPDL-compiler-only tests, since they're
+# quick and painless
+DIRS += ['ipdl']
+
+if CONFIG['MOZ_IPDL_TESTS']:
+ DIRS += ['cxx']