summaryrefslogtreecommitdiffstats
path: root/ipc/ipdl
diff options
context:
space:
mode:
Diffstat (limited to 'ipc/ipdl')
-rw-r--r--ipc/ipdl/Makefile.in33
-rw-r--r--ipc/ipdl/ipdl.py318
-rw-r--r--ipc/ipdl/ipdl/__init__.py100
-rw-r--r--ipc/ipdl/ipdl/ast.py406
-rw-r--r--ipc/ipdl/ipdl/builtin.py70
-rw-r--r--ipc/ipdl/ipdl/cgen.py108
-rw-r--r--ipc/ipdl/ipdl/checker.py81
-rw-r--r--ipc/ipdl/ipdl/cxx/__init__.py3
-rw-r--r--ipc/ipdl/ipdl/cxx/ast.py1018
-rw-r--r--ipc/ipdl/ipdl/cxx/cgen.py558
-rw-r--r--ipc/ipdl/ipdl/cxx/code.py187
-rw-r--r--ipc/ipdl/ipdl/direct_call.py763
-rw-r--r--ipc/ipdl/ipdl/lower.py5757
-rw-r--r--ipc/ipdl/ipdl/parser.py783
-rw-r--r--ipc/ipdl/ipdl/type.py1530
-rw-r--r--ipc/ipdl/ipdl/util.py12
-rw-r--r--ipc/ipdl/message-metadata.ini38
-rw-r--r--ipc/ipdl/moz.build21
-rw-r--r--ipc/ipdl/msgtype-components14
-rw-r--r--ipc/ipdl/sync-messages.ini1124
-rw-r--r--ipc/ipdl/test/README.txt10
-rw-r--r--ipc/ipdl/test/cxx/IPDLUnitTestProcessChild.cpp26
-rw-r--r--ipc/ipdl/test/cxx/IPDLUnitTestProcessChild.h29
-rw-r--r--ipc/ipdl/test/cxx/IPDLUnitTestSubprocess.cpp19
-rw-r--r--ipc/ipdl/test/cxx/IPDLUnitTestSubprocess.h34
-rw-r--r--ipc/ipdl/test/cxx/IPDLUnitTestTypes.h43
-rw-r--r--ipc/ipdl/test/cxx/IPDLUnitTestUtils.h29
-rw-r--r--ipc/ipdl/test/cxx/IPDLUnitTests.h85
-rw-r--r--ipc/ipdl/test/cxx/IPDLUnitTests.template.cpp347
-rw-r--r--ipc/ipdl/test/cxx/Makefile.in45
-rw-r--r--ipc/ipdl/test/cxx/PTestActorPunning.ipdl26
-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/PTestAsyncReturns.ipdl17
-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/PTestCancel.ipdl36
-rw-r--r--ipc/ipdl/test/cxx/PTestCrashCleanup.ipdl17
-rw-r--r--ipc/ipdl/test/cxx/PTestDataStructures.ipdl107
-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.ipdl21
-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.ipdl22
-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.ipdl22
-rw-r--r--ipc/ipdl/test/cxx/PTestFailedCtor.ipdl14
-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.ipdl19
-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.ipdl24
-rw-r--r--ipc/ipdl/test/cxx/PTestInterruptShutdownRace.ipdl16
-rw-r--r--ipc/ipdl/test/cxx/PTestJSON.ipdl46
-rw-r--r--ipc/ipdl/test/cxx/PTestLatency.ipdl26
-rw-r--r--ipc/ipdl/test/cxx/PTestLayoutThread.ipdl19
-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.ipdl22
-rw-r--r--ipc/ipdl/test/cxx/PTestMultiMgrsBottom.ipdl15
-rw-r--r--ipc/ipdl/test/cxx/PTestMultiMgrsLeft.ipdl18
-rw-r--r--ipc/ipdl/test/cxx/PTestMultiMgrsRight.ipdl18
-rw-r--r--ipc/ipdl/test/cxx/PTestNestedLoops.ipdl19
-rw-r--r--ipc/ipdl/test/cxx/PTestPaintThread.ipdl13
-rw-r--r--ipc/ipdl/test/cxx/PTestPriority.ipdl20
-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.ipdl19
-rw-r--r--ipc/ipdl/test/cxx/PTestRacyInterruptReplies.ipdl17
-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.ipdl18
-rw-r--r--ipc/ipdl/test/cxx/PTestSelfManage.ipdl18
-rw-r--r--ipc/ipdl/test/cxx/PTestSelfManageRoot.ipdl17
-rw-r--r--ipc/ipdl/test/cxx/PTestShmem.ipdl14
-rw-r--r--ipc/ipdl/test/cxx/PTestShutdown.ipdl26
-rw-r--r--ipc/ipdl/test/cxx/PTestShutdownSub.ipdl20
-rw-r--r--ipc/ipdl/test/cxx/PTestShutdownSubsub.ipdl14
-rw-r--r--ipc/ipdl/test/cxx/PTestStackHooks.ipdl26
-rw-r--r--ipc/ipdl/test/cxx/PTestSyncError.ipdl18
-rw-r--r--ipc/ipdl/test/cxx/PTestSyncHang.ipdl14
-rw-r--r--ipc/ipdl/test/cxx/PTestSyncWakeup.ipdl20
-rw-r--r--ipc/ipdl/test/cxx/PTestUniquePtrIPC.ipdl23
-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.cpp126
-rw-r--r--ipc/ipdl/test/cxx/TestActorPunning.h100
-rw-r--r--ipc/ipdl/test/cxx/TestAsyncReturns.cpp101
-rw-r--r--ipc/ipdl/test/cxx/TestAsyncReturns.h54
-rw-r--r--ipc/ipdl/test/cxx/TestBadActor.cpp59
-rw-r--r--ipc/ipdl/test/cxx/TestBadActor.h84
-rw-r--r--ipc/ipdl/test/cxx/TestCancel.cpp115
-rw-r--r--ipc/ipdl/test/cxx/TestCancel.h54
-rw-r--r--ipc/ipdl/test/cxx/TestCrashCleanup.cpp100
-rw-r--r--ipc/ipdl/test/cxx/TestCrashCleanup.h49
-rw-r--r--ipc/ipdl/test/cxx/TestDataStructures.cpp888
-rw-r--r--ipc/ipdl/test/cxx/TestDataStructures.h180
-rw-r--r--ipc/ipdl/test/cxx/TestDemon.cpp362
-rw-r--r--ipc/ipdl/test/cxx/TestDemon.h91
-rw-r--r--ipc/ipdl/test/cxx/TestDesc.cpp78
-rw-r--r--ipc/ipdl/test/cxx/TestDesc.h115
-rw-r--r--ipc/ipdl/test/cxx/TestEndpointBridgeMain.cpp226
-rw-r--r--ipc/ipdl/test/cxx/TestEndpointBridgeMain.h132
-rw-r--r--ipc/ipdl/test/cxx/TestEndpointOpens.cpp242
-rw-r--r--ipc/ipdl/test/cxx/TestEndpointOpens.h102
-rw-r--r--ipc/ipdl/test/cxx/TestFailedCtor.cpp112
-rw-r--r--ipc/ipdl/test/cxx/TestFailedCtor.h127
-rw-r--r--ipc/ipdl/test/cxx/TestHangs.cpp127
-rw-r--r--ipc/ipdl/test/cxx/TestHangs.h75
-rw-r--r--ipc/ipdl/test/cxx/TestHighestPrio.cpp91
-rw-r--r--ipc/ipdl/test/cxx/TestHighestPrio.h55
-rw-r--r--ipc/ipdl/test/cxx/TestInterruptErrorCleanup.cpp138
-rw-r--r--ipc/ipdl/test/cxx/TestInterruptErrorCleanup.h52
-rw-r--r--ipc/ipdl/test/cxx/TestInterruptRaces.cpp169
-rw-r--r--ipc/ipdl/test/cxx/TestInterruptRaces.h104
-rw-r--r--ipc/ipdl/test/cxx/TestInterruptShutdownRace.cpp111
-rw-r--r--ipc/ipdl/test/cxx/TestInterruptShutdownRace.h56
-rw-r--r--ipc/ipdl/test/cxx/TestJSON.cpp113
-rw-r--r--ipc/ipdl/test/cxx/TestJSON.h93
-rw-r--r--ipc/ipdl/test/cxx/TestLatency.cpp210
-rw-r--r--ipc/ipdl/test/cxx/TestLatency.h107
-rw-r--r--ipc/ipdl/test/cxx/TestManyChildAllocs.cpp83
-rw-r--r--ipc/ipdl/test/cxx/TestManyChildAllocs.h82
-rw-r--r--ipc/ipdl/test/cxx/TestMultiMgrs.cpp83
-rw-r--r--ipc/ipdl/test/cxx/TestMultiMgrs.h222
-rw-r--r--ipc/ipdl/test/cxx/TestNestedLoops.cpp78
-rw-r--r--ipc/ipdl/test/cxx/TestNestedLoops.h59
-rw-r--r--ipc/ipdl/test/cxx/TestOffMainThreadPainting.cpp237
-rw-r--r--ipc/ipdl/test/cxx/TestOffMainThreadPainting.h109
-rw-r--r--ipc/ipdl/test/cxx/TestRPC.cpp107
-rw-r--r--ipc/ipdl/test/cxx/TestRPC.h60
-rw-r--r--ipc/ipdl/test/cxx/TestRaceDeadlock.cpp102
-rw-r--r--ipc/ipdl/test/cxx/TestRaceDeadlock.h68
-rw-r--r--ipc/ipdl/test/cxx/TestRaceDeferral.cpp84
-rw-r--r--ipc/ipdl/test/cxx/TestRaceDeferral.h67
-rw-r--r--ipc/ipdl/test/cxx/TestRacyInterruptReplies.cpp94
-rw-r--r--ipc/ipdl/test/cxx/TestRacyInterruptReplies.h65
-rw-r--r--ipc/ipdl/test/cxx/TestRacyReentry.cpp63
-rw-r--r--ipc/ipdl/test/cxx/TestRacyReentry.h59
-rw-r--r--ipc/ipdl/test/cxx/TestRacyUndefer.cpp83
-rw-r--r--ipc/ipdl/test/cxx/TestRacyUndefer.h62
-rw-r--r--ipc/ipdl/test/cxx/TestSanity.cpp52
-rw-r--r--ipc/ipdl/test/cxx/TestSanity.h55
-rw-r--r--ipc/ipdl/test/cxx/TestSelfManageRoot.cpp54
-rw-r--r--ipc/ipdl/test/cxx/TestSelfManageRoot.h117
-rw-r--r--ipc/ipdl/test/cxx/TestShmem.cpp108
-rw-r--r--ipc/ipdl/test/cxx/TestShmem.h55
-rw-r--r--ipc/ipdl/test/cxx/TestShutdown.cpp185
-rw-r--r--ipc/ipdl/test/cxx/TestShutdown.h168
-rw-r--r--ipc/ipdl/test/cxx/TestStackHooks.cpp122
-rw-r--r--ipc/ipdl/test/cxx/TestStackHooks.h107
-rw-r--r--ipc/ipdl/test/cxx/TestSyncError.cpp45
-rw-r--r--ipc/ipdl/test/cxx/TestSyncError.h61
-rw-r--r--ipc/ipdl/test/cxx/TestSyncHang.cpp62
-rw-r--r--ipc/ipdl/test/cxx/TestSyncHang.h50
-rw-r--r--ipc/ipdl/test/cxx/TestSyncWakeup.cpp106
-rw-r--r--ipc/ipdl/test/cxx/TestSyncWakeup.h66
-rw-r--r--ipc/ipdl/test/cxx/TestUniquePtrIPC.cpp81
-rw-r--r--ipc/ipdl/test/cxx/TestUniquePtrIPC.h61
-rw-r--r--ipc/ipdl/test/cxx/TestUrgency.cpp117
-rw-r--r--ipc/ipdl/test/cxx/TestUrgency.h57
-rw-r--r--ipc/ipdl/test/cxx/TestUrgentHangs.cpp174
-rw-r--r--ipc/ipdl/test/cxx/TestUrgentHangs.h64
-rw-r--r--ipc/ipdl/test/cxx/app/TestIPDL.cpp24
-rw-r--r--ipc/ipdl/test/cxx/app/moz.build20
-rw-r--r--ipc/ipdl/test/cxx/genIPDLUnitTests.py195
-rw-r--r--ipc/ipdl/test/cxx/moz.build173
-rw-r--r--ipc/ipdl/test/ipdl/IPDLCompile.py78
-rw-r--r--ipc/ipdl/test/ipdl/Makefile.in17
-rw-r--r--ipc/ipdl/test/ipdl/error/AsyncCtorReturns.ipdl10
-rw-r--r--ipc/ipdl/test/ipdl/error/AsyncCtorReturnsManagee.ipdl10
-rw-r--r--ipc/ipdl/test/ipdl/error/AsyncInsideSync.ipdl9
-rw-r--r--ipc/ipdl/test/ipdl/error/ForgottenManagee.ipdl12
-rw-r--r--ipc/ipdl/test/ipdl/error/ForgottenManager.ipdl11
-rw-r--r--ipc/ipdl/test/ipdl/error/InsideCpowToChild.ipdl9
-rw-r--r--ipc/ipdl/test/ipdl/error/IntrAsyncManagee.ipdl9
-rw-r--r--ipc/ipdl/test/ipdl/error/IntrAsyncManager.ipdl9
-rw-r--r--ipc/ipdl/test/ipdl/error/IntrSyncManagee.ipdl9
-rw-r--r--ipc/ipdl/test/ipdl/error/IntrSyncManager.ipdl9
-rw-r--r--ipc/ipdl/test/ipdl/error/ManageeForgot.ipdl11
-rw-r--r--ipc/ipdl/test/ipdl/error/ManagerForgot.ipdl11
-rw-r--r--ipc/ipdl/test/ipdl/error/Nullable.ipdl6
-rw-r--r--ipc/ipdl/test/ipdl/error/Nullable2.ipdl10
-rw-r--r--ipc/ipdl/test/ipdl/error/PBadArrayBase.ipdl7
-rw-r--r--ipc/ipdl/test/ipdl/error/PBadNestedManagee.ipdl9
-rw-r--r--ipc/ipdl/test/ipdl/error/PBadNestedManager.ipdl9
-rw-r--r--ipc/ipdl/test/ipdl/error/PBadUniquePtrBase.ipdl7
-rw-r--r--ipc/ipdl/test/ipdl/error/PDouble.ipdl11
-rw-r--r--ipc/ipdl/test/ipdl/error/PInconsistentMoveOnly.ipdl9
-rw-r--r--ipc/ipdl/test/ipdl/error/PUniquePtrRecursive.ipdl5
-rw-r--r--ipc/ipdl/test/ipdl/error/PUniquePtrSelfRecStruct.ipdl9
-rw-r--r--ipc/ipdl/test/ipdl/error/PUniquePtrSelfRecUnion.ipdl7
-rw-r--r--ipc/ipdl/test/ipdl/error/PasyncMessageListed.ipdl6
-rw-r--r--ipc/ipdl/test/ipdl/error/SyncAsyncManagee.ipdl9
-rw-r--r--ipc/ipdl/test/ipdl/error/SyncAsyncManager.ipdl9
-rw-r--r--ipc/ipdl/test/ipdl/error/array_Recursive.ipdl5
-rw-r--r--ipc/ipdl/test/ipdl/error/badProtocolInclude.ipdl9
-rw-r--r--ipc/ipdl/test/ipdl/error/compressCtor.ipdl11
-rw-r--r--ipc/ipdl/test/ipdl/error/compressCtorManagee.ipdl11
-rw-r--r--ipc/ipdl/test/ipdl/error/conflictProtocolMsg.ipdl9
-rw-r--r--ipc/ipdl/test/ipdl/error/cyclecheck_Child.ipdl15
-rw-r--r--ipc/ipdl/test/ipdl/error/cyclecheck_Grandchild.ipdl15
-rw-r--r--ipc/ipdl/test/ipdl/error/cyclecheck_Parent.ipdl12
-rw-r--r--ipc/ipdl/test/ipdl/error/dtorReserved.ipdl10
-rw-r--r--ipc/ipdl/test/ipdl/error/empty.ipdl1
-rw-r--r--ipc/ipdl/test/ipdl/error/extra/PDouble.ipdl5
-rw-r--r--ipc/ipdl/test/ipdl/error/inconsistentRC.ipdl9
-rw-r--r--ipc/ipdl/test/ipdl/error/intrMessageCompress.ipdl9
-rw-r--r--ipc/ipdl/test/ipdl/error/lex1.ipdl2
-rw-r--r--ipc/ipdl/test/ipdl/error/manageSelfToplevel.ipdl10
-rw-r--r--ipc/ipdl/test/ipdl/error/managedNoDtor.ipdl7
-rw-r--r--ipc/ipdl/test/ipdl/error/managerNoDtor.ipdl11
-rw-r--r--ipc/ipdl/test/ipdl/error/maybe_Recursive.ipdl5
-rw-r--r--ipc/ipdl/test/ipdl/error/maybe_SelfRecStruct.ipdl9
-rw-r--r--ipc/ipdl/test/ipdl/error/maybe_SelfRecUnion.ipdl7
-rw-r--r--ipc/ipdl/test/ipdl/error/messageNoDirection.ipdl5
-rw-r--r--ipc/ipdl/test/ipdl/error/multimanDupMgrs.ipdl10
-rw-r--r--ipc/ipdl/test/ipdl/error/multimanDupMgrsMgr.ipdl11
-rw-r--r--ipc/ipdl/test/ipdl/error/multimanNonexistentMgrs.ipdl10
-rw-r--r--ipc/ipdl/test/ipdl/error/mutualRecStruct.ipdl24
-rw-r--r--ipc/ipdl/test/ipdl/error/mutualRecStructUnion.ipdl24
-rw-r--r--ipc/ipdl/test/ipdl/error/noEmptyToplevel.ipdl7
-rw-r--r--ipc/ipdl/test/ipdl/error/noProtocolInHeader.ipdlh6
-rw-r--r--ipc/ipdl/test/ipdl/error/oldIncludeSyntax.ipdl7
-rw-r--r--ipc/ipdl/test/ipdl/error/parser.ipdl3
-rw-r--r--ipc/ipdl/test/ipdl/error/redeclMessage.ipdl12
-rw-r--r--ipc/ipdl/test/ipdl/error/redeclParamReturn.ipdl9
-rw-r--r--ipc/ipdl/test/ipdl/error/redeclScope.ipdlh12
-rw-r--r--ipc/ipdl/test/ipdl/error/shmem.ipdl8
-rw-r--r--ipc/ipdl/test/ipdl/error/structRedecl.ipdl10
-rw-r--r--ipc/ipdl/test/ipdl/error/structUnknownField.ipdl9
-rw-r--r--ipc/ipdl/test/ipdl/error/syncMessageCompress.ipdl6
-rw-r--r--ipc/ipdl/test/ipdl/error/syncParentToChild.ipdl8
-rw-r--r--ipc/ipdl/test/ipdl/error/tooWeakIntrAsync.ipdl9
-rw-r--r--ipc/ipdl/test/ipdl/error/tooWeakIntrSync.ipdl8
-rw-r--r--ipc/ipdl/test/ipdl/error/tooWeakSyncAsync.ipdl9
-rw-r--r--ipc/ipdl/test/ipdl/error/twoprotocols.ipdl11
-rw-r--r--ipc/ipdl/test/ipdl/error/undeclParamType.ipdl7
-rw-r--r--ipc/ipdl/test/ipdl/error/undeclProtocol.ipdl8
-rw-r--r--ipc/ipdl/test/ipdl/error/undeclReturnType.ipdl7
-rw-r--r--ipc/ipdl/test/ipdl/error/undefMutualRecStruct.ipdl11
-rw-r--r--ipc/ipdl/test/ipdl/error/undefMutualRecStructUnion.ipdl11
-rw-r--r--ipc/ipdl/test/ipdl/error/undefMutualRecUnion.ipdl11
-rw-r--r--ipc/ipdl/test/ipdl/error/undefSelfRecStruct.ipdl7
-rw-r--r--ipc/ipdl/test/ipdl/error/undefSelfRecUnion.ipdl7
-rw-r--r--ipc/ipdl/test/ipdl/error/unknownIntrMessage.ipdl6
-rw-r--r--ipc/ipdl/test/ipdl/error/unknownSyncMessage.ipdl6
-rw-r--r--ipc/ipdl/test/ipdl/message-metadata.ini2
-rw-r--r--ipc/ipdl/test/ipdl/moz.build5
-rw-r--r--ipc/ipdl/test/ipdl/ok/MutRecHeader1.ipdlh34
-rw-r--r--ipc/ipdl/test/ipdl/ok/MutRecHeader2.ipdlh8
-rw-r--r--ipc/ipdl/test/ipdl/ok/MutRecHeader3.ipdlh8
-rw-r--r--ipc/ipdl/test/ipdl/ok/PAsyncReturn.ipdl8
-rw-r--r--ipc/ipdl/test/ipdl/ok/PDelete.ipdl8
-rw-r--r--ipc/ipdl/test/ipdl/ok/PDeleteSub.ipdl10
-rw-r--r--ipc/ipdl/test/ipdl/ok/PEndpointDecl.ipdl18
-rw-r--r--ipc/ipdl/test/ipdl/ok/PEndpointUse.ipdl9
-rw-r--r--ipc/ipdl/test/ipdl/ok/PManagedEndpointDecl.ipdl23
-rw-r--r--ipc/ipdl/test/ipdl/ok/PManagedEndpointManager.ipdl14
-rw-r--r--ipc/ipdl/test/ipdl/ok/PMessageTainted.ipdl4
-rw-r--r--ipc/ipdl/test/ipdl/ok/PNullable.ipdl11
-rw-r--r--ipc/ipdl/test/ipdl/ok/PRefcounted.ipdl4
-rw-r--r--ipc/ipdl/test/ipdl/ok/PStruct.ipdl10
-rw-r--r--ipc/ipdl/test/ipdl/ok/PStructComparable.ipdl10
-rw-r--r--ipc/ipdl/test/ipdl/ok/PSyncSyncManagee.ipdl7
-rw-r--r--ipc/ipdl/test/ipdl/ok/PSyncSyncManager.ipdl11
-rw-r--r--ipc/ipdl/test/ipdl/ok/PUniquePtrBasic.ipdl4
-rw-r--r--ipc/ipdl/test/ipdl/ok/PUniquePtrOfActors.ipdl10
-rw-r--r--ipc/ipdl/test/ipdl/ok/PUniquePtrOfActorsSub.ipdl7
-rw-r--r--ipc/ipdl/test/ipdl/ok/PUniquePtrRecUnion.ipdl21
-rw-r--r--ipc/ipdl/test/ipdl/ok/PUniquePtrUnion.ipdl10
-rw-r--r--ipc/ipdl/test/ipdl/ok/Pactorparam.ipdl5
-rw-r--r--ipc/ipdl/test/ipdl/ok/Pactorreturn.ipdl6
-rw-r--r--ipc/ipdl/test/ipdl/ok/Parray_Basic.ipdl4
-rw-r--r--ipc/ipdl/test/ipdl/ok/Parray_OfActors.ipdl10
-rw-r--r--ipc/ipdl/test/ipdl/ok/Parray_OfActorsSub.ipdl7
-rw-r--r--ipc/ipdl/test/ipdl/ok/Parray_Union.ipdl10
-rw-r--r--ipc/ipdl/test/ipdl/ok/PbasicUsing.ipdl36
-rw-r--r--ipc/ipdl/test/ipdl/ok/Pbuiltins.ipdl21
-rw-r--r--ipc/ipdl/test/ipdl/ok/Pbytebuf.ipdl13
-rw-r--r--ipc/ipdl/test/ipdl/ok/Pempty.ipdl3
-rw-r--r--ipc/ipdl/test/ipdl/ok/PemptyStruct.ipdl3
-rw-r--r--ipc/ipdl/test/ipdl/ok/PheaderProto.ipdl10
-rw-r--r--ipc/ipdl/test/ipdl/ok/PintrProtocol.ipdl13
-rw-r--r--ipc/ipdl/test/ipdl/ok/Pjetpack.ipdl5
-rw-r--r--ipc/ipdl/test/ipdl/ok/PmanageSelf.ipdl10
-rw-r--r--ipc/ipdl/test/ipdl/ok/PmanageSelf_Toplevel.ipdl9
-rw-r--r--ipc/ipdl/test/ipdl/ok/PmanagedProtocol.ipdl8
-rw-r--r--ipc/ipdl/test/ipdl/ok/PmanagerProtocol.ipdl11
-rw-r--r--ipc/ipdl/test/ipdl/ok/Pmaybe_Basic.ipdl4
-rw-r--r--ipc/ipdl/test/ipdl/ok/Pmaybe_OfActors.ipdl10
-rw-r--r--ipc/ipdl/test/ipdl/ok/Pmaybe_OfActorsSub.ipdl7
-rw-r--r--ipc/ipdl/test/ipdl/ok/Pmaybe_Union.ipdl10
-rw-r--r--ipc/ipdl/test/ipdl/ok/Pmedia.ipdl5
-rw-r--r--ipc/ipdl/test/ipdl/ok/PmessageCompress.ipdl4
-rw-r--r--ipc/ipdl/test/ipdl/ok/PmessageVerify.ipdl14
-rw-r--r--ipc/ipdl/test/ipdl/ok/PmessageVerifyTopLevel.ipdl7
-rw-r--r--ipc/ipdl/test/ipdl/ok/PmultiManaged.ipdl9
-rw-r--r--ipc/ipdl/test/ipdl/ok/PmultiManager1.ipdl8
-rw-r--r--ipc/ipdl/test/ipdl/ok/PmultiManager2.ipdl8
-rw-r--r--ipc/ipdl/test/ipdl/ok/PmultipleUsingCxxTypes.ipdl7
-rw-r--r--ipc/ipdl/test/ipdl/ok/PmutualRecStructUnion.ipdl21
-rw-r--r--ipc/ipdl/test/ipdl/ok/PmutualRecUnion.ipdl21
-rw-r--r--ipc/ipdl/test/ipdl/ok/Pnamespace_Basic.ipdl12
-rw-r--r--ipc/ipdl/test/ipdl/ok/PnoRedeclCrossMessage.ipdl9
-rw-r--r--ipc/ipdl/test/ipdl/ok/Pplugin.ipdl5
-rw-r--r--ipc/ipdl/test/ipdl/ok/Prio.ipdl8
-rw-r--r--ipc/ipdl/test/ipdl/ok/PselfRecUnion.ipdl11
-rw-r--r--ipc/ipdl/test/ipdl/ok/Pshmem.ipdl13
-rw-r--r--ipc/ipdl/test/ipdl/ok/PsyncProtocol.ipdl11
-rw-r--r--ipc/ipdl/test/ipdl/ok/PthreeDirections.ipdl13
-rw-r--r--ipc/ipdl/test/ipdl/ok/Punion_Basic.ipdl11
-rw-r--r--ipc/ipdl/test/ipdl/ok/Punion_Comparable.ipdl11
-rw-r--r--ipc/ipdl/test/ipdl/ok/Punion_Namespaced.ipdl18
-rw-r--r--ipc/ipdl/test/ipdl/ok/header.ipdlh15
-rw-r--r--ipc/ipdl/test/ipdl/runtests.py116
-rw-r--r--ipc/ipdl/test/ipdl/sync-messages.ini52
-rw-r--r--ipc/ipdl/test/moz.build12
329 files changed, 26030 insertions, 0 deletions
diff --git a/ipc/ipdl/Makefile.in b/ipc/ipdl/Makefile.in
new file mode 100644
index 0000000000..7a4d2acfba
--- /dev/null
+++ b/ipc/ipdl/Makefile.in
@@ -0,0 +1,33 @@
+# 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/.
+
+ifdef COMPILE_ENVIRONMENT
+
+# This file is generated by the moz.build backend.
+include ipdlsrcs.mk
+
+include $(topsrcdir)/config/rules.mk
+
+ipdl_py_deps := \
+ $(wildcard $(srcdir)/*.py) \
+ $(wildcard $(srcdir)/ipdl/*.py) \
+ $(wildcard $(srcdir)/ipdl/cxx/*.py) \
+ $(wildcard $(topsrcdir)/other-licenses/ply/ply/*.py) \
+ $(NULL)
+
+# NB: the IPDL compiler manages .ipdl-->.h/.cpp dependencies itself,
+# which is why we don't have explicit .h/.cpp targets here
+ipdl.track: $(ALL_IPDLSRCS) $(srcdir)/sync-messages.ini $(srcdir)/message-metadata.ini $(ipdl_py_deps)
+ $(PYTHON3) $(srcdir)/ipdl.py \
+ --sync-msg-list=$(srcdir)/sync-messages.ini \
+ --msg-metadata=$(srcdir)/message-metadata.ini \
+ --outheaders-dir=_ipdlheaders \
+ --outcpp-dir=. \
+ $(IPDLDIRS:%=-I%) \
+ $(ALL_IPDLSRCS)
+ touch $@
+
+export:: ipdl.track
+endif
+
diff --git a/ipc/ipdl/ipdl.py b/ipc/ipdl/ipdl.py
new file mode 100644
index 0000000000..e41691a742
--- /dev/null
+++ b/ipc/ipdl/ipdl.py
@@ -0,0 +1,318 @@
+# 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/.
+from __future__ import print_function
+
+from io import StringIO
+import optparse
+import os
+import sys
+from configparser import RawConfigParser
+
+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(
+ "-s",
+ "--sync-msg-list",
+ dest="syncMsgList",
+ default="sync-messages.ini",
+ help="Config file listing allowed sync messages",
+)
+op.add_option(
+ "-m",
+ "--msg-metadata",
+ dest="msgMetadata",
+ default="message-metadata.ini",
+ help="Predicted message sizes for reducing serialization malloc overhead.",
+)
+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
+syncMsgList = options.syncMsgList
+msgMetadata = options.msgMetadata
+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")
+
+log(2, 'Generated C++ headers will be generated relative to "%s"', headersdir)
+log(2, 'Generated C++ sources will be generated in "%s"', cppdir)
+
+allmessages = {}
+allmessageprognames = []
+allprotocols = []
+
+
+def normalizedFilename(f):
+ if f == "-":
+ return "<stdin>"
+ return f
+
+
+log(2, "Reading sync message list")
+parser = RawConfigParser()
+parser.read_file(open(options.syncMsgList))
+syncMsgList = parser.sections()
+
+for section in syncMsgList:
+ if not parser.get(section, "description"):
+ print("Error: Sync message %s lacks a description" % section, file=sys.stderr)
+ sys.exit(1)
+
+# Read message metadata. Right now we only have 'segment_capacity'
+# for the standard segment size used for serialization.
+log(2, "Reading message metadata...")
+msgMetadataConfig = RawConfigParser()
+msgMetadataConfig.read_file(open(options.msgMetadata))
+
+segmentCapacityDict = {}
+for msgName in msgMetadataConfig.sections():
+ if msgMetadataConfig.has_option(msgName, "segment_capacity"):
+ capacity = msgMetadataConfig.get(msgName, "segment_capacity")
+ segmentCapacityDict[msgName] = capacity
+
+# 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("Specification could not be parsed.", file=sys.stderr)
+ sys.exit(1)
+
+ log(2, "checking types")
+ if not ipdl.typecheck(ast):
+ print("Specification is not well typed.", file=sys.stderr)
+ sys.exit(1)
+
+ if not ipdl.checkSyncMessage(ast, syncMsgList):
+ print(
+ "Error: New sync IPC messages must be reviewed by an IPC peer and recorded in %s"
+ % options.syncMsgList,
+ file=sys.stderr,
+ ) # NOQA: E501
+ sys.exit(1)
+
+if not ipdl.checkFixedSyncMessages(parser):
+ # Errors have alraedy been printed to stderr, just exit
+ sys.exit(1)
+
+# 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, segmentCapacityDict)
+
+ if ast.protocol:
+ allmessages[ast.protocol.name] = ipdl.genmsgenum(ast)
+ allprotocols.append(ast.protocol.name)
+ # e.g. PContent::RequestMemoryReport (not prefixed or suffixed.)
+ for md in ast.protocol.messageDecls:
+ allmessageprognames.append("%s::%s" % (md.namespace, md.decl.progname))
+
+allprotocols.sort()
+
+# Check if we have undefined message names in segmentCapacityDict.
+# This is a fool-proof of the 'message-metadata.ini' file.
+undefinedMessages = set(segmentCapacityDict.keys()) - set(allmessageprognames)
+if len(undefinedMessages) > 0:
+ print("Error: Undefined message names in message-metadata.ini:", file=sys.stderr)
+ print(undefinedMessages, file=sys.stderr)
+ sys.exit(1)
+
+ipcmsgstart = StringIO()
+
+print(
+ """
+// CODE GENERATED by ipdl.py. Do not edit.
+
+#ifndef IPCMessageStart_h
+#define IPCMessageStart_h
+
+enum IPCMessageStart {
+""",
+ file=ipcmsgstart,
+)
+
+for name in allprotocols:
+ print(" %sMsgStart," % name, file=ipcmsgstart)
+
+print(
+ """
+ LastMsgIndex
+};
+
+static_assert(LastMsgIndex <= 65536, "need to update IPC_MESSAGE_MACRO");
+
+#endif // ifndef IPCMessageStart_h
+""",
+ file=ipcmsgstart,
+)
+
+ipc_msgtype_name = StringIO()
+print(
+ """
+// CODE GENERATED by ipdl.py. Do not edit.
+#include <cstdint>
+
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "IPCMessageStart.h"
+
+using std::uint32_t;
+
+namespace {
+
+enum IPCMessages {
+""",
+ file=ipc_msgtype_name,
+)
+
+for protocol in sorted(allmessages.keys()):
+ for (msg, num) in allmessages[protocol].idnums:
+ if num:
+ print(" %s = %s," % (msg, num), file=ipc_msgtype_name)
+ elif not msg.endswith("End"):
+ print(" %s__%s," % (protocol, msg), file=ipc_msgtype_name)
+
+print(
+ """
+};
+
+} // anonymous namespace
+
+namespace IPC {
+
+const char* StringFromIPCMessageType(uint32_t aMessageType)
+{
+ switch (aMessageType) {
+""",
+ file=ipc_msgtype_name,
+)
+
+for protocol in sorted(allmessages.keys()):
+ for (msg, num) in allmessages[protocol].idnums:
+ if num or msg.endswith("End"):
+ continue
+ print(
+ """
+ case %s__%s:
+ return "%s::%s";"""
+ % (protocol, msg, protocol, msg),
+ file=ipc_msgtype_name,
+ )
+
+print(
+ """
+ case CHANNEL_OPENED_MESSAGE_TYPE:
+ return "CHANNEL_OPENED_MESSAGE";
+ case SHMEM_DESTROYED_MESSAGE_TYPE:
+ return "SHMEM_DESTROYED_MESSAGE";
+ case SHMEM_CREATED_MESSAGE_TYPE:
+ return "SHMEM_CREATED_MESSAGE";
+ case GOODBYE_MESSAGE_TYPE:
+ return "GOODBYE_MESSAGE";
+ case CANCEL_MESSAGE_TYPE:
+ return "CANCEL_MESSAGE";
+ default:
+ return "<unknown IPC msg name>";
+ }
+}
+
+} // namespace IPC
+
+namespace mozilla {
+namespace ipc {
+
+const char* ProtocolIdToName(IPCMessageStart aId) {
+ switch (aId) {
+""",
+ file=ipc_msgtype_name,
+)
+
+for name in allprotocols:
+ print(" case %sMsgStart:" % name, file=ipc_msgtype_name)
+ print(' return "%s";' % name, file=ipc_msgtype_name)
+
+print(
+ """
+ default:
+ return "<unknown protocol id>";
+ }
+}
+
+} // namespace ipc
+} // namespace mozilla
+""",
+ file=ipc_msgtype_name,
+)
+
+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 0000000000..a85a12e14a
--- /dev/null
+++ b/ipc/ipdl/ipdl/__init__.py
@@ -0,0 +1,100 @@
+# 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/.
+
+from __future__ import print_function
+
+__all__ = [
+ "gencxx",
+ "genipdl",
+ "parse",
+ "typecheck",
+ "writeifmodified",
+ "checkSyncMessage",
+ "checkFixedSyncMessages",
+]
+
+import os
+import sys
+from io import StringIO
+
+from ipdl.cgen import IPDLCodeGen
+from ipdl.lower import LowerToCxx, msgenums
+from ipdl.parser import Parser, ParseError
+from ipdl.type import TypeCheck
+from ipdl.checker import checkSyncMessage, checkFixedSyncMessages
+
+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"
+
+ try:
+ return Parser(type, name).parse(
+ specstring, os.path.abspath(filename), includedirs
+ )
+ except ParseError as p:
+ print(p, file=errout)
+ return None
+
+
+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, segmentcapacitydict):
+ headers, cpps = LowerToCxx().lower(ast, segmentcapacitydict)
+
+ 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):
+ contents = contents.encode("utf-8")
+ 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 0000000000..cf37b39a76
--- /dev/null
+++ b/ipc/ipdl/ipdl/ast.py
@@ -0,0 +1,406 @@
+# 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/.
+
+from .util import hash_str
+
+
+NOT_NESTED = 1
+INSIDE_SYNC_NESTED = 2
+INSIDE_CPOW_NESTED = 3
+
+NORMAL_PRIORITY = 1
+INPUT_PRIORITY = 2
+HIGH_PRIORITY = 3
+MEDIUMHIGH_PRIORITY = 4
+
+
+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 mgr in p.managers:
+ mgr.accept(self)
+ for managed in p.managesStmts:
+ managed.accept(self)
+ for msgDecl in p.messageDecls:
+ msgDecl.accept(self)
+
+ def visitNamespace(self, ns):
+ 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 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,
+ refcounted=False,
+ moveonly=False,
+ ):
+ 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
+ self.refcounted = refcounted
+ self.moveonly = moveonly
+
+ def canBeForwardDeclared(self):
+ return self.isClass() or self.isStruct()
+
+ def isClass(self):
+ return self.kind == "class"
+
+ def isStruct(self):
+ return self.kind == "struct"
+
+ def isRefcounted(self):
+ return self.refcounted
+
+ def isMoveonly(self):
+ return self.moveonly
+
+
+# "singletons"
+
+
+class PrettyPrinted:
+ @classmethod
+ def __hash__(cls):
+ return hash_str(cls.pretty)
+
+ @classmethod
+ def __str__(cls):
+ return cls.pretty
+
+
+class ASYNC(PrettyPrinted):
+ pretty = "async"
+
+
+class TAINTED(PrettyPrinted):
+ pretty = "tainted"
+
+
+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.managers = []
+ self.managesStmts = []
+ self.messageDecls = []
+
+
+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, comparable):
+ NamespacedNode.__init__(self, loc, name)
+ self.fields = fields
+ self.comparable = comparable
+ # A list of indices into `fields` for determining the order in
+ # which fields are laid out in memory. We don't just reorder
+ # `fields` itself so as to keep the ordering reasonably stable
+ # for e.g. C++ constructors when new fields are added.
+ self.packed_field_ordering = []
+
+
+class UnionDecl(NamespacedNode):
+ def __init__(self, loc, name, components, comparable):
+ NamespacedNode.__init__(self, loc, name)
+ self.components = components
+ self.comparable = comparable
+
+
+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.tainted = ""
+ 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.startswith("tainted"):
+ self.tainted = modifier
+ elif modifier != "":
+ raise Exception("Unexpected message modifier `%s'" % modifier)
+
+
+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):
+ Node.__init__(self, loc)
+ self.spec = spec # QualifiedId
+ self.array = False # bool
+ self.maybe = False # bool
+ self.nullable = False # bool
+ self.uniqueptr = False # bool
+
+ def basename(self):
+ return self.spec.baseid
+
+ 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 0000000000..13ad77e61f
--- /dev/null
+++ b/ipc/ipdl/ipdl/builtin.py
@@ -0,0 +1,70 @@
+# 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",
+ # You may be tempted to add size_t. Do not! See bug 1525199.
+ # Mozilla types: "less" standard things we know how serialize/deserialize
+ "nsresult",
+ "nsString",
+ "nsCString",
+ "nsDependentSubstring",
+ "nsDependentCSubstring",
+ "mozilla::ipc::Shmem",
+ "mozilla::ipc::ByteBuf",
+ "mozilla::UniquePtr",
+ "mozilla::ipc::FileDescriptor",
+)
+
+
+# XXX(Bug 1677487) Can we restrict including ByteBuf.h, FileDescriptor.h,
+# MozPromise.h and Shmem.h to those protocols that really use them?
+HeaderIncludes = (
+ "mozilla/Attributes.h",
+ "IPCMessageStart.h",
+ "mozilla/RefPtr.h",
+ "nsString.h",
+ "nsTArray.h",
+ "nsTHashtable.h",
+ "mozilla/MozPromise.h",
+ "mozilla/OperatorNewExtensions.h",
+ "mozilla/UniquePtr.h",
+ "mozilla/ipc/ByteBuf.h",
+ "mozilla/ipc/FileDescriptor.h",
+ "mozilla/ipc/ProtocolUtilsFwd.h",
+ "mozilla/ipc/Shmem.h",
+)
+
+CppIncludes = (
+ "ipc/IPCMessageUtils.h",
+ "nsIFile.h",
+ "GeckoProfiler.h",
+ "mozilla/ipc/Endpoint.h",
+ "mozilla/ipc/ProtocolMessageUtils.h",
+ "mozilla/ipc/ProtocolUtils.h",
+ "mozilla/ipc/ShmemMessageUtils.h",
+ "mozilla/ipc/TaintingIPCUtils.h",
+)
diff --git a/ipc/ipdl/ipdl/cgen.py b/ipc/ipdl/ipdl/cgen.py
new file mode 100644
index 0000000000..8ed8da4d81
--- /dev/null
+++ b/ipc/ipdl/ipdl/cgen.py
@@ -0,0 +1,108 @@
+# 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.ast import Visitor
+
+
+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) # NOQA: F821
+
+ 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()
+
+ 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/checker.py b/ipc/ipdl/ipdl/checker.py
new file mode 100644
index 0000000000..eb969b4d60
--- /dev/null
+++ b/ipc/ipdl/ipdl/checker.py
@@ -0,0 +1,81 @@
+# 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/.
+
+from __future__ import print_function
+
+import sys
+from ipdl.ast import Visitor, ASYNC
+
+
+class SyncMessageChecker(Visitor):
+ syncMsgList = []
+ seenProtocols = []
+ seenSyncMessages = []
+
+ def __init__(self, syncMsgList):
+ SyncMessageChecker.syncMsgList = syncMsgList
+ self.errors = []
+
+ def prettyMsgName(self, msg):
+ return "%s::%s" % (self.currentProtocol, msg)
+
+ def errorUnknownSyncMessage(self, loc, msg):
+ self.errors.append("%s: error: Unknown sync IPC message %s" % (str(loc), msg))
+
+ def errorAsyncMessageCanRemove(self, loc, msg):
+ self.errors.append(
+ "%s: error: IPC message %s is async, can be delisted" % (str(loc), msg)
+ )
+
+ def visitProtocol(self, p):
+ self.errors = []
+ self.currentProtocol = p.name
+ SyncMessageChecker.seenProtocols.append(p.name)
+ Visitor.visitProtocol(self, p)
+
+ def visitMessageDecl(self, md):
+ pn = self.prettyMsgName(md.name)
+ if md.sendSemantics is not ASYNC:
+ if pn not in SyncMessageChecker.syncMsgList:
+ self.errorUnknownSyncMessage(md.loc, pn)
+ SyncMessageChecker.seenSyncMessages.append(pn)
+ elif pn in SyncMessageChecker.syncMsgList:
+ self.errorAsyncMessageCanRemove(md.loc, pn)
+
+ @staticmethod
+ def getFixedSyncMessages():
+ return set(SyncMessageChecker.syncMsgList) - set(
+ SyncMessageChecker.seenSyncMessages
+ )
+
+
+def checkSyncMessage(tu, syncMsgList, errout=sys.stderr):
+ checker = SyncMessageChecker(syncMsgList)
+ tu.accept(checker)
+ if len(checker.errors):
+ for error in checker.errors:
+ print(error, file=errout)
+ return False
+ return True
+
+
+def checkFixedSyncMessages(config, errout=sys.stderr):
+ fixed = SyncMessageChecker.getFixedSyncMessages()
+ error_free = True
+ for item in fixed:
+ protocol = item.split("::")[0]
+ # Ignore things like sync messages in test protocols we didn't compile.
+ # Also, ignore platform-specific IPC messages.
+ if (
+ protocol in SyncMessageChecker.seenProtocols
+ and "platform" not in config.options(item)
+ ):
+ print(
+ "Error: Sync IPC message %s not found, it appears to be fixed.\n"
+ "Please remove it from sync-messages.ini." % item,
+ file=errout,
+ )
+ error_free = False
+ return error_free
diff --git a/ipc/ipdl/ipdl/cxx/__init__.py b/ipc/ipdl/ipdl/cxx/__init__.py
new file mode 100644
index 0000000000..6fbe8159b2
--- /dev/null
+++ b/ipc/ipdl/ipdl/cxx/__init__.py
@@ -0,0 +1,3 @@
+# 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/ipdl/cxx/ast.py b/ipc/ipdl/ipdl/cxx/ast.py
new file mode 100644
index 0000000000..02f204f892
--- /dev/null
+++ b/ipc/ipdl/ipdl/cxx/ast.py
@@ -0,0 +1,1018 @@
+# 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
+import functools
+
+from ipdl.util import hash_str
+
+
+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 visitVerbatimNode(self, verb):
+ pass
+
+ def visitGroupNode(self, group):
+ for node in group.nodes:
+ node.accept(self)
+
+ 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 visitTypeFunction(self, fn):
+ 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 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 visitExprLambda(self, l):
+ self.visitBlock(l)
+
+ 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=False):
+ Node.__init__(self)
+ self.ws = ws
+ self.indent = indent
+
+
+Whitespace.NL = Whitespace("\n")
+
+
+class VerbatimNode(Node):
+ # A block of text to be written verbatim to the output file.
+ #
+ # NOTE: This node is usually created by `code`. See `code.py` for details.
+ # FIXME: Merge Whitespace and VerbatimNode? They're identical.
+ def __init__(self, text, indent=0):
+ Node.__init__(self)
+ self.text = text
+ self.indent = indent
+
+
+class GroupNode(Node):
+ # A group of nodes to be treated as a single node. These nodes have an
+ # optional indentation level which should be applied when generating them.
+ #
+ # NOTE: This node is usually created by `code`. See `code.py` for details.
+ def __init__(self, nodes, offset=0):
+ Node.__init__(self)
+ self.nodes = nodes
+ self.offset = offset
+
+
+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)
+
+ def addcode(self, tmpl, **context):
+ from ipdl.cxx.code import StmtCode
+
+ self.addstmt(StmtCode(tmpl, **context))
+
+
+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)
+
+ def addcode(self, tmpl, **context):
+ from ipdl.cxx.code import StmtCode
+
+ self.addstmt(StmtCode(tmpl, **context))
+
+
+# ------------------------------
+# 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=False,
+ ptr=False,
+ ptrptr=False,
+ ptrconstptr=False,
+ ref=False,
+ rvalref=False,
+ hasimplicitcopyctor=True,
+ T=None,
+ inner=None,
+ ):
+ """
+ Represents the type |name<T>::inner| with the ptr and const
+ modifiers as specified.
+
+ To avoid getting fancy with recursive types, we limit the kinds
+ of pointer types that can be be constructed.
+
+ ptr => T*
+ ptrptr => T**
+ ptrconstptr => T* const*
+ ref => T&
+ rvalref => T&&
+
+ Any type, naked or pointer, can be const (const T) or ref (T&)."""
+ assert isinstance(name, str)
+ assert isinstance(const, bool)
+ assert isinstance(ptr, bool)
+ assert isinstance(ptrptr, bool)
+ assert isinstance(ptrconstptr, bool)
+ assert isinstance(ref, bool)
+ assert isinstance(rvalref, bool)
+ assert not isinstance(T, str)
+
+ Node.__init__(self)
+ self.name = name
+ self.const = const
+ self.ptr = ptr
+ self.ptrptr = ptrptr
+ self.ptrconstptr = ptrconstptr
+ self.ref = ref
+ self.rvalref = rvalref
+ self.hasimplicitcopyctor = hasimplicitcopyctor
+ self.T = T
+ self.inner = inner
+ # 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,
+ ptrptr=self.ptrptr,
+ ptrconstptr=self.ptrconstptr,
+ ref=self.ref,
+ rvalref=self.rvalref,
+ T=copy.deepcopy(self.T, memo),
+ inner=copy.deepcopy(self.inner, 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=True)
+Type.SIZE = Type("size_t")
+Type.VOID = Type("void")
+Type.VOIDPTR = Type("void", ptr=True)
+Type.AUTO = Type("auto")
+Type.AUTORVAL = Type("auto", rvalref=True)
+
+
+class TypeArray(Node):
+ def __init__(self, basetype, nmemb):
+ """the type |basetype DECLNAME[nmemb]|. |nmemb| is an Expr"""
+ self.basetype = basetype
+ self.nmemb = 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 TypeFunction(Node):
+ def __init__(self, params=[], ret=Type("void")):
+ """Anonymous function type std::function<>"""
+ self.params = params
+ self.ret = ret
+
+
+@functools.total_ordering
+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 __lt__(self, other):
+ return self.totypename < other.totypename
+
+ def __eq__(self, other):
+ return self.__class__ == other.__class__ and self.totypename == other.totypename
+
+ def __hash__(self):
+ return hash_str(self.totypename)
+
+
+class Using(Node):
+ def __init__(self, type):
+ Node.__init__(self)
+ self.type = type
+
+
+class ForwardDecl(Node):
+ def __init__(self, pqname, cls=False, struct=False):
+ # Exactly one of cls and struct must be set
+ assert cls ^ 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=False,
+ abstract=False,
+ final=False,
+ specializes=None,
+ struct=False,
+ ):
+ 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
+
+
+# Python2 polyfill for Python3's Enum() functional API.
+
+
+def make_enum(name, members_str):
+ members_list = members_str.split()
+ members_dict = {}
+ for member_value, member in enumerate(members_list, start=1):
+ members_dict[member] = member_value
+ return type(name, (), members_dict)
+
+
+MethodSpec = make_enum("MethodSpec", "NONE VIRTUAL PURE OVERRIDE STATIC")
+
+
+class MethodDecl(Node):
+ def __init__(
+ self,
+ name,
+ params=[],
+ ret=Type("void"),
+ methodspec=MethodSpec.NONE,
+ const=False,
+ warn_unused=False,
+ force_inline=False,
+ typeop=None,
+ T=None,
+ cls=None,
+ ):
+ 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 isinstance(const, bool)
+ assert isinstance(warn_unused, bool)
+ assert isinstance(force_inline, bool)
+
+ if typeop is not None:
+ assert methodspec == MethodSpec.NONE
+ ret = None
+
+ Node.__init__(self)
+ self.name = name
+ self.params = params # [ Param ]
+ self.ret = ret # Type or None
+ self.methodspec = methodspec # enum
+ self.const = const # bool
+ self.warn_unused = warn_unused # bool
+ self.force_inline = force_inline or bool(T) # bool
+ self.typeop = typeop # Type or None
+ self.T = T # Type or None
+ self.cls = cls # Class 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),
+ methodspec=self.methodspec,
+ const=self.const,
+ warn_unused=self.warn_unused,
+ force_inline=self.force_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"),
+ methodspec=MethodSpec.NONE,
+ warn_unused=False,
+ force_inline=False,
+ T=None,
+ ):
+ assert methodspec == MethodSpec.NONE or methodspec == MethodSpec.STATIC
+ MethodDecl.__init__(
+ self,
+ name,
+ params=params,
+ ret=ret,
+ methodspec=methodspec,
+ warn_unused=warn_unused,
+ 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=False, force_inline=False):
+ 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, methodspec=MethodSpec.NONE, force_inline=False):
+ # C++ allows pure or override destructors, but ipdl cgen does not.
+ assert methodspec == MethodSpec.NONE or methodspec == MethodSpec.VIRTUAL
+ MethodDecl.__init__(
+ self,
+ name,
+ params=[],
+ ret=None,
+ methodspec=methodspec,
+ force_inline=force_inline,
+ )
+
+ def __deepcopy__(self, memo):
+ return DestructorDecl(
+ self.name, methodspec=self.methodspec, force_inline=self.force_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")
+
+ 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, static=False, const=False):
+ # Exactly one of these should be set
+ assert static ^ const
+
+ Node.__init__(self)
+ self.expr = expr
+ self.type = type
+ self.static = static
+ self.const = const
+
+
+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 ExprSelect(Node):
+ def __init__(self, obj, op, field):
+ assert obj and op and field
+ assert not isinstance(obj, str)
+ assert isinstance(op, str)
+
+ Node.__init__(self)
+ self.obj = obj
+ self.op = op
+ if isinstance(field, str):
+ self.field = ExprVar(field)
+ else:
+ 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("std::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 or ctype.rvalref)
+
+ 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 ExprLambda(Block):
+ def __init__(self, captures=[], params=[], ret=None):
+ Block.__init__(self)
+ assert isinstance(captures, list)
+ assert isinstance(params, list)
+ self.captures = captures
+ self.params = params
+ self.ret = ret
+
+
+# ------------------------------
+# 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 isinstance(block, GroupNode)
+ or isinstance(block, VerbatimNode)
+ or (
+ hasattr(block, "stmts")
+ and (
+ isinstance(block.stmts[-1], StmtBreak)
+ or isinstance(block.stmts[-1], StmtReturn)
+ or isinstance(block.stmts[-1], GroupNode)
+ or isinstance(block.stmts[-1], VerbatimNode)
+ )
+ )
+ )
+ self.addstmt(case)
+ self.addstmt(block)
+ 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 0000000000..2fa74e8d35
--- /dev/null
+++ b/ipc/ipdl/ipdl/cxx/cgen.py
@@ -0,0 +1,558 @@
+# 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 MethodSpec, TypeArray, Visitor, DestructorDecl
+
+
+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 visitVerbatimNode(self, verb):
+ if verb.indent:
+ self.printdent("")
+ self.write(verb.text)
+
+ def visitGroupNode(self, group):
+ offsetCols = self.indentCols * group.offset
+ self.col += offsetCols
+ for node in group.nodes:
+ node.accept(self)
+ self.col -= offsetCols
+
+ 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("<")
+ if type(t.T) is list:
+ t.T[0].accept(self)
+ for tt in t.T[1:]:
+ self.write(", ")
+ tt.accept(self)
+ else:
+ t.T.accept(self)
+ self.write(">")
+
+ if t.inner is not None:
+ self.write("::")
+ t.inner.accept(self)
+
+ ts = ""
+ if t.ptr:
+ ts += "*"
+ elif t.ptrptr:
+ ts += "**"
+ elif t.ptrconstptr:
+ ts += "* const*"
+
+ if t.ref:
+ ts += "&"
+ elif t.rvalref:
+ ts += "&&"
+
+ 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 visitTypeFunction(self, fn):
+ self.write("std::function<")
+ fn.ret.accept(self)
+ self.write("(")
+ self.writeDeclList(fn.params)
+ self.write(")>")
+
+ 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):
+ if md.T:
+ self.write("template<")
+ self.write("typename ")
+ md.T.accept(self)
+ self.println(">")
+ self.printdent()
+
+ if md.warn_unused:
+ self.write("[[nodiscard]] ")
+
+ if md.methodspec == MethodSpec.STATIC:
+ self.write("static ")
+ elif md.methodspec == MethodSpec.VIRTUAL or md.methodspec == MethodSpec.PURE:
+ 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.cls is not None:
+ assert md.only_for_definition
+
+ self.write(md.cls.name)
+ if md.cls.specializes is not None:
+ self.write("<")
+ md.cls.specializes.accept(self)
+ self.write(">")
+ self.write("::")
+
+ if md.typeop is not None:
+ self.write("operator ")
+ md.typeop.accept(self)
+ else:
+ if isinstance(md, DestructorDecl):
+ self.write("~")
+ 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.methodspec == MethodSpec.OVERRIDE:
+ self.write(" override")
+ elif md.methodspec == MethodSpec.PURE:
+ self.write(" = 0")
+
+ def visitMethodDefn(self, md):
+ # Method specifiers are for decls, not defns.
+ assert md.decl.methodspec == MethodSpec.NONE
+
+ 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):
+ self.visitMethodDecl(dd)
+
+ 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):
+ if c.static:
+ pfx, sfx = "static_cast<", ">"
+ else:
+ assert c.const
+ pfx, sfx = "const_cast<", ">"
+ 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 visitExprSelect(self, es):
+ self.write("(")
+ es.obj.accept(self)
+ self.write(")")
+ self.write(es.op)
+ es.field.accept(self)
+
+ 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 visitExprLambda(self, l):
+ self.write("[")
+ ncaptures = len(l.captures)
+ for i, c in enumerate(l.captures):
+ c.accept(self)
+ if i != (ncaptures - 1):
+ self.write(", ")
+ self.write("](")
+ self.writeDeclList(l.params)
+ self.write(")")
+ if l.ret:
+ self.write(" -> ")
+ l.ret.accept(self)
+ self.println(" {")
+ self.indent()
+ self.visitBlock(l)
+ self.dedent()
+ self.printdent("}")
+
+ 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/cxx/code.py b/ipc/ipdl/ipdl/cxx/code.py
new file mode 100644
index 0000000000..0b5019b623
--- /dev/null
+++ b/ipc/ipdl/ipdl/cxx/code.py
@@ -0,0 +1,187 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# This module contains functionality for adding formatted, opaque "code" blocks
+# into the IPDL ast. These code objects follow IPDL C++ ast patterns, and
+# perform lowering in much the same way.
+
+# In general it is recommended to use these for blocks of code which would
+# otherwise be specified by building a hardcoded IPDL-C++ AST, as users of this
+# API are often easier to read than users of the AST APIs in these cases.
+
+import re
+import math
+import textwrap
+
+from ipdl.cxx.ast import Node, Whitespace, GroupNode, VerbatimNode
+
+
+# -----------------------------------------------------------------------------
+# Public API.
+
+
+def StmtCode(tmpl, **kwargs):
+ """Perform template substitution to build opaque C++ AST nodes. See the
+ module documentation for more information on the templating syntax.
+
+ StmtCode nodes should be used where Stmt* nodes are used. They are placed
+ on their own line and indented."""
+ return _code(tmpl, False, kwargs)
+
+
+def ExprCode(tmpl, **kwargs):
+ """Perform template substitution to build opaque C++ AST nodes. See the
+ module documentation for more information on the templating syntax.
+
+ ExprCode nodes should be used where Expr* nodes are used. They are placed
+ inline, and no trailing newline is added."""
+ return _code(tmpl, True, kwargs)
+
+
+def StmtVerbatim(text):
+ """Build an opaque C++ AST node which emits input text verbatim.
+
+ StmtVerbatim nodes should be used where Stmt* nodes are used. They are placed
+ on their own line and indented."""
+ return _verbatim(text, False)
+
+
+def ExprVerbatim(text):
+ """Build an opaque C++ AST node which emits input text verbatim.
+
+ ExprVerbatim nodes should be used where Expr* nodes are used. They are
+ placed inline, and no trailing newline is added."""
+ return _verbatim(text, True)
+
+
+# -----------------------------------------------------------------------------
+# Implementation
+
+
+def _code(tmpl, inline, context):
+ # Remove common indentation, and strip the preceding newline from
+ # '''-quoting, because we usually don't want it.
+ if tmpl.startswith("\n"):
+ tmpl = tmpl[1:]
+ tmpl = textwrap.dedent(tmpl)
+
+ # Process each line in turn, building up a list of nodes.
+ nodes = []
+ for idx, line in enumerate(tmpl.splitlines()):
+ # Place newline tokens between lines in the input.
+ if idx > 0:
+ nodes.append(Whitespace.NL)
+
+ # Don't indent the first line if `inline` is set.
+ skip_indent = inline and idx == 0
+ nodes.append(_line(line.rstrip(), skip_indent, idx + 1, context))
+
+ # If we're inline, don't add the final trailing newline.
+ if not inline:
+ nodes.append(Whitespace.NL)
+ return GroupNode(nodes)
+
+
+def _verbatim(text, inline):
+ # For simplicitly, _verbatim is implemented using the same logic as _code,
+ # but with '$' characters escaped. This ensures we only need to worry about
+ # a single, albeit complex, codepath.
+ return _code(text.replace("$", "$$"), inline, {})
+
+
+# Pattern used to identify substitutions.
+_substPat = re.compile(
+ r"""
+ \$(?:
+ (?P<escaped>\$) | # '$$' is an escaped '$'
+ (?P<list>[*,])?{(?P<expr>[^}]+)} | # ${expr}, $*{expr}, or $,{expr}
+ (?P<invalid>) # For error reporting
+ )
+ """,
+ re.IGNORECASE | re.VERBOSE,
+)
+
+
+def _line(raw, skip_indent, lineno, context):
+ assert "\n" not in raw
+
+ # Determine the level of indentation used for this line
+ line = raw.lstrip()
+ offset = int(math.ceil((len(raw) - len(line)) / 4))
+
+ # If line starts with a directive, don't indent it.
+ if line.startswith("#"):
+ skip_indent = True
+
+ column = 0
+ children = []
+ for match in _substPat.finditer(line):
+ if match.group("invalid") is not None:
+ raise ValueError("Invalid substitution on line %d" % lineno)
+
+ # Any text from before the current entry should be written, and column
+ # advanced.
+ if match.start() > column:
+ before = line[column : match.start()]
+ children.append(VerbatimNode(before))
+ column = match.end()
+
+ # If we have an escaped group, emit a '$' node.
+ if match.group("escaped") is not None:
+ children.append(VerbatimNode("$"))
+ continue
+
+ # At this point we should have an expression.
+ list_chr = match.group("list")
+ expr = match.group("expr")
+ assert expr is not None
+
+ # Evaluate our expression in the context to get the values.
+ try:
+ values = eval(expr, context, {})
+ except Exception as e:
+ msg = "%s in substitution on line %d" % (repr(e), lineno)
+ raise ValueError(msg) from e
+
+ # If we aren't dealing with lists, wrap the result into a
+ # single-element list.
+ if list_chr is None:
+ values = [values]
+
+ # Check if this substitution is inline, or the entire line.
+ inline = match.span() != (0, len(line))
+
+ for idx, value in enumerate(values):
+ # If we're using ',' as list mode, put a comma between each node.
+ if idx > 0 and list_chr == ",":
+ children.append(VerbatimNode(", "))
+
+ # If our value isn't a node, turn it into one. Verbatim should be
+ # inline unless indent isn't being skipped, and the match isn't
+ # inline.
+ if not isinstance(value, Node):
+ value = _verbatim(str(value), skip_indent or inline)
+ children.append(value)
+
+ # If we were the entire line, indentation is handled by the added child
+ # nodes. Do this after the above loop such that created verbatims have
+ # the correct inline-ness.
+ if not inline:
+ skip_indent = True
+
+ # Add any remaining text in the line.
+ if len(line) > column:
+ children.append(VerbatimNode(line[column:]))
+
+ # If we have no children, just emit the empty string. This will become a
+ # blank line.
+ if len(children) == 0:
+ return VerbatimNode("")
+
+ # Add the initial indent if we aren't skipping it.
+ if not skip_indent:
+ children.insert(0, VerbatimNode("", indent=True))
+
+ # Wrap ourselves into a group node with the correct indent offset
+ return GroupNode(children, offset=offset)
diff --git a/ipc/ipdl/ipdl/direct_call.py b/ipc/ipdl/ipdl/direct_call.py
new file mode 100644
index 0000000000..42d89df1b0
--- /dev/null
+++ b/ipc/ipdl/ipdl/direct_call.py
@@ -0,0 +1,763 @@
+# 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/.
+
+
+# Our long term goal is to burn this list down, so new entries should be added
+# extremely sparingly and only with a very good reason! You must have an IPC
+# peer's r+ to add something new!
+
+# {(Protocol, side): (Class, HeaderFile)}
+DIRECT_CALL_OVERRIDES = {
+ ("PAPZ", "parent"): (
+ "RemoteContentController",
+ "mozilla/layers/RemoteContentController.h",
+ ),
+ ("PBackgroundMutableFile", "parent"): (
+ "BackgroundMutableFileParentBase",
+ "mozilla/dom/filehandle/ActorsParent.h",
+ ),
+ ("PCanvas", "parent"): ("CanvasTranslator", "mozilla/layers/CanvasTranslator.h"),
+ ("PChromiumCDM", "parent"): ("ChromiumCDMParent", "ChromiumCDMParent.h"),
+ ("PCompositorBridge", "parent"): (
+ "CompositorBridgeParentBase",
+ "mozilla/layers/CompositorBridgeParent.h",
+ ),
+ ("PContentPermissionRequest", "child"): (
+ "RemotePermissionRequest",
+ "nsContentPermissionHelper.h",
+ ),
+ ("PFileSystemRequest", "child"): (
+ "FileSystemTaskChildBase",
+ "mozilla/dom/FileSystemTaskBase.h",
+ ),
+ ("PGMP", "child"): ("GMPChild", "GMPChild.h"),
+ ("PGMP", "parent"): ("GMPParent", "GMPParent.h"),
+ ("PGMPContent", "child"): ("GMPContentChild", "GMPContentChild.h"),
+ ("PGMPStorage", "child"): ("GMPStorageChild", "GMPStorageChild.h"),
+ ("PGMPTimer", "child"): ("GMPTimerChild", "GMPTimerChild.h"),
+ ("PGMPTimer", "parent"): ("GMPTimerParent", "GMPTimerParent.h"),
+ ("PGMPVideoEncoder", "child"): ("GMPVideoEncoderChild", "GMPVideoEncoderChild.h"),
+ ("PGMPVideoDecoder", "child"): ("GMPVideoDecoderChild", "GMPVideoDecoderChild.h"),
+ ("PLoginReputation", "parent"): (
+ "LoginReputationParent",
+ "mozilla/LoginReputationIPC.h",
+ ),
+ ("PMedia", "child"): ("Child", "mozilla/media/MediaChild.h"),
+ ("PPresentationRequest", "child"): (
+ "PresentationRequestChild",
+ "mozilla/dom/PresentationChild.h",
+ ),
+ ("PPresentationRequest", "parent"): (
+ "PresentationRequestParent",
+ "mozilla/dom/PresentationParent.h",
+ ),
+ ("PRemoteSpellcheckEngine", "child"): (
+ "RemoteSpellcheckEngineChild",
+ "mozilla/RemoteSpellCheckEngineChild.h",
+ ),
+ ("PRemoteSpellcheckEngine", "parent"): (
+ "RemoteSpellcheckEngineParent",
+ "mozilla/RemoteSpellCheckEngineParent.h",
+ ),
+ ("PScriptCache", "child"): (
+ "ScriptCacheChild",
+ "mozilla/loader/ScriptCacheActors.h",
+ ),
+ ("PScriptCache", "parent"): (
+ "ScriptCacheParent",
+ "mozilla/loader/ScriptCacheActors.h",
+ ),
+ ("PTCPServerSocket", "child"): (
+ "mozilla::dom::TCPServerSocketChild",
+ "mozilla/dom/network/TCPServerSocketChild.h",
+ ),
+ ("PTCPServerSocket", "parent"): (
+ "mozilla::dom::TCPServerSocketParent",
+ "mozilla/dom/network/TCPServerSocketParent.h",
+ ),
+ ("PTCPSocket", "child"): (
+ "mozilla::dom::TCPSocketChild",
+ "mozilla/dom/network/TCPSocketChild.h",
+ ),
+ ("PTCPSocket", "parent"): (
+ "mozilla::dom::TCPSocketParent",
+ "mozilla/dom/network/TCPSocketParent.h",
+ ),
+ ("PTestShellCommand", "parent"): (
+ "TestShellCommandParent",
+ "mozilla/ipc/TestShellParent.h",
+ ),
+ ("PTransportProvider", "child"): (
+ "TransportProviderChild",
+ "mozilla/net/IPCTransportProvider.h",
+ ),
+ ("PTransportProvider", "parent"): (
+ "TransportProviderParent",
+ "mozilla/net/IPCTransportProvider.h",
+ ),
+ ("PUDPSocket", "child"): (
+ "mozilla::dom::UDPSocketChild",
+ "mozilla/dom/network/UDPSocketChild.h",
+ ),
+ ("PUDPSocket", "parent"): (
+ "mozilla::dom::UDPSocketParent",
+ "mozilla/dom/network/UDPSocketParent.h",
+ ),
+ ("PURLClassifierLocal", "child"): (
+ "URLClassifierLocalChild",
+ "mozilla/dom/URLClassifierChild.h",
+ ),
+ ("PURLClassifierLocal", "parent"): (
+ "URLClassifierLocalParent",
+ "mozilla/dom/URLClassifierParent.h",
+ ),
+ ("PVR", "child"): ("VRChild", "VRChild.h"),
+ ("PVR", "parent"): ("VRParent", "VRParent.h"),
+ ("PVRGPU", "child"): ("VRGPUChild", "VRGPUChild.h"),
+ ("PVRGPU", "parent"): ("VRGPUParent", "VRGPUParent.h"),
+ ("PVRLayer", "child"): ("VRLayerChild", "VRLayerChild.h"),
+ ("PVRManager", "child"): ("VRManagerChild", "VRManagerChild.h"),
+ ("PVRManager", "parent"): ("VRManagerParent", "VRManagerParent.h"),
+ ("PWebSocket", "child"): (
+ "WebSocketChannelChild",
+ "mozilla/net/WebSocketChannelChild.h",
+ ),
+ ("PWebSocket", "parent"): (
+ "WebSocketChannelParent",
+ "mozilla/net/WebSocketChannelParent.h",
+ ),
+ # _ipdltest
+ ("PTestActorPunning", "child"): (
+ "TestActorPunningChild",
+ "mozilla/_ipdltest/TestActorPunning.h",
+ ),
+ ("PTestActorPunning", "parent"): (
+ "TestActorPunningParent",
+ "mozilla/_ipdltest/TestActorPunning.h",
+ ),
+ ("PTestActorPunningPunned", "child"): (
+ "TestActorPunningPunnedChild",
+ "mozilla/_ipdltest/TestActorPunning.h",
+ ),
+ ("PTestActorPunningPunned", "parent"): (
+ "TestActorPunningPunnedParent",
+ "mozilla/_ipdltest/TestActorPunning.h",
+ ),
+ ("PTestActorPunningSub", "child"): (
+ "TestActorPunningSubChild",
+ "mozilla/_ipdltest/TestActorPunning.h",
+ ),
+ ("PTestActorPunningSub", "parent"): (
+ "TestActorPunningSubParent",
+ "mozilla/_ipdltest/TestActorPunning.h",
+ ),
+ ("PTestAsyncReturns", "child"): (
+ "TestAsyncReturnsChild",
+ "mozilla/_ipdltest/TestAsyncReturns.h",
+ ),
+ ("PTestAsyncReturns", "parent"): (
+ "TestAsyncReturnsParent",
+ "mozilla/_ipdltest/TestAsyncReturns.h",
+ ),
+ ("PTestBadActor", "parent"): (
+ "TestBadActorParent",
+ "mozilla/_ipdltest/TestBadActor.h",
+ ),
+ ("PTestBadActor", "child"): (
+ "TestBadActorChild",
+ "mozilla/_ipdltest/TestBadActor.h",
+ ),
+ ("PTestBadActorSub", "child"): (
+ "TestBadActorSubChild",
+ "mozilla/_ipdltest/TestBadActor.h",
+ ),
+ ("PTestBadActorSub", "parent"): (
+ "TestBadActorSubParent",
+ "mozilla/_ipdltest/TestBadActor.h",
+ ),
+ ("PTestCancel", "child"): ("TestCancelChild", "mozilla/_ipdltest/TestCancel.h"),
+ ("PTestCancel", "parent"): ("TestCancelParent", "mozilla/_ipdltest/TestCancel.h"),
+ ("PTestCrashCleanup", "child"): (
+ "TestCrashCleanupChild",
+ "mozilla/_ipdltest/TestCrashCleanup.h",
+ ),
+ ("PTestCrashCleanup", "parent"): (
+ "TestCrashCleanupParent",
+ "mozilla/_ipdltest/TestCrashCleanup.h",
+ ),
+ ("PTestDataStructures", "child"): (
+ "TestDataStructuresChild",
+ "mozilla/_ipdltest/TestDataStructures.h",
+ ),
+ ("PTestDataStructures", "parent"): (
+ "TestDataStructuresParent",
+ "mozilla/_ipdltest/TestDataStructures.h",
+ ),
+ ("PTestDataStructuresSub", "child"): (
+ "TestDataStructuresSub",
+ "mozilla/_ipdltest/TestDataStructures.h",
+ ),
+ ("PTestDataStructuresSub", "parent"): (
+ "TestDataStructuresSub",
+ "mozilla/_ipdltest/TestDataStructures.h",
+ ),
+ ("PTestDemon", "child"): ("TestDemonChild", "mozilla/_ipdltest/TestDemon.h"),
+ ("PTestDemon", "parent"): ("TestDemonParent", "mozilla/_ipdltest/TestDemon.h"),
+ ("PTestDesc", "child"): ("TestDescChild", "mozilla/_ipdltest/TestDesc.h"),
+ ("PTestDesc", "parent"): ("TestDescParent", "mozilla/_ipdltest/TestDesc.h"),
+ ("PTestDescSub", "child"): ("TestDescSubChild", "mozilla/_ipdltest/TestDesc.h"),
+ ("PTestDescSub", "parent"): ("TestDescSubParent", "mozilla/_ipdltest/TestDesc.h"),
+ ("PTestDescSubsub", "child"): (
+ "TestDescSubsubChild",
+ "mozilla/_ipdltest/TestDesc.h",
+ ),
+ ("PTestDescSubsub", "parent"): (
+ "TestDescSubsubParent",
+ "mozilla/_ipdltest/TestDesc.h",
+ ),
+ ("PTestEndpointBridgeMain", "child"): (
+ "TestEndpointBridgeMainChild",
+ "mozilla/_ipdltest/TestEndpointBridgeMain.h",
+ ),
+ ("PTestEndpointBridgeMain", "parent"): (
+ "TestEndpointBridgeMainParent",
+ "mozilla/_ipdltest/TestEndpointBridgeMain.h",
+ ),
+ ("PTestEndpointBridgeMainSub", "child"): (
+ "TestEndpointBridgeMainSubChild",
+ "mozilla/_ipdltest/TestEndpointBridgeMain.h",
+ ),
+ ("PTestEndpointBridgeMainSub", "parent"): (
+ "TestEndpointBridgeMainSubParent",
+ "mozilla/_ipdltest/TestEndpointBridgeMain.h",
+ ),
+ ("PTestEndpointBridgeSub", "child"): (
+ "TestEndpointBridgeSubChild",
+ "mozilla/_ipdltest/TestEndpointBridgeMain.h",
+ ),
+ ("PTestEndpointBridgeSub", "parent"): (
+ "TestEndpointBridgeSubParent",
+ "mozilla/_ipdltest/TestEndpointBridgeMain.h",
+ ),
+ ("PTestEndpointOpens", "child"): (
+ "TestEndpointOpensChild",
+ "mozilla/_ipdltest/TestEndpointOpens.h",
+ ),
+ ("PTestEndpointOpens", "parent"): (
+ "TestEndpointOpensParent",
+ "mozilla/_ipdltest/TestEndpointOpens.h",
+ ),
+ ("PTestEndpointOpensOpened", "child"): (
+ "TestEndpointOpensOpenedChild",
+ "mozilla/_ipdltest/TestEndpointOpens.h",
+ ),
+ ("PTestEndpointOpensOpened", "parent"): (
+ "TestEndpointOpensOpenedParent",
+ "mozilla/_ipdltest/TestEndpointOpens.h",
+ ),
+ ("PTestFailedCtor", "child"): (
+ "TestFailedCtorChild",
+ "mozilla/_ipdltest/TestFailedCtor.h",
+ ),
+ ("PTestFailedCtor", "parent"): (
+ "TestFailedCtorParent",
+ "mozilla/_ipdltest/TestFailedCtor.h",
+ ),
+ ("PTestFailedCtorSub", "child"): (
+ "TestFailedCtorSubChild",
+ "mozilla/_ipdltest/TestFailedCtor.h",
+ ),
+ ("PTestFailedCtorSub", "parent"): (
+ "TestFailedCtorSubParent",
+ "mozilla/_ipdltest/TestFailedCtor.h",
+ ),
+ ("PTestFailedCtorSubsub", "child"): (
+ "TestFailedCtorSubsub",
+ "mozilla/_ipdltest/TestFailedCtor.h",
+ ),
+ ("PTestFailedCtorSubsub", "parent"): (
+ "TestFailedCtorSubsub",
+ "mozilla/_ipdltest/TestFailedCtor.h",
+ ),
+ ("PTestHandle", "child"): ("TestHandleChild", "mozilla/_ipdltest/TestJSON.h"),
+ ("PTestHandle", "parent"): ("TestHandleParent", "mozilla/_ipdltest/TestJSON.h"),
+ ("PTestJSON", "child"): ("TestJSONChild", "mozilla/_ipdltest/TestJSON.h"),
+ ("PTestJSON", "parent"): ("TestJSONParent", "mozilla/_ipdltest/TestJSON.h"),
+ ("PTestHangs", "child"): ("TestHangsChild", "mozilla/_ipdltest/TestHangs.h"),
+ ("PTestHangs", "parent"): ("TestHangsParent", "mozilla/_ipdltest/TestHangs.h"),
+ ("PTestHighestPrio", "child"): (
+ "TestHighestPrioChild",
+ "mozilla/_ipdltest/TestHighestPrio.h",
+ ),
+ ("PTestHighestPrio", "parent"): (
+ "TestHighestPrioParent",
+ "mozilla/_ipdltest/TestHighestPrio.h",
+ ),
+ ("PTestInterruptErrorCleanup", "child"): (
+ "TestInterruptErrorCleanupChild",
+ "mozilla/_ipdltest/TestInterruptErrorCleanup.h",
+ ),
+ ("PTestInterruptErrorCleanup", "parent"): (
+ "TestInterruptErrorCleanupParent",
+ "mozilla/_ipdltest/TestInterruptErrorCleanup.h",
+ ),
+ ("PTestInterruptRaces", "child"): (
+ "TestInterruptRacesChild",
+ "mozilla/_ipdltest/TestInterruptRaces.h",
+ ),
+ ("PTestInterruptRaces", "parent"): (
+ "TestInterruptRacesParent",
+ "mozilla/_ipdltest/TestInterruptRaces.h",
+ ),
+ ("PTestInterruptShutdownRace", "child"): (
+ "TestInterruptShutdownRaceChild",
+ "mozilla/_ipdltest/TestInterruptShutdownRace.h",
+ ),
+ ("PTestInterruptShutdownRace", "parent"): (
+ "TestInterruptShutdownRaceParent",
+ "mozilla/_ipdltest/TestInterruptShutdownRace.h",
+ ),
+ ("PTestLatency", "child"): ("TestLatencyChild", "mozilla/_ipdltest/TestLatency.h"),
+ ("PTestLatency", "parent"): (
+ "TestLatencyParent",
+ "mozilla/_ipdltest/TestLatency.h",
+ ),
+ ("PTestLayoutThread", "child"): (
+ "TestOffMainThreadPaintingChild",
+ "mozilla/_ipdltest/TestOffMainThreadPainting.h",
+ ),
+ ("PTestLayoutThread", "parent"): (
+ "TestOffMainThreadPaintingParent",
+ "mozilla/_ipdltest/TestOffMainThreadPainting.h",
+ ),
+ ("PTestPaintThread", "child"): (
+ "TestPaintThreadChild",
+ "mozilla/_ipdltest/TestOffMainThreadPainting.h",
+ ),
+ ("PTestPaintThread", "parent"): (
+ "TestPaintThreadParent",
+ "mozilla/_ipdltest/TestOffMainThreadPainting.h",
+ ),
+ ("PTestManyChildAllocs", "child"): (
+ "TestManyChildAllocsChild",
+ "mozilla/_ipdltest/TestManyChildAllocs.h",
+ ),
+ ("PTestManyChildAllocs", "parent"): (
+ "TestManyChildAllocsParent",
+ "mozilla/_ipdltest/TestManyChildAllocs.h",
+ ),
+ ("PTestManyChildAllocsSub", "child"): (
+ "TestManyChildAllocsSubChild",
+ "mozilla/_ipdltest/TestManyChildAllocs.h",
+ ),
+ ("PTestManyChildAllocsSub", "parent"): (
+ "TestManyChildAllocsSubParent",
+ "mozilla/_ipdltest/TestManyChildAllocs.h",
+ ),
+ ("PTestMultiMgrs", "child"): (
+ "TestMultiMgrsChild",
+ "mozilla/_ipdltest/TestMultiMgrs.h",
+ ),
+ ("PTestMultiMgrs", "parent"): (
+ "TestMultiMgrsParent",
+ "mozilla/_ipdltest/TestMultiMgrs.h",
+ ),
+ ("PTestMultiMgrsBottom", "child"): (
+ "TestMultiMgrsBottomChild",
+ "mozilla/_ipdltest/TestMultiMgrs.h",
+ ),
+ ("PTestMultiMgrsBottom", "parent"): (
+ "TestMultiMgrsBottomParent",
+ "mozilla/_ipdltest/TestMultiMgrs.h",
+ ),
+ ("PTestMultiMgrsLeft", "child"): (
+ "TestMultiMgrsLeftChild",
+ "mozilla/_ipdltest/TestMultiMgrs.h",
+ ),
+ ("PTestMultiMgrsLeft", "parent"): (
+ "TestMultiMgrsLeftParent",
+ "mozilla/_ipdltest/TestMultiMgrs.h",
+ ),
+ ("PTestMultiMgrsRight", "child"): (
+ "TestMultiMgrsRightChild",
+ "mozilla/_ipdltest/TestMultiMgrs.h",
+ ),
+ ("PTestMultiMgrsRight", "parent"): (
+ "TestMultiMgrsRightParent",
+ "mozilla/_ipdltest/TestMultiMgrs.h",
+ ),
+ ("PTestNestedLoops", "child"): (
+ "TestNestedLoopsChild",
+ "mozilla/_ipdltest/TestNestedLoops.h",
+ ),
+ ("PTestNestedLoops", "parent"): (
+ "TestNestedLoopsParent",
+ "mozilla/_ipdltest/TestNestedLoops.h",
+ ),
+ ("PTestRaceDeadlock", "child"): (
+ "TestRaceDeadlockChild",
+ "mozilla/_ipdltest/TestRaceDeadlock.h",
+ ),
+ ("PTestRaceDeadlock", "parent"): (
+ "TestRaceDeadlockParent",
+ "mozilla/_ipdltest/TestRaceDeadlock.h",
+ ),
+ ("PTestRaceDeferral", "child"): (
+ "TestRaceDeferralChild",
+ "mozilla/_ipdltest/TestRaceDeferral.h",
+ ),
+ ("PTestRaceDeferral", "parent"): (
+ "TestRaceDeferralParent",
+ "mozilla/_ipdltest/TestRaceDeferral.h",
+ ),
+ ("PTestRacyInterruptReplies", "child"): (
+ "TestRacyInterruptRepliesChild",
+ "mozilla/_ipdltest/TestRacyInterruptReplies.h",
+ ),
+ ("PTestRacyInterruptReplies", "parent"): (
+ "TestRacyInterruptRepliesParent",
+ "mozilla/_ipdltest/TestRacyInterruptReplies.h",
+ ),
+ ("PTestRacyReentry", "child"): (
+ "TestRacyReentryChild",
+ "mozilla/_ipdltest/TestRacyReentry.h",
+ ),
+ ("PTestRacyReentry", "parent"): (
+ "TestRacyReentryParent",
+ "mozilla/_ipdltest/TestRacyReentry.h",
+ ),
+ ("PTestRacyUndefer", "child"): (
+ "TestRacyUndeferChild",
+ "mozilla/_ipdltest/TestRacyUndefer.h",
+ ),
+ ("PTestRacyUndefer", "parent"): (
+ "TestRacyUndeferParent",
+ "mozilla/_ipdltest/TestRacyUndefer.h",
+ ),
+ ("PTestRPC", "child"): ("TestRPCChild", "mozilla/_ipdltest/TestRPC.h"),
+ ("PTestRPC", "parent"): ("TestRPCParent", "mozilla/_ipdltest/TestRPC.h"),
+ ("PTestSanity", "child"): ("TestSanityChild", "mozilla/_ipdltest/TestSanity.h"),
+ ("PTestSanity", "parent"): ("TestSanityParent", "mozilla/_ipdltest/TestSanity.h"),
+ ("PTestSelfManage", "child"): (
+ "TestSelfManageChild",
+ "mozilla/_ipdltest/TestSelfManageRoot.h",
+ ),
+ ("PTestSelfManage", "parent"): (
+ "TestSelfManageParent",
+ "mozilla/_ipdltest/TestSelfManageRoot.h",
+ ),
+ ("PTestSelfManageRoot", "child"): (
+ "TestSelfManageRootChild",
+ "mozilla/_ipdltest/TestSelfManageRoot.h",
+ ),
+ ("PTestSelfManageRoot", "parent"): (
+ "TestSelfManageRootParent",
+ "mozilla/_ipdltest/TestSelfManageRoot.h",
+ ),
+ ("PTestShmem", "child"): ("TestShmemChild", "mozilla/_ipdltest/TestShmem.h"),
+ ("PTestShmem", "parent"): ("TestShmemParent", "mozilla/_ipdltest/TestShmem.h"),
+ ("PTestShutdown", "child"): (
+ "TestShutdownChild",
+ "mozilla/_ipdltest/TestShutdown.h",
+ ),
+ ("PTestShutdown", "parent"): (
+ "TestShutdownParent",
+ "mozilla/_ipdltest/TestShutdown.h",
+ ),
+ ("PTestShutdownSub", "child"): (
+ "TestShutdownSubChild",
+ "mozilla/_ipdltest/TestShutdown.h",
+ ),
+ ("PTestShutdownSub", "parent"): (
+ "TestShutdownSubParent",
+ "mozilla/_ipdltest/TestShutdown.h",
+ ),
+ ("PTestShutdownSubsub", "child"): (
+ "TestShutdownSubsubChild",
+ "mozilla/_ipdltest/TestShutdown.h",
+ ),
+ ("PTestShutdownSubsub", "parent"): (
+ "TestShutdownSubsubParent",
+ "mozilla/_ipdltest/TestShutdown.h",
+ ),
+ ("PTestStackHooks", "child"): (
+ "TestStackHooksChild",
+ "mozilla/_ipdltest/TestStackHooks.h",
+ ),
+ ("PTestStackHooks", "parent"): (
+ "TestStackHooksParent",
+ "mozilla/_ipdltest/TestStackHooks.h",
+ ),
+ ("PTestSyncError", "child"): (
+ "TestSyncErrorChild",
+ "mozilla/_ipdltest/TestSyncError.h",
+ ),
+ ("PTestSyncError", "parent"): (
+ "TestSyncErrorParent",
+ "mozilla/_ipdltest/TestSyncError.h",
+ ),
+ ("PTestSyncHang", "child"): (
+ "TestSyncHangChild",
+ "mozilla/_ipdltest/TestSyncHang.h",
+ ),
+ ("PTestSyncHang", "parent"): (
+ "TestSyncHangParent",
+ "mozilla/_ipdltest/TestSyncHang.h",
+ ),
+ ("PTestSyncWakeup", "child"): (
+ "TestSyncWakeupChild",
+ "mozilla/_ipdltest/TestSyncWakeup.h",
+ ),
+ ("PTestSyncWakeup", "parent"): (
+ "TestSyncWakeupParent",
+ "mozilla/_ipdltest/TestSyncWakeup.h",
+ ),
+ ("PTestUniquePtrIPC", "child"): (
+ "TestUniquePtrIPCChild",
+ "mozilla/_ipdltest/TestUniquePtrIPC.h",
+ ),
+ ("PTestUniquePtrIPC", "parent"): (
+ "TestUniquePtrIPCParent",
+ "mozilla/_ipdltest/TestUniquePtrIPC.h",
+ ),
+ ("PTestUrgency", "child"): ("TestUrgencyChild", "mozilla/_ipdltest/TestUrgency.h"),
+ ("PTestUrgency", "parent"): (
+ "TestUrgencyParent",
+ "mozilla/_ipdltest/TestUrgency.h",
+ ),
+ ("PTestUrgentHangs", "child"): (
+ "TestUrgentHangsChild",
+ "mozilla/_ipdltest/TestUrgentHangs.h",
+ ),
+ ("PTestUrgentHangs", "parent"): (
+ "TestUrgentHangsParent",
+ "mozilla/_ipdltest/TestUrgentHangs.h",
+ ),
+ ("PBackgroundFileHandle", "child"): (
+ "indexedDB::BackgroundFileHandleChild",
+ "mozilla/dom/indexedDB/ActorsChild.h",
+ ),
+ ("PBackgroundFileRequest", "child"): (
+ "indexedDB::BackgroundFileRequestChild",
+ "mozilla/dom/indexedDB/ActorsChild.h",
+ ),
+ ("PBackgroundIDBCursor", "child"): (
+ "indexedDB::BackgroundCursorChildBase",
+ "mozilla/dom/indexedDB/ActorsChild.h",
+ ),
+ ("PBackgroundIDBDatabase", "child"): (
+ "indexedDB::BackgroundDatabaseChild",
+ "mozilla/dom/indexedDB/ActorsChild.h",
+ ),
+ ("PBackgroundIDBDatabaseRequest", "child"): (
+ "indexedDB::BackgroundDatabaseRequestChild",
+ "mozilla/dom/indexedDB/ActorsChild.h",
+ ),
+ ("PBackgroundIDBFactory", "child"): (
+ "indexedDB::BackgroundFactoryChild",
+ "mozilla/dom/indexedDB/ActorsChild.h",
+ ),
+ ("PBackgroundIDBFactoryRequest", "child"): (
+ "indexedDB::BackgroundFactoryRequestChild",
+ "mozilla/dom/indexedDB/ActorsChild.h",
+ ),
+ ("PBackgroundIDBRequest", "child"): (
+ "indexedDB::BackgroundRequestChild",
+ "mozilla/dom/indexedDB/ActorsChild.h",
+ ),
+ ("PBackgroundIDBTransaction", "child"): (
+ "indexedDB::BackgroundTransactionChild",
+ "mozilla/dom/indexedDB/ActorsChild.h",
+ ),
+ ("PBackgroundIDBVersionChangeTransaction", "child"): (
+ "indexedDB::BackgroundVersionChangeTransactionChild",
+ "mozilla/dom/indexedDB/ActorsChild.h",
+ ),
+ ("PBackgroundIndexedDBUtils", "child"): (
+ "indexedDB::BackgroundUtilsChild",
+ "mozilla/dom/indexedDB/ActorsChild.h",
+ ),
+ ("PBackgroundMutableFile", "child"): (
+ "indexedDB::BackgroundMutableFileChild",
+ "mozilla/dom/indexedDB/ActorsChild.h",
+ ),
+}
+
+# Our long term goal is to burn this list down, so new entries should be added
+# extremely sparingly and only with a very good reason! You must have an IPC
+# peer's r+ to add something new!
+
+# set() of (Protocol, side)
+VIRTUAL_CALL_CLASSES = set(
+ [
+ # Defined as a strange template
+ ("PMedia", "parent"),
+ ("PTexture", "parent"),
+ # Defined in a .cpp
+ ("PBackgroundFileHandle", "parent"),
+ ("PBackgroundFileRequest", "parent"),
+ ("PBackgroundIDBCursor", "parent"),
+ ("PBackgroundIDBDatabase", "parent"),
+ ("PBackgroundIDBDatabaseFile", "child"),
+ ("PBackgroundIDBDatabaseFile", "parent"),
+ ("PBackgroundIDBDatabaseRequest", "parent"),
+ ("PBackgroundIDBFactory", "parent"),
+ ("PBackgroundIDBFactoryRequest", "parent"),
+ ("PBackgroundIDBRequest", "parent"),
+ ("PBackgroundIDBTransaction", "parent"),
+ ("PBackgroundIDBVersionChangeTransaction", "parent"),
+ ("PBackgroundIndexedDBUtils", "parent"),
+ ("PBackgroundLSDatabase", "parent"),
+ ("PBackgroundLSObserver", "parent"),
+ ("PBackgroundLSRequest", "parent"),
+ ("PBackgroundLSSimpleRequest", "parent"),
+ ("PBackgroundLSSnapshot", "parent"),
+ ("PBackgroundSDBConnection", "parent"),
+ ("PBackgroundSDBRequest", "parent"),
+ ("PBackgroundTest", "child"),
+ ("PBackgroundTest", "parent"),
+ ("PChildToParentStream", "child"),
+ ("PChildToParentStream", "parent"),
+ ("PContentPermissionRequest", "parent"),
+ ("PCycleCollectWithLogs", "child"),
+ ("PCycleCollectWithLogs", "parent"),
+ ("PHal", "child"),
+ ("PHal", "parent"),
+ ("PIndexedDBPermissionRequest", "parent"),
+ ("PParentToChildStream", "child"),
+ ("PParentToChildStream", "parent"),
+ ("PProcessHangMonitor", "child"),
+ ("PProcessHangMonitor", "parent"),
+ ("PQuota", "parent"),
+ ("PQuotaRequest", "parent"),
+ ("PQuotaUsageRequest", "parent"),
+ ("PSimpleChannel", "child"),
+ ("PTexture", "child"),
+ # .h is not exported
+ ("PBackground", "child"),
+ ("PBackground", "parent"),
+ ("PBackgroundLSDatabase", "child"),
+ ("PBackgroundLSObserver", "child"),
+ ("PBackgroundLSRequest", "child"),
+ ("PBackgroundLSSimpleRequest", "child"),
+ ("PBackgroundLSSnapshot", "child"),
+ ("PBackgroundSDBConnection", "child"),
+ ("PBackgroundSDBRequest", "child"),
+ ("PBroadcastChannel", "child"),
+ ("PBroadcastChannel", "parent"),
+ ("PChromiumCDM", "child"),
+ ("PClientHandle", "child"),
+ ("PClientHandle", "parent"),
+ ("PClientHandleOp", "child"),
+ ("PClientHandleOp", "parent"),
+ ("PClientManager", "child"),
+ ("PClientManager", "parent"),
+ ("PClientManagerOp", "child"),
+ ("PClientManagerOp", "parent"),
+ ("PClientNavigateOp", "child"),
+ ("PClientNavigateOp", "parent"),
+ ("PClientOpenWindowOp", "child"),
+ ("PClientOpenWindowOp", "parent"),
+ ("PClientSource", "child"),
+ ("PClientSource", "parent"),
+ ("PClientSourceOp", "child"),
+ ("PClientSourceOp", "parent"),
+ ("PColorPicker", "child"),
+ ("PColorPicker", "parent"),
+ ("PDataChannel", "child"),
+ ("PFileChannel", "child"),
+ ("PFilePicker", "child"),
+ ("PFunctionBroker", "child"),
+ ("PFunctionBroker", "parent"),
+ ("PHandlerService", "child"),
+ ("PHandlerService", "parent"),
+ ("PPluginBackgroundDestroyer", "child"),
+ ("PPluginBackgroundDestroyer", "parent"),
+ ("PRemotePrintJob", "child"),
+ ("PRemotePrintJob", "parent"),
+ # PRemotePrintJob, PPrinting, PPrintProgressDialog and PPrintSettingsDialog
+ # need to be virtual for --disable-printing builds.
+ ("PPrinting", "child"),
+ ("PPrinting", "parent"),
+ ("PPrintProgressDialog", "child"),
+ ("PPrintProgressDialog", "parent"),
+ ("PPrintSettingsDialog", "child"),
+ ("PPrintSettingsDialog", "parent"),
+ ("PQuota", "child"),
+ ("PQuotaRequest", "child"),
+ ("PQuotaUsageRequest", "child"),
+ ("PServiceWorker", "child"),
+ ("PServiceWorker", "parent"),
+ ("PServiceWorkerContainer", "child"),
+ ("PServiceWorkerContainer", "parent"),
+ ("PServiceWorkerRegistration", "child"),
+ ("PServiceWorkerRegistration", "parent"),
+ ("PServiceWorkerUpdater", "child"),
+ ("PServiceWorkerUpdater", "parent"),
+ ("PVRLayer", "parent"),
+ ("PWebBrowserPersistResources", "child"),
+ ("PWebBrowserPersistResources", "parent"),
+ ("PWebBrowserPersistSerialize", "child"),
+ ("PWebBrowserPersistSerialize", "parent"),
+ ("PWebrtcGlobal", "child"),
+ ("PWebrtcGlobal", "parent"),
+ # .h is only exported on some platforms/configs
+ ("PCameras", "child"),
+ ("PCameras", "parent"),
+ ("PCompositorWidget", "child"),
+ ("PCompositorWidget", "parent"),
+ ("PDocAccessible", "child"),
+ ("PDocAccessible", "parent"),
+ ("PPluginSurface", "parent"),
+ ("PPluginWidget", "child"),
+ ("PPluginWidget", "parent"),
+ ("PProfiler", "child"),
+ ("PProfiler", "parent"),
+ ("PSpeechSynthesisRequest", "child"),
+ ("PSpeechSynthesisRequest", "parent"),
+ ("PStunAddrsRequest", "child"),
+ ("PStunAddrsRequest", "parent"),
+ ("PWebrtcTCPSocket", "child"),
+ ("PWebrtcTCPSocket", "parent"),
+ # .h includes something that's a LOCAL_INCLUDE
+ ("PBackgroundLocalStorageCache", "child"),
+ ("PBackgroundLocalStorageCache", "parent"),
+ ("PBackgroundSessionStorageCache", "child"),
+ ("PBackgroundSessionStorageCache", "parent"),
+ ("PBackgroundSessionStorageManager", "child"),
+ ("PBackgroundSessionStorageManager", "parent"),
+ ("PBackgroundStorage", "child"),
+ ("PBackgroundStorage", "parent"),
+ ("PBrowserStream", "parent"),
+ ("PExternalHelperApp", "parent"),
+ ("PFTPChannel", "child"),
+ ("PFTPChannel", "parent"),
+ ("PHttpChannel", "child"),
+ ("PHttpChannel", "parent"),
+ ("PSessionStorageObserver", "child"),
+ ("PSessionStorageObserver", "parent"),
+ # can't be included safely for compilation error reasons
+ ("PGMPContent", "parent"),
+ ("PGMPService", "child"),
+ ("PGMPService", "parent"),
+ ("PGMPStorage", "parent"),
+ ("PGMPVideoDecoder", "parent"),
+ ("PGMPVideoEncoder", "parent"),
+ ("PWebRenderBridge", "parent"),
+ # Not actually subclassed
+ ("PLoginReputation", "child"),
+ ("PPluginSurface", "child"),
+ ("PTestShellCommand", "child"),
+ # _ipdltest
+ # Not actually subclassed
+ ("PTestIndirectProtocolParamFirst", "child"),
+ ("PTestIndirectProtocolParamFirst", "parent"),
+ ("PTestIndirectProtocolParamManage", "child"),
+ ("PTestIndirectProtocolParamManage", "parent"),
+ ("PTestIndirectProtocolParamSecond", "child"),
+ ("PTestIndirectProtocolParamSecond", "parent"),
+ ("PTestPriority", "child"),
+ ("PTestPriority", "parent"),
+ ]
+)
diff --git a/ipc/ipdl/ipdl/lower.py b/ipc/ipdl/ipdl/lower.py
new file mode 100644
index 0000000000..36046091cf
--- /dev/null
+++ b/ipc/ipdl/ipdl/lower.py
@@ -0,0 +1,5757 @@
+# 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 re
+from copy import deepcopy
+from collections import OrderedDict
+import itertools
+
+import ipdl.ast
+import ipdl.builtin
+from ipdl.cxx.ast import *
+from ipdl.cxx.code import *
+from ipdl.direct_call import VIRTUAL_CALL_CLASSES, DIRECT_CALL_OVERRIDES
+from ipdl.type import ActorType, UnionType, TypeVisitor, builtinHeaderIncludes
+from ipdl.util import hash_str
+
+
+# -----------------------------------------------------------------------------
+# "Public" interface to lowering
+##
+
+
+class LowerToCxx:
+ def lower(self, tu, segmentcapacitydict):
+ """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, segmentcapacitydict)
+ 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_str(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 _backstagePass():
+ return ExprCall(ExprVar("mozilla::ipc::PrivateIPDLInterface"))
+
+
+def _iterType(ptr):
+ return Type("PickleIterator", ptr=ptr)
+
+
+def _deleteId():
+ return ExprVar("Msg___delete____ID")
+
+
+def _deleteReplyId():
+ return ExprVar("Reply___delete____ID")
+
+
+def _lookupListener(idexpr):
+ return ExprCall(ExprVar("Lookup"), args=[idexpr])
+
+
+def _makeForwardDeclForQClass(clsname, quals, cls=True, struct=False):
+ 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()
+ if ipdltype.isIPDL() and ipdltype.isMaybe():
+ return "Maybe" + 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.hasBaseType() 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 _uniqueptr(T):
+ return Type("UniquePtr", T=T)
+
+
+def _alreadyaddrefed(T):
+ return Type("already_AddRefed", T=T)
+
+
+def _tuple(types, const=False, ref=False):
+ return Type("Tuple", T=types, const=const, ref=ref)
+
+
+def _promise(resolvetype, rejecttype, tail, resolver=False):
+ inner = Type("Private") if resolver else None
+ return Type("MozPromise", T=[resolvetype, rejecttype, tail], inner=inner)
+
+
+def _makePromise(returns, side, resolver=False):
+ if len(returns) > 1:
+ resolvetype = _tuple([d.bareType(side) for d in returns])
+ else:
+ resolvetype = returns[0].bareType(side)
+
+ # MozPromise is purposefully made to be exclusive only. Really, we mean it.
+ return _promise(
+ resolvetype, _ResponseRejectReason.Type(), ExprLiteral.TRUE, resolver=resolver
+ )
+
+
+def _resolveType(returns, side):
+ if len(returns) > 1:
+ return _tuple([d.moveType(side) for d in returns])
+ return returns[0].moveType(side)
+
+
+def _makeResolver(returns, side):
+ return TypeFunction([Decl(_resolveType(returns, side), "")])
+
+
+def _cxxArrayType(basetype, const=False, ref=False):
+ return Type("nsTArray", T=basetype, const=const, ref=ref, hasimplicitcopyctor=False)
+
+
+def _cxxMaybeType(basetype, const=False, ref=False):
+ return Type(
+ "mozilla::Maybe",
+ T=basetype,
+ const=const,
+ ref=ref,
+ hasimplicitcopyctor=basetype.hasimplicitcopyctor,
+ )
+
+
+def _cxxManagedContainerType(basetype, const=False, ref=False):
+ return Type(
+ "ManagedContainer", T=basetype, const=const, ref=ref, hasimplicitcopyctor=False
+ )
+
+
+def _cxxLifecycleProxyType(ptr=False):
+ return Type("mozilla::ipc::ActorLifecycleProxy", ptr=ptr)
+
+
+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 _callGetLifecycleProxy(actor=ExprVar.THIS):
+ return ExprCall(ExprSelect(actor, "->", "GetLifecycleProxy"))
+
+
+def _releaseLifecycleProxy(actor=None):
+ return StmtCode(
+ """
+ mozilla::ipc::ActorLifecycleProxy* proxy =
+ (${actor})->GetLifecycleProxy();
+ NS_IF_RELEASE(proxy);
+ """,
+ actor=actor or ExprVar.THIS,
+ )
+
+
+def _otherSide(side):
+ if side == "child":
+ return "parent"
+ if side == "parent":
+ return "child"
+ assert 0
+
+
+def _ifLogging(topLevelProtocol, stmts):
+ return StmtCode(
+ """
+ if (mozilla::ipc::LoggingEnabledFor(${proto})) {
+ $*{stmts}
+ }
+ """,
+ proto=topLevelProtocol,
+ stmts=stmts,
+ )
+
+
+# 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 _sentinelReadError(classname):
+ return StmtExpr(
+ ExprCall(
+ ExprVar("mozilla::ipc::SentinelReadError"),
+ args=[ExprLiteral.String(classname)],
+ )
+ )
+
+
+# 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)]
+
+
+def errfnSentinel(rvalue=ExprLiteral.FALSE):
+ def inner(msg):
+ return [_sentinelReadError(msg), StmtReturn(rvalue)]
+
+ return inner
+
+
+def _destroyMethod():
+ return ExprVar("ActorDestroy")
+
+
+def errfnUnreachable(msg):
+ return [_logicError(msg)]
+
+
+class _DestroyReason:
+ @staticmethod
+ def Type():
+ return Type("ActorDestroyReason")
+
+ Deletion = ExprVar("Deletion")
+ AncestorDeletion = ExprVar("AncestorDeletion")
+ NormalShutdown = ExprVar("NormalShutdown")
+ AbnormalShutdown = ExprVar("AbnormalShutdown")
+ FailedConstructor = ExprVar("FailedConstructor")
+
+
+class _ResponseRejectReason:
+ @staticmethod
+ def Type():
+ return Type("ResponseRejectReason")
+
+ SendError = ExprVar("ResponseRejectReason::SendError")
+ ChannelClosed = ExprVar("ResponseRejectReason::ChannelClosed")
+ HandlerRejected = ExprVar("ResponseRejectReason::HandlerRejected")
+ ActorDestroyed = ExprVar("ResponseRejectReason::ActorDestroyed")
+
+
+# -----------------------------------------------------------------------------
+# 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 visitImportedCxxType(self, t):
+ cxxtype = Type(self.typename(t))
+ if t.isRefcounted():
+ cxxtype = _refptr(cxxtype)
+ return cxxtype
+
+ def visitActorType(self, a):
+ return Type(_actorName(self.typename(a.protocol), self.side), ptr=True)
+
+ 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 visitMaybeType(self, m):
+ basecxxtype = m.basetype.accept(self)
+ return _cxxMaybeType(basecxxtype)
+
+ def visitShmemType(self, s):
+ return Type(self.typename(s))
+
+ def visitByteBufType(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 visitManagedEndpointType(self, s):
+ return Type(self.typename(s))
+
+ def visitUniquePtrType(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 _cxxBareType(ipdltype, side, fq=False):
+ return ipdltype.accept(_ConvertToCxxType(side, fq))
+
+
+def _cxxRefType(ipdltype, side):
+ t = _cxxBareType(ipdltype, side)
+ t.ref = True
+ 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 = True
+ return t
+ if ipdltype.isIPDL() and ipdltype.isByteBuf():
+ t.ref = True
+ return t
+ if ipdltype.isIPDL() and ipdltype.hasBaseType():
+ # Keep same constness as inner type.
+ inner = _cxxConstRefType(ipdltype.basetype, side)
+ t.const = inner.const or not inner.ref
+ t.ref = True
+ return t
+ if ipdltype.isCxx() and ipdltype.isMoveonly():
+ t.const = True
+ t.ref = True
+ return t
+ if ipdltype.isCxx() and ipdltype.isRefcounted():
+ # Use T* instead of const RefPtr<T>&
+ t = t.T
+ t.ptr = True
+ return t
+ if ipdltype.isUniquePtr():
+ t.ref = True
+ return t
+ t.const = True
+ t.ref = True
+ return t
+
+
+def _cxxTypeCanMoveSend(ipdltype):
+ return ipdltype.isUniquePtr()
+
+
+def _cxxTypeNeedsMove(ipdltype):
+ if _cxxTypeNeedsMoveForSend(ipdltype):
+ return True
+
+ if ipdltype.isIPDL():
+ return ipdltype.isArray()
+
+ return False
+
+
+def _cxxTypeNeedsMoveForSend(ipdltype):
+ if ipdltype.isUniquePtr():
+ return True
+
+ if ipdltype.isCxx():
+ return ipdltype.isMoveonly()
+
+ if ipdltype.isIPDL():
+ if ipdltype.hasBaseType():
+ return _cxxTypeNeedsMove(ipdltype.basetype)
+ return (
+ ipdltype.isShmem()
+ or ipdltype.isByteBuf()
+ or ipdltype.isEndpoint()
+ or ipdltype.isManagedEndpoint()
+ )
+
+ return False
+
+
+# FIXME Bug 1547019 This should be the same as _cxxTypeNeedsMoveForSend, but
+# a lot of existing code needs to be updated and fixed before
+# we can do that.
+def _cxxTypeCanOnlyMove(ipdltype, visited=None):
+ if visited is None:
+ visited = set()
+
+ visited.add(ipdltype)
+
+ if ipdltype.isCxx():
+ return ipdltype.isMoveonly()
+
+ if ipdltype.isIPDL():
+ if ipdltype.isMaybe() or ipdltype.isArray():
+ return _cxxTypeCanOnlyMove(ipdltype.basetype, visited)
+ if ipdltype.isStruct() or ipdltype.isUnion():
+ return any(
+ _cxxTypeCanOnlyMove(t, visited)
+ for t in ipdltype.itercomponents()
+ if t not in visited
+ )
+ return ipdltype.isManagedEndpoint()
+
+ return False
+
+
+def _cxxTypeCanMove(ipdltype):
+ return not (ipdltype.isIPDL() and ipdltype.isActor())
+
+
+def _cxxMoveRefType(ipdltype, side):
+ t = _cxxBareType(ipdltype, side)
+ if _cxxTypeNeedsMove(ipdltype):
+ t.rvalref = True
+ return t
+ return _cxxConstRefType(ipdltype, side)
+
+
+def _cxxForceMoveRefType(ipdltype, side):
+ assert _cxxTypeCanMove(ipdltype)
+ t = _cxxBareType(ipdltype, side)
+ t.rvalref = True
+ return t
+
+
+def _cxxPtrToType(ipdltype, side):
+ t = _cxxBareType(ipdltype, side)
+ if ipdltype.isIPDL() and ipdltype.isActor():
+ t.ptr = False
+ t.ptrptr = True
+ return t
+ t.ptr = True
+ return t
+
+
+def _cxxConstPtrToType(ipdltype, side):
+ t = _cxxBareType(ipdltype, side)
+ if ipdltype.isIPDL() and ipdltype.isActor():
+ t.ptr = False
+ t.ptrconstptr = True
+ return t
+ t.const = True
+ t.ptr = True
+ return t
+
+
+def _allocMethod(ptype, side):
+ return "Alloc" + ptype.name() + side.title()
+
+
+def _deallocMethod(ptype, side):
+ return "Dealloc" + ptype.name() + side.title()
+
+
+##
+# 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
+
+ def var(self):
+ return ExprVar(self.name)
+
+ def bareType(self, side, fq=False):
+ """Return this decl's unqualified C++ type."""
+ return _cxxBareType(self.ipdltype, side, fq=fq)
+
+ 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)
+ elif _cxxTypeNeedsMoveForSend(self.ipdltype):
+ return self.rvalueRefType(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 = False
+ t.ptrptr = True
+ return t
+ t.ptr = True
+ return t
+
+ def forceMoveType(self, side):
+ """Return this decl's C++ Type with forced move semantics."""
+ assert _cxxTypeCanMove(self.ipdltype)
+ return _cxxForceMoveRefType(self.ipdltype, side)
+
+
+# --------------------------------------------------
+
+
+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)
+
+ # @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, fq=False):
+ return _HybridDecl.bareType(self, self.side, fq=fq)
+
+ 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)
+
+ def forceMoveType(self, side=None):
+ return _HybridDecl.forceMoveType(self, self.side)
+
+
+class StructDecl(ipdl.ast.StructDecl, HasFQName):
+ def fields_ipdl_order(self):
+ for f in self.fields:
+ yield f
+
+ def fields_member_order(self):
+ assert len(self.packed_field_order) == len(self.fields)
+
+ for i in self.packed_field_order:
+ yield self.fields[i]
+
+ @staticmethod
+ def upgrade(structDecl):
+ assert isinstance(structDecl, ipdl.ast.StructDecl)
+ structDecl.__class__ = 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 refExpr(self, thisexpr=None):
+ ref = self.memberVar()
+ if thisexpr is not None:
+ ref = ExprSelect(thisexpr, ".", ref.name)
+ 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=True), const=True)
+ if "ByteBuf" == self.ipdltype.name():
+ refexpr = ExprCast(refexpr, Type("ByteBuf", ref=True), const=True)
+ if "FileDescriptor" == self.ipdltype.name():
+ refexpr = ExprCast(refexpr, Type("FileDescriptor", ref=True), const=True)
+ return refexpr
+
+ def argVar(self):
+ return ExprVar("_" + self.name)
+
+ def memberVar(self):
+ return ExprVar(self.name + "_")
+
+
+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
+
+
+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)
+
+ # To create a finite object with a mutually recursive type, a union must
+ # be present somewhere in the recursive loop. Because of that we only
+ # need to care about introducing indirections inside unions.
+ self.recursive = ud.decl.type.mutuallyRecursiveWith(ipdltype)
+
+ def enum(self):
+ return "T" + self.flattypename
+
+ def enumvar(self):
+ return ExprVar(self.enum())
+
+ def internalType(self):
+ if self.recursive:
+ return self.ptrToType()
+ else:
+ return self.bareType()
+
+ 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=True)
+ elif (
+ self.ipdltype.isIPDL()
+ and self.ipdltype.isArray()
+ and not isinstance(rhs, ExprMove)
+ ):
+ rhs = ExprCall(ExprSelect(rhs, ".", "Clone"), args=[])
+ 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=True)]
+ elif (
+ self.ipdltype.isIPDL()
+ and self.ipdltype.isArray()
+ and not isinstance(expr, ExprMove)
+ ):
+ args = [ExprCall(ExprSelect(expr, ".", "Clone"), args=[])]
+ 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 = True
+ return t
+
+ def defaultValue(self, fq=False):
+ # 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(fq=fq))
+
+ def getConstValue(self):
+ v = ExprDeref(self.callGetConstPtr())
+ # sigh
+ if "ByteBuf" == self.ipdltype.name():
+ v = ExprCast(v, Type("ByteBuf", ref=True), const=True)
+ if "Shmem" == self.ipdltype.name():
+ v = ExprCast(v, Type("Shmem", ref=True), const=True)
+ if "FileDescriptor" == self.ipdltype.name():
+ v = ExprCast(v, Type("FileDescriptor", ref=True), const=True)
+ 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 name
+
+ def sendMethod(self):
+ name = _sendPrefix(self.decl.type) + self.baseName()
+ if self.decl.type.isCtor():
+ name += "Constructor"
+ return name
+
+ def hasReply(self):
+ return (
+ self.decl.type.hasReply()
+ or self.decl.type.isCtor()
+ or self.decl.type.isDtor()
+ )
+
+ def hasAsyncReturns(self):
+ return self.decl.type.isAsync() and self.returns
+
+ 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 promiseName(self):
+ name = self.baseName()
+ if self.decl.type.isCtor():
+ name += "Constructor"
+ name += "Promise"
+ return name
+
+ def resolverName(self):
+ return self.baseName() + "Resolver"
+
+ def actorDecl(self):
+ return self.params[0]
+
+ def makeCxxParams(
+ self, paramsems="in", returnsems="out", side=None, implicit=True, direction=None
+ ):
+ """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 self.decl.type.tainted and direction == "recv":
+ # Tainted types are passed by-value, allowing the receiver to move them if desired.
+ assert sems != "out"
+ return Decl(Type("Tainted", T=d.bareType(side)), d.name)
+
+ if sems == "in":
+ return Decl(d.inType(side), d.name)
+ elif sems == "move":
+ return Decl(d.moveType(side), d.name)
+ elif sems == "out":
+ return Decl(d.outType(side), d.name)
+ else:
+ assert 0
+
+ def makeResolverDecl(returns):
+ return Decl(Type(self.resolverName(), rvalref=True), "aResolve")
+
+ def makeCallbackResolveDecl(returns):
+ if len(returns) > 1:
+ resolvetype = _tuple([d.bareType(side) for d in returns])
+ else:
+ resolvetype = returns[0].bareType(side)
+
+ return Decl(
+ Type("mozilla::ipc::ResolveCallback", T=resolvetype, rvalref=True),
+ "aResolve",
+ )
+
+ def makeCallbackRejectDecl(returns):
+ return Decl(Type("mozilla::ipc::RejectCallback", rvalref=True), "aReject")
+
+ cxxparams = []
+ if paramsems is not None:
+ cxxparams.extend([makeDecl(d, paramsems) for d in self.params])
+
+ if returnsems == "promise" and self.returns:
+ pass
+ elif returnsems == "callback" and self.returns:
+ cxxparams.extend(
+ [
+ makeCallbackResolveDecl(self.returns),
+ makeCallbackRejectDecl(self.returns),
+ ]
+ )
+ elif returnsems == "resolver" and self.returns:
+ cxxparams.extend([makeResolverDecl(self.returns)])
+ elif 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=True
+ ):
+ assert not retcallsems or retsems # retcallsems => returnsems
+ cxxargs = []
+
+ if paramsems == "move":
+ # We don't std::move() RefPtr<T> types because current Recv*()
+ # implementors take these parameters as T*, and
+ # std::move(RefPtr<T>) doesn't coerce to T*.
+ cxxargs.extend(
+ [
+ p.var() if p.ipdltype.isRefcounted() else ExprMove(p.var())
+ for p in self.params
+ ]
+ )
+ elif paramsems == "in":
+ cxxargs.extend([p.var() for p in self.params])
+ else:
+ assert False
+
+ for ret in self.returns:
+ if retsems == "in":
+ if retcallsems == "in":
+ cxxargs.append(ret.var())
+ elif retcallsems == "out":
+ cxxargs.append(ExprAddrOf(ret.var()))
+ else:
+ assert 0
+ elif retsems == "out":
+ if retcallsems == "in":
+ cxxargs.append(ExprDeref(ret.var()))
+ elif retcallsems == "out":
+ cxxargs.append(ret.var())
+ else:
+ assert 0
+ elif retsems == "resolver":
+ pass
+ if retsems == "resolver":
+ cxxargs.append(ExprMove(ExprVar("resolver")))
+
+ 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
+
+
+# --------------------------------------------------
+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 managerInterfaceType(self, ptr=False):
+ return Type("mozilla::ipc::IProtocol", ptr=ptr)
+
+ def openedProtocolInterfaceType(self, ptr=False):
+ 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=False):
+ return Type(_actorName(self._ipdlmgrtype().name(), side), ptr=ptr)
+
+ def unregisterMethod(self, actorThis=None):
+ if actorThis is not None:
+ return ExprSelect(actorThis, "->", "Unregister")
+ return ExprVar("Unregister")
+
+ def removeManageeMethod(self):
+ return ExprVar("RemoveManagee")
+
+ def deallocManageeMethod(self):
+ return ExprVar("DeallocManagee")
+
+ 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 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 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=True)
+
+ 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=False, ref=False):
+ assert self.decl.type.isManagerOf(actortype)
+ return _cxxManagedContainerType(
+ Type(_actorName(actortype.name(), side)), const=const, ref=ref
+ )
+
+ def subtreeUsesShmem(self):
+ return _subtreeUsesShmem(self)
+
+ @staticmethod
+ def upgrade(protocol):
+ assert isinstance(protocol, ipdl.ast.Protocol)
+ protocol.__class__ = Protocol
+
+
+class TranslationUnit(ipdl.ast.TranslationUnit):
+ @staticmethod
+ def upgrade(tu):
+ assert isinstance(tu, ipdl.ast.TranslationUnit)
+ tu.__class__ = TranslationUnit
+
+
+# -----------------------------------------------------------------------------
+
+pod_types = {
+ "int8_t": 1,
+ "uint8_t": 1,
+ "int16_t": 2,
+ "uint16_t": 2,
+ "int32_t": 4,
+ "uint32_t": 4,
+ "int64_t": 8,
+ "uint64_t": 8,
+ "float": 4,
+ "double": 8,
+}
+max_pod_size = max(pod_types.values())
+# We claim that all types we don't recognize are automatically "bigger"
+# than pod types for ease of sorting.
+pod_size_sentinel = max_pod_size * 2
+
+
+def pod_size(ipdltype):
+ if not isinstance(ipdltype, ipdl.type.ImportedCxxType):
+ return pod_size_sentinel
+
+ return pod_types.get(ipdltype.name(), pod_size_sentinel)
+
+
+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::ManagedEndpoint"),
+ "ManagedEndpoint",
+ ["FooSide"],
+ ),
+ Typedef(
+ Type("mozilla::ipc::TransportDescriptor"), "TransportDescriptor"
+ ),
+ Typedef(Type("mozilla::UniquePtr"), "UniquePtr", ["T"]),
+ Typedef(
+ Type("mozilla::ipc::ResponseRejectReason"), "ResponseRejectReason"
+ ),
+ ]
+ )
+ 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 = False
+ newfields = []
+ for f in sd.fields:
+ ftype = f.decl.type
+ if _hasVisibleActor(ftype):
+ sd.decl.special = True
+ # 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))
+
+ # Compute a permutation of the fields for in-memory storage such
+ # that the memory layout of the structure will be well-packed.
+ permutation = list(range(len(newfields)))
+
+ # Note that the results of `pod_size` ensure that non-POD fields
+ # sort before POD ones.
+ def size(idx):
+ return pod_size(newfields[idx].ipdltype)
+
+ permutation.sort(key=size, reverse=True)
+
+ sd.fields = newfields
+ sd.packed_field_order = permutation
+ 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 = False
+ newcomponents = []
+ for ctype in ud.decl.type.components:
+ if _hasVisibleActor(ctype):
+ ud.decl.special = True
+ # 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 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, segmentcapacitydict):
+ self.protocol = tu.protocol
+ self.hdrfile = cxxHeaderFile
+ self.cppfile = cxxFile
+ self.segmentcapacitydict = segmentcapacitydict
+ 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)
+ if typestr not in typesToIncludes:
+ typesToIncludes[typestr] = using.header
+ else:
+ assert 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 + '"'))
+
+ # Manually run Visitor.visitTranslationUnit. For dependency resolution
+ # we need to handle structs and unions separately.
+ for cxxInc in tu.cxxIncludes:
+ cxxInc.accept(self)
+ for inc in tu.includes:
+ inc.accept(self)
+ self.generateStructsAndUnions(tu)
+ for using in tu.builtinUsing:
+ using.accept(self)
+ for using in tu.using:
+ using.accept(self)
+ if tu.protocol:
+ tu.protocol.accept(self)
+
+ if tu.filetype == "header":
+ self.cppIncludeHeaders.append(_ipdlhHeaderName(tu) + ".h")
+
+ hf.addthing(Whitespace.NL)
+ hf.addthings(_includeGuardEnd(hf))
+
+ cf = self.cppfile
+ cf.addthings(
+ (
+ [_DISCLAIMER, Whitespace.NL]
+ + [
+ CppDirective("include", '"' + h + '"')
+ for h in self.cppIncludeHeaders
+ ]
+ + [Whitespace.NL]
+ + [
+ CppDirective("include", '"%s"' % filename)
+ for filename in ipdl.builtin.CppIncludes
+ ]
+ + [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 visitCxxInclude(self, inc):
+ self.cppIncludeHeaders.append(inc.file)
+
+ def visitInclude(self, inc):
+ if inc.tu.filetype == "header":
+ self.hdrfile.addthing(
+ CppDirective("include", '"' + _ipdlhHeaderName(inc.tu) + '.h"')
+ )
+ else:
+ self.cppIncludeHeaders += [
+ _protocolHeaderName(inc.tu.protocol, "parent") + ".h",
+ _protocolHeaderName(inc.tu.protocol, "child") + ".h",
+ ]
+
+ def generateStructsAndUnions(self, tu):
+ """Generate the definitions for all structs and unions. This will
+ re-order the declarations if needed in the C++ code such that
+ dependencies have already been defined."""
+ decls = OrderedDict()
+ for su in tu.structsAndUnions:
+ if isinstance(su, StructDecl):
+ which = "struct"
+ forwarddecls, fulldecltypes, cls = _generateCxxStruct(su)
+ traitsdecl, traitsdefns = _ParamTraits.structPickling(su.decl.type)
+ else:
+ assert isinstance(su, UnionDecl)
+ which = "union"
+ forwarddecls, fulldecltypes, cls = _generateCxxUnion(su)
+ traitsdecl, traitsdefns = _ParamTraits.unionPickling(su.decl.type)
+
+ clsdecl, methoddefns = _splitClassDeclDefn(cls)
+
+ # Store the declarations in the decls map so we can emit in
+ # dependency order.
+ decls[su.decl.type] = (
+ fulldecltypes,
+ [Whitespace.NL]
+ + forwarddecls
+ + [
+ Whitespace(
+ """
+//-----------------------------------------------------------------------------
+// Declaration of the IPDL type |%s %s|
+//
+"""
+ % (which, su.name)
+ ),
+ _putInNamespaces(clsdecl, su.namespaces),
+ ]
+ + [Whitespace.NL, traitsdecl],
+ )
+
+ self.structUnionDefns.extend(
+ [
+ Whitespace(
+ """
+//-----------------------------------------------------------------------------
+// Method definitions for the IPDL type |%s %s|
+//
+"""
+ % (which, su.name)
+ ),
+ _putInNamespaces(methoddefns, su.namespaces),
+ Whitespace.NL,
+ traitsdefns,
+ ]
+ )
+
+ # Generate the declarations structs in dependency order.
+ def gen_struct(deps, defn):
+ for dep in deps:
+ if dep in decls:
+ d, t = decls[dep]
+ del decls[dep]
+ gen_struct(d, t)
+ self.hdrfile.addthings(defn)
+
+ while len(decls) > 0:
+ _, (d, t) = decls.popitem(False)
+ gen_struct(d, t)
+
+ def visitProtocol(self, p):
+ self.cppIncludeHeaders.append(_protocolHeaderName(self.protocol, "") + ".h")
+ self.cppIncludeHeaders.append(
+ _protocolHeaderName(self.protocol, "Parent") + ".h"
+ )
+ self.cppIncludeHeaders.append(
+ _protocolHeaderName(self.protocol, "Child") + ".h"
+ )
+
+ # Forward declare our own actors.
+ self.hdrfile.addthings(
+ [
+ Whitespace.NL,
+ _makeForwardDeclForActor(p.decl.type, "Parent"),
+ _makeForwardDeclForActor(p.decl.type, "Child"),
+ ]
+ )
+
+ 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)
+
+ edecl, edefn = _splitFuncDeclDefn(self.genEndpointFunc())
+ ns.addstmts([edecl, Whitespace.NL])
+ self.funcDefns.append(edefn)
+
+ # spit out message type enum and classes
+ msgenum = msgenums(self.protocol)
+ ns.addstmts([StmtDecl(Decl(msgenum, "")), Whitespace.NL])
+
+ for md in p.messageDecls:
+ decls = []
+
+ # Look up the segment capacity used for serializing this
+ # message. If the capacity is not specified, use '0' for
+ # the default capacity (defined in ipc_message.cc)
+ name = "%s::%s" % (md.namespace, md.decl.progname)
+ segmentcapacity = self.segmentcapacitydict.get(name, 0)
+
+ mfDecl, mfDefn = _splitFuncDeclDefn(
+ _generateMessageConstructor(md, segmentcapacity, p, forReply=False)
+ )
+ decls.append(mfDecl)
+ self.funcDefns.append(mfDefn)
+
+ if md.hasReply():
+ rfDecl, rfDefn = _splitFuncDeclDefn(
+ _generateMessageConstructor(md, 0, p, forReply=True)
+ )
+ decls.append(rfDecl)
+ self.funcDefns.append(rfDefn)
+
+ decls.append(Whitespace.NL)
+ ns.addstmts(decls)
+
+ ns.addstmts([Whitespace.NL, Whitespace.NL])
+
+ # Generate code for PFoo::CreateEndpoints.
+ def genEndpointFunc(self):
+ p = self.protocol.decl.type
+ tparent = _cxxBareType(ActorType(p), "Parent", fq=True)
+ tchild = _cxxBareType(ActorType(p), "Child", fq=True)
+
+ openfunc = MethodDefn(
+ MethodDecl(
+ "CreateEndpoints",
+ params=[
+ Decl(Type("base::ProcessId"), "aParentDestPid"),
+ Decl(Type("base::ProcessId"), "aChildDestPid"),
+ Decl(
+ Type("mozilla::ipc::Endpoint<" + tparent.name + ">", ptr=True),
+ "aParent",
+ ),
+ Decl(
+ Type("mozilla::ipc::Endpoint<" + tchild.name + ">", ptr=True),
+ "aChild",
+ ),
+ ],
+ ret=Type.NSRESULT,
+ )
+ )
+ openfunc.addcode(
+ """
+ return mozilla::ipc::CreateEndpoints(
+ mozilla::ipc::PrivateIPDLInterface(),
+ aParentDestPid, aChildDestPid,
+ aParent, aChild);
+ """
+ )
+ return openfunc
+
+
+# --------------------------------------------------
+
+
+def _generateMessageConstructor(md, segmentSize, protocol, forReply=False):
+ if forReply:
+ clsname = md.replyCtorFunc()
+ msgid = md.replyId()
+ replyEnum = "REPLY"
+ else:
+ clsname = md.msgCtorFunc()
+ msgid = md.msgId()
+ replyEnum = "NOT_REPLY"
+
+ nested = md.decl.type.nested
+ prio = md.decl.type.prio
+ compress = md.decl.type.compress
+
+ routingId = ExprVar("routingId")
+
+ func = FunctionDefn(
+ FunctionDecl(
+ clsname,
+ params=[Decl(Type("int32_t"), routingId.name)],
+ ret=Type("IPC::Message", ptr=True),
+ )
+ )
+
+ if compress == "compress":
+ compression = "COMPRESSION_ENABLED"
+ elif compress:
+ assert compress == "compressall"
+ compression = "COMPRESSION_ALL"
+ else:
+ compression = "COMPRESSION_NONE"
+
+ if nested == ipdl.ast.NOT_NESTED:
+ nestedEnum = "NOT_NESTED"
+ elif nested == ipdl.ast.INSIDE_SYNC_NESTED:
+ nestedEnum = "NESTED_INSIDE_SYNC"
+ else:
+ assert nested == ipdl.ast.INSIDE_CPOW_NESTED
+ nestedEnum = "NESTED_INSIDE_CPOW"
+
+ if prio == ipdl.ast.NORMAL_PRIORITY:
+ prioEnum = "NORMAL_PRIORITY"
+ elif prio == ipdl.ast.INPUT_PRIORITY:
+ prioEnum = "INPUT_PRIORITY"
+ elif prio == ipdl.ast.HIGH_PRIORITY:
+ prioEnum = "HIGH_PRIORITY"
+ else:
+ prioEnum = "MEDIUMHIGH_PRIORITY"
+
+ if md.decl.type.isSync():
+ syncEnum = "SYNC"
+ else:
+ syncEnum = "ASYNC"
+
+ if md.decl.type.isInterrupt():
+ interruptEnum = "INTERRUPT"
+ else:
+ interruptEnum = "NOT_INTERRUPT"
+
+ if md.decl.type.isCtor():
+ ctorEnum = "CONSTRUCTOR"
+ else:
+ ctorEnum = "NOT_CONSTRUCTOR"
+
+ def messageEnum(valname):
+ return ExprVar("IPC::Message::" + valname)
+
+ flags = ExprCall(
+ ExprVar("IPC::Message::HeaderFlags"),
+ args=[
+ messageEnum(nestedEnum),
+ messageEnum(prioEnum),
+ messageEnum(compression),
+ messageEnum(ctorEnum),
+ messageEnum(syncEnum),
+ messageEnum(interruptEnum),
+ messageEnum(replyEnum),
+ ],
+ )
+
+ segmentSize = int(segmentSize)
+ if segmentSize:
+ func.addstmt(
+ StmtReturn(
+ ExprNew(
+ Type("IPC::Message"),
+ args=[
+ routingId,
+ ExprVar(msgid),
+ ExprLiteral.Int(int(segmentSize)),
+ flags,
+ # Pass `true` to recordWriteLatency to collect telemetry
+ ExprLiteral.TRUE,
+ ],
+ )
+ )
+ )
+ else:
+ func.addstmt(
+ StmtReturn(
+ ExprCall(
+ ExprVar("IPC::Message::IPDLMessage"),
+ args=[routingId, ExprVar(msgid), flags],
+ )
+ )
+ )
+
+ return func
+
+
+# --------------------------------------------------
+
+
+class _ParamTraits:
+ var = ExprVar("aVar")
+ msgvar = ExprVar("aMsg")
+ itervar = ExprVar("aIter")
+ actor = ExprVar("aActor")
+
+ @classmethod
+ def ifsideis(cls, side, then, els=None):
+ cxxside = ExprVar("mozilla::ipc::ChildSide")
+ if side == "parent":
+ cxxside = ExprVar("mozilla::ipc::ParentSide")
+
+ ifstmt = StmtIf(
+ ExprBinary(cxxside, "==", ExprCall(ExprSelect(cls.actor, "->", "GetSide")))
+ )
+ ifstmt.addifstmt(then)
+ if els is not None:
+ ifstmt.addelsestmt(els)
+ return ifstmt
+
+ @classmethod
+ def fatalError(cls, reason):
+ return StmtCode(
+ "aActor->FatalError(${reason});", reason=ExprLiteral.String(reason)
+ )
+
+ @classmethod
+ def writeSentinel(cls, msgvar, sentinelKey):
+ return [
+ Whitespace("// Sentinel = " + repr(sentinelKey) + "\n", indent=True),
+ StmtExpr(
+ ExprCall(
+ ExprSelect(msgvar, "->", "WriteSentinel"),
+ args=[ExprLiteral.Int(hashfunc(sentinelKey))],
+ )
+ ),
+ ]
+
+ @classmethod
+ def readSentinel(cls, msgvar, itervar, sentinelKey, sentinelFail):
+ # Read the sentinel
+ read = ExprCall(
+ ExprSelect(msgvar, "->", "ReadSentinel"),
+ args=[itervar, ExprLiteral.Int(hashfunc(sentinelKey))],
+ )
+ ifsentinel = StmtIf(ExprNot(read))
+ ifsentinel.addifstmts(sentinelFail)
+
+ return [
+ Whitespace("// Sentinel = " + repr(sentinelKey) + "\n", indent=True),
+ ifsentinel,
+ ]
+
+ @classmethod
+ def write(cls, var, msgvar, actor, ipdltype=None):
+ # WARNING: This doesn't set AutoForActor for you, make sure this is
+ # only called when the actor is already correctly set.
+ if ipdltype and _cxxTypeNeedsMoveForSend(ipdltype):
+ var = ExprMove(var)
+ return ExprCall(ExprVar("WriteIPDLParam"), args=[msgvar, actor, var])
+
+ @classmethod
+ def checkedWrite(cls, ipdltype, var, msgvar, sentinelKey, actor):
+ assert sentinelKey
+ block = Block()
+
+ # Assert we aren't serializing a null non-nullable actor
+ if (
+ ipdltype
+ and ipdltype.isIPDL()
+ and ipdltype.isActor()
+ and not ipdltype.nullable
+ ):
+ block.addstmt(
+ _abortIfFalse(var, "NULL actor value passed to non-nullable param")
+ )
+
+ block.addstmts(
+ [
+ StmtExpr(cls.write(var, msgvar, actor, ipdltype)),
+ ]
+ )
+ block.addstmts(cls.writeSentinel(msgvar, sentinelKey))
+ return block
+
+ @classmethod
+ def bulkSentinelKey(cls, fields):
+ return " | ".join(f.basename for f in fields)
+
+ @classmethod
+ def checkedBulkWrite(cls, size, fields):
+ block = Block()
+ first = fields[0]
+
+ block.addstmts(
+ [
+ StmtExpr(
+ ExprCall(
+ ExprSelect(cls.msgvar, "->", "WriteBytes"),
+ args=[
+ ExprAddrOf(
+ ExprCall(first.getMethod(thisexpr=cls.var, sel="."))
+ ),
+ ExprLiteral.Int(size * len(fields)),
+ ],
+ )
+ )
+ ]
+ )
+ block.addstmts(cls.writeSentinel(cls.msgvar, cls.bulkSentinelKey(fields)))
+
+ return block
+
+ @classmethod
+ def checkedBulkRead(cls, size, fields):
+ block = Block()
+ first = fields[0]
+
+ readbytes = ExprCall(
+ ExprSelect(cls.msgvar, "->", "ReadBytesInto"),
+ args=[
+ cls.itervar,
+ ExprAddrOf(ExprCall(first.getMethod(thisexpr=cls.var, sel="->"))),
+ ExprLiteral.Int(size * len(fields)),
+ ],
+ )
+ ifbad = StmtIf(ExprNot(readbytes))
+ errmsg = "Error bulk reading fields from %s" % first.ipdltype.name()
+ ifbad.addifstmts([cls.fatalError(errmsg), StmtReturn.FALSE])
+ block.addstmt(ifbad)
+ block.addstmts(
+ cls.readSentinel(
+ cls.msgvar,
+ cls.itervar,
+ cls.bulkSentinelKey(fields),
+ errfnSentinel()(errmsg),
+ )
+ )
+
+ return block
+
+ @classmethod
+ def checkedRead(
+ cls,
+ ipdltype,
+ var,
+ msgvar,
+ itervar,
+ errfn,
+ paramtype,
+ sentinelKey,
+ errfnSentinel,
+ actor,
+ ):
+ block = Block()
+
+ # Read the data
+ ifbad = StmtIf(
+ ExprNot(
+ ExprCall(ExprVar("ReadIPDLParam"), args=[msgvar, itervar, actor, var])
+ )
+ )
+ if not isinstance(paramtype, list):
+ paramtype = ["Error deserializing " + paramtype]
+ ifbad.addifstmts(errfn(*paramtype))
+ block.addstmt(ifbad)
+
+ # Check if we got a null non-nullable actor
+ if (
+ ipdltype
+ and ipdltype.isIPDL()
+ and ipdltype.isActor()
+ and not ipdltype.nullable
+ ):
+ ifnull = StmtIf(ExprNot(ExprDeref(var)))
+ ifnull.addifstmts(errfn(*paramtype))
+ block.addstmt(ifnull)
+
+ block.addstmts(
+ cls.readSentinel(msgvar, itervar, sentinelKey, errfnSentinel(*paramtype))
+ )
+
+ return block
+
+ # Helper wrapper for checkedRead for use within _ParamTraits
+ @classmethod
+ def _checkedRead(cls, ipdltype, var, sentinelKey, what):
+ def errfn(msg):
+ return [cls.fatalError(msg), StmtReturn.FALSE]
+
+ return cls.checkedRead(
+ ipdltype,
+ var,
+ cls.msgvar,
+ cls.itervar,
+ errfn=errfn,
+ paramtype=what,
+ sentinelKey=sentinelKey,
+ errfnSentinel=errfnSentinel(),
+ actor=cls.actor,
+ )
+
+ @classmethod
+ def generateDecl(cls, fortype, write, read, constin=True):
+ # IPDLParamTraits impls are selected ignoring constness, and references.
+ pt = Class(
+ "IPDLParamTraits",
+ specializes=Type(
+ fortype.name, T=fortype.T, inner=fortype.inner, ptr=fortype.ptr
+ ),
+ struct=True,
+ )
+
+ # typedef T paramType;
+ pt.addstmt(Typedef(fortype, "paramType"))
+
+ iprotocoltype = Type("mozilla::ipc::IProtocol", ptr=True)
+
+ # static void Write(Message*, const T&);
+ intype = Type("paramType", ref=True, const=constin)
+ writemthd = MethodDefn(
+ MethodDecl(
+ "Write",
+ params=[
+ Decl(Type("IPC::Message", ptr=True), cls.msgvar.name),
+ Decl(iprotocoltype, cls.actor.name),
+ Decl(intype, cls.var.name),
+ ],
+ methodspec=MethodSpec.STATIC,
+ )
+ )
+ writemthd.addstmts(write)
+ pt.addstmt(writemthd)
+
+ # static bool Read(const Message*, PickleIterator*, T*);
+ outtype = Type("paramType", ptr=True)
+ readmthd = MethodDefn(
+ MethodDecl(
+ "Read",
+ params=[
+ Decl(Type("IPC::Message", ptr=True, const=True), cls.msgvar.name),
+ Decl(_iterType(ptr=True), cls.itervar.name),
+ Decl(iprotocoltype, cls.actor.name),
+ Decl(outtype, cls.var.name),
+ ],
+ ret=Type.BOOL,
+ methodspec=MethodSpec.STATIC,
+ )
+ )
+ readmthd.addstmts(read)
+ pt.addstmt(readmthd)
+
+ # Split the class into declaration and definition
+ clsdecl, methoddefns = _splitClassDeclDefn(pt)
+
+ namespaces = [Namespace("mozilla"), Namespace("ipc")]
+ clsns = _putInNamespaces(clsdecl, namespaces)
+ defns = _putInNamespaces(methoddefns, namespaces)
+ return clsns, defns
+
+ @classmethod
+ def actorPickling(cls, actortype, side):
+ """Generates pickling for IPDL actors. This is a |nullable| deserializer.
+ Write and read callers will perform nullability validation."""
+
+ cxxtype = _cxxBareType(actortype, side, fq=True)
+
+ write = StmtCode(
+ """
+ int32_t id;
+ if (!${var}) {
+ id = 0; // kNullActorId
+ } else {
+ id = ${var}->Id();
+ if (id == 1) { // kFreedActorId
+ ${var}->FatalError("Actor has been |delete|d");
+ }
+ MOZ_RELEASE_ASSERT(
+ ${actor}->GetIPCChannel() == ${var}->GetIPCChannel(),
+ "Actor must be from the same channel as the"
+ " actor it's being sent over");
+ MOZ_RELEASE_ASSERT(
+ ${var}->CanSend(),
+ "Actor must still be open when sending");
+ }
+
+ ${write};
+ """,
+ var=cls.var,
+ actor=cls.actor,
+ write=cls.write(ExprVar("id"), cls.msgvar, cls.actor),
+ )
+
+ # bool Read(..) impl
+ read = StmtCode(
+ """
+ mozilla::Maybe<mozilla::ipc::IProtocol*> actor =
+ ${actor}->ReadActor(${msgvar}, ${itervar}, true, ${actortype}, ${protocolid});
+ if (actor.isNothing()) {
+ return false;
+ }
+
+ *${var} = static_cast<${cxxtype}>(actor.value());
+ return true;
+ """,
+ actor=cls.actor,
+ msgvar=cls.msgvar,
+ itervar=cls.itervar,
+ actortype=ExprLiteral.String(actortype.name()),
+ protocolid=_protocolId(actortype),
+ var=cls.var,
+ cxxtype=cxxtype,
+ )
+
+ return cls.generateDecl(cxxtype, [write], [read])
+
+ @classmethod
+ def structPickling(cls, structtype):
+ sd = structtype._ast
+ # NOTE: Not using _cxxBareType here as we don't have a side
+ cxxtype = Type(structtype.fullname())
+
+ def get(sel, f):
+ return ExprCall(f.getMethod(thisexpr=cls.var, sel=sel))
+
+ write = []
+ read = []
+
+ for (size, fields) in itertools.groupby(
+ sd.fields_member_order(), lambda f: pod_size(f.ipdltype)
+ ):
+ fields = list(fields)
+
+ if size == pod_size_sentinel:
+ for f in fields:
+ writefield = cls.checkedWrite(
+ f.ipdltype,
+ get(".", f),
+ cls.msgvar,
+ sentinelKey=f.basename,
+ actor=cls.actor,
+ )
+ readfield = cls._checkedRead(
+ f.ipdltype,
+ ExprAddrOf(get("->", f)),
+ f.basename,
+ "'"
+ + f.getMethod().name
+ + "' "
+ + "("
+ + f.ipdltype.name()
+ + ") member of "
+ + "'"
+ + structtype.name()
+ + "'",
+ )
+
+ # Wrap the read/write in a side check if the field is special.
+ if f.special:
+ writefield = cls.ifsideis(f.side, writefield)
+ readfield = cls.ifsideis(f.side, readfield)
+
+ write.append(writefield)
+ read.append(readfield)
+ else:
+ for f in fields:
+ assert not f.special
+
+ writefield = cls.checkedBulkWrite(size, fields)
+ readfield = cls.checkedBulkRead(size, fields)
+
+ write.append(writefield)
+ read.append(readfield)
+
+ read.append(StmtReturn.TRUE)
+
+ return cls.generateDecl(cxxtype, write, read)
+
+ @classmethod
+ def unionPickling(cls, uniontype):
+ # NOTE: Not using _cxxBareType here as we don't have a side
+ cxxtype = Type(uniontype.fullname())
+ ud = uniontype._ast
+
+ # Use typedef to set up an alias so it's easier to reference the struct type.
+ alias = "union__"
+ typevar = ExprVar("type")
+
+ prelude = [
+ Typedef(cxxtype, alias),
+ ]
+
+ writeswitch = StmtSwitch(typevar)
+ write = prelude + [
+ StmtDecl(Decl(Type.INT, typevar.name), init=ud.callType(cls.var)),
+ cls.checkedWrite(
+ None, typevar, cls.msgvar, sentinelKey=uniontype.name(), actor=cls.actor
+ ),
+ Whitespace.NL,
+ writeswitch,
+ ]
+
+ readswitch = StmtSwitch(typevar)
+ read = prelude + [
+ StmtDecl(Decl(Type.INT, typevar.name), init=ExprLiteral.ZERO),
+ cls._checkedRead(
+ None,
+ ExprAddrOf(typevar),
+ uniontype.name(),
+ "type of union " + uniontype.name(),
+ ),
+ Whitespace.NL,
+ readswitch,
+ ]
+
+ for c in ud.components:
+ ct = c.ipdltype
+ caselabel = CaseLabel(alias + "::" + c.enum())
+ origenum = c.enum()
+
+ writecase = StmtBlock()
+ wstmt = cls.checkedWrite(
+ c.ipdltype,
+ ExprCall(ExprSelect(cls.var, ".", c.getTypeName())),
+ cls.msgvar,
+ sentinelKey=c.enum(),
+ actor=cls.actor,
+ )
+ if c.special:
+ # Report an error if the type is special and the side is wrong
+ wstmt = cls.ifsideis(c.side, wstmt, els=cls.fatalError("wrong side!"))
+ writecase.addstmts([wstmt, StmtReturn()])
+ writeswitch.addcase(caselabel, writecase)
+
+ readcase = StmtBlock()
+ if c.special:
+ # The type comes across flipped from what the actor will be on
+ # this side; i.e. child->parent messages will have PFooChild
+ # when received on the parent side. Report an error if the sides
+ # match, and handle c.other instead.
+ readcase.addstmt(
+ cls.ifsideis(
+ c.side,
+ StmtBlock([cls.fatalError("wrong side!"), StmtReturn.FALSE]),
+ )
+ )
+ c = c.other
+ tmpvar = ExprVar("tmp")
+ ct = c.bareType(fq=True)
+ readcase.addstmts(
+ [
+ StmtDecl(Decl(ct, tmpvar.name), init=c.defaultValue(fq=True)),
+ StmtExpr(ExprAssn(ExprDeref(cls.var), ExprMove(tmpvar))),
+ cls._checkedRead(
+ c.ipdltype,
+ ExprAddrOf(
+ ExprCall(ExprSelect(cls.var, "->", c.getTypeName()))
+ ),
+ origenum,
+ "variant " + origenum + " of union " + uniontype.name(),
+ ),
+ StmtReturn.TRUE,
+ ]
+ )
+ readswitch.addcase(caselabel, readcase)
+
+ # Add the error default case
+ writeswitch.addcase(
+ DefaultLabel(),
+ StmtBlock([cls.fatalError("unknown union type"), StmtReturn()]),
+ )
+ readswitch.addcase(
+ DefaultLabel(),
+ StmtBlock([cls.fatalError("unknown union type"), StmtReturn.FALSE]),
+ )
+
+ return cls.generateDecl(cxxtype, write, read)
+
+
+# --------------------------------------------------
+
+
+class _ComputeTypeDeps(TypeVisitor):
+ """Pass that gathers the C++ types that a particular IPDL type
+ (recursively) depends on. There are three kinds of dependencies: (i)
+ types that need forward declaration; (ii) types that need a |using|
+ stmt; (iii) IPDL structs or unions which must be fully declared
+ before this struct. Some types generate multiple kinds."""
+
+ def __init__(self, fortype, unqualifiedTypedefs=False):
+ ipdl.type.TypeVisitor.__init__(self)
+ self.usingTypedefs = []
+ self.forwardDeclStmts = []
+ self.fullDeclTypes = []
+ self.fortype = fortype
+ self.unqualifiedTypedefs = unqualifiedTypedefs
+
+ def maybeTypedef(self, fqname, name, templateargs=[]):
+ if fqname != name or self.unqualifiedTypedefs:
+ self.usingTypedefs.append(Typedef(Type(fqname), name, templateargs))
+
+ 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())
+
+ # Mutually recursive fields in unions are behind indirection, so we only
+ # need a forward decl, and don't need a full type declaration.
+ if isinstance(self.fortype, UnionType) and self.fortype.mutuallyRecursiveWith(
+ su
+ ):
+ self.forwardDeclStmts.append(_makeForwardDecl(su))
+ else:
+ self.fullDeclTypes.append(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 visitMaybeType(self, m):
+ return TypeVisitor.visitMaybeType(self, m)
+
+ def visitShmemType(self, s):
+ if s in self.visited:
+ return
+ self.visited.add(s)
+ self.maybeTypedef("mozilla::ipc::Shmem", "Shmem")
+
+ def visitByteBufType(self, s):
+ if s in self.visited:
+ return
+ self.visited.add(s)
+ self.maybeTypedef("mozilla::ipc::ByteBuf", "ByteBuf")
+
+ def visitFDType(self, s):
+ if s in self.visited:
+ return
+ self.visited.add(s)
+ self.maybeTypedef("mozilla::ipc::FileDescriptor", "FileDescriptor")
+
+ def visitEndpointType(self, s):
+ if s in self.visited:
+ return
+ self.visited.add(s)
+ self.maybeTypedef("mozilla::ipc::Endpoint", "Endpoint", ["FooSide"])
+ self.visitActorType(s.actor)
+
+ def visitManagedEndpointType(self, s):
+ if s in self.visited:
+ return
+ self.visited.add(s)
+ self.maybeTypedef(
+ "mozilla::ipc::ManagedEndpoint", "ManagedEndpoint", ["FooSide"]
+ )
+ self.visitActorType(s.actor)
+
+ def visitUniquePtrType(self, s):
+ if s in self.visited:
+ return
+ self.visited.add(s)
+
+ def visitVoidType(self, v):
+ assert 0
+
+ def visitMessageType(self, v):
+ assert 0
+
+ def visitProtocolType(self, v):
+ assert 0
+
+
+def _fieldStaticAssertions(sd):
+ staticasserts = []
+ for (size, fields) in itertools.groupby(
+ sd.fields_member_order(), lambda f: pod_size(f.ipdltype)
+ ):
+ if size == pod_size_sentinel:
+ continue
+
+ fields = list(fields)
+ if len(fields) == 1:
+ continue
+
+ staticasserts.append(
+ StmtCode(
+ """
+ static_assert(
+ (offsetof(${struct}, ${last}) - offsetof(${struct}, ${first})) == ${expected},
+ "Bad assumptions about field layout!");
+ """,
+ struct=sd.name,
+ first=fields[0].memberVar(),
+ last=fields[-1].memberVar(),
+ expected=ExprLiteral.Int(size * (len(fields) - 1)),
+ )
+ )
+
+ return staticasserts
+
+
+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
+ fulldecltypes = gettypedeps.fullDeclTypes
+
+ struct = Class(sd.name, final=True)
+ struct.addstmts([Label.PRIVATE] + usingTypedefs + [Whitespace.NL, Label.PUBLIC])
+
+ constreftype = Type(sd.name, const=True, ref=True)
+
+ def fieldsAsParamList():
+ # FIXME Bug 1547019 inType() should do the right thing once
+ # _cxxTypeCanOnlyMove is replaced with
+ # _cxxTypeNeedsMoveForSend
+ return [
+ Decl(
+ f.forceMoveType() if _cxxTypeCanOnlyMove(f.ipdltype) else f.inType(),
+ f.argVar().name,
+ )
+ for f in sd.fields_ipdl_order()
+ ]
+
+ # 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):
+ assert len(sd.fields) == len(sd.packed_field_order)
+
+ # Struct()
+ defctor = ConstructorDefn(ConstructorDecl(sd.name, force_inline=True))
+
+ # We want to explicitly default-construct every member of the struct.
+ # This will initialize all primitives which wouldn't be initialized
+ # normally to their default values, and will initialize any actor member
+ # pointers to the correct default value of `nullptr`. Other C++ types
+ # with custom constructors must also provide a default constructor.
+ defctor.memberinits = [
+ ExprMemberInit(f.memberVar()) for f in sd.fields_member_order()
+ ]
+ struct.addstmts([defctor, Whitespace.NL])
+
+ # Struct(const field1& _f1, ...)
+ valctor = ConstructorDefn(
+ ConstructorDecl(sd.name, params=fieldsAsParamList(), force_inline=True)
+ )
+ valctor.memberinits = []
+ for f in sd.fields_member_order():
+ arg = f.argVar()
+ if _cxxTypeCanOnlyMove(f.ipdltype):
+ arg = ExprMove(arg)
+ valctor.memberinits.append(ExprMemberInit(f.memberVar(), args=[arg]))
+
+ struct.addstmts([valctor, Whitespace.NL])
+
+ # The default copy, move, and assignment constructors, and the default
+ # destructor, will do the right thing.
+
+ if sd.comparable:
+ # bool operator==(const Struct& _o)
+ ovar = ExprVar("_o")
+ opeqeq = MethodDefn(
+ MethodDecl(
+ "operator==",
+ params=[Decl(constreftype, ovar.name)],
+ ret=Type.BOOL,
+ const=True,
+ )
+ )
+ for f in sd.fields_ipdl_order():
+ 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])
+
+ # bool operator!=(const Struct& _o)
+ opneq = MethodDefn(
+ MethodDecl(
+ "operator!=",
+ params=[Decl(constreftype, ovar.name)],
+ ret=Type.BOOL,
+ const=True,
+ )
+ )
+ opneq.addstmt(StmtReturn(ExprNot(ExprCall(ExprVar("operator=="), args=[ovar]))))
+ struct.addstmts([opneq, Whitespace.NL])
+
+ # field1& f1()
+ # const field1& f1() const
+ for f in sd.fields_ipdl_order():
+ get = MethodDefn(
+ MethodDecl(
+ f.getMethod().name, params=[], ret=f.refType(), force_inline=True
+ )
+ )
+ get.addstmt(StmtReturn(f.refExpr()))
+
+ getconstdecl = deepcopy(get.decl)
+ getconstdecl.ret = f.constRefType()
+ getconstdecl.const = True
+ getconst = MethodDefn(getconstdecl)
+ getconst.addstmt(StmtReturn(f.constRefExpr()))
+
+ struct.addstmts([get, getconst, Whitespace.NL])
+
+ # private:
+ struct.addstmt(Label.PRIVATE)
+
+ # Static assertions to ensure our assumptions about field layout match
+ # what the compiler is actually producing. We define this as a member
+ # function, rather than throwing the assertions in the constructor or
+ # similar, because we don't want to evaluate the static assertions every
+ # time the header file containing the structure is included.
+ staticasserts = _fieldStaticAssertions(sd)
+ if staticasserts:
+ method = MethodDefn(
+ MethodDecl("StaticAssertions", params=[], ret=Type.VOID, const=True)
+ )
+ method.addstmts(staticasserts)
+ struct.addstmts([method])
+
+ # members
+ struct.addstmts(
+ [
+ StmtDecl(Decl(_effectiveMemberType(f), f.memberVar().name))
+ for f in sd.fields_member_order()
+ ]
+ )
+
+ return forwarddeclstmts, fulldecltypes, struct
+
+
+def _effectiveMemberType(f):
+ effective_type = f.bareType()
+ # Structs must be copyable for backwards compatibility reasons, so we use
+ # CopyableTArray<T> as their member type for arrays. This is not exposed
+ # in the method signatures, these keep using nsTArray<T>, which is a base
+ # class of CopyableTArray<T>.
+ if effective_type.name == "nsTArray":
+ effective_type.name = "CopyableTArray"
+ return effective_type
+
+
+# --------------------------------------------------
+
+
+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/move ctor, assignment operators,
+ # 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
+ # - move ctor
+ # - case in generic copy ctor
+ # - copy operator= impl
+ # - move 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=True)
+ # const Union&, i.e., Union type with inparam semantics
+ inClsType = Type(ud.name, const=True, ref=True)
+ refClsType = Type(ud.name, ref=True)
+ rvalueRefClsType = Type(ud.name, rvalref=True)
+ 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
+
+ def voidCast(expr):
+ return ExprCast(expr, Type.VOID, static=True)
+
+ # 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
+ fulldecltypes = gettypedeps.fullDeclTypes
+
+ # 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=True
+ )
+ )
+ getptr.addstmt(StmtReturn(c.ptrToSelfExpr()))
+
+ getptrconst = MethodDefn(
+ MethodDecl(
+ c.getConstPtrName(),
+ params=[],
+ ret=c.constPtrToType(),
+ const=True,
+ force_inline=True,
+ )
+ )
+ 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=True, force_inline=True)
+ )
+ 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=True,
+ force_inline=True,
+ )
+ )
+ 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=True),
+ memberinits=[ExprMemberInit(mtypevar, [tnonevar])],
+ ),
+ Whitespace.NL,
+ ]
+ )
+
+ # Union(const T&) copy & Union(T&&) move ctors
+ othervar = ExprVar("aOther")
+ for c in ud.components:
+ if not _cxxTypeCanOnlyMove(c.ipdltype):
+ 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])
+
+ if not _cxxTypeCanMove(c.ipdltype) or _cxxTypeNeedsMoveForSend(c.ipdltype):
+ continue
+ movector = ConstructorDefn(
+ ConstructorDecl(ud.name, params=[Decl(c.forceMoveType(), othervar.name)])
+ )
+ movector.addstmts(
+ [
+ StmtExpr(c.callCtor(ExprMove(othervar))),
+ StmtExpr(ExprAssn(mtypevar, c.enumvar())),
+ ]
+ )
+ cls.addstmts([movector, Whitespace.NL])
+
+ unionNeedsMove = any(_cxxTypeCanOnlyMove(c.ipdltype) for c in ud.components)
+
+ # Union(const Union&) copy ctor
+ if not unionNeedsMove:
+ 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(Union&&) move ctor
+ movector = ConstructorDefn(
+ ConstructorDecl(ud.name, params=[Decl(rvalueRefClsType, othervar.name)])
+ )
+ othertypevar = ExprVar("t")
+ moveswitch = StmtSwitch(othertypevar)
+ for c in ud.components:
+ case = StmtBlock()
+ if c.recursive:
+ # This is sound as we set othervar.mTypeVar to T__None after the
+ # switch. The pointer in the union will be left dangling.
+ case.addstmts(
+ [
+ # ptr_C() = other.ptr_C()
+ StmtExpr(
+ ExprAssn(
+ c.callGetPtr(),
+ ExprCall(
+ ExprSelect(othervar, ".", ExprVar(c.getPtrName()))
+ ),
+ )
+ )
+ ]
+ )
+ else:
+ case.addstmts(
+ [
+ # new ... (Move(other.get_C()))
+ StmtExpr(
+ c.callCtor(
+ ExprMove(
+ ExprCall(ExprSelect(othervar, ".", c.getTypeName()))
+ )
+ )
+ ),
+ # other.MaybeDestroy(T__None)
+ StmtExpr(
+ voidCast(
+ ExprCall(
+ ExprSelect(othervar, ".", maybedtorvar), args=[tnonevar]
+ )
+ )
+ ),
+ ]
+ )
+ case.addstmts([StmtBreak()])
+ moveswitch.addcase(CaseLabel(c.enum()), case)
+ moveswitch.addcase(CaseLabel(tnonevar.name), StmtBlock([StmtBreak()]))
+ moveswitch.addcase(
+ DefaultLabel(), StmtBlock([_logicError("unreached"), StmtReturn()])
+ )
+ movector.addstmts(
+ [
+ StmtExpr(callAssertSanity(uvar=othervar)),
+ StmtDecl(Decl(typetype, othertypevar.name), init=ud.callType(othervar)),
+ moveswitch,
+ StmtExpr(ExprAssn(ExprSelect(othervar, ".", mtypevar), tnonevar)),
+ StmtExpr(ExprAssn(mtypevar, othertypevar)),
+ ]
+ )
+ cls.addstmts([movector, Whitespace.NL])
+
+ # ~Union()
+ dtor = DestructorDefn(DestructorDecl(ud.name))
+ # The void cast prevents Coverity from complaining about missing return
+ # value checks.
+ dtor.addstmt(StmtExpr(voidCast(callMaybeDestroy(tnonevar))))
+ cls.addstmts([dtor, Whitespace.NL])
+
+ # type()
+ typemeth = MethodDefn(
+ MethodDecl("type", ret=typetype, const=True, force_inline=True)
+ )
+ typemeth.addstmt(StmtReturn(mtypevar))
+ cls.addstmts([typemeth, Whitespace.NL])
+
+ # Union& operator= methods
+ rhsvar = ExprVar("aRhs")
+ for c in ud.components:
+ if not _cxxTypeCanOnlyMove(c.ipdltype):
+ # Union& operator=(const T&)
+ 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=(T&&)
+ if not _cxxTypeCanMove(c.ipdltype) or _cxxTypeNeedsMoveForSend(c.ipdltype):
+ continue
+
+ opeq = MethodDefn(
+ MethodDecl(
+ "operator=",
+ params=[Decl(c.forceMoveType(), rhsvar.name)],
+ ret=refClsType,
+ )
+ )
+ opeq.addstmts(
+ [
+ # might need to placement-delete old value first
+ maybeReconstruct(c, c.enumvar()),
+ StmtExpr(c.callOperatorEq(ExprMove(rhsvar))),
+ StmtExpr(ExprAssn(mtypevar, c.enumvar())),
+ StmtReturn(ExprDeref(ExprVar.THIS)),
+ ]
+ )
+ cls.addstmts([opeq, Whitespace.NL])
+
+ # Union& operator=(const Union&)
+ if not unionNeedsMove:
+ 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=True)
+ ),
+ 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])
+
+ # Union& operator=(Union&&)
+ opeq = MethodDefn(
+ MethodDecl(
+ "operator=", params=[Decl(rvalueRefClsType, rhsvar.name)], ret=refClsType
+ )
+ )
+ rhstypevar = ExprVar("t")
+ opeqswitch = StmtSwitch(rhstypevar)
+ for c in ud.components:
+ case = StmtBlock()
+ if c.recursive:
+ case.addstmts(
+ [
+ StmtExpr(voidCast(callMaybeDestroy(tnonevar))),
+ StmtExpr(
+ ExprAssn(
+ c.callGetPtr(),
+ ExprCall(ExprSelect(rhsvar, ".", ExprVar(c.getPtrName()))),
+ )
+ ),
+ ]
+ )
+ else:
+ case.addstmts(
+ [
+ maybeReconstruct(c, rhstypevar),
+ StmtExpr(
+ c.callOperatorEq(
+ ExprMove(ExprCall(ExprSelect(rhsvar, ".", c.getTypeName())))
+ )
+ ),
+ # other.MaybeDestroy(T__None)
+ StmtExpr(
+ voidCast(
+ ExprCall(
+ ExprSelect(rhsvar, ".", maybedtorvar), args=[tnonevar]
+ )
+ )
+ ),
+ ]
+ )
+ case.addstmts([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(voidCast(callMaybeDestroy(rhstypevar))), 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(ExprSelect(rhsvar, ".", mtypevar), tnonevar)),
+ StmtExpr(ExprAssn(mtypevar, rhstypevar)),
+ StmtReturn(ExprDeref(ExprVar.THIS)),
+ ]
+ )
+ cls.addstmts([opeq, Whitespace.NL])
+
+ if ud.comparable:
+ # bool operator==(const T&)
+ for c in ud.components:
+ opeqeq = MethodDefn(
+ MethodDecl(
+ "operator==",
+ params=[Decl(c.inType(), rhsvar.name)],
+ ret=Type.BOOL,
+ const=True,
+ )
+ )
+ 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=True,
+ )
+ )
+ 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=True)
+ )
+ getvalue.addstmts(
+ [
+ StmtExpr(callAssertSanity(expectTypeVar=c.enumvar())),
+ StmtReturn(ExprDeref(c.callGetPtr())),
+ ]
+ )
+
+ getconstvalue = MethodDefn(
+ MethodDecl(
+ getConstValueVar.name,
+ ret=c.constRefType(),
+ const=True,
+ force_inline=True,
+ )
+ )
+ getconstvalue.addstmts(
+ [
+ StmtExpr(callAssertSanity(expectTypeVar=c.enumvar())),
+ StmtReturn(c.getConstValue()),
+ ]
+ )
+
+ cls.addstmts([getvalue, getconstvalue])
+
+ optype = MethodDefn(MethodDecl("", typeop=c.refType(), force_inline=True))
+ optype.addstmt(StmtReturn(ExprCall(getValueVar)))
+ opconsttype = MethodDefn(
+ MethodDecl("", const=True, typeop=c.constRefType(), force_inline=True)
+ )
+ opconsttype.addstmt(StmtReturn(ExprCall(getConstValueVar)))
+
+ cls.addstmts([optype, opconsttype, Whitespace.NL])
+ # private vars
+ cls.addstmts(
+ [
+ Label.PRIVATE,
+ StmtDecl(Decl(valuetype, mvaluevar.name)),
+ StmtDecl(Decl(typetype, mtypevar.name)),
+ ]
+ )
+
+ return forwarddeclstmts, fulldecltypes, 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"), "IProtocol"),
+ Typedef(Type("IPC::Message"), "Message"),
+ Typedef(Type("base::ProcessHandle"), "ProcessHandle"),
+ Typedef(Type("mozilla::ipc::MessageChannel"), "MessageChannel"),
+ Typedef(Type("mozilla::ipc::SharedMemory"), "SharedMemory"),
+ ]
+
+ 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]
+ )
+
+ actortype = ActorType(tu.protocol.decl.type)
+ traitsdecl, traitsdefn = _ParamTraits.actorPickling(actortype, self.side)
+
+ self.hdrfile.addthings([traitsdecl, Whitespace.NL] + _includeGuardEnd(hf))
+
+ # make the .cpp file
+ if (self.protocol.name, self.side) not in VIRTUAL_CALL_CLASSES:
+ if (self.protocol.name, self.side) in DIRECT_CALL_OVERRIDES:
+ (_, header_file) = DIRECT_CALL_OVERRIDES[self.protocol.name, self.side]
+ else:
+ assert self.protocol.name.startswith("P")
+ header_file = "{}/{}{}.h".format(
+ "/".join(n.name for n in self.protocol.namespaces),
+ self.protocol.name[1:],
+ self.side.capitalize(),
+ )
+ self.externalIncludes.add(header_file)
+
+ cf.addthings(
+ [
+ _DISCLAIMER,
+ Whitespace.NL,
+ CppDirective(
+ "include",
+ '"' + _protocolHeaderName(self.protocol, self.side) + '.h"',
+ ),
+ ]
+ + setToIncludes(self.externalIncludes)
+ )
+
+ cf.addthings(
+ (
+ [Whitespace.NL]
+ + [
+ CppDirective("include", '"%s.h"' % (inc))
+ for inc in self.protocolCxxIncludes
+ ]
+ + [Whitespace.NL]
+ + [
+ CppDirective("include", '"%s"' % filename)
+ for filename in ipdl.builtin.CppIncludes
+ ]
+ + [Whitespace.NL]
+ )
+ )
+
+ cppns = makeNamespace(self.protocol, cf)
+ cppns.addstmts(
+ [Whitespace.NL, Whitespace.NL, clsdefn, Whitespace.NL, Whitespace.NL]
+ )
+
+ cf.addthing(traitsdefn)
+
+ def visitUsingStmt(self, using):
+ if using.header is None:
+ return
+
+ if using.canBeForwardDeclared() and not using.decl.type.isUniquePtr():
+ 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.externalIncludes.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.addcode(
+ """
+ #ifdef DEBUG
+ #include "prenv.h"
+ #endif // DEBUG
+
+ #include "mozilla/Tainting.h"
+ #include "mozilla/ipc/MessageChannel.h"
+ #include "mozilla/ipc/ProtocolUtils.h"
+ """
+ )
+
+ self.protocol = p
+ ptype = p.decl.type
+ toplevel = p.decl.type.toplevel()
+
+ hasAsyncReturns = False
+ for md in p.messageDecls:
+ if md.hasAsyncReturns():
+ hasAsyncReturns = True
+ break
+
+ 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 == "parent":
+ self.hdrfile.addthings(
+ [_makeForwardDeclForQClass("nsIFile", []), Whitespace.NL]
+ )
+
+ self.cls = Class(self.clsname, inherits=inherits, abstract=True)
+
+ self.cls.addstmt(Label.PRIVATE)
+ 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.addstmt(
+ FriendClassDecl(_actorName(friend.fullname(), self.prettyside))
+ )
+
+ 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)
+
+ if hasAsyncReturns:
+ self.cls.addstmt(Label.PUBLIC)
+ for md in p.messageDecls:
+ if self.sendsMessage(md) and md.hasAsyncReturns():
+ self.cls.addstmt(
+ Typedef(_makePromise(md.returns, self.side), md.promiseName())
+ )
+ if self.receivesMessage(md) and md.hasAsyncReturns():
+ self.cls.addstmt(
+ Typedef(_makeResolver(md.returns, self.side), md.resolverName())
+ )
+ self.cls.addstmt(Whitespace.NL)
+
+ self.cls.addstmt(Label.PROTECTED)
+ # 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
+ returnsems = "resolver" if md.decl.type.isAsync() else "out"
+ recvDecl = MethodDecl(
+ md.recvMethod(),
+ params=md.makeCxxParams(
+ paramsems="move",
+ returnsems=returnsems,
+ side=self.side,
+ implicit=implicit,
+ direction="recv",
+ ),
+ ret=Type("mozilla::ipc::IPCResult"),
+ methodspec=MethodSpec.VIRTUAL,
+ )
+
+ # These method implementations cause problems when trying to
+ # override them with different types in a direct call class.
+ #
+ # For the `isdtor` case there's a simple solution: it doesn't
+ # make much sense to specify arguments and then completely
+ # ignore them, and the no-arg case isn't a problem for
+ # overriding.
+ if isctor or (isdtor and not md.inParams):
+ defaultRecv = MethodDefn(recvDecl)
+ defaultRecv.addcode("return IPC_OK();\n")
+ self.cls.addstmt(defaultRecv)
+ elif (self.protocol.name, self.side) in VIRTUAL_CALL_CLASSES:
+ # If we're using virtual calls, we need the methods to be
+ # declared on the base class.
+ recvDecl.methodspec = MethodSpec.PURE
+ self.cls.addstmt(StmtDecl(recvDecl))
+
+ # If we're using virtual calls, we need the methods to be declared on
+ # the base class.
+ if (self.protocol.name, self.side) in VIRTUAL_CALL_CLASSES:
+ for md in p.messageDecls:
+ managed = md.decl.type.constructedType()
+ if not ptype.isManagerOf(managed) or md.decl.type.isDtor():
+ continue
+
+ # add the Alloc interface for managed actors
+ actortype = md.actorDecl().bareType(self.side)
+
+ if managed.isRefcounted():
+ if not self.receivesMessage(md):
+ continue
+
+ actortype.ptr = False
+ actortype = _alreadyaddrefed(actortype)
+
+ self.cls.addstmt(
+ StmtDecl(
+ MethodDecl(
+ _allocMethod(managed, self.side),
+ params=md.makeCxxParams(
+ side=self.side, implicit=False, direction="recv"
+ ),
+ ret=actortype,
+ methodspec=MethodSpec.PURE,
+ )
+ )
+ )
+
+ # add the Dealloc interface for all managed non-refcounted actors,
+ # even without ctors. This is useful for protocols which use
+ # ManagedEndpoint for construction.
+ for managed in ptype.manages:
+ if managed.isRefcounted():
+ continue
+
+ self.cls.addstmt(
+ StmtDecl(
+ MethodDecl(
+ _deallocMethod(managed, self.side),
+ params=[
+ Decl(p.managedCxxType(managed, self.side), "aActor")
+ ],
+ ret=Type.BOOL,
+ methodspec=MethodSpec.PURE,
+ )
+ )
+ )
+
+ 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=True, ptr=True), "aReason"),
+ ],
+ methodspec=MethodSpec.OVERRIDE,
+ )
+ )
+
+ # bool ShouldContinueFromReplyTimeout(); default to |true|
+ shouldcontinue = MethodDefn(
+ MethodDecl(
+ p.shouldContinueFromTimeoutVar().name,
+ ret=Type.BOOL,
+ methodspec=MethodSpec.OVERRIDE,
+ )
+ )
+ shouldcontinue.addcode("return true;\n")
+
+ # void Entered*()/Exited*(); default to no-op
+ entered = MethodDefn(
+ MethodDecl(p.enteredCxxStackVar().name, methodspec=MethodSpec.OVERRIDE)
+ )
+ exited = MethodDefn(
+ MethodDecl(p.exitedCxxStackVar().name, methodspec=MethodSpec.OVERRIDE)
+ )
+ enteredcall = MethodDefn(
+ MethodDecl(p.enteredCallVar().name, methodspec=MethodSpec.OVERRIDE)
+ )
+ exitedcall = MethodDefn(
+ MethodDecl(p.exitedCallVar().name, methodspec=MethodSpec.OVERRIDE)
+ )
+
+ 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():
+ name = ExprLiteral.String(_actorName(p.name, self.side))
+ ctor.memberinits = [
+ ExprMemberInit(
+ ExprVar("mozilla::ipc::IToplevelProtocol"),
+ [name, _protocolId(ptype), side],
+ )
+ ]
+ else:
+ ctor.memberinits = [
+ ExprMemberInit(
+ ExprVar("mozilla::ipc::IProtocol"), [_protocolId(ptype), side]
+ )
+ ]
+
+ ctor.addcode("MOZ_COUNT_CTOR(${clsname});\n", clsname=self.clsname)
+ self.cls.addstmts([ctor, Whitespace.NL])
+
+ # ~Actor()
+ dtor = DestructorDefn(
+ DestructorDecl(self.clsname, methodspec=MethodSpec.VIRTUAL)
+ )
+ dtor.addcode("MOZ_COUNT_DTOR(${clsname});\n", clsname=self.clsname)
+
+ self.cls.addstmts([dtor, Whitespace.NL])
+
+ if ptype.isRefcounted():
+ self.cls.addcode(
+ """
+ NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
+ """
+ )
+ self.cls.addstmt(Label.PROTECTED)
+ self.cls.addcode(
+ """
+ void ActorAlloc() final { AddRef(); }
+ void ActorDealloc() final { Release(); }
+ """
+ )
+
+ self.cls.addstmt(Label.PUBLIC)
+ if not ptype.isToplevel():
+ if 1 == len(p.managers):
+ # manager() const
+ managertype = p.managerActorType(self.side, ptr=True)
+ managermeth = MethodDefn(
+ MethodDecl("Manager", ret=managertype, const=True)
+ )
+ managermeth.addcode(
+ """
+ return static_cast<${type}>(IProtocol::Manager());
+ """,
+ type=managertype,
+ )
+
+ self.cls.addstmts([managermeth, Whitespace.NL])
+
+ def actorFromIter(itervar):
+ return ExprCode("${iter}.Get()->GetKey()", iter=itervar)
+
+ def forLoopOverHashtable(hashtable, itervar, const=False):
+ itermeth = "ConstIter" if const else "Iter"
+ return StmtFor(
+ init=ExprCode(
+ "auto ${itervar} = ${hashtable}.${itermeth}()",
+ itervar=itervar,
+ hashtable=hashtable,
+ itermeth=itermeth,
+ ),
+ cond=ExprCode("!${itervar}.Done()", itervar=itervar),
+ update=ExprCode("${itervar}.Next()", itervar=itervar),
+ )
+
+ # Managed[T](Array& inout) const
+ # const Array<T>& Managed() const
+ for managed in ptype.manages:
+ container = p.managedVar(managed, self.side)
+
+ meth = MethodDefn(
+ MethodDecl(
+ p.managedMethod(managed, self.side).name,
+ params=[
+ Decl(
+ _cxxArrayType(
+ p.managedCxxType(managed, self.side), ref=True
+ ),
+ "aArr",
+ )
+ ],
+ const=True,
+ )
+ )
+ meth.addcode("${container}.ToArray(aArr);\n", container=container)
+
+ refmeth = MethodDefn(
+ MethodDecl(
+ p.managedMethod(managed, self.side).name,
+ params=[],
+ ret=p.managedVarType(managed, self.side, const=True, ref=True),
+ const=True,
+ )
+ )
+ refmeth.addcode("return ${container};\n", container=container)
+
+ self.cls.addstmts([meth, refmeth, Whitespace.NL])
+
+ # AllManagedActors(Array& inout) const
+ arrvar = ExprVar("arr__")
+ managedmeth = MethodDefn(
+ MethodDecl(
+ "AllManagedActors",
+ params=[
+ Decl(
+ _cxxArrayType(_refptr(_cxxLifecycleProxyType()), ref=True),
+ arrvar.name,
+ )
+ ],
+ methodspec=MethodSpec.OVERRIDE,
+ const=True,
+ )
+ )
+
+ # Count the number of managed actors, and allocate space in the output array.
+ managedmeth.addcode(
+ """
+ uint32_t total = 0;
+ """
+ )
+ for managed in ptype.manages:
+ managedmeth.addcode(
+ """
+ total += ${container}.Count();
+ """,
+ container=p.managedVar(managed, self.side),
+ )
+ managedmeth.addcode(
+ """
+ arr__.SetCapacity(total);
+
+ """
+ )
+
+ for managed in ptype.manages:
+ managedmeth.addcode(
+ """
+ for (auto it = ${container}.ConstIter(); !it.Done(); it.Next()) {
+ arr__.AppendElement(it.Get()->GetKey()->GetLifecycleProxy());
+ }
+
+ """,
+ container=p.managedVar(managed, self.side),
+ )
+
+ self.cls.addstmts([managedmeth, Whitespace.NL])
+
+ # OpenPEndpoint(...)/BindPEndpoint(...)
+ for managed in ptype.manages:
+ self.genManagedEndpoint(managed)
+
+ # 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 = ExprCode("msg__.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)
+
+ # add default cases
+ default = StmtCode(
+ """
+ return MsgNotKnown;
+ """
+ )
+ self.asyncSwitch.addcase(DefaultLabel(), default)
+ if toplevel.isSync() or toplevel.isInterrupt():
+ self.syncSwitch.addcase(DefaultLabel(), default)
+ if toplevel.isInterrupt():
+ self.interruptSwitch.addcase(DefaultLabel(), default)
+
+ self.cls.addstmts(self.implementManagerIface())
+
+ def makeHandlerMethod(name, switch, hasReply, dispatches=False):
+ params = [Decl(Type("Message", const=True, ref=True), msgvar.name)]
+ if hasReply:
+ params.append(Decl(Type("Message", ref=True, ptr=True), replyvar.name))
+
+ method = MethodDefn(
+ MethodDecl(
+ name,
+ methodspec=MethodSpec.OVERRIDE,
+ params=params,
+ ret=_Result.Type(),
+ )
+ )
+
+ if not switch:
+ method.addcode(
+ """
+ MOZ_ASSERT_UNREACHABLE("message protocol not supported");
+ return MsgNotKnown;
+ """
+ )
+ return method
+
+ if dispatches:
+ method.addcode(
+ """
+ int32_t route__ = ${msgvar}.routing_id();
+ if (MSG_ROUTING_CONTROL != route__) {
+ IProtocol* routed__ = Lookup(route__);
+ if (!routed__ || !routed__->GetLifecycleProxy()) {
+ ${logignored}
+ return MsgProcessed;
+ }
+
+ RefPtr<mozilla::ipc::ActorLifecycleProxy> proxy__ =
+ routed__->GetLifecycleProxy();
+ return proxy__->Get()->${name}($,{args});
+ }
+
+ """,
+ msgvar=msgvar,
+ logignored=self.logMessage(
+ None, ExprAddrOf(msgvar), "Ignored message for dead actor"
+ ),
+ name=name,
+ args=[p.name for p in params],
+ )
+
+ # 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=False,
+ dispatches=dispatches,
+ ),
+ Whitespace.NL,
+ ]
+ )
+ self.cls.addstmts(
+ [
+ makeHandlerMethod(
+ "OnMessageReceived",
+ self.syncSwitch,
+ hasReply=True,
+ dispatches=dispatches,
+ ),
+ Whitespace.NL,
+ ]
+ )
+ self.cls.addstmts(
+ [
+ makeHandlerMethod(
+ "OnCallReceived",
+ self.interruptSwitch,
+ hasReply=True,
+ dispatches=dispatches,
+ ),
+ Whitespace.NL,
+ ]
+ )
+
+ clearsubtreevar = ExprVar("ClearSubtree")
+
+ if ptype.isToplevel():
+ # OnChannelClose()
+ onclose = MethodDefn(
+ MethodDecl("OnChannelClose", methodspec=MethodSpec.OVERRIDE)
+ )
+ onclose.addcode(
+ """
+ DestroySubtree(NormalShutdown);
+ ClearSubtree();
+ DeallocShmems();
+ if (GetLifecycleProxy()) {
+ GetLifecycleProxy()->Release();
+ }
+ """
+ )
+ self.cls.addstmts([onclose, Whitespace.NL])
+
+ # OnChannelError()
+ onerror = MethodDefn(
+ MethodDecl("OnChannelError", methodspec=MethodSpec.OVERRIDE)
+ )
+ onerror.addcode(
+ """
+ DestroySubtree(AbnormalShutdown);
+ ClearSubtree();
+ DeallocShmems();
+ if (GetLifecycleProxy()) {
+ GetLifecycleProxy()->Release();
+ }
+ """
+ )
+ self.cls.addstmts([onerror, Whitespace.NL])
+
+ if ptype.isToplevel() and ptype.isInterrupt():
+ processnative = MethodDefn(
+ MethodDecl("ProcessNativeEventsInInterruptCall", ret=Type.VOID)
+ )
+ processnative.addcode(
+ """
+ #ifdef OS_WIN
+ GetIPCChannel()->ProcessNativeEventsInInterruptCall();
+ #else
+ FatalError("This method is Windows-only");
+ #endif
+ """
+ )
+
+ self.cls.addstmts([processnative, Whitespace.NL])
+
+ # private methods
+ self.cls.addstmt(Label.PRIVATE)
+
+ # ClearSubtree()
+ clearsubtree = MethodDefn(MethodDecl(clearsubtreevar.name))
+ for managed in ptype.manages:
+ clearsubtree.addcode(
+ """
+ for (auto it = ${container}.Iter(); !it.Done(); it.Next()) {
+ it.Get()->GetKey()->ClearSubtree();
+ }
+ for (auto it = ${container}.Iter(); !it.Done(); it.Next()) {
+ // Recursively releasing ${container} kids.
+ auto* proxy = it.Get()->GetKey()->GetLifecycleProxy();
+ NS_IF_RELEASE(proxy);
+ }
+ ${container}.Clear();
+
+ """,
+ container=p.managedVar(managed, self.side),
+ )
+
+ # don't release our own IPC reference: either the manager will do it,
+ # or we're toplevel
+ self.cls.addstmts([clearsubtree, Whitespace.NL])
+
+ for managed in ptype.manages:
+ self.cls.addstmts(
+ [
+ StmtDecl(
+ Decl(
+ p.managedVarType(managed, self.side),
+ p.managedVar(managed, self.side).name,
+ )
+ )
+ ]
+ )
+
+ def genManagedEndpoint(self, managed):
+ hereEp = "ManagedEndpoint<%s>" % _actorName(managed.name(), self.side)
+ thereEp = "ManagedEndpoint<%s>" % _actorName(
+ managed.name(), _otherSide(self.side)
+ )
+
+ actor = _HybridDecl(ipdl.type.ActorType(managed), "aActor")
+
+ # ManagedEndpoint<PThere> OpenPEndpoint(PHere* aActor)
+ openmeth = MethodDefn(
+ MethodDecl(
+ "Open%sEndpoint" % managed.name(),
+ params=[
+ Decl(self.protocol.managedCxxType(managed, self.side), actor.name)
+ ],
+ ret=Type(thereEp),
+ )
+ )
+ openmeth.addcode(
+ """
+ $*{bind}
+ return ${thereEp}(mozilla::ipc::PrivateIPDLInterface(), aActor->Id());
+ """,
+ bind=self.bindManagedActor(actor, errfn=ExprCall(ExprVar(thereEp))),
+ thereEp=thereEp,
+ )
+
+ # void BindPEndpoint(ManagedEndpoint<PHere>&& aEndpoint, PHere* aActor)
+ bindmeth = MethodDefn(
+ MethodDecl(
+ "Bind%sEndpoint" % managed.name(),
+ params=[
+ Decl(Type(hereEp), "aEndpoint"),
+ Decl(self.protocol.managedCxxType(managed, self.side), actor.name),
+ ],
+ ret=Type.BOOL,
+ )
+ )
+ bindmeth.addcode(
+ """
+ MOZ_RELEASE_ASSERT(aEndpoint.ActorId(), "Invalid Endpoint!");
+ $*{bind}
+ return true;
+ """,
+ bind=self.bindManagedActor(
+ actor, errfn=ExprLiteral.FALSE, idexpr=ExprCode("*aEndpoint.ActorId()")
+ ),
+ )
+
+ self.cls.addstmts([openmeth, bindmeth, Whitespace.NL])
+
+ def implementManagerIface(self):
+ p = self.protocol
+ protocolbase = Type("IProtocol", ptr=True)
+
+ methods = []
+
+ if p.decl.type.isToplevel():
+
+ # "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
+ )
+
+ # 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),
+ ],
+ methodspec=MethodSpec.OVERRIDE,
+ )
+ )
+
+ if not len(p.managesStmts):
+ removemanagee.addcode(
+ """
+ FatalError("unreached");
+ return;
+ """
+ )
+ else:
+ switchontype = StmtSwitch(pvar)
+ for managee in p.managesStmts:
+ manageeipdltype = managee.decl.type
+ manageecxxtype = _cxxBareType(
+ ipdl.type.ActorType(manageeipdltype), self.side
+ )
+ case = ExprCode(
+ """
+ {
+ ${manageecxxtype} actor = static_cast<${manageecxxtype}>(aListener);
+ auto& container = ${container};
+
+ // Use a temporary variable here so all the assertion expressions
+ // in the MOZ_RELEASE_ASSERT call below are textually identical;
+ // the linker can then merge the strings from the assertion macro(s).
+ MOZ_RELEASE_ASSERT(container.Contains(actor),
+ "actor not managed by this!");
+ container.RemoveEntry(actor);
+
+ auto* proxy = actor->GetLifecycleProxy();
+ NS_IF_RELEASE(proxy);
+ return;
+ }
+ """,
+ manageecxxtype=manageecxxtype,
+ container=p.managedVar(manageeipdltype, self.side),
+ )
+ switchontype.addcase(CaseLabel(_protocolId(manageeipdltype).name), case)
+ switchontype.addcase(
+ DefaultLabel(),
+ ExprCode(
+ """
+ FatalError("unreached");
+ return;
+ """
+ ),
+ )
+ removemanagee.addstmt(switchontype)
+
+ # The `DeallocManagee` method is called for managed actors to trigger
+ # deallocation when ActorLifecycleProxy is freed.
+ deallocmanagee = MethodDefn(
+ MethodDecl(
+ p.deallocManageeMethod().name,
+ params=[
+ Decl(_protocolIdType(), pvar.name),
+ Decl(protocolbase, listenervar.name),
+ ],
+ methodspec=MethodSpec.OVERRIDE,
+ )
+ )
+
+ if not len(p.managesStmts):
+ deallocmanagee.addcode(
+ """
+ FatalError("unreached");
+ return;
+ """
+ )
+ else:
+ switchontype = StmtSwitch(pvar)
+ for managee in p.managesStmts:
+ manageeipdltype = managee.decl.type
+ # Reference counted actor types don't have corresponding
+ # `Dealloc` methods, as they are deallocated by releasing the
+ # IPDL-held reference.
+ if manageeipdltype.isRefcounted():
+ continue
+
+ case = StmtCode(
+ """
+ ${concrete}->${dealloc}(static_cast<${type}>(aListener));
+ return;
+ """,
+ concrete=self.concreteThis(),
+ dealloc=_deallocMethod(manageeipdltype, self.side),
+ type=_cxxBareType(ipdl.type.ActorType(manageeipdltype), self.side),
+ )
+ switchontype.addcase(CaseLabel(_protocolId(manageeipdltype).name), case)
+ switchontype.addcase(
+ DefaultLabel(),
+ StmtCode(
+ """
+ FatalError("unreached");
+ return;
+ """
+ ),
+ )
+ deallocmanagee.addstmt(switchontype)
+
+ return methods + [removemanagee, deallocmanagee, Whitespace.NL]
+
+ def genShmemCreatedHandler(self):
+ assert self.protocol.decl.type.isToplevel()
+
+ return StmtCode(
+ """
+ {
+ if (!ShmemCreated(${msgvar})) {
+ return MsgPayloadError;
+ }
+ return MsgProcessed;
+ }
+ """,
+ msgvar=self.msgvar,
+ )
+
+ def genShmemDestroyedHandler(self):
+ assert self.protocol.decl.type.isToplevel()
+
+ return StmtCode(
+ """
+ {
+ if (!ShmemDestroyed(${msgvar})) {
+ return MsgPayloadError;
+ }
+ return MsgProcessed;
+ }
+ """,
+ msgvar=self.msgvar,
+ )
+
+ # -------------------------------------------------------------------------
+ # 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 concreteThis(self):
+ if (self.protocol.name, self.side) in VIRTUAL_CALL_CLASSES:
+ return ExprVar.THIS
+
+ if (self.protocol.name, self.side) in DIRECT_CALL_OVERRIDES:
+ (class_name, _) = DIRECT_CALL_OVERRIDES[self.protocol.name, self.side]
+ else:
+ assert self.protocol.name.startswith("P")
+ class_name = "{}{}".format(self.protocol.name[1:], self.side.capitalize())
+
+ return ExprCode("static_cast<${class_name}*>(this)", class_name=class_name)
+
+ def thisCall(self, function, args):
+ return ExprCall(ExprSelect(self.concreteThis(), "->", function), args=args)
+
+ def visitMessageDecl(self, md):
+ isctor = md.decl.type.isCtor()
+ isdtor = md.decl.type.isDtor()
+ decltype = md.decl.type
+ sendmethod = None
+ movesendmethod = None
+ promisesendmethod = 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()
+
+ # NOTE: Don't generate helper ctors for refcounted types.
+ #
+ # Safety concerns around providing your own actor to a ctor (namely
+ # that the return value won't be checked, and the argument will be
+ # `delete`-ed) are less critical with refcounted actors, due to the
+ # actor being held alive by the callsite.
+ #
+ # This allows refcounted actors to not implement crashing AllocPFoo
+ # methods on the sending side.
+ if isctor and not md.decl.type.constructedType().isRefcounted():
+ 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,
+ movesendmethod,
+ promisesendmethod,
+ (recvlbl, recvcase),
+ ) = self.genAsyncSendMethod(md)
+ else:
+ sendmethod, movesendmethod = 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 movesendmethod is not None:
+ self.cls.addstmts([movesendmethod, Whitespace.NL])
+ if promisesendmethod is not None:
+ self.cls.addstmts([promisesendmethod, 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))
+
+ msgvar, stmts = self.makeMessage(md, errfnSendCtor)
+ sendok, sendstmts = self.sendAsync(md, msgvar)
+
+ method.addcode(
+ """
+ $*{bind}
+
+ // Build our constructor message & verify it.
+ $*{stmts}
+ $*{verify}
+
+ // Notify the other side about the newly created actor. This can
+ // fail if our manager has already been destroyed.
+ //
+ // NOTE: If the send call fails due to toplevel channel teardown,
+ // the `IProtocol::ChannelSend` wrapper absorbs the error for us,
+ // so we don't tear down actors unexpectedly.
+ $*{sendstmts}
+
+ // Warn, destroy the actor, and return null if the message failed to
+ // send. Otherwise, return the successfully created actor reference.
+ if (!${sendok}) {
+ NS_WARNING("Error sending ${actorname} constructor");
+ $*{destroy}
+ return nullptr;
+ }
+ return ${actor};
+ """,
+ bind=self.bindManagedActor(actor),
+ stmts=stmts,
+ verify=self.genVerifyMessage(
+ md.decl.type.verify, md.params, errfnSendCtor, ExprVar("msg__")
+ ),
+ sendstmts=sendstmts,
+ sendok=sendok,
+ destroy=self.destroyActor(
+ md, actor.var(), why=_DestroyReason.FailedConstructor
+ ),
+ actor=actor.var(),
+ actorname=actor.ipdltype.protocol.name() + self.side.capitalize(),
+ )
+
+ 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))
+
+ msgvar, stmts = self.makeMessage(md, errfnSendCtor)
+ verify = self.genVerifyMessage(
+ md.decl.type.verify, md.params, errfnSendCtor, ExprVar("msg__")
+ )
+
+ replyvar = self.replyvar
+ sendok, sendstmts = self.sendBlocking(md, msgvar, replyvar)
+ replystmts = self.deserializeReply(
+ md,
+ ExprAddrOf(replyvar),
+ self.side,
+ errfnSendCtor,
+ errfnSentinel(ExprLiteral.NULL),
+ )
+
+ method.addcode(
+ """
+ $*{bind}
+
+ // Build our constructor message & verify it.
+ $*{stmts}
+ $*{verify}
+
+ // Synchronously send the constructor message to the other side. If
+ // the send fails, e.g. due to the remote side shutting down, the
+ // actor will be destroyed and potentially freed.
+ Message ${replyvar};
+ $*{sendstmts}
+
+ if (!(${sendok})) {
+ // Warn, destroy the actor and return null if the message
+ // failed to send.
+ NS_WARNING("Error sending constructor");
+ $*{destroy}
+ return nullptr;
+ }
+
+ $*{replystmts}
+ return ${actor};
+ """,
+ bind=self.bindManagedActor(actor),
+ stmts=stmts,
+ verify=verify,
+ replyvar=replyvar,
+ sendstmts=sendstmts,
+ sendok=sendok,
+ destroy=self.destroyActor(
+ md, actor.var(), why=_DestroyReason.FailedConstructor
+ ),
+ replystmts=replystmts,
+ actor=actor.var(),
+ actorname=actor.ipdltype.protocol.name() + self.side.capitalize(),
+ )
+
+ return method
+
+ def bindManagedActor(self, actordecl, errfn=ExprLiteral.NULL, idexpr=None):
+ actorproto = actordecl.ipdltype.protocol
+
+ if idexpr is None:
+ setManagerArgs = [ExprVar.THIS]
+ else:
+ setManagerArgs = [ExprVar.THIS, idexpr]
+
+ return [
+ StmtCode(
+ """
+ if (!${actor}) {
+ NS_WARNING("Cannot bind null ${actorname} actor");
+ return ${errfn};
+ }
+
+ ${actor}->SetManagerAndRegister($,{setManagerArgs});
+ ${container}.PutEntry(${actor});
+ """,
+ actor=actordecl.var(),
+ actorname=actorproto.name() + self.side.capitalize(),
+ errfn=errfn,
+ setManagerArgs=setManagerArgs,
+ container=self.protocol.managedVar(actorproto, self.side),
+ )
+ ]
+
+ def genHelperCtor(self, md):
+ helperdecl = self.makeSendMethodDecl(md)
+ helperdecl.params = helperdecl.params[1:]
+ helper = MethodDefn(helperdecl)
+
+ helper.addstmts(
+ [
+ self.callAllocActor(md, retsems="out", side=self.side),
+ StmtReturn(ExprCall(ExprVar(helperdecl.name), args=md.makeCxxArgs())),
+ ]
+ )
+ return helper
+
+ def genAsyncDtor(self, md):
+ actor = md.actorDecl()
+ actorvar = actor.var()
+ method = MethodDefn(self.makeDtorMethodDecl(md))
+
+ method.addstmt(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.addstmt(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, errfnSentinel(), actorvar
+ )
+ ifsendok = StmtIf(ExprLiteral.FALSE)
+ ifsendok.addifstmts(destmts)
+ ifsendok.addifstmts(
+ [Whitespace.NL, StmtExpr(ExprAssn(sendok, ExprLiteral.FALSE, "&="))]
+ )
+
+ method.addstmt(ifsendok)
+
+ 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 [
+ StmtCode(
+ """
+ IProtocol* mgr = ${actor}->Manager();
+ ${actor}->DestroySubtree(${why});
+ ${actor}->ClearSubtree();
+ mgr->RemoveManagee(${protoId}, ${actor});
+ """,
+ actor=actorexpr,
+ why=why,
+ protoId=_protocolId(destroyedType),
+ )
+ ]
+
+ def dtorPrologue(self, actorexpr):
+ return StmtCode(
+ """
+ if (!${actor} || !${actor}->CanSend()) {
+ NS_WARNING("Attempt to __delete__ missing or closed actor");
+ return false;
+ }
+ """,
+ actor=actorexpr,
+ )
+
+ def dtorEpilogue(self, md, actorexpr):
+ return self.destroyActor(md, actorexpr)
+
+ def genRecvAsyncReplyCase(self, md):
+ lbl = CaseLabel(md.pqReplyId())
+ case = StmtBlock()
+ resolve, reason, prologue, desrej, desstmts = self.deserializeAsyncReply(
+ md, self.side, errfnRecv, errfnSentinel(_Result.ValuError)
+ )
+
+ if len(md.returns) > 1:
+ resolvetype = _tuple([d.bareType(self.side) for d in md.returns])
+ resolvearg = ExprCall(
+ ExprVar("MakeTuple"), args=[ExprMove(p.var()) for p in md.returns]
+ )
+ else:
+ resolvetype = md.returns[0].bareType(self.side)
+ resolvearg = ExprMove(md.returns[0].var())
+
+ case.addcode(
+ """
+ $*{prologue}
+
+ UniquePtr<MessageChannel::UntypedCallbackHolder> untypedCallback =
+ GetIPCChannel()->PopCallback(${msgvar});
+
+ typedef MessageChannel::CallbackHolder<${resolvetype}> CallbackHolder;
+ auto* callback = static_cast<CallbackHolder*>(untypedCallback.get());
+ if (!callback) {
+ FatalError("Error unknown callback");
+ return MsgProcessingError;
+ }
+
+ if (${resolve}) {
+ $*{desstmts}
+ callback->Resolve(${resolvearg});
+ } else {
+ $*{desrej}
+ callback->Reject(std::move(${reason}));
+ }
+ return MsgProcessed;
+ """,
+ prologue=prologue,
+ msgvar=self.msgvar,
+ resolve=resolve,
+ resolvetype=resolvetype,
+ desstmts=desstmts,
+ resolvearg=resolvearg,
+ desrej=desrej,
+ reason=reason,
+ )
+
+ return (lbl, case)
+
+ def genAsyncSendMethod(self, md):
+ method = MethodDefn(self.makeSendMethodDecl(md))
+ msgvar, stmts = self.makeMessage(md, errfnSend)
+ retvar, sendstmts = self.sendAsync(md, msgvar)
+
+ method.addstmts(
+ stmts
+ + [Whitespace.NL]
+ + self.genVerifyMessage(
+ md.decl.type.verify, md.params, errfnSend, ExprVar("msg__")
+ )
+ + sendstmts
+ + [StmtReturn(retvar)]
+ )
+
+ movemethod = None
+
+ # Add the promise overload if we need one.
+ if md.returns:
+ promisemethod = MethodDefn(self.makeSendMethodDecl(md, promise=True))
+ stmts = self.sendAsyncWithPromise(md)
+ promisemethod.addstmts(stmts)
+
+ (lbl, case) = self.genRecvAsyncReplyCase(md)
+ else:
+ (promisemethod, lbl, case) = (None, None, None)
+
+ return method, movemethod, promisemethod, (lbl, case)
+
+ 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, errfnSentinel()
+ )
+
+ 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]
+ )
+
+ movemethod = None
+
+ return method, movemethod
+
+ def genCtorRecvCase(self, md):
+ lbl = CaseLabel(md.pqMsgId())
+ case = StmtBlock()
+ actorhandle = self.handlevar
+
+ stmts = self.deserializeMessage(
+ md, self.side, errfnRecv, errfnSent=errfnSentinel(_Result.ValuError)
+ )
+
+ idvar, saveIdStmts = self.saveActorId(md)
+ case.addstmts(
+ stmts
+ + [
+ StmtDecl(Decl(r.bareType(self.side), r.var().name), initargs=[])
+ for r in md.returns
+ ]
+ # alloc the actor, register it under the foreign ID
+ + [self.callAllocActor(md, retsems="in", side=self.side)]
+ + self.bindManagedActor(
+ md.actorDecl(), 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, errfnSent=errfnSentinel(_Result.ValuError)
+ )
+
+ idvar, saveIdStmts = self.saveActorId(md)
+ case.addstmts(
+ stmts
+ + [
+ StmtDecl(Decl(r.bareType(self.side), r.var().name), initargs=[])
+ for r in md.returns
+ ]
+ + self.invokeRecvHandler(md, implicit=False)
+ + [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, errfnSent=errfnSentinel(_Result.ValuError)
+ )
+
+ idvar, saveIdStmts = self.saveActorId(md)
+ declstmts = [
+ StmtDecl(Decl(r.bareType(self.side), r.var().name), initargs=[])
+ for r in md.returns
+ ]
+ if md.decl.type.isAsync() and md.returns:
+ declstmts = self.makeResolver(md, errfnRecv, routingId=idvar)
+ case.addstmts(
+ stmts
+ + saveIdStmts
+ + declstmts
+ + 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 makeMessage(self, md, errfn, fromActor=None):
+ msgvar = self.msgvar
+ routingId = self.protocol.routingId(fromActor)
+ this = ExprVar.THIS
+ if md.decl.type.isDtor():
+ this = md.actorDecl().var()
+
+ stmts = (
+ [
+ StmtDecl(
+ Decl(Type("IPC::Message", ptr=True), msgvar.name),
+ init=ExprCall(ExprVar(md.pqMsgCtorFunc()), args=[routingId]),
+ )
+ ]
+ + [Whitespace.NL]
+ + [
+ _ParamTraits.checkedWrite(
+ p.ipdltype, p.var(), msgvar, sentinelKey=p.name, actor=this
+ )
+ for p in md.params
+ ]
+ + [Whitespace.NL]
+ + self.setMessageFlags(md, msgvar)
+ )
+ return msgvar, stmts
+
+ def makeResolver(self, md, errfn, routingId):
+ if routingId is None:
+ routingId = self.protocol.routingId()
+ if not md.decl.type.isAsync() or not md.hasReply():
+ return []
+
+ selfvar = ExprVar("self__")
+ serializeParams = [
+ StmtCode("bool resolve__ = true;\n"),
+ _ParamTraits.checkedWrite(
+ None,
+ ExprVar("resolve__"),
+ self.replyvar,
+ sentinelKey="resolve__",
+ actor=selfvar,
+ ),
+ ]
+
+ def paramValue(idx):
+ assert idx < len(md.returns)
+ if len(md.returns) > 1:
+ return ExprCode("mozilla::Get<${idx}>(aParam)", idx=idx)
+ return ExprVar("aParam")
+
+ serializeParams += [
+ _ParamTraits.checkedWrite(
+ p.ipdltype,
+ paramValue(idx),
+ self.replyvar,
+ sentinelKey=p.name,
+ actor=selfvar,
+ )
+ for idx, p in enumerate(md.returns)
+ ]
+
+ return [
+ StmtCode(
+ """
+ int32_t seqno__ = ${msgvar}.seqno();
+ RefPtr<mozilla::ipc::ActorLifecycleProxy> proxy__ =
+ GetLifecycleProxy();
+
+ ${resolvertype} resolver = [proxy__, seqno__, ${routingId}](${resolveType} aParam) {
+ if (!proxy__->Get()) {
+ NS_WARNING("Not resolving response because actor is dead.");
+ return;
+ }
+ ${actortype} self__ = static_cast<${actortype}>(proxy__->Get());
+
+ IPC::Message* ${replyvar} = ${replyCtor}(${routingId});
+ ${replyvar}->set_seqno(seqno__);
+
+ $*{serializeParams}
+ ${logSendingReply}
+ bool sendok__ = self__->ChannelSend(${replyvar});
+ if (!sendok__) {
+ NS_WARNING("Error sending reply");
+ }
+ };
+ """,
+ msgvar=self.msgvar,
+ resolvertype=Type(md.resolverName()),
+ routingId=routingId,
+ resolveType=_resolveType(md.returns, self.side),
+ actortype=_cxxBareType(ActorType(self.protocol.decl.type), self.side),
+ replyvar=self.replyvar,
+ replyCtor=ExprVar(md.pqReplyCtorFunc()),
+ serializeParams=serializeParams,
+ logSendingReply=self.logMessage(
+ md,
+ self.replyvar,
+ "Sending reply ",
+ actor=selfvar,
+ ),
+ )
+ ]
+
+ 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 []
+ if md.decl.type.isAsync() and md.decl.type.hasReply():
+ return []
+
+ replyvar = self.replyvar
+ return (
+ [
+ StmtExpr(
+ ExprAssn(
+ replyvar,
+ ExprCall(ExprVar(md.pqReplyCtorFunc()), args=[routingId]),
+ )
+ ),
+ Whitespace.NL,
+ ]
+ + [
+ _ParamTraits.checkedWrite(
+ r.ipdltype,
+ r.var(),
+ replyvar,
+ sentinelKey=r.name,
+ actor=ExprVar.THIS,
+ )
+ for r in md.returns
+ ]
+ + self.setMessageFlags(md, replyvar)
+ + [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=False), "msgverify__"),
+ init=ExprMove(ExprDeref(msgsrcVar)),
+ )
+ )
+
+ stmts.extend(
+ (
+ # PickleIterator msgverifyIter__(msgverify__);
+ [StmtDecl(Decl(_iterType(ptr=False), itervar.name), initargs=[msgvar])]
+ # declare varCopy for each variable to deserialize.
+ + [
+ StmtDecl(Decl(p.bareType(side), p.var().name + "Copy"), initargs=[])
+ for p in params
+ ]
+ + [Whitespace.NL]
+ # checked Read(&(varCopy), &(msgverify__), &(msgverifyIter__))
+ + [
+ _ParamTraits.checkedRead(
+ p.ipdltype,
+ ExprAddrOf(ExprVar(p.var().name + "Copy")),
+ msgexpr,
+ ExprAddrOf(itervar),
+ errfn,
+ p.ipdltype.name(),
+ sentinelKey=p.name,
+ errfnSentinel=errfnSentinel(),
+ actor=ExprVar.THIS,
+ )
+ 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, seqno=None):
+ stmts = []
+
+ if seqno:
+ stmts.append(
+ StmtExpr(ExprCall(ExprSelect(var, "->", "set_seqno"), args=[seqno]))
+ )
+
+ return stmts + [Whitespace.NL]
+
+ def deserializeMessage(self, md, side, errfn, errfnSent):
+ 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), initargs=[])]
+ reads = [
+ _ParamTraits.checkedRead(
+ None,
+ ExprAddrOf(handlevar),
+ msgexpr,
+ ExprAddrOf(self.itervar),
+ errfn,
+ "'%s'" % handletype.name,
+ sentinelKey="actor",
+ errfnSentinel=errfnSent,
+ actor=ExprVar.THIS,
+ )
+ ]
+ start = 1
+
+ decls.extend(
+ [
+ StmtDecl(
+ Decl(
+ (
+ Type("Tainted", T=p.bareType(side))
+ if md.decl.type.tainted
+ else p.bareType(side)
+ ),
+ p.var().name,
+ ),
+ initargs=[],
+ )
+ for p in md.params[start:]
+ ]
+ )
+ reads.extend(
+ [
+ _ParamTraits.checkedRead(
+ p.ipdltype,
+ ExprAddrOf(p.var()),
+ msgexpr,
+ ExprAddrOf(itervar),
+ errfn,
+ "'%s'" % p.ipdltype.name(),
+ sentinelKey=p.name,
+ errfnSentinel=errfnSent,
+ actor=ExprVar.THIS,
+ )
+ for p in md.params[start:]
+ ]
+ )
+
+ stmts.extend(
+ (
+ [
+ StmtDecl(
+ Decl(_iterType(ptr=False), self.itervar.name), initargs=[msgvar]
+ )
+ ]
+ + decls
+ + [Whitespace.NL]
+ + reads
+ + [self.endRead(msgvar, itervar)]
+ )
+ )
+
+ return stmts
+
+ def deserializeAsyncReply(self, md, side, errfn, errfnSent):
+ msgvar = self.msgvar
+ itervar = self.itervar
+ msgexpr = ExprAddrOf(msgvar)
+ isctor = md.decl.type.isCtor()
+ resolve = ExprVar("resolve__")
+ reason = ExprVar("reason__")
+ desresolve = [
+ StmtDecl(Decl(Type.BOOL, resolve.name), init=ExprLiteral.FALSE),
+ _ParamTraits.checkedRead(
+ None,
+ ExprAddrOf(resolve),
+ msgexpr,
+ ExprAddrOf(itervar),
+ errfn,
+ "'%s'" % resolve.name,
+ sentinelKey=resolve.name,
+ errfnSentinel=errfnSent,
+ actor=ExprVar.THIS,
+ ),
+ ]
+ desrej = [
+ StmtDecl(Decl(_ResponseRejectReason.Type(), reason.name), initargs=[]),
+ _ParamTraits.checkedRead(
+ None,
+ ExprAddrOf(reason),
+ msgexpr,
+ ExprAddrOf(itervar),
+ errfn,
+ "'%s'" % reason.name,
+ sentinelKey=reason.name,
+ errfnSentinel=errfnSent,
+ actor=ExprVar.THIS,
+ ),
+ self.endRead(msgvar, itervar),
+ ]
+ prologue = [
+ self.logMessage(md, msgexpr, "Received ", receiving=True),
+ self.profilerLabel(md),
+ Whitespace.NL,
+ ]
+
+ if not md.returns:
+ return prologue
+
+ prologue.extend(
+ [StmtDecl(Decl(_iterType(ptr=False), itervar.name), initargs=[msgvar])]
+ + desresolve
+ )
+
+ 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), initargs=[])]
+ reads = [
+ _ParamTraits.checkedRead(
+ None,
+ ExprAddrOf(handlevar),
+ msgexpr,
+ ExprAddrOf(itervar),
+ errfn,
+ "'%s'" % handletype.name,
+ sentinelKey="actor",
+ errfnSentinel=errfnSent,
+ actor=ExprVar.THIS,
+ )
+ ]
+ start = 1
+
+ stmts = (
+ decls
+ + [
+ StmtDecl(Decl(p.bareType(side), p.var().name), initargs=[])
+ for p in md.returns
+ ]
+ + [Whitespace.NL]
+ + reads
+ + [
+ _ParamTraits.checkedRead(
+ p.ipdltype,
+ ExprAddrOf(p.var()),
+ msgexpr,
+ ExprAddrOf(itervar),
+ errfn,
+ "'%s'" % p.ipdltype.name(),
+ sentinelKey=p.name,
+ errfnSentinel=errfnSent,
+ actor=ExprVar.THIS,
+ )
+ for p in md.returns[start:]
+ ]
+ + [self.endRead(msgvar, itervar)]
+ )
+
+ return resolve, reason, prologue, desrej, stmts
+
+ def deserializeReply(
+ self, md, replyexpr, side, errfn, errfnSentinel, actor=None, decls=False
+ ):
+ stmts = [
+ Whitespace.NL,
+ self.logMessage(md, replyexpr, "Received reply ", actor, receiving=True),
+ ]
+ if 0 == len(md.returns):
+ return stmts
+
+ itervar = self.itervar
+ declstmts = []
+ if decls:
+ declstmts = [
+ StmtDecl(Decl(p.bareType(side), p.var().name), initargs=[])
+ for p in md.returns
+ ]
+ stmts.extend(
+ [
+ Whitespace.NL,
+ StmtDecl(
+ Decl(_iterType(ptr=False), itervar.name), initargs=[self.replyvar]
+ ),
+ ]
+ + declstmts
+ + [Whitespace.NL]
+ + [
+ _ParamTraits.checkedRead(
+ r.ipdltype,
+ r.var(),
+ ExprAddrOf(self.replyvar),
+ ExprAddrOf(self.itervar),
+ errfn,
+ "'%s'" % r.ipdltype.name(),
+ sentinelKey=r.name,
+ errfnSentinel=errfnSentinel,
+ actor=ExprVar.THIS,
+ )
+ for r in md.returns
+ ]
+ + [self.endRead(self.replyvar, itervar)]
+ )
+
+ return stmts
+
+ def sendAsync(self, md, msgexpr, actor=None):
+ sendok = ExprVar("sendok__")
+ resolvefn = ExprVar("aResolve")
+ rejectfn = ExprVar("aReject")
+
+ stmts = [
+ Whitespace.NL,
+ self.logMessage(md, msgexpr, "Sending ", actor),
+ self.profilerLabel(md),
+ ]
+ stmts.append(Whitespace.NL)
+
+ # Generate the actual call expression.
+ send = ExprVar("ChannelSend")
+ if actor is not None:
+ send = ExprSelect(actor, "->", send.name)
+ if md.returns:
+ stmts.append(
+ StmtExpr(
+ ExprCall(
+ send, args=[msgexpr, ExprMove(resolvefn), ExprMove(rejectfn)]
+ )
+ )
+ )
+ retvar = None
+ else:
+ stmts.append(
+ StmtDecl(
+ Decl(Type.BOOL, sendok.name), init=ExprCall(send, args=[msgexpr])
+ )
+ )
+ retvar = sendok
+
+ return (retvar, stmts)
+
+ def sendBlocking(self, md, msgexpr, replyexpr, actor=None):
+ send = ExprVar("ChannelSend")
+ if md.decl.type.isInterrupt():
+ send = ExprVar("ChannelCall")
+ if actor is not None:
+ send = ExprSelect(actor, "->", send.name)
+
+ sendok = ExprVar("sendok__")
+ return (
+ sendok,
+ (
+ [
+ Whitespace.NL,
+ self.logMessage(md, msgexpr, "Sending ", actor),
+ self.profilerLabel(md),
+ ]
+ + [
+ Whitespace.NL,
+ StmtDecl(Decl(Type.BOOL, sendok.name), init=ExprLiteral.FALSE),
+ StmtBlock(
+ [
+ StmtExpr(
+ ExprAssn(
+ sendok,
+ ExprCall(
+ send, args=[msgexpr, ExprAddrOf(replyexpr)]
+ ),
+ )
+ ),
+ ]
+ ),
+ ]
+ ),
+ )
+
+ def sendAsyncWithPromise(self, md):
+ # Create a new promise, and forward to the callback send overload.
+ promise = _makePromise(md.returns, self.side, resolver=True)
+
+ if len(md.returns) > 1:
+ resolvetype = _tuple([d.bareType(self.side) for d in md.returns])
+ else:
+ resolvetype = md.returns[0].bareType(self.side)
+
+ resolve = ExprCode(
+ """
+ [promise__](${resolvetype}&& aValue) {
+ promise__->Resolve(std::move(aValue), __func__);
+ }
+ """,
+ resolvetype=resolvetype,
+ )
+ reject = ExprCode(
+ """
+ [promise__](ResponseRejectReason&& aReason) {
+ promise__->Reject(std::move(aReason), __func__);
+ }
+ """,
+ resolvetype=resolvetype,
+ )
+
+ args = [ExprMove(p.var()) for p in md.params] + [resolve, reject]
+ stmt = StmtCode(
+ """
+ RefPtr<${promise}> promise__ = new ${promise}(__func__);
+ promise__->UseDirectTaskDispatch(__func__);
+ ${send}($,{args});
+ return promise__;
+ """,
+ promise=promise,
+ send=md.sendMethod(),
+ args=args,
+ )
+ return [stmt]
+
+ def callAllocActor(self, md, retsems, side):
+ actortype = md.actorDecl().bareType(self.side)
+ if md.decl.type.constructedType().isRefcounted():
+ actortype.ptr = False
+ actortype = _refptr(actortype)
+
+ callalloc = self.thisCall(
+ _allocMethod(md.decl.type.constructedType(), side),
+ args=md.makeCxxArgs(retsems=retsems, retcallsems="out", implicit=False),
+ )
+
+ return StmtDecl(Decl(actortype, md.actorDecl().var().name), init=callalloc)
+
+ def invokeRecvHandler(self, md, implicit=True):
+ retsems = "in"
+ if md.decl.type.isAsync() and md.returns:
+ retsems = "resolver"
+ failif = StmtIf(
+ ExprNot(
+ self.thisCall(
+ md.recvMethod(),
+ md.makeCxxArgs(
+ paramsems="move",
+ retsems=retsems,
+ retcallsems="out",
+ implicit=implicit,
+ ),
+ )
+ )
+ )
+ failif.addifstmts(
+ [
+ _protocolErrorBreakpoint("Handler returned error code!"),
+ Whitespace(
+ "// Error handled in mozilla::ipc::IPCResult\n", indent=True
+ ),
+ StmtReturn(_Result.ProcessingError),
+ ]
+ )
+ return [failif]
+
+ def makeDtorMethodDecl(self, md):
+ decl = self.makeSendMethodDecl(md)
+ decl.methodspec = MethodSpec.STATIC
+ return decl
+
+ def makeSendMethodDecl(self, md, promise=False, paramsems="in"):
+ implicit = md.decl.type.hasImplicitActorParam()
+ if md.decl.type.isAsync() and md.returns:
+ if promise:
+ returnsems = "promise"
+ rettype = _refptr(Type(md.promiseName()))
+ else:
+ returnsems = "callback"
+ rettype = Type.VOID
+ else:
+ assert not promise
+ returnsems = "out"
+ rettype = Type.BOOL
+ decl = MethodDecl(
+ md.sendMethod(),
+ params=md.makeCxxParams(
+ paramsems,
+ returnsems=returnsems,
+ side=self.side,
+ implicit=implicit,
+ direction="send",
+ ),
+ warn_unused=(
+ (self.side == "parent" and returnsems != "callback")
+ or (md.decl.type.isCtor() and not md.decl.type.isAsync())
+ ),
+ ret=rettype,
+ )
+ 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 StmtCode(
+ """
+ if (mozilla::ipc::LoggingEnabledFor(${actorname})) {
+ mozilla::ipc::LogMessageForProtocol(
+ ${actorname},
+ ${otherpid},
+ ${pfx},
+ ${msgptr}->type(),
+ mozilla::ipc::MessageDirection::${direction});
+ }
+ """,
+ actorname=ExprLiteral.String(actorname),
+ otherpid=self.protocol.callOtherPid(actor),
+ pfx=ExprLiteral.String(pfx),
+ msgptr=msgptr,
+ direction="eReceiving" if receiving else "eSending",
+ )
+
+ def profilerLabel(self, md):
+ return StmtCode(
+ """
+ AUTO_PROFILER_LABEL("${name}::${msgname}", OTHER);
+ """,
+ name=self.protocol.name,
+ msgname=md.prettyMsgName(),
+ )
+
+ 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 endRead(self, msgexpr, iterexpr):
+ return StmtCode(
+ """
+ ${msg}.EndRead(${iter}, ${msg}.type());
+ """,
+ msg=msgexpr,
+ iter=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 = _splitMethodDeclDefn(stmt, cls)
+ cls.stmts[i] = StmtDecl(decl)
+ if defn:
+ defns.addstmts([defn, Whitespace.NL])
+
+ return cls, defns
+
+
+def _splitMethodDeclDefn(md, cls):
+ # Pure methods have decls but no defns.
+ if md.decl.methodspec == MethodSpec.PURE:
+ return md.decl, None
+
+ saveddecl = deepcopy(md.decl)
+ md.decl.cls = cls
+ # Don't emit method specifiers on method defns.
+ md.decl.methodspec = MethodSpec.NONE
+ md.decl.warn_unused = False
+ 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.force_inline
+ return StmtDecl(fun.decl), fun
diff --git a/ipc/ipdl/ipdl/parser.py b/ipc/ipdl/ipdl/parser.py
new file mode 100644
index 0000000000..9ffe9e5bed
--- /dev/null
+++ b/ipc/ipdl/ipdl/parser.py
@@ -0,0 +1,783 @@
+# 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
+from ply import lex, yacc
+
+from ipdl.ast import *
+
+# -----------------------------------------------------------------------------
+
+
+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=False):
+ 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
+
+ def parse(self, input, filename, includedirs):
+ assert os.path.isabs(filename)
+
+ if self.tu.name in Parser.parsed:
+ priorTU = Parser.parsed[self.tu.name].tu
+ if priorTU.filename != filename:
+ _error(
+ Loc(filename),
+ "Trying to load `%s' from a file when we'd already seen it in file `%s'"
+ % (self.tu.name, priorTU.filename),
+ )
+
+ return priorTU
+
+ self.lexer = lex.lex(debug=self.debug)
+ self.parser = yacc.yacc(debug=self.debug, write_tables=False)
+ self.filename = filename
+ self.includedirs = includedirs
+ self.tu.filename = filename
+
+ Parser.parsed[self.tu.name] = self
+ Parser.parseStack.append(Parser.current)
+ Parser.current = self
+
+ try:
+ ast = self.parser.parse(input=input, lexer=self.lexer, debug=self.debug)
+ finally:
+ 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(
+ (
+ "async",
+ "both",
+ "child",
+ "class",
+ "comparable",
+ "compress",
+ "compressall",
+ "from",
+ "include",
+ "intr",
+ "manager",
+ "manages",
+ "namespace",
+ "nested",
+ "nullable",
+ "or",
+ "parent",
+ "prio",
+ "protocol",
+ "refcounted",
+ "moveonly",
+ "returns",
+ "struct",
+ "sync",
+ "tainted",
+ "union",
+ "UniquePtr",
+ "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)
+ p[0] = inc
+
+
+def p_UsingKind(p):
+ """UsingKind : CLASS
+ | STRUCT
+ |"""
+ p[0] = p[1] if 2 == len(p) else None
+
+
+def p_MaybeComparable(p):
+ """MaybeComparable : COMPARABLE
+ |"""
+ p[0] = 2 == len(p)
+
+
+def p_MaybeRefcounted(p):
+ """MaybeRefcounted : REFCOUNTED
+ |"""
+ p[0] = 2 == len(p)
+
+
+def p_MaybeMoveOnly(p):
+ """MaybeMoveOnly : MOVEONLY
+ |"""
+ p[0] = 2 == len(p)
+
+
+def p_UsingStmt(p):
+ """UsingStmt : USING MaybeRefcounted MaybeMoveOnly UsingKind CxxType FROM STRING"""
+ p[0] = UsingStmt(
+ locFromTok(p, 1),
+ refcounted=p[2],
+ moveonly=p[3],
+ kind=p[4],
+ cxxTypeSpec=p[5],
+ cxxHeader=p[7],
+ )
+
+
+# --------------------
+# 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 : MaybeComparable STRUCT ID '{' StructFields '}' ';'
+ | MaybeComparable STRUCT ID '{' '}' ';'"""
+ if 8 == len(p):
+ p[0] = StructDecl(locFromTok(p, 2), p[3], p[5], p[1])
+ else:
+ p[0] = StructDecl(locFromTok(p, 2), p[3], [], p[1])
+
+
+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 : MaybeComparable UNION ID '{' ComponentTypes '}' ';'"""
+ p[0] = UnionDecl(locFromTok(p, 2), p[3], p[5], p[1])
+
+
+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 MaybeRefcounted \
+ PROTOCOL ID '{' ProtocolBody '}' ';'"""
+ protocol = p[6]
+ protocol.loc = locFromTok(p, 3)
+ protocol.name = p[4]
+ protocol.nested = p[1][0]
+ protocol.sendSemantics = p[1][1]
+ protocol.refcounted = p[2]
+ 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 : ManagersStmtOpt"""
+ p[0] = p[1]
+
+
+# --------------------
+# 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
+ |"""
+ if 1 == len(p):
+ # we fill in |loc| in the Protocol rule
+ p[0] = Protocol(None)
+ 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 : ID MessageInParams MessageOutParams OptionalMessageModifiers"""
+ # FIXME/cjones: need better loc info: use one of the quals
+ name = p[1]
+ msg = MessageDecl(locFromTok(p, 1))
+ msg.name = name
+ msg.addInParams(p[2])
+ msg.addOutParams(p[3])
+ msg.addModifiers(p[4])
+
+ p[0] = msg
+
+
+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
+ | MessageTainted"""
+ 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]
+
+
+def p_MessageTainted(p):
+ """MessageTainted : TAINTED"""
+ p[0] = 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, "input": 2, "high": 3, "mediumhigh": 4}
+ if p[1] not in kinds:
+ _error(
+ locFromTok(p, 1), "Expected normal, input, high or mediumhigh 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, 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 = p[4]
+ else:
+ mtype = p[1]
+ 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 : CxxID
+ | CxxID '[' ']'
+ | CxxID '?'
+ | CxxUniquePtrInst"""
+ # ID == CxxType; we forbid qnames here,
+ # in favor of the |using| declaration
+ if not isinstance(p[1], TypeSpec):
+ assert (len(p[1]) == 2) or (len(p[1]) == 3)
+ if 2 == len(p[1]):
+ # p[1] is CxxID. isunique = False
+ p[1] = p[1] + (False,)
+ loc, id, isunique = p[1]
+ p[1] = TypeSpec(loc, QualifiedId(loc, id))
+ p[1].uniqueptr = isunique
+ if 4 == len(p):
+ p[1].array = True
+ if 3 == len(p):
+ p[1].maybe = True
+ p[0] = p[1]
+
+
+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_CxxUniquePtrInst(p):
+ """CxxUniquePtrInst : UNIQUEPTR '<' ID '>'"""
+ p[0] = (locFromTok(p, 1), str(p[3]), True)
+
+
+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 0000000000..652d0b3bbc
--- /dev/null
+++ b/ipc/ipdl/ipdl/type.py
@@ -0,0 +1,1530 @@
+# 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/.
+
+from __future__ import print_function
+
+import os
+import sys
+
+from ipdl.ast import CxxInclude, Decl, Loc, QualifiedId, StructDecl
+from ipdl.ast import TypeSpec, UnionDecl, UsingStmt, Visitor
+from ipdl.ast import ASYNC, SYNC, INTR
+from ipdl.ast import IN, OUT, INOUT
+from ipdl.ast import NOT_NESTED, INSIDE_SYNC_NESTED, INSIDE_CPOW_NESTED
+import ipdl.builtin as builtin
+from ipdl.util import hash_str
+
+_DELETE_MSG = "__delete__"
+
+
+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 visitImportedCxxType(self, t, *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)
+
+ 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 visitMaybeType(self, m, *args):
+ m.basetype.accept(self, *args)
+
+ def visitUniquePtrType(self, m, *args):
+ m.basetype.accept(self, *args)
+
+ def visitShmemType(self, s, *args):
+ pass
+
+ def visitByteBufType(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
+
+ def visitManagedEndpointType(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_str(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
+
+ def isRefcounted(self):
+ return False
+
+ def isUniquePtr(self):
+ return False
+
+ def typename(self):
+ return self.__class__.__name__
+
+ def name(self):
+ raise NotImplementedError()
+
+ def fullname(self):
+ raise NotImplementedError()
+
+ 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 name(self):
+ return "void"
+
+ def fullname(self):
+ return "void"
+
+
+VOID = VoidType()
+
+# --------------------
+
+
+class ImportedCxxType(Type):
+ def __init__(self, qname, refcounted, moveonly):
+ assert isinstance(qname, QualifiedId)
+ self.loc = qname.loc
+ self.qname = qname
+ self.refcounted = refcounted
+ self.moveonly = moveonly
+
+ def isCxx(self):
+ return True
+
+ def isAtom(self):
+ return True
+
+ def isRefcounted(self):
+ return self.refcounted
+
+ def isMoveonly(self):
+ return self.moveonly
+
+ def name(self):
+ return self.qname.baseid
+
+ def fullname(self):
+ return str(self.qname)
+
+
+# --------------------
+
+
+class IPDLType(Type):
+ def isIPDL(self):
+ return True
+
+ 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 isMaybe(self):
+ return False
+
+ def isAtom(self):
+ return True
+
+ def isCompound(self):
+ return False
+
+ def isShmem(self):
+ return False
+
+ def isByteBuf(self):
+ return False
+
+ def isFD(self):
+ return False
+
+ def isEndpoint(self):
+ return False
+
+ def isManagedEndpoint(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()
+
+ def hasBaseType(self):
+ return False
+
+ @classmethod
+ def convertsTo(cls, lesser, greater):
+ def _unwrap(nr):
+ if isinstance(nr, dict):
+ return _unwrap(nr["nested"])
+ elif isinstance(nr, int):
+ return nr
+ else:
+ raise ValueError("Got unexpected nestedRange value: %s" % nr)
+
+ lnr0, gnr0, lnr1, gnr1 = (
+ _unwrap(lesser.nestedRange[0]),
+ _unwrap(greater.nestedRange[0]),
+ _unwrap(lesser.nestedRange[1]),
+ _unwrap(greater.nestedRange[1]),
+ )
+ if lnr0 < gnr0 or lnr1 > gnr1:
+ 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 MessageType(IPDLType):
+ def __init__(
+ self,
+ nested,
+ prio,
+ sendSemantics,
+ direction,
+ ctor=False,
+ dtor=False,
+ cdtype=None,
+ compress=False,
+ tainted=False,
+ verify=False,
+ ):
+ assert not (ctor and dtor)
+ assert not (ctor or dtor) or cdtype 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.tainted = tainted
+ 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 hasReply(self):
+ return len(self.returns) or IPDLType.hasReply(self)
+
+ def hasImplicitActorParam(self):
+ return self.isCtor() or self.isDtor()
+
+
+class ProtocolType(IPDLType):
+ def __init__(self, qname, nested, sendSemantics, refcounted):
+ self.qname = qname
+ self.nestedRange = (NOT_NESTED, nested)
+ self.sendSemantics = sendSemantics
+ self.managers = [] # ProtocolType
+ self.manages = []
+ self.hasDelete = False
+ self.refcounted = refcounted
+
+ def isProtocol(self):
+ return True
+
+ def isRefcounted(self):
+ return self.refcounted
+
+ 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 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, nullable=False):
+ self.protocol = protocol
+ self.nullable = nullable
+
+ def isActor(self):
+ return True
+
+ def isRefcounted(self):
+ return self.protocol.isRefcounted()
+
+ 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.hasBaseType():
+ 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 hasBaseType(self):
+ return True
+
+ def name(self):
+ return self.basetype.name() + "[]"
+
+ def fullname(self):
+ return self.basetype.fullname() + "[]"
+
+
+class MaybeType(IPDLType):
+ def __init__(self, basetype):
+ self.basetype = basetype
+
+ def isAtom(self):
+ return False
+
+ def isMaybe(self):
+ return True
+
+ def hasBaseType(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 ByteBufType(IPDLType):
+ def __init__(self, qname):
+ self.qname = qname
+
+ def isByteBuf(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, actor):
+ self.qname = qname
+ self.actor = actor
+
+ def isEndpoint(self):
+ return True
+
+ def name(self):
+ return self.qname.baseid
+
+ def fullname(self):
+ return str(self.qname)
+
+
+class ManagedEndpointType(IPDLType):
+ def __init__(self, qname, actor):
+ self.qname = qname
+ self.actor = actor
+
+ def isManagedEndpoint(self):
+ return True
+
+ def name(self):
+ return self.qname.baseid
+
+ def fullname(self):
+ return str(self.qname)
+
+
+class UniquePtrType(IPDLType):
+ def __init__(self, basetype):
+ self.basetype = basetype
+
+ def isAtom(self):
+ return False
+
+ def isUniquePtr(self):
+ return True
+
+ def hasBaseType(self):
+ return True
+
+ def name(self):
+ return "UniquePtr<" + self.basetype.name() + ">"
+
+ def fullname(self):
+ return "mozilla::UniquePtr<" + self.basetype.fullname() + ">"
+
+
+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.hasBaseType():
+ 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 hasshmem(type):
+ """Return true iff |type| is shmem or has it buried within."""
+
+ class found(BaseException):
+ pass
+
+ class findShmem(TypeVisitor):
+ def visitShmemType(self, s):
+ raise found()
+
+ try:
+ type.accept(findShmem())
+ 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.currentScope = self.scopes[0]
+
+ def enterScope(self):
+ assert isinstance(self.scopes[0], dict)
+ assert isinstance(self.currentScope, dict)
+
+ self.scopes.append({})
+ self.currentScope = self.scopes[-1]
+
+ def exitScope(self):
+ symtab = self.scopes.pop()
+ assert self.currentScope is symtab
+
+ self.currentScope = self.scopes[-1]
+
+ assert isinstance(self.scopes[0], dict)
+ 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 .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 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
+
+ return True
+
+ def reportErrors(self, errout):
+ for error in self.errors:
+ print(error, file=errout)
+
+
+class TcheckVisitor(Visitor):
+ def __init__(self, errors):
+ self.errors = errors
+
+ def error(self, loc, fmt, *args):
+ self.errors.append(errormsg(loc, fmt, *args))
+
+
+class GatherDecls(TcheckVisitor):
+ def __init__(self, builtinUsing, errors):
+ TcheckVisitor.__init__(self, errors)
+
+ # |self.symtab| is the symbol table for the translation unit
+ # currently being visited
+ self.symtab = None
+ self.builtinUsing = builtinUsing
+
+ 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
+
+ def visitTranslationUnit(self, tu):
+ # all TranslationUnits declare symbols in global scope
+ if hasattr(tu, "visited"):
+ return
+ tu.visited = True
+ savedSymtab = self.symtab
+ self.symtab = SymbolTable(self.errors)
+
+ # 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'", # NOQA: E501
+ 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()
+ fullname = str(qname)
+ p.decl = self.declare(
+ loc=p.loc,
+ type=ProtocolType(qname, p.nested, p.sendSemantics, p.refcounted),
+ shortname=p.name,
+ fullname=None if 0 == len(qname.quals) else fullname,
+ )
+
+ p.parentEndpointDecl = self.declare(
+ loc=p.loc,
+ type=EndpointType(
+ QualifiedId(
+ p.loc, "Endpoint<" + fullname + "Parent>", ["mozilla", "ipc"]
+ ),
+ ActorType(p.decl.type),
+ ),
+ shortname="Endpoint<" + p.name + "Parent>",
+ )
+ p.childEndpointDecl = self.declare(
+ loc=p.loc,
+ type=EndpointType(
+ QualifiedId(
+ p.loc, "Endpoint<" + fullname + "Child>", ["mozilla", "ipc"]
+ ),
+ ActorType(p.decl.type),
+ ),
+ shortname="Endpoint<" + p.name + "Child>",
+ )
+
+ p.parentManagedEndpointDecl = self.declare(
+ loc=p.loc,
+ type=ManagedEndpointType(
+ QualifiedId(
+ p.loc,
+ "ManagedEndpoint<" + fullname + "Parent>",
+ ["mozilla", "ipc"],
+ ),
+ ActorType(p.decl.type),
+ ),
+ shortname="ManagedEndpoint<" + p.name + "Parent>",
+ )
+ p.childManagedEndpointDecl = self.declare(
+ loc=p.loc,
+ type=ManagedEndpointType(
+ QualifiedId(
+ p.loc,
+ "ManagedEndpoint<" + fullname + "Child>",
+ ["mozilla", "ipc"],
+ ),
+ ActorType(p.decl.type),
+ ),
+ shortname="ManagedEndpoint<" + 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)
+
+ if tu.protocol:
+ # grab symbols in the protocol itself
+ p.accept(self)
+
+ 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)
+ self.symtab.declare(inc.tu.protocol.parentManagedEndpointDecl)
+ self.symtab.declare(inc.tu.protocol.childManagedEndpointDecl)
+ 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, "visited"):
+ return
+
+ stype = sd.decl.type
+
+ self.symtab.enterScope()
+ sd.visited = True
+
+ 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()
+
+ 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) or using.type.uniqueptr:
+ # Prevent generation of typedefs. If basename == fullname then
+ # there is nothing to typedef. With UniquePtrs, basenames
+ # are generic so typedefs would be illegal.
+ fullname = None
+ if fullname == "mozilla::ipc::Shmem":
+ ipdltype = ShmemType(using.type.spec)
+ elif fullname == "mozilla::ipc::ByteBuf":
+ ipdltype = ByteBufType(using.type.spec)
+ elif fullname == "mozilla::ipc::FileDescriptor":
+ ipdltype = FDType(using.type.spec)
+ else:
+ ipdltype = ImportedCxxType(
+ using.type.spec, using.isRefcounted(), using.isMoveonly()
+ )
+ existingType = self.symtab.lookup(ipdltype.fullname())
+ if existingType and existingType.fullname == ipdltype.fullname():
+ if ipdltype.isRefcounted() != existingType.type.isRefcounted():
+ self.error(
+ using.loc,
+ "inconsistent refcounted status of type `%s`",
+ str(using.type),
+ )
+ if ipdltype.isMoveonly() != existingType.type.isMoveonly():
+ self.error(
+ using.loc,
+ "inconsistent moveonly status of type `%s`",
+ str(using.type),
+ )
+ 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()
+
+ 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 not (p.managers or p.messageDecls or p.managesStmts):
+ 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,
+ )
+
+ # 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()
+
+ 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'", # NOQA: E501
+ 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()
+
+ msgtype = MessageType(
+ md.nested,
+ md.prio,
+ md.sendSemantics,
+ md.direction,
+ ctor=isctor,
+ dtor=isdtor,
+ cdtype=cdtype,
+ compress=md.compress,
+ tainted=md.tainted,
+ 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)
+ 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.decl = self.declare(loc=loc, type=msgtype, progname=msgname)
+ md.protocolDecl = self.currentProtocolDecl
+ md.decl._md = md
+
+ def _canonicalType(self, itype, typespec):
+ loc = typespec.loc
+ if itype.isIPDL():
+ if itype.isProtocol():
+ itype = ActorType(itype, nullable=typespec.nullable)
+
+ 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)
+
+ if typespec.maybe:
+ itype = MaybeType(itype)
+
+ if typespec.uniqueptr:
+ itype = UniquePtrType(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.hasBaseType():
+ 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):
+ TcheckVisitor.__init__(self, 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
+
+ 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", # NOQA: E501
+ pname,
+ mgrtype.name(),
+ )
+
+ if ptype.isToplevel():
+ cycles = checkcycles(p.decl.type)
+ if cycles:
+ self.error(
+ p.decl.loc,
+ "cycle(s) detected in manager/manages hierarchy: %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 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'", # NOQA: E501
+ pname,
+ mgsname,
+ )
+
+ def visitManager(self, mgr):
+ 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'", # NOQA: E501
+ 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')", # NOQA: E501
+ 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", # NOQA: E501
+ mname,
+ pname,
+ )
+
+ if (mtype.isCtor() or mtype.isDtor()) and mtype.isAsync() and mtype.returns:
+ self.error(
+ loc, "asynchronous ctor/dtor message `%s' declares return values", mname
+ )
+
+ if mtype.compress and (not mtype.isAsync() or mtype.isCtor() or mtype.isDtor()):
+
+ if mtype.isCtor() or mtype.isDtor():
+ message_type = "constructor" if mtype.isCtor() else "destructor"
+ error_message = (
+ "%s messages can't use compression (here, in protocol `%s')"
+ % (message_type, pname)
+ )
+ else:
+ error_message = (
+ "message `%s' in protocol `%s' requests compression but is not async"
+ % (mname, pname) # NOQA: E501
+ )
+
+ self.error(loc, error_message)
+
+ 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,
+ )
diff --git a/ipc/ipdl/ipdl/util.py b/ipc/ipdl/ipdl/util.py
new file mode 100644
index 0000000000..60d9c904e2
--- /dev/null
+++ b/ipc/ipdl/ipdl/util.py
@@ -0,0 +1,12 @@
+# 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 zlib
+
+
+# The built-in hash over the str type is non-deterministic, so we need to do
+# this instead.
+def hash_str(s):
+ assert isinstance(s, str)
+ return zlib.adler32(s.encode("utf-8"))
diff --git a/ipc/ipdl/message-metadata.ini b/ipc/ipdl/message-metadata.ini
new file mode 100644
index 0000000000..88c4715026
--- /dev/null
+++ b/ipc/ipdl/message-metadata.ini
@@ -0,0 +1,38 @@
+#############################################################
+# #
+# Any changes to this file must be reviewed by an IPC peer. #
+# #
+#############################################################
+
+[PContent::AccumulateChildKeyedHistograms]
+segment_capacity = 16384
+[PContent::AccumulateChildHistograms]
+segment_capacity = 16384
+[PLayerTransaction::Update]
+segment_capacity = 8192
+[PContent::StoreAndBroadcastBlobURLRegistration]
+segment_capacity = 8192
+[PHttpChannel::Redirect1Begin]
+segment_capacity = 8192
+[PHttpBackgroundChannel::OnStartRequest]
+segment_capacity = 8192
+[PHttpBackgroundChannel::OnTransportAndData]
+segment_capacity = 8192
+[PNecko::PHttpChannelConstructor]
+segment_capacity = 8192
+[PMessagePort::PostMessages]
+segment_capacity = 12288
+[PMessagePort::ReceiveData]
+segment_capacity = 12288
+
+#------------------------------------------------------------
+# Small-size messages.
+#------------------------------------------------------------
+[PCompositorBridge::DidComposite]
+segment_capacity = 128
+[PBrowser::RealMouseMoveEvent]
+segment_capacity = 192
+[PCompositorBridge::PTextureConstructor]
+segment_capacity = 192
+[PHttpBackgroundChannel::OnStopRequest]
+segment_capacity = 192
diff --git a/ipc/ipdl/moz.build b/ipc/ipdl/moz.build
new file mode 100644
index 0000000000..b5f01c5267
--- /dev/null
+++ b/ipc/ipdl/moz.build
@@ -0,0 +1,21 @@
+# -*- 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 += ["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"]
+
+# Add libFuzzer configuration directives
+include("/tools/fuzzing/libfuzzer-config.mozbuild")
diff --git a/ipc/ipdl/msgtype-components b/ipc/ipdl/msgtype-components
new file mode 100644
index 0000000000..35229182a2
--- /dev/null
+++ b/ipc/ipdl/msgtype-components
@@ -0,0 +1,14 @@
+#!/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/.
+
+from __future__ import print_function
+
+import sys
+
+msgid = int(sys.argv[1])
+protocol = (msgid >> 16)
+msg = (msgid - (protocol << 16))
+
+print('protocol', protocol, 'message', msg)
diff --git a/ipc/ipdl/sync-messages.ini b/ipc/ipdl/sync-messages.ini
new file mode 100644
index 0000000000..ed5a001a0c
--- /dev/null
+++ b/ipc/ipdl/sync-messages.ini
@@ -0,0 +1,1124 @@
+#############################################################
+# #
+# AVOID ADDING NEW MESSAGES TO THIS FILE #
+# #
+# New sync IPC messages will only be approved in #
+# exceptional circumstances, and must be reviewed by an IPC #
+# peer. Please be prepared with a detailed justification #
+# before requesting IPC peer review. #
+# #
+#############################################################
+
+# C++ unit tests
+[PTestBadActorSub::__delete__]
+description = Only used by C++ unit tests
+[PTestCancel::Test1_1]
+description = Only used by C++ unit tests
+[PTestCancel::Test2_2]
+description = Only used by C++ unit tests
+[PTestCancel::Test2_1]
+description = Only used by C++ unit tests
+[PTestCancel::Test3_1]
+description = Only used by C++ unit tests
+[PTestCancel::Test3_2]
+description = Only used by C++ unit tests
+[PTestCancel::CheckChild]
+description = Only used by C++ unit tests
+[PTestCancel::CheckParent]
+description = Only used by C++ unit tests
+[PTestCrashCleanup::DIEDIEDIE]
+description = Only used by C++ unit tests
+[PTestDataStructures::Test1]
+description = Only used by C++ unit tests
+[PTestDataStructures::Test2]
+description = Only used by C++ unit tests
+[PTestDataStructures::Test3]
+description = Only used by C++ unit tests
+[PTestDataStructures::Test4]
+description = Only used by C++ unit tests
+[PTestDataStructures::Test5]
+description = Only used by C++ unit tests
+[PTestDataStructures::Test6]
+description = Only used by C++ unit tests
+[PTestDataStructures::Test7_0]
+description = Only used by C++ unit tests
+[PTestDataStructures::Test7]
+description = Only used by C++ unit tests
+[PTestDataStructures::Test8]
+description = Only used by C++ unit tests
+[PTestDataStructures::Test9]
+description = Only used by C++ unit tests
+[PTestDataStructures::Test10]
+description = Only used by C++ unit tests
+[PTestDataStructures::Test11]
+description = Only used by C++ unit tests
+[PTestDataStructures::Test12]
+description = Only used by C++ unit tests
+[PTestDataStructures::Test13]
+description = Only used by C++ unit tests
+[PTestDataStructures::Test14]
+description = Only used by C++ unit tests
+[PTestDataStructures::Test15]
+description = Only used by C++ unit tests
+[PTestDataStructures::Test16]
+description = Only used by C++ unit tests
+[PTestDataStructures::Test17]
+description = Only used by C++ unit tests
+[PTestDataStructures::Test18]
+description = Only used by C++ unit tests
+[PTestDataStructures::Dummy]
+description = Only used by C++ unit tests
+[PTestDataStructuresSub::__delete__]
+description = Only used by C++ unit tests
+[PTestDemon::HiPrioSyncMessage]
+description = Only used by C++ unit tests
+[PTestDemon::SyncMessage]
+description = Only used by C++ unit tests
+[PTestDemon::UrgentSyncMessage]
+description = Only used by C++ unit tests
+[PTestDesc::PTestDescSub]
+description = Only used by C++ unit tests
+[PTestDescSub::PTestDescSubsub]
+description = Only used by C++ unit tests
+[PTestDescSubsub::__delete__]
+description = Only used by C++ unit tests
+[PTestEndpointBridgeMainSub::HiRpc]
+description = Only used by C++ unit tests
+[PTestEndpointBridgeMainSub::HelloSync]
+description = Only used by C++ unit tests
+[PTestEndpointBridgeMainSub::HelloRpc]
+description = Only used by C++ unit tests
+[PTestEndpointOpensOpened::HiRpc]
+description = Only used by C++ unit tests
+[PTestEndpointOpensOpened::HelloSync]
+description = Only used by C++ unit tests
+[PTestEndpointOpensOpened::HelloRpc]
+description = Only used by C++ unit tests
+[PTestFailedCtor::PTestFailedCtorSub]
+description = Only used by C++ unit tests
+[PTestFailedCtorSub::Sync]
+description = Only used by C++ unit tests
+[PTestHangs::StackFrame]
+description = Only used by C++ unit tests
+[PTestHangs::Hang]
+description = Only used by C++ unit tests
+[PTestHighestPrio::Msg2]
+description = Only used by C++ unit tests
+[PTestHighestPrio::Msg4]
+description = Only used by C++ unit tests
+[PTestHighestPrio::StartInner]
+description = Only used by C++ unit tests
+[PTestIndirectProtocolParamFirst::Test]
+description = Only used by C++ unit tests
+[PTestInterruptErrorCleanup::Error]
+description = Only used by C++ unit tests
+[PTestInterruptErrorCleanup::__delete__]
+description = Only used by C++ unit tests
+[PTestInterruptRaces::Race]
+description = Only used by C++ unit tests
+[PTestInterruptRaces::StackFrame]
+description = Only used by C++ unit tests
+[PTestInterruptRaces::StackFrame3]
+description = Only used by C++ unit tests
+[PTestInterruptRaces::StartRace]
+description = Only used by C++ unit tests
+[PTestInterruptRaces::Parent]
+description = Only used by C++ unit tests
+[PTestInterruptRaces::GetAnsweredParent]
+description = Only used by C++ unit tests
+[PTestInterruptRaces::Child]
+description = Only used by C++ unit tests
+[PTestInterruptShutdownRace::StartDeath]
+description = Only used by C++ unit tests
+[PTestInterruptShutdownRace::Exit]
+description = Only used by C++ unit tests
+[PTestJSON::Test]
+description = Only used by C++ unit tests
+[PTestLatency::Rpc]
+description = Only used by C++ unit tests
+[PTestLatency::Synchro]
+description = Only used by C++ unit tests
+[PTestLatency::Synchro2]
+description = Only used by C++ unit tests
+[PTestNestedLoops::R]
+description = Only used by C++ unit tests
+[PTestPriority::PMsg2]
+description = Only used by C++ unit tests
+[PTestPriority::PMsg4]
+description = Only used by C++ unit tests
+[PTestPriority::PMsg6]
+description = Only used by C++ unit tests
+[PTestRPC::Test1_Start]
+description = Only used by C++ unit tests
+[PTestRPC::Test1_InnerEvent]
+description = Only used by C++ unit tests
+[PTestRPC::Test2_OutOfOrder]
+description = Only used by C++ unit tests
+[PTestRPC::Test1_InnerQuery]
+description = Only used by C++ unit tests
+[PTestRPC::Test1_NoReenter]
+description = Only used by C++ unit tests
+[PTestRPC::Test2_FirstUrgent]
+description = Only used by C++ unit tests
+[PTestRPC::Test2_SecondUrgent]
+description = Only used by C++ unit tests
+[PTestRaceDeadlock::Lose]
+description = Only used by C++ unit tests
+[PTestRaceDeadlock::Win]
+description = Only used by C++ unit tests
+[PTestRaceDeadlock::Rpc]
+description = Only used by C++ unit tests
+[PTestRaceDeferral::Lose]
+description = Only used by C++ unit tests
+[PTestRaceDeferral::Win]
+description = Only used by C++ unit tests
+[PTestRaceDeferral::Rpc]
+description = Only used by C++ unit tests
+[PTestRacyInterruptReplies::R_]
+description = Only used by C++ unit tests
+[PTestRacyInterruptReplies::_R]
+description = Only used by C++ unit tests
+[PTestRacyReentry::E]
+description = Only used by C++ unit tests
+[PTestRacyReentry::H]
+description = Only used by C++ unit tests
+[PTestRacyUndefer::Race]
+description = Only used by C++ unit tests
+[PTestRacyUndefer::Spam]
+description = Only used by C++ unit tests
+[PTestRacyUndefer::RaceWinTwice]
+description = Only used by C++ unit tests
+[PTestShutdown::Sync]
+description = Only used by C++ unit tests
+[PTestShutdownSub::StackFrame]
+description = Only used by C++ unit tests
+[PTestShutdownSub::__delete__]
+description = Only used by C++ unit tests
+[PTestShutdownSubsub::__delete__]
+description = Only used by C++ unit tests
+[PTestStackHooks::Sync]
+description = Only used by C++ unit tests
+[PTestStackHooks::Rpc]
+description = Only used by C++ unit tests
+[PTestStackHooks::StackFrame]
+description = Only used by C++ unit tests
+[PTestSyncError::Error]
+description = Only used by C++ unit tests
+[PTestSyncWakeup::StackFrame]
+description = Only used by C++ unit tests
+[PTestSyncWakeup::Sync1]
+description = Only used by C++ unit tests
+[PTestSyncWakeup::Sync2]
+description = Only used by C++ unit tests
+[PTestUrgency::Test1]
+description = Only used by C++ unit tests
+[PTestUrgency::Test3]
+description = Only used by C++ unit tests
+[PTestUrgency::FinalTest_Begin]
+description = Only used by C++ unit tests
+[PTestUrgency::Reply1]
+description = Only used by C++ unit tests
+[PTestUrgency::Reply2]
+description = Only used by C++ unit tests
+[PTestUrgentHangs::Test1_2]
+description = Only used by C++ unit tests
+[PTestUrgentHangs::TestInner]
+description = Only used by C++ unit tests
+[PTestUrgentHangs::TestInnerUrgent]
+description = Only used by C++ unit tests
+[PTestUrgentHangs::Test1_1]
+description = Only used by C++ unit tests
+[PTestUrgentHangs::Test1_3]
+description = Only used by C++ unit tests
+[PTestUrgentHangs::Test2]
+description = Only used by C++ unit tests
+[PTestUrgentHangs::Test3]
+description = Only used by C++ unit tests
+[PTestUrgentHangs::Test4_1]
+description = Only used by C++ unit tests
+[PTestUrgentHangs::Test5_1]
+description = Only used by C++ unit tests
+[PTestLayoutThread::SyncMessage]
+description = Only used by C++ unit tests
+[PTestPaintThread::FinishedPaint]
+description = Only used by C++ unit tests
+
+# A11y code
+[PDocAccessible::State]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::NativeState]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::Name]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::Value]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::Help]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::Description]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::Attributes]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::RelationByType]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::Relations]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::IsSearchbox]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::LandmarkRole]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::ARIARoleAtom]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::GroupPosition]
+description = Legacy a11y IPC. Platform accessibility APIs need to query content synchronously for information.
+platform = notwin
+[PDocAccessible::CaretLineNumber]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::CaretOffset]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::CharacterCount]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::SelectionCount]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::TextSubstring]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::GetTextAfterOffset]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::GetTextAtOffset]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::GetTextBeforeOffset]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::CharAt]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::TextAttributes]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::DefaultTextAttributes]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::TextBounds]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::CharBounds]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::OffsetAtPoint]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::SelectionBoundsAt]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::SetSelectionBoundsAt]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::AddToSelection]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::RemoveFromSelection]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::Text]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::ReplaceText]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::InsertText]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::CopyText]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::CutText]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::DeleteText]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::PasteText]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::ImagePosition]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::ImageSize]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::StartOffset]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::EndOffset]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::IsLinkValid]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::AnchorCount]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::AnchorURIAt]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::AnchorAt]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::LinkCount]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::LinkAt]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::LinkIndexOf]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::LinkIndexAtOffset]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::TableOfACell]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::ColIdx]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::RowIdx]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::GetPosition]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::ColExtent]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::RowExtent]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::GetColRowExtents]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::ColHeaderCells]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::RowHeaderCells]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::IsCellSelected]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::TableCaption]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::TableSummary]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::TableColumnCount]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::TableRowCount]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::TableCellAt]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::TableCellIndexAt]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::TableColumnIndexAt]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::TableRowIndexAt]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::TableRowAndColumnIndicesAt]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::TableColumnExtentAt]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::TableRowExtentAt]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::TableColumnDescription]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::TableRowDescription]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::TableColumnSelected]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::TableRowSelected]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::TableCellSelected]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::TableSelectedCellCount]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::TableSelectedColumnCount]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::TableSelectedRowCount]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::TableSelectedCells]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::TableSelectedCellIndices]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::TableSelectedColumnIndices]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::TableSelectedRowIndices]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::TableSelectColumn]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::TableSelectRow]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::TableUnselectColumn]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::TableUnselectRow]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::TableIsProbablyForLayout]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::AtkTableColumnHeader]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::AtkTableRowHeader]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::SelectedItems]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::SelectedItemCount]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::GetSelectedItem]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::IsItemSelected]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::AddItemToSelection]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::RemoveItemFromSelection]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::SelectAll]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::UnselectAll]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::DoAction]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::ActionCount]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::ActionDescriptionAt]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::ActionNameAt]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::AccessKey]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::KeyboardShortcut]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::AtkKeyBinding]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::CurValue]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::SetCurValue]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::MinValue]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::MaxValue]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::Step]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::FocusedChild]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::Language]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::DocType]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::Title]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::URL]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::MimeType]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::URLDocTypeMimeType]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::ChildAtPoint]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::Extents]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::ExtentsInCSSPixels]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::DOMNodeID]
+description = Legacy a11y IPC
+platform = notwin
+[PDocAccessible::GetWindowedPluginIAccessible]
+description = Legacy a11y IPC
+platform = win
+[PDocAccessible::SyncTextChangeEvent]
+description = Legacy a11y IPC
+platform = win
+
+[PDocAccessiblePlatformExt::RangeAt]
+description = Retrieve a text range given an offset and range type. Platform API is synchronous, so this needs to be too.
+platform = mac
+[PDocAccessiblePlatformExt::NextClusterAt]
+description = Retrieve next character from offset. Platform API is synchronous, so this needs to be too.
+platform = mac
+[PDocAccessiblePlatformExt::PreviousClusterAt]
+description = Retrieve previous character from offset. Platform API is synchronous, so this needs to be too.
+platform = mac
+[PDocAccessiblePlatformExt::TextForRange]
+description = Retrieve flattened string for text range. Platform API is synchronous, so this needs to be too.
+platform = mac
+[PDocAccessiblePlatformExt::BoundsForRange]
+description = Retrieve geometric bounds for text range. Platform API is synchronous, so this needs to be too.
+platform = mac
+[PDocAccessiblePlatformExt::LengthForRange]
+description = Retrieve length of text range. Platform API is synchronous, so this needs to be too.
+platform = mac
+[PDocAccessiblePlatformExt::OffsetAtIndex]
+description = Retrieve text offset at absolute index. Platform API is synchronous, so this needs to be too.
+platform = mac
+[PDocAccessiblePlatformExt::RangeOfChild]
+description = Retrieve text range in parent of child. Platform API is synchronous, so this needs to be too.
+platform = mac
+[PDocAccessiblePlatformExt::LeafAtOffset]
+description = Retrieve text leaf at given offset. Platform API is synchronous, so this needs to be too.
+platform = mac
+[PDocAccessiblePlatformExt::ApplyPostSearchFilter]
+description = Filter a preliminary list of accessibles that match a predicate. Platform API is synchronous, so this needs to be too.
+platform = mac
+
+# Plugins
+[PPluginWidget::Create]
+description = Legacy NPAPI IPC
+[PPluginWidget::GetNativePluginPort]
+description = Legacy NPAPI IPC
+[PPluginWidget::SetNativeChildWindow]
+description = Legacy NPAPI IPC
+[PFunctionBroker::BrokerFunction]
+description = Legacy NPAPI IPC
+[PPluginInstance::CreateChildPluginWindow]
+description = Legacy NPAPI IPC
+[PPluginInstance::NPP_SetWindow]
+description = Legacy NPAPI IPC
+[PPluginInstance::NPP_GetValue_NPPVpluginWantsAllNetworkStreams]
+description = Legacy NPAPI IPC
+[PPluginInstance::NPP_GetValue_NPPVpluginScriptableNPObject]
+description = Legacy NPAPI IPC
+[PPluginInstance::NPP_SetValue_NPNVprivateModeBool]
+description = Legacy NPAPI IPC
+[PPluginInstance::NPP_GetValue_NPPVpluginNativeAccessibleAtkPlugId]
+description = Legacy NPAPI IPC
+[PPluginInstance::NPP_SetValue_NPNVCSSZoomFactor]
+description = Legacy NPAPI IPC
+[PPluginInstance::NPP_SetValue_NPNVmuteAudioBool]
+description = Legacy NPAPI IPC
+[PPluginInstance::NPP_HandleEvent]
+description = Legacy NPAPI IPC
+[PPluginInstance::NPP_HandleEvent_Shmem]
+description = Legacy NPAPI IPC
+[PPluginInstance::NPP_HandleEvent_IOSurface]
+description = Legacy NPAPI IPC
+[PPluginInstance::Paint]
+description = Legacy NPAPI IPC
+[PPluginInstance::NPP_Destroy]
+description = Legacy NPAPI IPC
+[PPluginInstance::NPN_GetValue_NPNVWindowNPObject]
+description = Legacy NPAPI IPC
+[PPluginInstance::NPN_GetValue_NPNVPluginElementNPObject]
+description = Legacy NPAPI IPC
+[PPluginInstance::NPN_GetValue_NPNVprivateModeBool]
+description = Legacy NPAPI IPC
+[PPluginInstance::NPN_GetValue_NPNVnetscapeWindow]
+description = Legacy NPAPI IPC
+[PPluginInstance::NPN_GetValue_NPNVdocumentOrigin]
+description = Legacy NPAPI IPC
+[PPluginInstance::NPN_GetValue_DrawingModelSupport]
+description = Legacy NPAPI IPC
+[PPluginInstance::NPN_GetValue_SupportsAsyncBitmapSurface]
+description = Legacy NPAPI IPC
+[PPluginInstance::NPN_GetValue_SupportsAsyncDXGISurface]
+description = Legacy NPAPI IPC
+[PPluginInstance::NPN_GetValue_PreferredDXGIAdapter]
+description = Legacy NPAPI IPC
+[PPluginInstance::NPN_SetValue_NPPVpluginWindow]
+description = Legacy NPAPI IPC
+[PPluginInstance::NPN_SetValue_NPPVpluginTransparent]
+description = Legacy NPAPI IPC
+[PPluginInstance::NPN_SetValue_NPPVpluginUsesDOMForCursor]
+description = Legacy NPAPI IPC
+[PPluginInstance::NPN_SetValue_NPPVpluginDrawingModel]
+description = Legacy NPAPI IPC
+[PPluginInstance::NPN_SetValue_NPPVpluginEventModel]
+description = Legacy NPAPI IPC
+[PPluginInstance::NPN_SetValue_NPPVpluginIsPlayingAudio]
+description = Legacy NPAPI IPC
+[PPluginInstance::NPN_GetURL]
+description = Legacy NPAPI IPC
+[PPluginInstance::NPN_PostURL]
+description = Legacy NPAPI IPC
+[PPluginInstance::PStreamNotify]
+description = Legacy NPAPI IPC
+[PPluginInstance::RevokeCurrentDirectSurface]
+description = Legacy NPAPI IPC
+[PPluginInstance::InitDXGISurface]
+description = Legacy NPAPI IPC
+[PPluginInstance::FinalizeDXGISurface]
+description = Legacy NPAPI IPC
+[PPluginInstance::ShowDirectBitmap]
+description = Legacy NPAPI IPC
+[PPluginInstance::ShowDirectDXGISurface]
+description = Legacy NPAPI IPC
+[PPluginInstance::Show]
+description = Legacy NPAPI IPC
+[PPluginInstance::NPN_PushPopupsEnabledState]
+description = Legacy NPAPI IPC
+[PPluginInstance::NPN_PopPopupsEnabledState]
+description = Legacy NPAPI IPC
+[PPluginInstance::NPN_GetValueForURL]
+description = Legacy NPAPI IPC
+[PPluginInstance::NPN_SetValueForURL]
+description = Legacy NPAPI IPC
+[PPluginInstance::NPN_ConvertPoint]
+description = Legacy NPAPI IPC
+[PPluginInstance::GetCompositionString]
+description = Legacy NPAPI IPC
+[PPluginInstance::NPP_NewStream]
+description = Legacy NPAPI IPC
+[PPluginInstance::PluginFocusChange]
+description = Legacy NPAPI IPC
+[PPluginInstance::SetPluginFocus]
+description = Legacy NPAPI IPC
+[PPluginInstance::UpdateWindow]
+description = Legacy NPAPI IPC
+[PPluginModule::ModuleSupportsAsyncRender]
+description = Legacy NPAPI IPC
+[PPluginModule::NP_GetEntryPoints]
+description = Legacy NPAPI IPC
+[PPluginModule::NP_Initialize]
+description = Legacy NPAPI IPC
+[PPluginModule::SyncNPP_New]
+description = Legacy NPAPI IPC
+[PPluginModule::NP_Shutdown]
+description = Legacy NPAPI IPC
+[PPluginModule::OptionalFunctionsSupported]
+description = Legacy NPAPI IPC
+[PPluginModule::ProcessSomeEvents]
+description = Legacy NPAPI IPC
+[PPluginModule::NPN_SetException]
+description = Legacy NPAPI IPC
+[PPluginModule::NPN_SetValue_NPPVpluginRequiresAudioDeviceChanges]
+description = Legacy NPAPI IPC
+[PPluginModule::InitCrashReporter]
+description = Legacy NPAPI IPC
+[PPluginScriptableObject::NPN_Evaluate]
+description = Legacy NPAPI IPC
+[PPluginScriptableObject::Invalidate]
+description = Legacy NPAPI IPC
+[PPluginScriptableObject::HasMethod]
+description = Legacy NPAPI IPC
+[PPluginScriptableObject::Invoke]
+description = Legacy NPAPI IPC
+[PPluginScriptableObject::InvokeDefault]
+description = Legacy NPAPI IPC
+[PPluginScriptableObject::HasProperty]
+description = Legacy NPAPI IPC
+[PPluginScriptableObject::SetProperty]
+description = Legacy NPAPI IPC
+[PPluginScriptableObject::RemoveProperty]
+description = Legacy NPAPI IPC
+[PPluginScriptableObject::Enumerate]
+description = Legacy NPAPI IPC
+[PPluginScriptableObject::Construct]
+description = Legacy NPAPI IPC
+[PPluginScriptableObject::GetParentProperty]
+description = Legacy NPAPI IPC
+[PPluginScriptableObject::GetChildProperty]
+description = Legacy NPAPI IPC
+[PPluginStream::NPN_Write]
+description = Legacy NPAPI IPC
+[PPluginStream::__delete__]
+description = Legacy NPAPI IPC
+# The rest
+[PHeapSnapshotTempFileHelper::OpenHeapSnapshotTempFile]
+description = legacy sync IPC - please add detailed description
+[PBackgroundMutableFile::GetFileId]
+description = legacy sync IPC - please add detailed description
+[PBackgroundIndexedDBUtils::GetFileReferences]
+description = legacy sync IPC - please add detailed description
+[PBrowser::SyncMessage]
+description = JS MessageManager implementation
+[PBrowser::PPluginWidget]
+description = Legacy NPAPI IPC
+[PBrowser::DispatchFocusToTopLevelWindow]
+description = legacy sync IPC - please add detailed description
+[PBrowser::NotifyIMEMouseButtonEvent]
+description = legacy sync IPC - please add detailed description
+[PBrowser::RequestIMEToCommitComposition]
+description = legacy sync IPC - please add detailed description
+[PBrowser::GetInputContext]
+description = legacy sync IPC - please add detailed description
+[PBrowser::RequestNativeKeyBindings]
+description = legacy sync IPC - please add detailed description
+[PBrowser::DispatchWheelEvent]
+description = legacy sync IPC - please add detailed description
+[PBrowser::DispatchMouseEvent]
+description = legacy sync IPC - please add detailed description
+[PBrowser::DispatchKeyboardEvent]
+description = legacy sync IPC - please add detailed description
+[PBrowser::EnsureLayersConnected]
+description = legacy sync IPC - please add detailed description
+[PBrowser::SetSystemFont]
+description = test only
+[PBrowser::GetSystemFont]
+description = test only
+[PContent::SyncMessage]
+description = JS MessageManager implementation
+[PContent::LoadPlugin]
+description = Legacy NPAPI IPC
+[PContent::ConnectPluginBridge]
+description = Legacy NPAPI IPC
+[PContent::IsSecureURI]
+description = legacy sync IPC - please add detailed description
+[PContent::PURLClassifier]
+description = legacy sync IPC - please add detailed description
+[PContent::GetGfxVars]
+description = legacy sync IPC - please add detailed description
+[PContent::GetClipboard]
+description = Legacy synchronous clipboard API
+[PContent::ClipboardHasType]
+description = Legacy synchronous clipboard API
+[PContent::GetExternalClipboardFormats]
+description = Retrieve supported clipboard formats synchronously
+[PContent::GetIconForExtension]
+description = legacy sync IPC - please add detailed description
+[PContent::BeginDriverCrashGuard]
+description = legacy sync IPC - please add detailed description
+[PContent::EndDriverCrashGuard]
+description = legacy sync IPC - please add detailed description
+[PContent::GetGraphicsDeviceInitData]
+description = Retrieve information needed to initialize the graphics device in the content process
+[PContent::GetOutputColorProfileData]
+description = Retrieve the contents of the output color profile file
+[PContent::GetFontListShmBlock]
+description = for bug 1514869 - layout code needs synchronous access to font list, but this is used only once per block, after which content directly reads the shared memory
+[PContent::InitializeFamily]
+description = for bug 1514869 - layout is blocked on needing sync access to a specific font family - used once per family, then the data is cached in shared memory
+[PContent::InitOtherFamilyNames]
+description = for bug 1514869 - layout is blocked on font lookup, needs complete family-name information - not used after loading is complete
+[PContent::GetHyphDict]
+description = for bug 1487212 - layout requires hyphenation data from a given omnijar resource - only called once per locale by a given content process
+[PContent::UngrabPointer]
+description = legacy sync IPC - please add detailed description
+[PContent::RemovePermission]
+description = legacy sync IPC - please add detailed description
+[PContent::GetA11yContentId]
+description = legacy sync IPC - please add detailed description
+[PGMP::StartPlugin]
+description = legacy sync IPC - please add detailed description
+[PGMPService::LaunchGMP]
+description = legacy sync IPC - please add detailed description
+[PGMPService::GetGMPNodeId]
+description = legacy sync IPC - please add detailed description
+[PGMPVideoDecoder::NeedShmem]
+description = legacy sync IPC - please add detailed description
+[PGMPVideoEncoder::NeedShmem]
+description = legacy sync IPC - please add detailed description
+[PRemoteDecoderManager::Readback]
+description = legacy sync IPC - please add detailed description
+[PBackgroundStorage::Preload]
+description = legacy sync IPC - please add detailed description
+[PBackgroundLSDatabase::PBackgroundLSSnapshot]
+description = See corresponding comment in PBackgroundLSDatabase.ipdl
+[PBackgroundLSSnapshot::LoadValueAndMoreItems]
+description = See corresponding comment in PBackgroundLSSnapshot.ipdl
+[PBackgroundLSSnapshot::LoadKeys]
+description = See corresponding comment in PBackgroundLSSnapshot.ipdl
+[PBackgroundLSSnapshot::IncreasePeakUsage]
+description = See corresponding comment in PBackgroundLSSnapshot.ipdl
+[PBackgroundLSSnapshot::Ping]
+description = See corresponding comment in PBackgroundLSSnapshot.ipdl
+[PBackgroundSessionStorageCache::Load]
+description = See corresponding comment in PBackgroundSessionStorageCache.ipdl
+[PRemoteSpellcheckEngine::CheckAndSuggest]
+description = legacy sync IPC - please add detailed description
+[PRemoteSpellcheckEngine::SetDictionary]
+description = legacy sync IPC - please add detailed description
+[PGPU::AddLayerTreeIdMapping]
+description = legacy sync IPC - please add detailed description
+[PGPU::GetDeviceStatus]
+description = legacy sync IPC - please add detailed description
+[PGPU::SimulateDeviceReset]
+description = legacy sync IPC - please add detailed description
+[PAPZInputBridge::ReceiveMultiTouchInputEvent]
+description = legacy sync IPC - please add detailed description
+[PAPZInputBridge::ReceiveMouseInputEvent]
+description = legacy sync IPC - please add detailed description
+[PAPZInputBridge::ReceivePanGestureInputEvent]
+description = legacy sync IPC - please add detailed description
+[PAPZInputBridge::ReceivePinchGestureInputEvent]
+description = legacy sync IPC - please add detailed description
+[PAPZInputBridge::ReceiveTapGestureInputEvent]
+description = legacy sync IPC - please add detailed description
+[PAPZInputBridge::ReceiveScrollWheelInputEvent]
+description = legacy sync IPC - please add detailed description
+[PAPZInputBridge::ProcessUnhandledEvent]
+description = legacy sync IPC - please add detailed description
+[PAPZInputBridge::ReceiveKeyboardInputEvent]
+description = legacy sync IPC - please add detailed description
+[PCompositorBridge::Initialize]
+description = legacy sync IPC - please add detailed description
+[PCompositorBridge::WillClose]
+description = legacy sync IPC - please add detailed description
+[PCompositorBridge::Pause]
+description = legacy sync IPC - please add detailed description
+[PCompositorBridge::Resume]
+description = legacy sync IPC - please add detailed description
+[PCompositorBridge::NotifyChildCreated]
+description = legacy sync IPC - please add detailed description
+[PCompositorBridge::MapAndNotifyChildCreated]
+description = bug 1350660
+[PCompositorBridge::NotifyChildRecreated]
+description = legacy sync IPC - please add detailed description
+[PCompositorBridge::MakeSnapshot]
+description = legacy sync IPC - please add detailed description
+[PCompositorBridge::FlushRendering]
+description = legacy sync IPC - please add detailed description
+[PCompositorBridge::WaitOnTransactionProcessed]
+description = bug 1364626
+[PCompositorBridge::StartFrameTimeRecording]
+description = legacy sync IPC - please add detailed description
+[PCompositorBridge::StopFrameTimeRecording]
+description = legacy sync IPC - please add detailed description
+[PCompositorBridge::SyncWithCompositor]
+description = legacy sync IPC - please add detailed description
+[PCompositorBridge::CheckContentOnlyTDR]
+description = legacy sync IPC - please add detailed description
+[PCompositorWidget::Initialize]
+description = Fallable initialization for the widget. Must be called right after construction before any other messages are sent
+platform = win
+[PCompositorWidget::EnterPresentLock]
+description = Obtain exclusive access to the widget surface. Used to ensure events don't change the surface while it's being used as a render target
+platform = win
+[PCompositorWidget::LeavePresentLock]
+description = Release the exclusive lock that EnterPresentLock obtained
+platform = win
+[PCompositorBridge::SupportsAsyncDXGISurface]
+description = legacy sync IPC - please add detailed description
+[PCompositorBridge::PreferredDXGIAdapter]
+description = legacy sync IPC - please add detailed description
+[PCompositorWidget::ClearTransparentWindow]
+description = Synchronously clear the widget surface. Does not rely on (Enter|Leave)PresentLock. When call returns, window surface has been fully updated with cleared pixel values.
+platform = win
+[PImageBridge::NewCompositable]
+description = legacy sync IPC - please add detailed description
+[PImageBridge::MakeAsyncPluginSurfaces]
+description = When plugin asks content to synchronously make surfaces, content needs to synchronously get those surfaces from the compositor process since sandboxing forbids content to make them itself.
+[PImageBridge::ReadbackAsyncPluginSurface]
+description = When content needs to synchronously readback a plugin surface, the compositor process needs to synchronously perform the operation since sandboxing forbids content to perform graphics operations.
+[PLayerTransaction::SetTestSampleTime]
+description = legacy sync IPC - please add detailed description
+[PLayerTransaction::LeaveTestMode]
+description = legacy sync IPC - please add detailed description
+[PLayerTransaction::GetAnimationValue]
+description = test only
+[PLayerTransaction::GetTransform]
+description = test only
+[PLayerTransaction::SetAsyncScrollOffset]
+description = legacy sync IPC - please add detailed description
+[PLayerTransaction::SetAsyncZoom]
+description = legacy sync IPC - please add detailed description
+[PLayerTransaction::GetAPZTestData]
+description = legacy sync IPC - please add detailed description
+[PLayerTransaction::GetFrameUniformity]
+description = test only
+[PLayerTransaction::RequestProperty]
+description = legacy sync IPC - please add detailed description
+[PLayerTransaction::GetTextureFactoryIdentifier]
+description = bug 1350634
+[PUiCompositorController::Pause]
+description = legacy sync IPC - please add detailed description
+[PUiCompositorController::Resume]
+description = legacy sync IPC - please add detailed description
+[PUiCompositorController::ResumeAndResize]
+description = legacy sync IPC - please add detailed description
+[PWebRenderBridge::EnsureConnected]
+description = legacy sync IPC - please add detailed description
+[PWebRenderBridge::GetSnapshot]
+description = legacy sync IPC - please add detailed description
+[PWebRenderBridge::SetTestSampleTime]
+description = test only
+[PWebRenderBridge::LeaveTestMode]
+description = test only
+[PWebRenderBridge::GetAnimationValue]
+description = test only
+[PWebRenderBridge::SetAsyncScrollOffset]
+description = test only
+[PWebRenderBridge::SetAsyncZoom]
+description = test only
+[PWebRenderBridge::GetAPZTestData]
+description = test only
+[PWebRenderBridge::GetFrameUniformity]
+description = test only
+[PWebRenderBridge::ShutdownSync]
+description = bug 1377024
+[PWebRenderBridge::SyncWithCompositor]
+description = WebRender equivalent for PCompositorBridge::SyncWithCompositor
+[PHal::GetCurrentBatteryInformation]
+description = legacy sync IPC - please add detailed description
+[PHal::GetCurrentNetworkInformation]
+description = legacy sync IPC - please add detailed description
+[PHal::GetWakeLockInfo]
+description = legacy sync IPC - please add detailed description
+[PHal::LockScreenOrientation]
+description = legacy sync IPC - please add detailed description
+[PPrinting::SavePrintSettings]
+description = legacy sync IPC - please add detailed description
+[PHandlerService::FillHandlerInfo]
+description = legacy sync IPC - please add detailed description
+[PHandlerService::GetMIMEInfoFromOS]
+description = Lets unprivileged child processes synchronously get MIME type/handler information from the OS
+[PHandlerService::ExistsForProtocolOS]
+description = bug 1382323
+[PHandlerService::ExistsForProtocol]
+description = legacy sync IPC - please add detailed description
+[PHandlerService::Exists]
+description = legacy sync IPC - please add detailed description
+[PHandlerService::GetTypeFromExtension]
+description = legacy sync IPC - please add detailed description
+[PHandlerService::GetApplicationDescription]
+description = Lets unprivileged child processes synchronously get a description of the app that handles a given protocol scheme
+[PLayerTransaction::ShutdownSync]
+description = bug 1363126
+[PClientSource::WorkerSyncPing]
+description = Synchronous ping allowing worker thread to confirm actor is created. Necessary to avoid racing with ClientHandle actors on main thread.
+[PRemoteSandboxBroker::LaunchApp]
+description = Synchronous launch of a child process that in turn launches and sandboxes another process. Called on a dedicated thread and targets a dedicated process, so this shouldn't block anything.
+# WebGL internals
+[PWebGL::Initialize]
+description = Initialization of WebGL contexts is synchronous by spec.
+[PWebGL::GetFrontBuffer]
+description = Publishing a WebGL frame for compositing is synchronous for now to ensure DOM transaction atomicity.
+[PWebGL::OnMemoryPressure]
+description = Synchronous to ensure immediate memory pressure relief.
+# WebGL spec-synchronous functions
+[PWebGL::CheckFramebufferStatus]
+description = Checking framebuffer completenss must ask the driver.
+[PWebGL::ClientWaitSync]
+description = Checking fence-sync completenss must ask the driver.
+[PWebGL::CreateOpaqueFramebuffer]
+description = Must synchronously check for allocation success.
+[PWebGL::DrawingBufferSize]
+description = The returned size might be smaller than requested due to allocation failure.
+[PWebGL::Finish]
+description = Synchronous by spec, but not generally used.
+[PWebGL::GetBufferSubData]
+description = Retrieving buffer contents is synchronous in the worst case.
+[PWebGL::GetFrontBufferSnapshot]
+description = Retrieving canvas contents is synchronous.
+[PWebGL::ReadPixels]
+description = Retrieving WebGL framebuffer contents is synchronous.
+# WebGL reflection functions
+[PWebGL::GetBufferParameter]
+description = Reflection is cold code, but synchronous by spec.
+[PWebGL::GetCompileResult]
+description = Reflection is cold code, but synchronous by spec.
+[PWebGL::GetError]
+description = Reflection is cold code, but synchronous by spec.
+[PWebGL::GetFragDataLocation]
+description = Reflection is cold code, but synchronous by spec.
+[PWebGL::GetFramebufferAttachmentParameter]
+description = Reflection is cold code, but synchronous by spec.
+[PWebGL::GetIndexedParameter]
+description = Reflection is cold code, but synchronous by spec.
+[PWebGL::GetInternalformatParameter]
+description = Reflection is cold code, but synchronous by spec.
+[PWebGL::GetLinkResult]
+description = Reflection is cold code, but synchronous by spec.
+[PWebGL::GetNumber]
+description = Reflection is cold code, but synchronous by spec.
+[PWebGL::GetQueryParameter]
+description = Reflection is cold code, but synchronous by spec.
+[PWebGL::GetRenderbufferParameter]
+description = Reflection is cold code, but synchronous by spec.
+[PWebGL::GetSamplerParameter]
+description = Reflection is cold code, but synchronous by spec.
+[PWebGL::GetShaderPrecisionFormat]
+description = Reflection is cold code, but synchronous by spec.
+[PWebGL::GetString]
+description = Reflection is cold code, but synchronous by spec.
+[PWebGL::GetTexParameter]
+description = Reflection is cold code, but synchronous by spec.
+[PWebGL::GetUniform]
+description = Reflection is cold code, but synchronous by spec.
+[PWebGL::GetVertexAttrib]
+description = Reflection is cold code, but synchronous by spec.
+[PWebGL::IsEnabled]
+description = Reflection is cold code, but synchronous by spec.
+[PWebGL::ValidateProgram]
+description = Reflection is cold code, but synchronous by spec.
+# -
+[PSocketProcess::GetTLSClientCert]
+description = Synchronously get client certificate and key from parent process. Once bug 696976 has been fixed, this can be removed.
+
+#############################################################
+# AVOID ADDING NEW MESSAGES TO THIS FILE #
+# see comment at top of file #
+#############################################################
diff --git a/ipc/ipdl/test/README.txt b/ipc/ipdl/test/README.txt
new file mode 100644
index 0000000000..8c5eb04110
--- /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 0000000000..d2a1f9872a
--- /dev/null
+++ b/ipc/ipdl/test/cxx/IPDLUnitTestProcessChild.cpp
@@ -0,0 +1,26 @@
+/* -*- 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(int aArgc, char* aArgv[]) {
+ IPDLUnitTestChildInit(IOThreadChild::TakeChannel(), ParentPid(),
+ IOThreadChild::message_loop());
+
+ 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 0000000000..bae927cf24
--- /dev/null
+++ b/ipc/ipdl/test/cxx/IPDLUnitTestProcessChild.h
@@ -0,0 +1,29 @@
+/* -*- 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(int aArgc, char* aArgv[]) override;
+};
+
+} // 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 0000000000..660e5928d3
--- /dev/null
+++ b/ipc/ipdl/test/cxx/IPDLUnitTestSubprocess.cpp
@@ -0,0 +1,19 @@
+/* -*- 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 0000000000..384cf25ce9
--- /dev/null
+++ b/ipc/ipdl/test/cxx/IPDLUnitTestSubprocess.h
@@ -0,0 +1,34 @@
+/* -*- 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();
+
+ /**
+ * Asynchronously launch the plugin process.
+ */
+ // Could override parent Launch, but don't need to here
+ // bool Launch();
+
+ private:
+ ~IPDLUnitTestSubprocess();
+
+ 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 0000000000..7d20c77bf0
--- /dev/null
+++ b/ipc/ipdl/test/cxx/IPDLUnitTestTypes.h
@@ -0,0 +1,43 @@
+/* -*- 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 _ipdltest
+} // namespace mozilla
+
+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));
+ }
+};
+} // namespace IPC
+
+#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 0000000000..a5d2345654
--- /dev/null
+++ b/ipc/ipdl/test/cxx/IPDLUnitTestUtils.h
@@ -0,0 +1,29 @@
+
+#ifndef mozilla__ipdltest_IPDLUnitTestUtils
+#define mozilla__ipdltest_IPDLUnitTestUtils 1
+
+#include "ipc/IPCMessageUtils.h"
+
+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 0000000000..28fc4ee372
--- /dev/null
+++ b/ipc/ipdl/test/cxx/IPDLUnitTests.h
@@ -0,0 +1,85 @@
+/* -*- 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 "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);
+
+ MOZ_CRASH("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 0000000000..30eea42838
--- /dev/null
+++ b/ipc/ipdl/test/cxx/IPDLUnitTests.template.cpp
@@ -0,0 +1,347 @@
+//
+// 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"
+
+// clang-format off
+//-----------------------------------------------------------------------------
+//===== TEMPLATED =====
+${INCLUDES}
+//-----------------------------------------------------------------------------
+// clang-format on
+
+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,
+
+ // clang-format off
+//-----------------------------------------------------------------------------
+//===== TEMPLATED =====
+${ENUM_VALUES}
+
+ LastTest = ${LAST_ENUM}
+//-----------------------------------------------------------------------------
+//clang-format on
+};
+
+IPDLUnitTestType IPDLUnitTestFromString(const char* const aString) {
+ if (!aString) return static_cast<IPDLUnitTestType>(0);
+// clang-format off
+//-----------------------------------------------------------------------------
+//===== TEMPLATED =====
+${STRING_TO_ENUMS}
+//-----------------------------------------------------------------------------
+ // clang-format on
+ else return static_cast<IPDLUnitTestType>(0);
+}
+
+IPDLUnitTestType IPDLUnitTest() {
+ return IPDLUnitTestFromString(::mozilla::_ipdltest::IPDLUnitTestName());
+}
+
+} // namespace
+
+//-----------------------------------------------------------------------------
+// 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);
+ MOZ_CRASH("can't continue");
+ }
+ gIPDLUnitTestName = testString;
+
+ // Check whether this test is enabled for processes:
+ switch (test) {
+ // clang-format off
+//-----------------------------------------------------------------------------
+//===== TEMPLATED =====
+${PARENT_ENABLED_CASES_PROC}
+//-----------------------------------------------------------------------------
+ // clang-format on
+
+ 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) {
+ // clang-format off
+//-----------------------------------------------------------------------------
+//===== TEMPLATED =====
+${PARENT_MAIN_CASES_PROC}
+//-----------------------------------------------------------------------------
+ // clang-format on
+
+ 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);
+ MOZ_CRASH("can't continue");
+ }
+ gIPDLUnitTestName = testString;
+
+ // Check whether this test is enabled for threads:
+ switch (test) {
+ // clang-format off
+//-----------------------------------------------------------------------------
+//===== TEMPLATED =====
+${PARENT_ENABLED_CASES_THREAD}
+//-----------------------------------------------------------------------------
+ // clang-format on
+
+ 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) {
+ // clang-format off
+//-----------------------------------------------------------------------------
+//===== TEMPLATED =====
+${PARENT_MAIN_CASES_THREAD}
+//-----------------------------------------------------------------------------
+ // clang-format on
+
+ default:
+ fail("not reached");
+ return; // unreached
+ }
+}
+
+void DeleteParentActor() {
+ if (!gParentActor) return;
+
+ switch (IPDLUnitTest()) {
+ // clang-format off
+//-----------------------------------------------------------------------------
+//===== TEMPLATED =====
+${PARENT_DELETE_CASES}
+//-----------------------------------------------------------------------------
+ // clang-format on
+ 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
+ gSubprocess->Destroy();
+ gSubprocess = nullptr;
+ uiLoop->PostTask(NewRunnableFunction("QuitXPCOM", QuitXPCOM));
+}
+
+void DeferredParentShutdown() {
+ // ping to DeleteSubprocess
+ XRE_GetIOMessageLoop()->PostTask(NewRunnableFunction(
+ "DeleteSubprocess", 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", TryThreadedShutdown));
+ } else {
+ // defer "real" shutdown to avoid *Channel::Close() racing with the
+ // deletion of the subprocess
+ MessageLoop::current()->PostTask(
+ NewRunnableFunction("DeferredParentShutdown", DeferredParentShutdown));
+ }
+}
+
+static void ChildDie() {
+ DeleteChildActor();
+ XRE_ShutdownChildProcess();
+}
+
+void QuitChild() {
+ if (gChildThread) { // Threaded-mode test
+ gParentMessageLoop->PostTask(
+ NewRunnableFunction("ChildCompleted", ChildCompleted));
+ } else { // Process-mode test
+ MessageLoop::current()->PostTask(NewRunnableFunction("ChildDie", ChildDie));
+ }
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+//-----------------------------------------------------------------------------
+// child process only
+
+namespace mozilla {
+namespace _ipdltest {
+
+void DeleteChildActor() {
+ if (!gChildActor) return;
+
+ switch (IPDLUnitTest()) {
+ // clang-format off
+//-----------------------------------------------------------------------------
+//===== TEMPLATED =====
+${CHILD_DELETE_CASES}
+//-----------------------------------------------------------------------------
+ // clang-format on
+ default:
+ ::mozilla::_ipdltest::fail("???");
+ }
+}
+
+void IPDLUnitTestChildInit(IPC::Channel* transport, base::ProcessId parentPid,
+ MessageLoop* worker) {
+ switch (IPDLUnitTest()) {
+ // clang-format off
+//-----------------------------------------------------------------------------
+//===== TEMPLATED =====
+${CHILD_INIT_CASES}
+//-----------------------------------------------------------------------------
+ // clang-format on
+
+ 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 0000000000..b32533a648
--- /dev/null
+++ b/ipc/ipdl/test/cxx/Makefile.in
@@ -0,0 +1,45 @@
+# 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 = \
+ 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)
+ $(PYTHON3) $(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 0000000000..5868f379e4
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestActorPunning.ipdl
@@ -0,0 +1,26 @@
+
+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__();
+};
+
+} // 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 0000000000..a6b875920e
--- /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 0000000000..1441219c38
--- /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/PTestAsyncReturns.ipdl b/ipc/ipdl/test/cxx/PTestAsyncReturns.ipdl
new file mode 100644
index 0000000000..723e973b32
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestAsyncReturns.ipdl
@@ -0,0 +1,17 @@
+namespace mozilla {
+namespace _ipdltest {
+
+
+protocol PTestAsyncReturns {
+
+child:
+ async Ping() returns (bool one);
+ async NoReturn() returns (bool unused);
+
+parent:
+ async Pong() returns (uint32_t param1, uint32_t param2);
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestBadActor.ipdl b/ipc/ipdl/test/cxx/PTestBadActor.ipdl
new file mode 100644
index 0000000000..841d89ff63
--- /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 0000000000..99c78f4ac9
--- /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/PTestCancel.ipdl b/ipc/ipdl/test/cxx/PTestCancel.ipdl
new file mode 100644
index 0000000000..3a6b46b437
--- /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 0000000000..7d8f8f6614
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestCrashCleanup.ipdl
@@ -0,0 +1,17 @@
+// 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__();
+};
+
+}
+}
diff --git a/ipc/ipdl/test/cxx/PTestDataStructures.ipdl b/ipc/ipdl/test/cxx/PTestDataStructures.ipdl
new file mode 100644
index 0000000000..301cf251f7
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestDataStructures.ipdl
@@ -0,0 +1,107 @@
+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);
+};
+
+} // 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 0000000000..39d7f482b3
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestDataStructuresCommon.ipdlh
@@ -0,0 +1,107 @@
+include protocol PTestDataStructuresSub;
+
+using struct mozilla::null_t from "mozilla/ipc/IPCCore.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 0000000000..7a4e87d83a
--- /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 0000000000..25fa5d0f92
--- /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 0000000000..9091237e68
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestDesc.ipdl
@@ -0,0 +1,21 @@
+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);
+};
+
+}
+}
diff --git a/ipc/ipdl/test/cxx/PTestDescSub.ipdl b/ipc/ipdl/test/cxx/PTestDescSub.ipdl
new file mode 100644
index 0000000000..67d4fe1660
--- /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 0000000000..c449f27441
--- /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 0000000000..51ac5f615c
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestEndpointBridgeMain.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;
+include protocol PTestEndpointBridgeSub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+protocol PTestEndpointBridgeMain {
+
+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 0000000000..7364057acc
--- /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 0000000000..0bc09b70e1
--- /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 0000000000..7be99ddd2b
--- /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 0000000000..d0da59d0ce
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestEndpointOpensOpened.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: */
+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__();
+};
+
+
+} // 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 0000000000..1f27d7ce66
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestFailedCtor.ipdl
@@ -0,0 +1,14 @@
+include protocol PTestFailedCtorSub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+intr protocol PTestFailedCtor {
+ manages PTestFailedCtorSub;
+child:
+ intr PTestFailedCtorSub();
+ async __delete__();
+};
+
+}
+}
diff --git a/ipc/ipdl/test/cxx/PTestFailedCtorSub.ipdl b/ipc/ipdl/test/cxx/PTestFailedCtorSub.ipdl
new file mode 100644
index 0000000000..b1d18a05fd
--- /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 0000000000..654170d976
--- /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 0000000000..aad92bae18
--- /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 0000000000..e2c34c2a72
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestHangs.ipdl
@@ -0,0 +1,19 @@
+
+namespace mozilla {
+namespace _ipdltest {
+
+intr protocol PTestHangs {
+both:
+ intr StackFrame();
+
+parent:
+ async Nonce();
+
+child:
+ async Start();
+ intr Hang();
+ async __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 0000000000..0192f59b21
--- /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 0000000000..a81fcdee46
--- /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 0000000000..228ec04e88
--- /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 0000000000..db7c828a22
--- /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 0000000000..ed21f58f88
--- /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 0000000000..95f933bba7
--- /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 0000000000..b37faa13aa
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestInterruptRaces.ipdl
@@ -0,0 +1,24 @@
+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__();
+};
+
+} // 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 0000000000..19c06353b3
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestInterruptShutdownRace.ipdl
@@ -0,0 +1,16 @@
+namespace mozilla {
+namespace _ipdltest {
+
+intr protocol PTestInterruptShutdownRace {
+parent:
+ sync StartDeath();
+ async Orphan();
+
+child:
+ async Start();
+ intr Exit();
+ async __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 0000000000..558512eb13
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestJSON.ipdl
@@ -0,0 +1,46 @@
+include protocol PTestHandle;
+
+using struct mozilla::void_t from "mozilla/ipc/IPCCore.h";
+using struct mozilla::null_t from "mozilla/ipc/IPCCore.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__();
+};
+
+} // 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 0000000000..45429c3314
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestLatency.ipdl
@@ -0,0 +1,26 @@
+
+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();
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestLayoutThread.ipdl b/ipc/ipdl/test/cxx/PTestLayoutThread.ipdl
new file mode 100644
index 0000000000..a2153ee462
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestLayoutThread.ipdl
@@ -0,0 +1,19 @@
+include protocol PTestPaintThread;
+
+namespace mozilla {
+namespace _ipdltest {
+
+// This is supposed to be analagous to PLayerTransaction.
+sync protocol PTestLayoutThread
+{
+parent:
+ async FinishedLayout(uint64_t aTxnId);
+ async AsyncMessage(uint64_t aTxnId);
+ sync SyncMessage(uint64_t aTxnId);
+ async EndTest();
+child:
+ async StartTest(Endpoint<PTestPaintThreadChild> endpoint);
+};
+
+} // 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 0000000000..767af85a20
--- /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 0000000000..e3d9c98df9
--- /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 0000000000..5bc9abf986
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestMultiMgrs.ipdl
@@ -0,0 +1,22 @@
+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__();
+};
+
+} // 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 0000000000..0c51d68414
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestMultiMgrsBottom.ipdl
@@ -0,0 +1,15 @@
+include protocol PTestMultiMgrsLeft;
+include protocol PTestMultiMgrsRight;
+
+namespace mozilla {
+namespace _ipdltest {
+
+protocol PTestMultiMgrsBottom {
+ manager PTestMultiMgrsLeft or PTestMultiMgrsRight;
+
+child:
+ async __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 0000000000..d1bab6262a
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestMultiMgrsLeft.ipdl
@@ -0,0 +1,18 @@
+include protocol PTestMultiMgrs;
+include protocol PTestMultiMgrsBottom;
+
+namespace mozilla {
+namespace _ipdltest {
+
+protocol PTestMultiMgrsLeft {
+ manager PTestMultiMgrs;
+
+ manages PTestMultiMgrsBottom;
+
+child:
+ async PTestMultiMgrsBottom();
+ async __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 0000000000..eef6a2af75
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestMultiMgrsRight.ipdl
@@ -0,0 +1,18 @@
+include protocol PTestMultiMgrs;
+include protocol PTestMultiMgrsBottom;
+
+namespace mozilla {
+namespace _ipdltest {
+
+protocol PTestMultiMgrsRight {
+ manager PTestMultiMgrs;
+
+ manages PTestMultiMgrsBottom;
+
+child:
+ async PTestMultiMgrsBottom();
+ async __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 0000000000..0123a40691
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestNestedLoops.ipdl
@@ -0,0 +1,19 @@
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+intr protocol PTestNestedLoops {
+
+child:
+ async Start();
+ intr R();
+ async __delete__();
+
+parent:
+ async Nonce();
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestPaintThread.ipdl b/ipc/ipdl/test/cxx/PTestPaintThread.ipdl
new file mode 100644
index 0000000000..0a37fda1be
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestPaintThread.ipdl
@@ -0,0 +1,13 @@
+
+namespace mozilla {
+namespace _ipdltest {
+
+// This is supposed to be analagous to PPaintingBridge.
+sync protocol PTestPaintThread
+{
+parent:
+ sync FinishedPaint(uint64_t aTxnId);
+};
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestPriority.ipdl b/ipc/ipdl/test/cxx/PTestPriority.ipdl
new file mode 100644
index 0000000000..c4a2c93c39
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestPriority.ipdl
@@ -0,0 +1,20 @@
+namespace mozilla {
+namespace _ipdltest {
+
+sync protocol PTestPriority {
+parent:
+ prio(input) async PMsg1();
+ prio(input) sync PMsg2();
+ prio(high) async PMsg3();
+ prio(high) sync PMsg4();
+ prio(mediumhigh) async PMsg5();
+ prio(mediumhigh) sync PMsg6();
+
+child:
+ prio(input) async CMsg1();
+ prio(high) async CMsg2();
+ prio(mediumhigh) async CMsg3();
+};
+
+} // 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 0000000000..f51ee2735d
--- /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 0000000000..1e4f574509
--- /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 0000000000..6a5a84c559
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestRaceDeferral.ipdl
@@ -0,0 +1,19 @@
+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
+};
+
+} // 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 0000000000..48921ed697
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestRacyInterruptReplies.ipdl
@@ -0,0 +1,17 @@
+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_();
+};
+
+} // 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 0000000000..4dd5fe54fb
--- /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 0000000000..8863d61795
--- /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 0000000000..c4dbec4076
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestSanity.ipdl
@@ -0,0 +1,18 @@
+
+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);
+};
+
+
+} // 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 0000000000..2cd4375abd
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestSelfManage.ipdl
@@ -0,0 +1,18 @@
+include protocol PTestSelfManageRoot;
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+protocol PTestSelfManage {
+ manager PTestSelfManageRoot or PTestSelfManage;
+ manages PTestSelfManage;
+
+child:
+ async PTestSelfManage();
+ async __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 0000000000..ec86783ea7
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestSelfManageRoot.ipdl
@@ -0,0 +1,17 @@
+include protocol PTestSelfManage;
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+protocol PTestSelfManageRoot {
+ manages PTestSelfManage;
+
+child:
+ async PTestSelfManage();
+ async __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 0000000000..262e34fabe
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestShmem.ipdl
@@ -0,0 +1,14 @@
+namespace mozilla {
+namespace _ipdltest {
+
+protocol PTestShmem {
+child:
+ async Give(Shmem mem, Shmem unsafe, uint32_t expectedSize);
+
+parent:
+ async Take(Shmem mem, Shmem unsafe, uint32_t expectedSize);
+ async __delete__();
+};
+
+}
+}
diff --git a/ipc/ipdl/test/cxx/PTestShutdown.ipdl b/ipc/ipdl/test/cxx/PTestShutdown.ipdl
new file mode 100644
index 0000000000..2cfa489f8d
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestShutdown.ipdl
@@ -0,0 +1,26 @@
+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__();
+};
+
+} // 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 0000000000..c58b30e159
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestShutdownSub.ipdl
@@ -0,0 +1,20 @@
+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__();
+};
+
+} // 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 0000000000..8300792bb4
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestShutdownSubsub.ipdl
@@ -0,0 +1,14 @@
+include protocol PTestShutdownSub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+sync protocol PTestShutdownSubsub {
+ manager PTestShutdownSub;
+
+parent:
+ sync __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 0000000000..e576f8634f
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestStackHooks.ipdl
@@ -0,0 +1,26 @@
+
+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__();
+};
+
+
+} // 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 0000000000..9a9e0db748
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestSyncError.ipdl
@@ -0,0 +1,18 @@
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+sync protocol PTestSyncError {
+
+child:
+ async Start();
+
+parent:
+ sync Error();
+ async __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 0000000000..49489aaa9c
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestSyncHang.ipdl
@@ -0,0 +1,14 @@
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+protocol PTestSyncHang {
+
+child:
+ async UnusedMessage();
+};
+
+
+} // 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 0000000000..4f23153340
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestSyncWakeup.ipdl
@@ -0,0 +1,20 @@
+namespace mozilla {
+namespace _ipdltest {
+
+intr protocol PTestSyncWakeup {
+both:
+ intr StackFrame();
+
+child:
+ async Start();
+ async Note1();
+ async Note2();
+
+parent:
+ sync Sync1();
+ sync Sync2();
+ async __delete__();
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestUniquePtrIPC.ipdl b/ipc/ipdl/test/cxx/PTestUniquePtrIPC.ipdl
new file mode 100644
index 0000000000..6c09c7174c
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestUniquePtrIPC.ipdl
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+namespace mozilla {
+namespace _ipdltest {
+
+struct DummyStruct {
+ int x;
+};
+
+protocol PTestUniquePtrIPC
+{
+child:
+ async TestMessage(UniquePtr<int> a1, UniquePtr<DummyStruct> a2,
+ DummyStruct a3, UniquePtr<int> a4);
+ async TestSendReference(UniquePtr<DummyStruct> a);
+};
+
+}
+}
diff --git a/ipc/ipdl/test/cxx/PTestUrgency.ipdl b/ipc/ipdl/test/cxx/PTestUrgency.ipdl
new file mode 100644
index 0000000000..79b479ca2e
--- /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 0000000000..8ce8d17473
--- /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 0000000000..0fe6c07320
--- /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 0000000000..c969ef60eb
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestActorPunning.cpp
@@ -0,0 +1,126 @@
+#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");
+}
+
+mozilla::ipc::IPCResult TestActorPunningParent::RecvPun(
+ PTestActorPunningSubParent* a, const Bad& bad) {
+ if (a->SendBad()) fail("bad!");
+ fail("shouldn't have received this message in the first place");
+ return IPC_OK();
+}
+
+// By default, fatal errors kill the parent process, but this makes it
+// hard to test, so instead we use the previous behavior and kill the
+// child process.
+void TestActorPunningParent::HandleFatalError(const char* aErrorMsg) const {
+ if (!!strcmp(aErrorMsg, "Error deserializing 'PTestActorPunningSubParent'")) {
+ fail("wrong fatal error");
+ }
+
+ ipc::ScopedProcessHandle otherProcessHandle;
+ if (!base::OpenProcessHandle(OtherPid(), &otherProcessHandle.rwget())) {
+ fail("couldn't open child process");
+ } else {
+ if (!base::KillProcess(otherProcessHandle, 0, false)) {
+ fail("terminating child process");
+ }
+ }
+}
+
+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;
+}
+
+mozilla::ipc::IPCResult TestActorPunningChild::RecvStart() {
+ SendPTestActorPunningSubConstructor();
+ SendPTestActorPunningPunnedConstructor();
+ PTestActorPunningSubChild* a = SendPTestActorPunningSubConstructor();
+ // We can't assert whether this succeeds or fails, due to race
+ // conditions.
+ SendPun(a, Bad());
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestActorPunningSubChild::RecvBad() {
+ fail("things are going really badly right now");
+ return IPC_OK();
+}
+
+} // 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 0000000000..9c19a0e0dd
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestActorPunning.h
@@ -0,0 +1,100 @@
+#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 {
+ friend class PTestActorPunningParent;
+
+ public:
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+
+ protected:
+ PTestActorPunningPunnedParent* AllocPTestActorPunningPunnedParent();
+ bool DeallocPTestActorPunningPunnedParent(PTestActorPunningPunnedParent* a);
+
+ PTestActorPunningSubParent* AllocPTestActorPunningSubParent();
+ bool DeallocPTestActorPunningSubParent(PTestActorPunningSubParent* a);
+
+ mozilla::ipc::IPCResult RecvPun(PTestActorPunningSubParent* a,
+ const Bad& bad);
+
+ virtual void ActorDestroy(ActorDestroyReason why) override {
+ if (NormalShutdown == why) fail("should have died from error!");
+ passed("ok");
+ QuitParent();
+ }
+
+ virtual void HandleFatalError(const char* aErrorMsg) const override;
+};
+
+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 {
+ friend class PTestActorPunningChild;
+
+ public:
+ TestActorPunningChild() {}
+ virtual ~TestActorPunningChild() {}
+
+ protected:
+ PTestActorPunningPunnedChild* AllocPTestActorPunningPunnedChild();
+ bool DeallocPTestActorPunningPunnedChild(PTestActorPunningPunnedChild* a);
+
+ PTestActorPunningSubChild* AllocPTestActorPunningSubChild();
+ bool DeallocPTestActorPunningSubChild(PTestActorPunningSubChild* a);
+
+ mozilla::ipc::IPCResult RecvStart();
+
+ 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() {}
+
+ mozilla::ipc::IPCResult RecvBad();
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+#endif // ifndef mozilla__ipdltest_TestActorPunning_h
diff --git a/ipc/ipdl/test/cxx/TestAsyncReturns.cpp b/ipc/ipdl/test/cxx/TestAsyncReturns.cpp
new file mode 100644
index 0000000000..081222395d
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestAsyncReturns.cpp
@@ -0,0 +1,101 @@
+#include "TestAsyncReturns.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+#include "mozilla/AbstractThread.h"
+#include "mozilla/Unused.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+static uint32_t sMagic1 = 0x105b59fb;
+static uint32_t sMagic2 = 0x09b6f5e3;
+
+//-----------------------------------------------------------------------------
+// parent
+
+TestAsyncReturnsParent::TestAsyncReturnsParent() {
+ MOZ_COUNT_CTOR(TestAsyncReturnsParent);
+}
+
+TestAsyncReturnsParent::~TestAsyncReturnsParent() {
+ MOZ_COUNT_DTOR(TestAsyncReturnsParent);
+}
+
+void TestAsyncReturnsParent::Main() {
+ SendNoReturn()->Then(
+ MessageLoop::current()->SerialEventTarget(), __func__,
+ [](bool unused) { fail("resolve handler should not be called"); },
+ [](ResponseRejectReason&& aReason) {
+ // MozPromise asserts in debug build if the
+ // handler is not called
+ if (aReason != ResponseRejectReason::ChannelClosed) {
+ fail("reject with wrong reason");
+ }
+ passed("reject handler called on channel close");
+ });
+ SendPing()->Then(
+ MessageLoop::current()->SerialEventTarget(), __func__,
+ [this](bool one) {
+ if (one) {
+ passed("take one argument");
+ } else {
+ fail("get one argument but has wrong value");
+ }
+
+ // Also try with the callback-based API.
+ SendPing(
+ [this](bool one) {
+ if (one) {
+ passed("take one argument");
+ } else {
+ fail("get one argument but has wrong value");
+ }
+ Close();
+ },
+ [](ResponseRejectReason&& aReason) { fail("sending Ping"); });
+ },
+ [](ResponseRejectReason&& aReason) { fail("sending Ping"); });
+}
+
+mozilla::ipc::IPCResult TestAsyncReturnsParent::RecvPong(
+ PongResolver&& aResolve) {
+ aResolve(Tuple<const uint32_t&, const uint32_t&>(sMagic1, sMagic2));
+ return IPC_OK();
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+TestAsyncReturnsChild::TestAsyncReturnsChild() {
+ MOZ_COUNT_CTOR(TestAsyncReturnsChild);
+}
+
+TestAsyncReturnsChild::~TestAsyncReturnsChild() {
+ MOZ_COUNT_DTOR(TestAsyncReturnsChild);
+}
+
+mozilla::ipc::IPCResult TestAsyncReturnsChild::RecvNoReturn(
+ NoReturnResolver&& aResolve) {
+ // Not resolving the promise intentionally
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestAsyncReturnsChild::RecvPing(
+ PingResolver&& aResolve) {
+ SendPong()->Then(
+ MessageLoop::current()->SerialEventTarget(), __func__,
+ [aResolve](const Tuple<uint32_t, uint32_t>& aParam) {
+ if (Get<0>(aParam) == sMagic1 && Get<1>(aParam) == sMagic2) {
+ passed("take two arguments");
+ } else {
+ fail("get two argument but has wrong value");
+ }
+ aResolve(true);
+ },
+ [](ResponseRejectReason&& aReason) { fail("sending Pong"); });
+ return IPC_OK();
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestAsyncReturns.h b/ipc/ipdl/test/cxx/TestAsyncReturns.h
new file mode 100644
index 0000000000..5dad3da0ab
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestAsyncReturns.h
@@ -0,0 +1,54 @@
+#ifndef mozilla__ipdltest_TestAsyncReturns_h
+#define mozilla__ipdltest_TestAsyncReturns_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestAsyncReturnsParent.h"
+#include "mozilla/_ipdltest/PTestAsyncReturnsChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+class TestAsyncReturnsParent : public PTestAsyncReturnsParent {
+ friend class PTestAsyncReturnsParent;
+
+ public:
+ TestAsyncReturnsParent();
+ virtual ~TestAsyncReturnsParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+ protected:
+ mozilla::ipc::IPCResult RecvPong(PongResolver&& aResolve);
+
+ virtual void ActorDestroy(ActorDestroyReason why) override {
+ if (NormalShutdown != why) fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+};
+
+class TestAsyncReturnsChild : public PTestAsyncReturnsChild {
+ friend class PTestAsyncReturnsChild;
+
+ public:
+ TestAsyncReturnsChild();
+ virtual ~TestAsyncReturnsChild();
+
+ protected:
+ mozilla::ipc::IPCResult RecvPing(PingResolver&& aResolve);
+ mozilla::ipc::IPCResult RecvNoReturn(NoReturnResolver&& aResolve);
+
+ virtual void ActorDestroy(ActorDestroyReason why) override {
+ if (NormalShutdown != why) fail("unexpected destruction!");
+ QuitChild();
+ }
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+#endif // ifndef mozilla__ipdltest_TestAsyncReturns_h
diff --git a/ipc/ipdl/test/cxx/TestBadActor.cpp b/ipc/ipdl/test/cxx/TestBadActor.cpp
new file mode 100644
index 0000000000..42a0faf19d
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestBadActor.cpp
@@ -0,0 +1,59 @@
+#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);
+}
+
+// By default, fatal errors kill the parent process, but this makes it
+// hard to test, so instead we use the previous behavior and kill the
+// child process.
+void TestBadActorParent::HandleFatalError(const char* aErrorMsg) const {
+ if (!!strcmp(aErrorMsg, "incoming message racing with actor deletion")) {
+ fail("wrong fatal error");
+ }
+
+ ipc::ScopedProcessHandle otherProcessHandle;
+ if (!base::OpenProcessHandle(OtherPid(), &otherProcessHandle.rwget())) {
+ fail("couldn't open child process");
+ } else {
+ if (!base::KillProcess(otherProcessHandle, 0, false)) {
+ fail("terminating child process");
+ }
+ }
+}
+
+PTestBadActorSubParent* TestBadActorParent::AllocPTestBadActorSubParent() {
+ return new TestBadActorSubParent();
+}
+
+mozilla::ipc::IPCResult TestBadActorSubParent::RecvPing() {
+ fail("Shouldn't have received ping.");
+ return IPC_FAIL_NO_REASON(this);
+}
+
+PTestBadActorSubChild* TestBadActorChild::AllocPTestBadActorSubChild() {
+ return new TestBadActorSubChild();
+}
+
+mozilla::ipc::IPCResult TestBadActorChild::RecvPTestBadActorSubConstructor(
+ PTestBadActorSubChild* actor) {
+ if (!actor->SendPing()) {
+ fail("Couldn't send ping to an actor which supposedly isn't dead yet.");
+ }
+ return IPC_OK();
+}
+
+} // 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 0000000000..0157b898f4
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestBadActor.h
@@ -0,0 +1,84 @@
+#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 {
+ friend class 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 void HandleFatalError(const char* aErrorMsg) const override;
+
+ PTestBadActorSubParent* AllocPTestBadActorSubParent();
+
+ bool DeallocPTestBadActorSubParent(PTestBadActorSubParent* actor) {
+ delete actor;
+ return true;
+ }
+};
+
+class TestBadActorSubParent : public PTestBadActorSubParent {
+ friend class PTestBadActorSubParent;
+
+ public:
+ TestBadActorSubParent() {}
+ virtual ~TestBadActorSubParent() {}
+
+ protected:
+ virtual void ActorDestroy(ActorDestroyReason why) override {}
+ mozilla::ipc::IPCResult RecvPing();
+};
+
+class TestBadActorChild : public PTestBadActorChild {
+ friend class PTestBadActorChild;
+
+ public:
+ TestBadActorChild() {}
+ virtual ~TestBadActorChild() {}
+
+ protected:
+ virtual PTestBadActorSubChild* AllocPTestBadActorSubChild();
+
+ virtual bool DeallocPTestBadActorSubChild(PTestBadActorSubChild* actor) {
+ delete actor;
+ return true;
+ }
+
+ virtual mozilla::ipc::IPCResult 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/TestCancel.cpp b/ipc/ipdl/test/cxx/TestCancel.cpp
new file mode 100644
index 0000000000..2f97aa2e93
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestCancel.cpp
@@ -0,0 +1,115 @@
+#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");
+}
+
+mozilla::ipc::IPCResult TestCancelParent::RecvDone1() {
+ if (!SendStart2()) fail("sending Start2");
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestCancelParent::RecvTest2_1() {
+ if (SendTest2_2()) fail("sending Test2_2");
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestCancelParent::RecvTest3_2() {
+ GetIPCChannel()->CancelCurrentTransaction();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestCancelParent::RecvDone() {
+ MessageLoop::current()->PostTask(NewNonOwningRunnableMethod(
+ "ipc::IToplevelProtocol::Close", this, &TestCancelParent::Close));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestCancelParent::RecvCheckParent(uint32_t* reply) {
+ *reply = 12;
+ return IPC_OK();
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestCancelChild::RecvStart2() {
+ if (!SendTest2_1()) fail("sending Test2_1");
+
+ if (!SendStart3()) fail("sending Start3");
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestCancelChild::RecvTest2_2() {
+ GetIPCChannel()->CancelCurrentTransaction();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestCancelChild::RecvCheckChild(uint32_t* reply) {
+ *reply = 12;
+ return IPC_OK();
+}
+
+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 0000000000..7d944d6a69
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestCancel.h
@@ -0,0 +1,54 @@
+#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();
+
+ mozilla::ipc::IPCResult RecvDone1();
+ mozilla::ipc::IPCResult RecvTest2_1();
+ mozilla::ipc::IPCResult RecvStart3();
+ mozilla::ipc::IPCResult RecvTest3_2();
+ mozilla::ipc::IPCResult RecvDone();
+
+ mozilla::ipc::IPCResult RecvCheckParent(uint32_t* reply);
+
+ virtual void ActorDestroy(ActorDestroyReason why) override {
+ passed("ok");
+ QuitParent();
+ }
+};
+
+class TestCancelChild : public PTestCancelChild {
+ public:
+ TestCancelChild();
+ virtual ~TestCancelChild();
+
+ mozilla::ipc::IPCResult RecvTest1_1();
+ mozilla::ipc::IPCResult RecvStart2();
+ mozilla::ipc::IPCResult RecvTest2_2();
+ mozilla::ipc::IPCResult RecvTest3_1();
+
+ mozilla::ipc::IPCResult RecvCheckChild(uint32_t* reply);
+
+ 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 0000000000..6807bd296d
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestCrashCleanup.cpp
@@ -0,0 +1,100 @@
+#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);
+
+ gSubprocess->Destroy();
+ 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", 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
+
+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", DeleteTheWorld));
+
+ if (CallDIEDIEDIE()) fail("expected an error!");
+
+ Close();
+
+ MessageLoop::current()->PostTask(NewRunnableFunction("Done", Done));
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+TestCrashCleanupChild::TestCrashCleanupChild() {
+ MOZ_COUNT_CTOR(TestCrashCleanupChild);
+}
+
+TestCrashCleanupChild::~TestCrashCleanupChild() {
+ MOZ_COUNT_DTOR(TestCrashCleanupChild);
+}
+
+mozilla::ipc::IPCResult TestCrashCleanupChild::AnswerDIEDIEDIE() {
+ _exit(0);
+ MOZ_CRASH("unreached");
+}
+
+} // 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 0000000000..30371264e6
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestCrashCleanup.h
@@ -0,0 +1,49 @@
+#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 {
+ friend class PTestCrashCleanupChild;
+
+ public:
+ TestCrashCleanupChild();
+ virtual ~TestCrashCleanupChild();
+
+ protected:
+ mozilla::ipc::IPCResult AnswerDIEDIEDIE();
+
+ 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 0000000000..5b1cabfe7d
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestDataStructures.cpp
@@ -0,0 +1,888 @@
+#include "TestDataStructures.h"
+
+#include "mozilla/Unused.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+typedef nsTArray<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 nsTArray<T>& a, const nsTArray<T>& b) {
+ test_assert(a == b, "arrays equal");
+}
+
+inline static TestDataStructuresSub& Cast(PTestDataStructuresSubParent* 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;
+}
+
+mozilla::ipc::IPCResult TestDataStructuresParent::RecvTest1(nsTArray<int>&& ia,
+ nsTArray<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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestDataStructuresParent::RecvTest2(
+ nsTArray<PTestDataStructuresSubParent*>&& i1,
+ nsTArray<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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestDataStructuresParent::RecvTest4(
+ nsTArray<IntDouble>&& i1, nsTArray<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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 nsTArray<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 nsTArray<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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestDataStructuresParent::RecvTest6(
+ nsTArray<IntDoubleArrays>&& i1, nsTArray<IntDoubleArrays>* o1) {
+ test_assert(3 == i1.Length(), "wrong length");
+
+ IntDoubleArrays id1(i1[0]);
+ test_assert(42 == id1.get_int(), "wrong value");
+
+ nsTArray<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");
+
+ nsTArray<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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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");
+
+ nsTArray<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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestDataStructuresParent::RecvTest8(
+ nsTArray<Actors>&& i1, nsTArray<Actors>* o1) {
+ test_assert(3 == i1.Length(), "wrong length");
+ test_assert(42 == i1[0].get_int(), "wrong value");
+
+ const nsTArray<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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 nsTArray<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 nsTArray<PTestDataStructuresSubParent*>& i4a =
+ i4.get_ArrayOfActors()[0].get_ArrayOfPTestDataStructuresSubParent();
+ assert_arrays_equal(mKids, i4a);
+
+ *o1 = i1;
+ *o2 = i2;
+ *o3 = i3;
+ *o4 = i4;
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestDataStructuresParent::RecvTest10(
+ nsTArray<Unions>&& i1, nsTArray<Unions>* o1) {
+ test_assert(42 == i1[0].get_int(), "wrong value");
+
+ const nsTArray<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 nsTArray<PTestDataStructuresSubParent*>& i4a =
+ i1[3].get_ArrayOfActors()[0].get_ArrayOfPTestDataStructuresSubParent();
+ assert_arrays_equal(mKids, i4a);
+
+ *o1 = i1;
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestDataStructuresParent::RecvTest12(
+ const SIntDoubleArrays& i, SIntDoubleArrays* o) {
+ nsTArray<int> ai;
+ ai.AppendElement(1);
+ ai.AppendElement(2);
+ ai.AppendElement(3);
+
+ nsTArray<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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestDataStructuresParent::RecvTest13(const SActors& i,
+ SActors* o) {
+ nsTArray<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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestDataStructuresParent::RecvTest14(const Structs& i,
+ Structs* o) {
+ nsTArray<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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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) {
+ nsTArray<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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestDataStructuresParent::RecvTest16(
+ const WithUnions& i, WithUnions* o) {
+ test_assert(i.i() == 42, "wrong value");
+
+ nsTArray<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 nsTArray<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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestDataStructuresParent::RecvTest17(
+ nsTArray<Op>&& sa) {
+ test_assert(sa.Length() == 1 && Op::TSetAttrs == sa[0].type(), "wrong value");
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+TestDataStructuresChild::TestDataStructuresChild() {
+ MOZ_COUNT_CTOR(TestDataStructuresChild);
+}
+
+TestDataStructuresChild::~TestDataStructuresChild() {
+ MOZ_COUNT_DTOR(TestDataStructuresChild);
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+void TestDataStructuresChild::Test1() {
+ nsTArray<int> ia;
+
+ for (int i = 0; i < 5; ++i) ia.AppendElement(i);
+
+ nsTArray<int> oa;
+ if (!SendTest1(ia, &oa)) fail("can't send Test1");
+
+ assert_arrays_equal(ia, oa);
+
+ printf(" passed %s\n", __FUNCTION__);
+}
+
+void TestDataStructuresChild::Test2() {
+ nsTArray<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() {
+ nsTArray<IntDouble> i1;
+ i1.AppendElement(IntDouble(int(1)));
+ i1.AppendElement(IntDouble(2.0));
+ i1.AppendElement(IntDouble(int(3)));
+ i1.AppendElement(IntDouble(4.0));
+
+ nsTArray<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));
+ nsTArray<int> i2;
+ i2.AppendElement(1);
+ i2.AppendElement(2);
+ i2.AppendElement(3);
+ nsTArray<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));
+ nsTArray<int> id2;
+ id2.AppendElement(1);
+ id2.AppendElement(2);
+ id2.AppendElement(3);
+ nsTArray<double> id3;
+ id3.AppendElement(1.0);
+ id3.AppendElement(2.0);
+ id3.AppendElement(3.0);
+
+ nsTArray<IntDoubleArrays> i1;
+ i1.AppendElement(id1);
+ i1.AppendElement(IntDoubleArrays(id2));
+ i1.AppendElement(IntDoubleArrays(id3));
+
+ nsTArray<IntDoubleArrays> o1;
+ if (!SendTest6(i1, &o1)) fail("can't send Test6");
+
+ test_assert(3 == o1.Length(), "wrong length");
+ IntDoubleArrays od1(o1[0]);
+ nsTArray<int> od2(o1[1].get_ArrayOfint());
+ nsTArray<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);
+ nsTArray<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);
+ nsTArray<int> i2a;
+ i2a.AppendElement(1);
+ i2a.AppendElement(2);
+ i2a.AppendElement(3);
+
+ nsTArray<Actors> i1;
+ i1.AppendElement(i1e);
+ i1.AppendElement(i2a);
+ i1.AppendElement(mKids);
+
+ nsTArray<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));
+
+ nsTArray<int> i2a;
+ i2a.AppendElement(1);
+ i2a.AppendElement(2);
+ i2a.AppendElement(3);
+
+ nsTArray<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));
+
+ nsTArray<int> i2a;
+ i2a.AppendElement(1);
+ i2a.AppendElement(2);
+ i2a.AppendElement(3);
+
+ nsTArray<Actors> i4a;
+ i4a.AppendElement(mKids);
+
+ nsTArray<Unions> i1;
+ i1.AppendElement(i1a);
+ i1.AppendElement(Unions(i2a));
+ i1.AppendElement(Unions(mKids));
+ i1.AppendElement(Unions(i4a));
+
+ nsTArray<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() {
+ nsTArray<int> ai;
+ ai.AppendElement(1);
+ ai.AppendElement(2);
+ ai.AppendElement(3);
+
+ nsTArray<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() {
+ nsTArray<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() {
+ nsTArray<int> ai;
+ ai.AppendElement(1);
+ ai.AppendElement(2);
+ ai.AppendElement(3);
+
+ SActors ia;
+ ia.i() = 42;
+ ia.ai() = ai;
+ ia.apChild() = mKids;
+ nsTArray<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() {
+ nsTArray<int> ai;
+ ai.AppendElement(1);
+ ai.AppendElement(2);
+ ai.AppendElement(3);
+
+ SActors ia;
+ ia.i() = 42;
+ ia.ai() = ai;
+ ia.apChild() = mKids;
+ nsTArray<SActors> iaa;
+ iaa.AppendElement(ia);
+
+ Structs is;
+ is.i() = 42;
+ is.ai() = ai;
+ is.apChild() = mKids;
+ is.aa() = iaa;
+ nsTArray<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;
+
+ nsTArray<int> ai;
+ ai.AppendElement(1);
+ ai.AppendElement(2);
+ ai.AppendElement(3);
+ i.ai() = ai;
+
+ i.apChild() = mKids;
+
+ nsTArray<Actors> iaa;
+ iaa.AppendElement(mKids);
+ i.aa() = iaa;
+
+ nsTArray<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 nsTArray<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);
+
+ nsTArray<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 0000000000..dc25b5282c
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestDataStructures.h
@@ -0,0 +1,180 @@
+#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 {
+ friend class PTestDataStructuresParent;
+
+ public:
+ TestDataStructuresParent();
+ virtual ~TestDataStructuresParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+ protected:
+ PTestDataStructuresSubParent* AllocPTestDataStructuresSubParent(
+ const int& i) {
+ PTestDataStructuresSubParent* actor = new TestDataStructuresSub(i);
+ mKids.AppendElement(actor);
+ return actor;
+ }
+
+ bool DeallocPTestDataStructuresSubParent(PTestDataStructuresSubParent* actor);
+
+ mozilla::ipc::IPCResult RecvTest1(nsTArray<int>&& i1, nsTArray<int>* o1);
+
+ mozilla::ipc::IPCResult RecvTest2(
+ nsTArray<PTestDataStructuresSubParent*>&& i1,
+ nsTArray<PTestDataStructuresSubParent*>* o1);
+
+ mozilla::ipc::IPCResult RecvTest3(const IntDouble& i1, const IntDouble& i2,
+ IntDouble* o1, IntDouble* o2);
+
+ mozilla::ipc::IPCResult RecvTest4(nsTArray<IntDouble>&& i1,
+ nsTArray<IntDouble>* o1);
+
+ mozilla::ipc::IPCResult RecvTest5(const IntDoubleArrays& i1,
+ const IntDoubleArrays& i2,
+ const IntDoubleArrays& i3,
+ IntDoubleArrays* o1, IntDoubleArrays* o2,
+ IntDoubleArrays* o3);
+
+ mozilla::ipc::IPCResult RecvTest6(nsTArray<IntDoubleArrays>&& i1,
+ nsTArray<IntDoubleArrays>* o1);
+
+ mozilla::ipc::IPCResult RecvTest7_0(const ActorWrapper& i1, ActorWrapper* o1);
+
+ mozilla::ipc::IPCResult RecvTest7(const Actors& i1, const Actors& i2,
+ const Actors& i3, Actors* o1, Actors* o2,
+ Actors* o3);
+
+ mozilla::ipc::IPCResult RecvTest8(nsTArray<Actors>&& i1,
+ nsTArray<Actors>* o1);
+
+ mozilla::ipc::IPCResult RecvTest9(const Unions& i1, const Unions& i2,
+ const Unions& i3, const Unions& i4,
+ Unions* o1, Unions* o2, Unions* o3,
+ Unions* o4);
+
+ mozilla::ipc::IPCResult RecvTest10(nsTArray<Unions>&& i1,
+ nsTArray<Unions>* o1);
+
+ mozilla::ipc::IPCResult RecvTest11(const SIntDouble& i, SIntDouble* o);
+
+ mozilla::ipc::IPCResult RecvTest12(const SIntDoubleArrays& i,
+ SIntDoubleArrays* o);
+
+ mozilla::ipc::IPCResult RecvTest13(const SActors& i, SActors* o);
+
+ mozilla::ipc::IPCResult RecvTest14(const Structs& i, Structs* o);
+
+ mozilla::ipc::IPCResult 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);
+
+ mozilla::ipc::IPCResult RecvTest16(const WithUnions& i, WithUnions* o);
+
+ mozilla::ipc::IPCResult RecvTest17(nsTArray<Op>&& sa);
+
+ mozilla::ipc::IPCResult RecvTest18(nsTArray<nsIntRegion>&& ra);
+
+ mozilla::ipc::IPCResult RecvDummy(const ShmemUnion& su, ShmemUnion* rsu) {
+ *rsu = su;
+ return IPC_OK();
+ }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override {
+ if (NormalShutdown != why) fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+
+ private:
+ nsTArray<PTestDataStructuresSubParent*> mKids;
+};
+
+class TestDataStructuresChild : public PTestDataStructuresChild {
+ friend class PTestDataStructuresChild;
+
+ public:
+ TestDataStructuresChild();
+ virtual ~TestDataStructuresChild();
+
+ protected:
+ PTestDataStructuresSubChild* AllocPTestDataStructuresSubChild(const int& i) {
+ PTestDataStructuresSubChild* actor = new TestDataStructuresSub(i);
+ mKids.AppendElement(actor);
+ return actor;
+ }
+
+ bool DeallocPTestDataStructuresSubChild(PTestDataStructuresSubChild* actor) {
+ delete actor;
+ return true;
+ }
+
+ mozilla::ipc::IPCResult RecvStart();
+
+ 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();
+
+ nsTArray<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 0000000000..811f6c3314
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestDemon.cpp
@@ -0,0 +1,362 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 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
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestDemonParent::RecvHiPrioSyncMessage() {
+ DEMON_LOG("Start RecvHiPrioSyncMessage");
+ RunLimitedSequence();
+ DEMON_LOG("End RecvHiPrioSyncMessage");
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+void TestDemonParent::RunUnlimitedSequence() {
+ if (mDone) {
+ return;
+ }
+
+ gFlushStack = false;
+ DoAction();
+
+ MessageLoop::current()->PostTask(NewNonOwningRunnableMethod(
+ "_ipdltest::TestDemonParent::RunUnlimitedSequence", 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); }
+
+mozilla::ipc::IPCResult TestDemonChild::RecvStart() {
+#ifdef OS_POSIX
+ srandom(time(nullptr));
+#else
+ srand(time(nullptr));
+#endif
+
+ DEMON_LOG("RecvStart");
+
+ RunUnlimitedSequence();
+ return IPC_OK();
+}
+
+#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
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestDemonChild::RecvHiPrioSyncMessage() {
+ DEMON_LOG("Start RecvHiPrioSyncMessage");
+ RunLimitedSequence();
+ DEMON_LOG("End RecvHiPrioSyncMessage");
+ return IPC_OK();
+}
+
+void TestDemonChild::RunUnlimitedSequence() {
+ gFlushStack = false;
+ DoAction();
+
+ MessageLoop::current()->PostTask(NewNonOwningRunnableMethod(
+ "_ipdltest::TestDemonChild::RunUnlimitedSequence", 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 0000000000..ba5b003436
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestDemon.h
@@ -0,0 +1,91 @@
+#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
+
+ mozilla::ipc::IPCResult RecvAsyncMessage(const int& n);
+ mozilla::ipc::IPCResult RecvHiPrioSyncMessage();
+
+ mozilla::ipc::IPCResult RecvSyncMessage(const int& n);
+ mozilla::ipc::IPCResult RecvUrgentAsyncMessage(const int& n);
+ mozilla::ipc::IPCResult RecvUrgentSyncMessage(const int& n);
+
+ 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();
+
+ mozilla::ipc::IPCResult RecvStart();
+
+#ifdef DEBUG
+ bool NeedArtificialSleep() override { return true; }
+ void ArtificialSleep() override;
+#endif
+
+ mozilla::ipc::IPCResult RecvAsyncMessage(const int& n);
+ mozilla::ipc::IPCResult RecvHiPrioSyncMessage();
+
+ 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 0000000000..2ae199457d
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestDesc.cpp
@@ -0,0 +1,78 @@
+#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");
+}
+
+mozilla::ipc::IPCResult TestDescParent::RecvOk(PTestDescSubsubParent* a) {
+ if (!a) fail("didn't receive Subsub");
+
+ if (!PTestDescSubsubParent::Call__delete__(a)) fail("deleting Subsub");
+
+ Close();
+
+ return IPC_OK();
+}
+
+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
+
+mozilla::ipc::IPCResult TestDescChild::RecvTest(PTestDescSubsubChild* a) {
+ if (!a) fail("didn't receive Subsub");
+ if (!SendOk(a)) fail("couldn't send Ok()");
+ return IPC_OK();
+}
+
+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 0000000000..c9fa04f89c
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestDesc.h
@@ -0,0 +1,115 @@
+#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 {
+ friend class PTestDescParent;
+
+ public:
+ TestDescParent() {}
+ virtual ~TestDescParent() {}
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+ mozilla::ipc::IPCResult RecvOk(PTestDescSubsubParent* a);
+
+ protected:
+ PTestDescSubParent* AllocPTestDescSubParent(PTestDescSubsubParent*);
+ bool DeallocPTestDescSubParent(PTestDescSubParent* actor);
+
+ virtual void ActorDestroy(ActorDestroyReason why) override {
+ if (NormalShutdown != why) fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+};
+
+class TestDescChild : public PTestDescChild {
+ friend class PTestDescChild;
+
+ public:
+ TestDescChild() {}
+ virtual ~TestDescChild() {}
+
+ protected:
+ PTestDescSubChild* AllocPTestDescSubChild(PTestDescSubsubChild*);
+
+ bool DeallocPTestDescSubChild(PTestDescSubChild* actor);
+
+ mozilla::ipc::IPCResult RecvTest(PTestDescSubsubChild* a);
+
+ virtual void ActorDestroy(ActorDestroyReason why) override {
+ if (NormalShutdown != why) fail("unexpected destruction!");
+ QuitChild();
+ }
+};
+
+//-----------------------------------------------------------------------------
+// First descendent
+//
+class TestDescSubParent : public PTestDescSubParent {
+ friend class PTestDescSubParent;
+
+ public:
+ TestDescSubParent() {}
+ virtual ~TestDescSubParent() {}
+
+ protected:
+ virtual void ActorDestroy(ActorDestroyReason why) override {}
+ PTestDescSubsubParent* AllocPTestDescSubsubParent();
+ bool DeallocPTestDescSubsubParent(PTestDescSubsubParent* actor);
+};
+
+class TestDescSubChild : public PTestDescSubChild {
+ friend class PTestDescSubChild;
+
+ public:
+ TestDescSubChild() {}
+ virtual ~TestDescSubChild() {}
+
+ protected:
+ PTestDescSubsubChild* AllocPTestDescSubsubChild();
+ bool DeallocPTestDescSubsubChild(PTestDescSubsubChild* actor);
+};
+
+//-----------------------------------------------------------------------------
+// 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() {}
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+#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 0000000000..4a4366f0ba
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestEndpointBridgeMain.cpp
@@ -0,0 +1,226 @@
+/* -*- 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");
+ }
+}
+
+mozilla::ipc::IPCResult TestEndpointBridgeMainParent::RecvBridged(
+ Endpoint<PTestEndpointBridgeMainSubParent>&& endpoint) {
+ TestEndpointBridgeMainSubParent* a = new TestEndpointBridgeMainSubParent();
+ if (!endpoint.Bind(a)) {
+ fail("Bind failed");
+ }
+ return IPC_OK();
+}
+
+void TestEndpointBridgeMainParent::ActorDestroy(ActorDestroyReason why) {
+ if (NormalShutdown != why) {
+ fail("unexpected destruction!");
+ }
+ passed("ok");
+ QuitParent();
+}
+
+mozilla::ipc::IPCResult TestEndpointBridgeMainSubParent::RecvHello() {
+ if (!SendHi()) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestEndpointBridgeMainSubParent::RecvHelloSync() {
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestEndpointBridgeMainSubParent::AnswerHelloRpc() {
+ if (!CallHiRpc()) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ return IPC_OK();
+}
+
+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;
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+void TestEndpointBridgeMainChild::ActorDestroy(ActorDestroyReason why) {
+ if (NormalShutdown != why) {
+ fail("unexpected destruction!");
+ }
+ // NB: this is kosher because QuitChild() joins with the IO thread
+ mSubprocess->Destroy();
+ mSubprocess = nullptr;
+ QuitChild();
+}
+
+void TestEndpointBridgeSubParent::Main() {
+ if (!SendPing()) {
+ fail("sending Ping");
+ }
+}
+
+mozilla::ipc::IPCResult 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(std::move(parent))) {
+ fail("SendBridge failed for parent");
+ }
+ if (!SendBridged(std::move(child))) {
+ fail("SendBridge failed for child");
+ }
+
+ return IPC_OK();
+}
+
+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;
+}
+
+mozilla::ipc::IPCResult TestEndpointBridgeSubChild::RecvPing() {
+ if (!SendBridgeEm()) {
+ fail("sending BridgeEm");
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestEndpointBridgeSubChild::RecvBridged(
+ Endpoint<PTestEndpointBridgeMainSubChild>&& endpoint) {
+ TestEndpointBridgeMainSubChild* a = new TestEndpointBridgeMainSubChild();
+
+ if (!endpoint.Bind(a)) {
+ fail("failed to Bind");
+ }
+
+ if (!a->SendHello()) {
+ fail("sending Hello");
+ }
+
+ return IPC_OK();
+}
+
+void TestEndpointBridgeSubChild::ActorDestroy(ActorDestroyReason why) {
+ if (NormalShutdown != why) {
+ fail("unexpected destruction!");
+ }
+ QuitChild();
+}
+
+mozilla::ipc::IPCResult 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("ipc::IToplevelProtocol::Close", this,
+ &TestEndpointBridgeMainSubChild::Close));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestEndpointBridgeMainSubChild::AnswerHiRpc() {
+ mGotHi = true; // d00d
+ return IPC_OK();
+}
+
+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 _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestEndpointBridgeMain.h b/ipc/ipdl/test/cxx/TestEndpointBridgeMain.h
new file mode 100644
index 0000000000..88ac12d71b
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestEndpointBridgeMain.h
@@ -0,0 +1,132 @@
+/* -*- 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 {
+ friend class PTestEndpointBridgeMainParent;
+
+ public:
+ TestEndpointBridgeMainParent() {}
+ virtual ~TestEndpointBridgeMainParent() {}
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+
+ protected:
+ mozilla::ipc::IPCResult RecvBridged(
+ mozilla::ipc::Endpoint<PTestEndpointBridgeMainSubParent>&& endpoint);
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+};
+
+class TestEndpointBridgeMainSubParent
+ : public PTestEndpointBridgeMainSubParent {
+ friend class PTestEndpointBridgeMainSubParent;
+
+ public:
+ explicit TestEndpointBridgeMainSubParent() {}
+ virtual ~TestEndpointBridgeMainSubParent() {}
+
+ protected:
+ mozilla::ipc::IPCResult RecvHello();
+ mozilla::ipc::IPCResult RecvHelloSync();
+ mozilla::ipc::IPCResult AnswerHelloRpc();
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+};
+
+//-----------------------------------------------------------------------------
+// "Sub" process --- child of "main"
+//
+class TestEndpointBridgeSubParent;
+
+class TestEndpointBridgeMainChild : public PTestEndpointBridgeMainChild {
+ friend class PTestEndpointBridgeMainChild;
+
+ public:
+ TestEndpointBridgeMainChild();
+ virtual ~TestEndpointBridgeMainChild() {}
+
+ protected:
+ mozilla::ipc::IPCResult RecvStart();
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ IPDLUnitTestSubprocess* mSubprocess;
+};
+
+class TestEndpointBridgeSubParent : public PTestEndpointBridgeSubParent {
+ friend class PTestEndpointBridgeSubParent;
+
+ public:
+ TestEndpointBridgeSubParent() {}
+ virtual ~TestEndpointBridgeSubParent() {}
+
+ void Main();
+
+ protected:
+ mozilla::ipc::IPCResult RecvBridgeEm();
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+};
+
+//-----------------------------------------------------------------------------
+// "Subsub" process --- child of "sub"
+//
+class TestEndpointBridgeSubChild : public PTestEndpointBridgeSubChild {
+ friend class PTestEndpointBridgeSubChild;
+
+ public:
+ TestEndpointBridgeSubChild();
+ virtual ~TestEndpointBridgeSubChild() {}
+
+ protected:
+ mozilla::ipc::IPCResult RecvPing();
+
+ mozilla::ipc::IPCResult RecvBridged(
+ Endpoint<PTestEndpointBridgeMainSubChild>&& endpoint);
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+};
+
+class TestEndpointBridgeMainSubChild : public PTestEndpointBridgeMainSubChild {
+ friend class PTestEndpointBridgeMainSubChild;
+
+ public:
+ explicit TestEndpointBridgeMainSubChild() : mGotHi(false) {}
+ virtual ~TestEndpointBridgeMainSubChild() {}
+
+ protected:
+ mozilla::ipc::IPCResult RecvHi();
+ mozilla::ipc::IPCResult AnswerHiRpc();
+
+ 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 0000000000..ca141f7b58
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestEndpointOpens.cpp
@@ -0,0 +1,242 @@
+/* -*- 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");
+ }
+}
+
+mozilla::ipc::IPCResult 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", OpenParent, a, std::move(endpoint)));
+
+ return IPC_OK();
+}
+
+void TestEndpointOpensParent::ActorDestroy(ActorDestroyReason why) {
+ // Stops the thread and joins it
+ delete gParentThread;
+
+ if (NormalShutdown != why) {
+ fail("unexpected destruction A!");
+ }
+ passed("ok");
+ QuitParent();
+}
+
+mozilla::ipc::IPCResult TestEndpointOpensOpenedParent::RecvHello() {
+ AssertNotMainThread();
+ if (!SendHi()) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestEndpointOpensOpenedParent::RecvHelloSync() {
+ AssertNotMainThread();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestEndpointOpensOpenedParent::AnswerHelloRpc() {
+ AssertNotMainThread();
+ if (!CallHiRpc()) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ return IPC_OK();
+}
+
+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",
+ 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");
+ }
+}
+
+mozilla::ipc::IPCResult 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", OpenChild, a, std::move(child)));
+
+ if (!SendStartSubprotocol(std::move(parent))) {
+ fail("send StartSubprotocol");
+ }
+
+ return IPC_OK();
+}
+
+void TestEndpointOpensChild::ActorDestroy(ActorDestroyReason why) {
+ // Stops the thread and joins it
+ delete gChildThread;
+
+ if (NormalShutdown != why) {
+ fail("unexpected destruction C!");
+ }
+ QuitChild();
+}
+
+mozilla::ipc::IPCResult 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("ipc::IToplevelProtocol::Close", this,
+ &TestEndpointOpensOpenedChild::Close));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestEndpointOpensOpenedChild::AnswerHiRpc() {
+ AssertNotMainThread();
+
+ mGotHi = true; // d00d
+ return IPC_OK();
+}
+
+static void ShutdownTestEndpointOpensOpenedChild(
+ TestEndpointOpensOpenedChild* child, Transport* transport) {
+ delete child;
+
+ // Kick off main-thread shutdown.
+ gMainThread->PostTask(
+ NewNonOwningRunnableMethod("ipc::IToplevelProtocol::Close", 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",
+ 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 0000000000..6721274937
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestEndpointOpens.h
@@ -0,0 +1,102 @@
+/* -*- 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 {
+ friend class PTestEndpointOpensParent;
+
+ public:
+ TestEndpointOpensParent() {}
+ virtual ~TestEndpointOpensParent() {}
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+
+ protected:
+ mozilla::ipc::IPCResult RecvStartSubprotocol(
+ mozilla::ipc::Endpoint<PTestEndpointOpensOpenedParent>&& endpoint);
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+};
+
+} // namespace _ipdltest
+
+namespace _ipdltest2 {
+
+class TestEndpointOpensOpenedParent : public PTestEndpointOpensOpenedParent {
+ friend class PTestEndpointOpensOpenedParent;
+
+ public:
+ explicit TestEndpointOpensOpenedParent() {}
+ virtual ~TestEndpointOpensOpenedParent() {}
+
+ protected:
+ mozilla::ipc::IPCResult RecvHello();
+ mozilla::ipc::IPCResult RecvHelloSync();
+ mozilla::ipc::IPCResult AnswerHelloRpc();
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+};
+
+} // namespace _ipdltest2
+
+// child process
+
+namespace _ipdltest {
+
+class TestEndpointOpensChild : public PTestEndpointOpensChild {
+ friend class PTestEndpointOpensChild;
+
+ public:
+ TestEndpointOpensChild();
+ virtual ~TestEndpointOpensChild() {}
+
+ protected:
+ mozilla::ipc::IPCResult RecvStart();
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+};
+
+} // namespace _ipdltest
+
+namespace _ipdltest2 {
+
+class TestEndpointOpensOpenedChild : public PTestEndpointOpensOpenedChild {
+ friend class PTestEndpointOpensOpenedChild;
+
+ public:
+ explicit TestEndpointOpensOpenedChild() : mGotHi(false) {}
+ virtual ~TestEndpointOpensOpenedChild() {}
+
+ protected:
+ mozilla::ipc::IPCResult RecvHi();
+ mozilla::ipc::IPCResult AnswerHiRpc();
+
+ 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 0000000000..2f08d2eeb0
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestFailedCtor.cpp
@@ -0,0 +1,112 @@
+#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();
+}
+
+mozilla::ipc::IPCResult
+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 IPC_FAIL_NO_REASON(this);
+}
+
+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 0000000000..6fac4eb278
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestFailedCtor.h
@@ -0,0 +1,127 @@
+#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 {
+ friend class 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:
+ PTestFailedCtorSubParent* AllocPTestFailedCtorSubParent();
+ bool DeallocPTestFailedCtorSubParent(PTestFailedCtorSubParent* actor);
+
+ virtual void ActorDestroy(ActorDestroyReason why) override {
+ if (AbnormalShutdown != why) fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+};
+
+class TestFailedCtorChild : public PTestFailedCtorChild {
+ friend class PTestFailedCtorChild;
+
+ public:
+ TestFailedCtorChild() {}
+ virtual ~TestFailedCtorChild() {}
+
+ protected:
+ PTestFailedCtorSubChild* AllocPTestFailedCtorSubChild();
+
+ mozilla::ipc::IPCResult AnswerPTestFailedCtorSubConstructor(
+ PTestFailedCtorSubChild* actor) override;
+
+ bool DeallocPTestFailedCtorSubChild(PTestFailedCtorSubChild* actor);
+
+ 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 {
+ friend class PTestFailedCtorSubParent;
+
+ public:
+ TestFailedCtorSubParent() : mOne(nullptr), mTwo(nullptr), mThree(nullptr) {}
+ virtual ~TestFailedCtorSubParent();
+
+ protected:
+ PTestFailedCtorSubsubParent* AllocPTestFailedCtorSubsubParent();
+
+ bool DeallocPTestFailedCtorSubsubParent(PTestFailedCtorSubsubParent* actor);
+ mozilla::ipc::IPCResult RecvSync() { return IPC_OK(); }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ TestFailedCtorSubsub* mOne;
+ TestFailedCtorSubsub* mTwo;
+ TestFailedCtorSubsub* mThree;
+};
+
+class TestFailedCtorSubChild : public PTestFailedCtorSubChild {
+ friend class PTestFailedCtorSubChild;
+
+ public:
+ TestFailedCtorSubChild() {}
+ virtual ~TestFailedCtorSubChild() {}
+
+ protected:
+ PTestFailedCtorSubsubChild* AllocPTestFailedCtorSubsubChild();
+ bool DeallocPTestFailedCtorSubsubChild(PTestFailedCtorSubsubChild* actor);
+
+ 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;
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+#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 0000000000..2694cbfdab
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestHangs.cpp
@@ -0,0 +1,127 @@
+#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), mNumAnswerStackFrame(0) {
+ 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(
+ "_ipdltest::TestHangsParent::CleanUp", this, &TestHangsParent::CleanUp));
+
+ GetIPCChannel()->CloseWithTimeout();
+
+ return false;
+}
+
+mozilla::ipc::IPCResult TestHangsParent::AnswerStackFrame() {
+ ++mNumAnswerStackFrame;
+
+ if (mNumAnswerStackFrame == 1) {
+ if (CallStackFrame()) {
+ fail("should have timed out!");
+ }
+ } else if (mNumAnswerStackFrame == 2) {
+ // 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!");
+ } else {
+ fail("unexpected state");
+ }
+
+ return IPC_OK();
+}
+
+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); }
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+} // 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 0000000000..a3dfb991f5
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestHangs.h
@@ -0,0 +1,75 @@
+#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 {
+ friend class 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;
+
+ mozilla::ipc::IPCResult RecvNonce() { return IPC_OK(); }
+
+ mozilla::ipc::IPCResult AnswerStackFrame();
+
+ virtual void ActorDestroy(ActorDestroyReason why) override {
+ if (AbnormalShutdown != why) fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+
+ void CleanUp();
+
+ bool mDetectedHang;
+ int32_t mNumAnswerStackFrame;
+};
+
+class TestHangsChild : public PTestHangsChild {
+ friend class PTestHangsChild;
+
+ public:
+ TestHangsChild();
+ virtual ~TestHangsChild();
+
+ protected:
+ mozilla::ipc::IPCResult RecvStart() {
+ if (!SendNonce()) fail("sending Nonce");
+ return IPC_OK();
+ }
+
+ mozilla::ipc::IPCResult AnswerStackFrame() {
+ if (CallStackFrame()) fail("should have failed");
+ return IPC_OK();
+ }
+
+ mozilla::ipc::IPCResult AnswerHang();
+
+ 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 0000000000..e6715fba8e
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestHighestPrio.cpp
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: sw=2 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");
+}
+
+mozilla::ipc::IPCResult TestHighestPrioParent::RecvMsg1() {
+ MOZ_ASSERT(msg_num_ == 0);
+ msg_num_ = 1;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestHighestPrioParent::RecvMsg2() {
+ MOZ_ASSERT(msg_num_ == 1);
+ msg_num_ = 2;
+
+ if (!SendStartInner()) fail("sending StartInner");
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestHighestPrioParent::RecvMsg3() {
+ MOZ_ASSERT(msg_num_ == 2);
+ msg_num_ = 3;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestHighestPrioParent::RecvMsg4() {
+ MOZ_ASSERT(msg_num_ == 3);
+ msg_num_ = 4;
+ return IPC_OK();
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+TestHighestPrioChild::TestHighestPrioChild() {
+ MOZ_COUNT_CTOR(TestHighestPrioChild);
+}
+
+TestHighestPrioChild::~TestHighestPrioChild() {
+ MOZ_COUNT_DTOR(TestHighestPrioChild);
+}
+
+mozilla::ipc::IPCResult TestHighestPrioChild::RecvStart() {
+ if (!SendMsg1()) fail("sending Msg1");
+
+ if (!SendMsg2()) fail("sending Msg2");
+
+ Close();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestHighestPrioChild::RecvStartInner() {
+ if (!SendMsg3()) fail("sending Msg3");
+
+ if (!SendMsg4()) fail("sending Msg4");
+
+ return IPC_OK();
+}
+
+} // 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 0000000000..34563d7708
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestHighestPrio.h
@@ -0,0 +1,55 @@
+#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();
+
+ mozilla::ipc::IPCResult RecvMsg1();
+ mozilla::ipc::IPCResult RecvMsg2();
+ mozilla::ipc::IPCResult RecvMsg3();
+ mozilla::ipc::IPCResult RecvMsg4();
+
+ 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();
+
+ mozilla::ipc::IPCResult RecvStart();
+ mozilla::ipc::IPCResult RecvStartInner();
+
+ 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 0000000000..30a90e8928
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestInterruptErrorCleanup.cpp
@@ -0,0 +1,138 @@
+#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);
+
+ gSubprocess->Destroy();
+ 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", 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
+
+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", 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", 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);
+}
+
+mozilla::ipc::IPCResult TestInterruptErrorCleanupChild::AnswerError() {
+ _exit(0);
+ MOZ_CRASH("unreached");
+}
+
+} // 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 0000000000..9ae0493cf0
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestInterruptErrorCleanup.h
@@ -0,0 +1,52 @@
+#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 {
+ friend class PTestInterruptErrorCleanupChild;
+
+ public:
+ TestInterruptErrorCleanupChild();
+ virtual ~TestInterruptErrorCleanupChild();
+
+ protected:
+ mozilla::ipc::IPCResult AnswerError();
+
+ 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 0000000000..02b42cfbc3
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestInterruptRaces.cpp
@@ -0,0 +1,169 @@
+#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()");
+}
+
+mozilla::ipc::IPCResult TestInterruptRacesParent::RecvStartRace() {
+ MessageLoop::current()->PostTask(NewNonOwningRunnableMethod(
+ "_ipdltest::TestInterruptRacesParent::OnRaceTime", this,
+ &TestInterruptRacesParent::OnRaceTime));
+ return IPC_OK();
+}
+
+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("_ipdltest::TestInterruptRacesParent::Test2",
+ this, &TestInterruptRacesParent::Test2));
+}
+
+mozilla::ipc::IPCResult TestInterruptRacesParent::AnswerRace(bool* hasReply) {
+ if (mHasReply) fail("apparently the parent won the Interrupt race!");
+ *hasReply = hasReply;
+ return IPC_OK();
+}
+
+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("_ipdltest::TestInterruptRacesParent::Test3",
+ this, &TestInterruptRacesParent::Test3));
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+void TestInterruptRacesParent::Test3() {
+ puts("Test 3");
+
+ if (!CallStackFrame3()) fail("can't set up a stack frame");
+
+ puts(" passed");
+
+ Close();
+}
+
+mozilla::ipc::IPCResult TestInterruptRacesParent::AnswerStackFrame3() {
+ if (!SendWakeup3()) fail("can't wake up the child");
+
+ if (!CallChild()) fail("can't set up race condition");
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestInterruptRacesParent::AnswerParent() {
+ mAnsweredParent = true;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestInterruptRacesParent::RecvGetAnsweredParent(
+ bool* answeredParent) {
+ *answeredParent = mAnsweredParent;
+ return IPC_OK();
+}
+
+//-----------------------------------------------------------------------------
+// child
+mozilla::ipc::IPCResult TestInterruptRacesChild::RecvStart() {
+ puts("Test 1");
+
+ if (!SendStartRace()) fail("problem sending StartRace()");
+
+ bool dontcare;
+ if (!CallRace(&dontcare)) fail("problem calling Race()");
+
+ mHasReply = true;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestInterruptRacesChild::AnswerRace(bool* hasReply) {
+ if (!mHasReply) fail("apparently the child lost the Interrupt race!");
+
+ *hasReply = mHasReply;
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestInterruptRacesChild::RecvWakeup() {
+ bool dontcare;
+ if (!CallRace(&dontcare)) fail("can't set up race condition");
+
+ mHasReply = true;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestInterruptRacesChild::AnswerStackFrame3() {
+ if (!CallStackFrame3()) fail("can't set up stack frame");
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestInterruptRacesChild::RecvWakeup3() {
+ if (!CallParent()) fail("can't set up race condition");
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+} // 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 0000000000..6fa184da1a
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestInterruptRaces.h
@@ -0,0 +1,104 @@
+#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 {
+ friend class PTestInterruptRacesParent;
+
+ public:
+ TestInterruptRacesParent()
+ : mHasReply(false), mChildHasReply(false), mAnsweredParent(false) {}
+ virtual ~TestInterruptRacesParent() {}
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+ protected:
+ mozilla::ipc::IPCResult RecvStartRace();
+
+ mozilla::ipc::IPCResult AnswerRace(bool* hasRace);
+
+ mozilla::ipc::IPCResult AnswerStackFrame();
+
+ mozilla::ipc::IPCResult AnswerStackFrame3();
+
+ mozilla::ipc::IPCResult AnswerParent();
+
+ mozilla::ipc::IPCResult RecvGetAnsweredParent(bool* answeredParent);
+
+ 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 {
+ friend class PTestInterruptRacesChild;
+
+ public:
+ TestInterruptRacesChild() : mHasReply(false) {}
+ virtual ~TestInterruptRacesChild() {}
+
+ protected:
+ mozilla::ipc::IPCResult RecvStart();
+
+ mozilla::ipc::IPCResult AnswerRace(bool* hasRace);
+
+ mozilla::ipc::IPCResult AnswerStackFrame();
+
+ mozilla::ipc::IPCResult AnswerStackFrame3();
+
+ mozilla::ipc::IPCResult RecvWakeup();
+
+ mozilla::ipc::IPCResult RecvWakeup3();
+
+ mozilla::ipc::IPCResult AnswerChild();
+
+ 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 0000000000..5482893995
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestInterruptShutdownRace.cpp
@@ -0,0 +1,111 @@
+#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() {
+ gSubprocess->Destroy();
+ gSubprocess = nullptr;
+}
+
+void Done() {
+ passed(__FILE__);
+ QuitParent();
+}
+
+} // namespace
+
+TestInterruptShutdownRaceParent::TestInterruptShutdownRaceParent() {
+ MOZ_COUNT_CTOR(TestInterruptShutdownRaceParent);
+}
+
+TestInterruptShutdownRaceParent::~TestInterruptShutdownRaceParent() {
+ MOZ_COUNT_DTOR(TestInterruptShutdownRaceParent);
+}
+
+void TestInterruptShutdownRaceParent::Main() {
+ if (!SendStart()) fail("sending Start");
+}
+
+mozilla::ipc::IPCResult TestInterruptShutdownRaceParent::RecvStartDeath() {
+ // this will be ordered before the OnMaybeDequeueOne event of
+ // Orphan in the queue
+ MessageLoop::current()->PostTask(NewNonOwningRunnableMethod(
+ "_ipdltest::TestInterruptShutdownRaceParent::StartShuttingDown", this,
+ &TestInterruptShutdownRaceParent::StartShuttingDown));
+ return IPC_OK();
+}
+
+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", DeleteSubprocess));
+
+ // this is ordered after the OnMaybeDequeueOne event in the queue
+ MessageLoop::current()->PostTask(NewRunnableFunction("Done", Done));
+
+ // |this| has been deleted, be mindful
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+TestInterruptShutdownRaceChild::TestInterruptShutdownRaceChild() {
+ MOZ_COUNT_CTOR(TestInterruptShutdownRaceChild);
+}
+
+TestInterruptShutdownRaceChild::~TestInterruptShutdownRaceChild() {
+ MOZ_COUNT_DTOR(TestInterruptShutdownRaceChild);
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestInterruptShutdownRaceChild::AnswerExit() {
+ _exit(0);
+ MOZ_CRASH("unreached");
+}
+
+} // 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 0000000000..17425fc06c
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestInterruptShutdownRace.h
@@ -0,0 +1,56 @@
+#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();
+
+ mozilla::ipc::IPCResult RecvStartDeath();
+
+ mozilla::ipc::IPCResult RecvOrphan();
+
+ protected:
+ void StartShuttingDown();
+
+ virtual void ActorDestroy(ActorDestroyReason why) override {
+ if (AbnormalShutdown != why) fail("unexpected destruction!");
+ }
+};
+
+class TestInterruptShutdownRaceChild : public PTestInterruptShutdownRaceChild {
+ friend class PTestInterruptShutdownRaceChild;
+
+ public:
+ TestInterruptShutdownRaceChild();
+ virtual ~TestInterruptShutdownRaceChild();
+
+ protected:
+ mozilla::ipc::IPCResult RecvStart();
+
+ mozilla::ipc::IPCResult AnswerExit();
+
+ 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 0000000000..42fac919d1
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestJSON.cpp
@@ -0,0 +1,113 @@
+#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(nsTArray<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 ]
+ // }
+ // ]
+ //
+ nsTArray<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);
+
+ nsTArray<JSONVariant> tmp;
+ Array123(tmp);
+ outer.AppendElement(tmp);
+
+ nsTArray<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));
+ nsTArray<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");
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+} // 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 0000000000..eaf7516525
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestJSON.h
@@ -0,0 +1,93 @@
+#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 {
+ friend class PTestJSONParent;
+
+ public:
+ TestJSONParent() {}
+ virtual ~TestJSONParent() {}
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+ protected:
+ mozilla::ipc::IPCResult RecvTest(const JSONVariant& i, JSONVariant* o);
+
+ PTestHandleParent* AllocPTestHandleParent() {
+ return mKid = new TestHandleParent();
+ }
+
+ bool DeallocPTestHandleParent(PTestHandleParent* actor) {
+ 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 {
+ friend class PTestJSONChild;
+
+ public:
+ TestJSONChild() {}
+ virtual ~TestJSONChild() {}
+
+ protected:
+ mozilla::ipc::IPCResult RecvStart();
+
+ PTestHandleChild* AllocPTestHandleChild() {
+ return mKid = new TestHandleChild();
+ }
+
+ bool DeallocPTestHandleChild(PTestHandleChild* actor) {
+ 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 0000000000..f88d8ea644
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestLatency.cpp
@@ -0,0 +1,210 @@
+#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),
+ mWhichPong5(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())
+ MOZ_CRASH(
+ "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()");
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestLatencyParent::RecvPong5() {
+ ++mWhichPong5;
+
+ if (mWhichPong5 < 5) {
+ return IPC_OK();
+ }
+
+ mWhichPong5 = 0;
+
+ 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 IPC_OK();
+}
+
+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), mWhichPing5(0) {
+ MOZ_COUNT_CTOR(TestLatencyChild);
+}
+
+TestLatencyChild::~TestLatencyChild() { MOZ_COUNT_DTOR(TestLatencyChild); }
+
+mozilla::ipc::IPCResult TestLatencyChild::RecvPing() {
+ SendPong();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestLatencyChild::RecvPing5() {
+ ++mWhichPing5;
+
+ if (mWhichPing5 < 5) {
+ return IPC_OK();
+ }
+
+ mWhichPing5 = 0;
+
+ if (!SendPong5() || !SendPong5() || !SendPong5() || !SendPong5() ||
+ !SendPong5())
+ fail("sending Pong5()");
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestLatencyChild::AnswerRpc() { return IPC_OK(); }
+
+mozilla::ipc::IPCResult TestLatencyChild::RecvSpam() {
+ // no-op
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestLatencyChild::AnswerSynchro() { return IPC_OK(); }
+
+mozilla::ipc::IPCResult TestLatencyChild::RecvCompressedSpam(
+ const uint32_t& seqno) {
+ if (seqno <= mLastSeqno)
+ fail("compressed seqnos must monotonically increase");
+
+ mLastSeqno = seqno;
+ ++mNumProcessedCompressedSpams;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestLatencyChild::AnswerSynchro2(
+ uint32_t* lastSeqno, uint32_t* numMessagesDispatched) {
+ *lastSeqno = mLastSeqno;
+ *numMessagesDispatched = mNumProcessedCompressedSpams;
+ return IPC_OK();
+}
+
+} // 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 0000000000..687b107808
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestLatency.h
@@ -0,0 +1,107 @@
+#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 {
+ friend class 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:
+ mozilla::ipc::IPCResult RecvPong();
+ mozilla::ipc::IPCResult RecvPong5();
+
+ 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;
+ uint32_t mWhichPong5;
+};
+
+class TestLatencyChild : public PTestLatencyChild {
+ friend class PTestLatencyChild;
+
+ public:
+ TestLatencyChild();
+ virtual ~TestLatencyChild();
+
+ protected:
+ mozilla::ipc::IPCResult RecvPing();
+ mozilla::ipc::IPCResult RecvPing5();
+ mozilla::ipc::IPCResult AnswerRpc();
+ mozilla::ipc::IPCResult RecvSpam();
+ mozilla::ipc::IPCResult AnswerSynchro();
+ mozilla::ipc::IPCResult RecvCompressedSpam(const uint32_t& seqno);
+ mozilla::ipc::IPCResult AnswerSynchro2(uint32_t* lastSeqno,
+ uint32_t* numMessagesDispatched);
+
+ virtual void ActorDestroy(ActorDestroyReason why) override {
+ if (NormalShutdown != why) fail("unexpected destruction!");
+ QuitChild();
+ }
+
+ uint32_t mLastSeqno;
+ uint32_t mNumProcessedCompressedSpams;
+ uint32_t mWhichPing5;
+};
+
+} // 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 0000000000..77d5786cfa
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestManyChildAllocs.cpp
@@ -0,0 +1,83 @@
+#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()");
+}
+
+mozilla::ipc::IPCResult TestManyChildAllocsParent::RecvDone() {
+ // explicitly *not* cleaning up, so we can sanity-check IPDL's
+ // auto-shutdown/cleanup handling
+ Close();
+
+ return IPC_OK();
+}
+
+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);
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+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 0000000000..3da6461810
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestManyChildAllocs.h
@@ -0,0 +1,82 @@
+#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 {
+ friend class PTestManyChildAllocsParent;
+
+ public:
+ TestManyChildAllocsParent();
+ virtual ~TestManyChildAllocsParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+ protected:
+ mozilla::ipc::IPCResult RecvDone();
+ bool DeallocPTestManyChildAllocsSubParent(PTestManyChildAllocsSubParent* __a);
+ PTestManyChildAllocsSubParent* AllocPTestManyChildAllocsSubParent();
+
+ virtual void ActorDestroy(ActorDestroyReason why) override {
+ if (NormalShutdown != why) fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+};
+
+class TestManyChildAllocsChild : public PTestManyChildAllocsChild {
+ friend class PTestManyChildAllocsChild;
+
+ public:
+ TestManyChildAllocsChild();
+ virtual ~TestManyChildAllocsChild();
+
+ protected:
+ mozilla::ipc::IPCResult RecvGo();
+ bool DeallocPTestManyChildAllocsSubChild(PTestManyChildAllocsSubChild* __a);
+ PTestManyChildAllocsSubChild* AllocPTestManyChildAllocsSubChild();
+
+ virtual void ActorDestroy(ActorDestroyReason why) override {
+ if (NormalShutdown != why) fail("unexpected destruction!");
+ QuitChild();
+ }
+};
+
+// do-nothing sub-protocol actors
+
+class TestManyChildAllocsSubParent : public PTestManyChildAllocsSubParent {
+ friend class PTestManyChildAllocsSubParent;
+
+ public:
+ TestManyChildAllocsSubParent() {}
+ virtual ~TestManyChildAllocsSubParent() {}
+
+ protected:
+ virtual void ActorDestroy(ActorDestroyReason why) override {}
+ mozilla::ipc::IPCResult RecvHello() { return IPC_OK(); }
+};
+
+class TestManyChildAllocsSubChild : public PTestManyChildAllocsSubChild {
+ public:
+ TestManyChildAllocsSubChild() {}
+ virtual ~TestManyChildAllocsSubChild() {}
+};
+
+} // namespace _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 0000000000..f09bb9ca7b
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestMultiMgrs.cpp
@@ -0,0 +1,83 @@
+#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");
+}
+
+mozilla::ipc::IPCResult TestMultiMgrsParent::RecvOK() {
+ Close();
+ return IPC_OK();
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+mozilla::ipc::IPCResult
+TestMultiMgrsLeftChild::RecvPTestMultiMgrsBottomConstructor(
+ PTestMultiMgrsBottomChild* actor) {
+ static_cast<TestMultiMgrsChild*>(Manager())->mBottomL = actor;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+TestMultiMgrsRightChild::RecvPTestMultiMgrsBottomConstructor(
+ PTestMultiMgrsBottomChild* actor) {
+ static_cast<TestMultiMgrsChild*>(Manager())->mBottomR = actor;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+} // 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 0000000000..71826d42a7
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestMultiMgrs.h
@@ -0,0 +1,222 @@
+#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 {
+ friend class PTestMultiMgrsLeftParent;
+
+ public:
+ TestMultiMgrsLeftParent() {}
+ virtual ~TestMultiMgrsLeftParent() {}
+
+ bool HasChild(TestMultiMgrsBottomParent* c) {
+ return ManagedPTestMultiMgrsBottomParent().Contains(c);
+ }
+
+ protected:
+ virtual void ActorDestroy(ActorDestroyReason why) override {}
+
+ PTestMultiMgrsBottomParent* AllocPTestMultiMgrsBottomParent() {
+ return new TestMultiMgrsBottomParent();
+ }
+
+ bool DeallocPTestMultiMgrsBottomParent(PTestMultiMgrsBottomParent* actor) {
+ delete actor;
+ return true;
+ }
+};
+
+class TestMultiMgrsRightParent : public PTestMultiMgrsRightParent {
+ friend class PTestMultiMgrsRightParent;
+
+ public:
+ TestMultiMgrsRightParent() {}
+ virtual ~TestMultiMgrsRightParent() {}
+
+ bool HasChild(TestMultiMgrsBottomParent* c) {
+ return ManagedPTestMultiMgrsBottomParent().Contains(c);
+ }
+
+ protected:
+ virtual void ActorDestroy(ActorDestroyReason why) override {}
+
+ PTestMultiMgrsBottomParent* AllocPTestMultiMgrsBottomParent() {
+ return new TestMultiMgrsBottomParent();
+ }
+
+ bool DeallocPTestMultiMgrsBottomParent(PTestMultiMgrsBottomParent* actor) {
+ delete actor;
+ return true;
+ }
+};
+
+class TestMultiMgrsParent : public PTestMultiMgrsParent {
+ friend class PTestMultiMgrsParent;
+
+ public:
+ TestMultiMgrsParent() {}
+ virtual ~TestMultiMgrsParent() {}
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+ protected:
+ mozilla::ipc::IPCResult RecvOK();
+
+ PTestMultiMgrsLeftParent* AllocPTestMultiMgrsLeftParent() {
+ return new TestMultiMgrsLeftParent();
+ }
+
+ bool DeallocPTestMultiMgrsLeftParent(PTestMultiMgrsLeftParent* actor) {
+ delete actor;
+ return true;
+ }
+
+ PTestMultiMgrsRightParent* AllocPTestMultiMgrsRightParent() {
+ return new TestMultiMgrsRightParent();
+ }
+
+ bool DeallocPTestMultiMgrsRightParent(PTestMultiMgrsRightParent* actor) {
+ 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 {
+ friend class PTestMultiMgrsLeftChild;
+
+ public:
+ TestMultiMgrsLeftChild() {}
+ virtual ~TestMultiMgrsLeftChild() {}
+
+ bool HasChild(PTestMultiMgrsBottomChild* c) {
+ return ManagedPTestMultiMgrsBottomChild().Contains(c);
+ }
+
+ protected:
+ virtual mozilla::ipc::IPCResult RecvPTestMultiMgrsBottomConstructor(
+ PTestMultiMgrsBottomChild* actor) override;
+
+ PTestMultiMgrsBottomChild* AllocPTestMultiMgrsBottomChild() {
+ return new TestMultiMgrsBottomChild();
+ }
+
+ bool DeallocPTestMultiMgrsBottomChild(PTestMultiMgrsBottomChild* actor) {
+ delete actor;
+ return true;
+ }
+};
+
+class TestMultiMgrsRightChild : public PTestMultiMgrsRightChild {
+ friend class PTestMultiMgrsRightChild;
+
+ public:
+ TestMultiMgrsRightChild() {}
+ virtual ~TestMultiMgrsRightChild() {}
+
+ bool HasChild(PTestMultiMgrsBottomChild* c) {
+ return ManagedPTestMultiMgrsBottomChild().Contains(c);
+ }
+
+ protected:
+ virtual mozilla::ipc::IPCResult RecvPTestMultiMgrsBottomConstructor(
+ PTestMultiMgrsBottomChild* actor) override;
+
+ PTestMultiMgrsBottomChild* AllocPTestMultiMgrsBottomChild() {
+ return new TestMultiMgrsBottomChild();
+ }
+
+ bool DeallocPTestMultiMgrsBottomChild(PTestMultiMgrsBottomChild* actor) {
+ delete actor;
+ return true;
+ }
+};
+
+class TestMultiMgrsChild : public PTestMultiMgrsChild {
+ friend class PTestMultiMgrsChild;
+
+ public:
+ TestMultiMgrsChild() {}
+ virtual ~TestMultiMgrsChild() {}
+
+ void Main();
+
+ PTestMultiMgrsBottomChild* mBottomL;
+ PTestMultiMgrsBottomChild* mBottomR;
+
+ protected:
+ mozilla::ipc::IPCResult RecvCheck();
+
+ PTestMultiMgrsLeftChild* AllocPTestMultiMgrsLeftChild() {
+ return new TestMultiMgrsLeftChild();
+ }
+
+ bool DeallocPTestMultiMgrsLeftChild(PTestMultiMgrsLeftChild* actor) {
+ delete actor;
+ return true;
+ }
+
+ PTestMultiMgrsRightChild* AllocPTestMultiMgrsRightChild() {
+ return new TestMultiMgrsRightChild();
+ }
+
+ bool DeallocPTestMultiMgrsRightChild(PTestMultiMgrsRightChild* actor) {
+ 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 0000000000..3658d7dfb6
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestNestedLoops.cpp
@@ -0,0 +1,78 @@
+#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();
+}
+
+mozilla::ipc::IPCResult 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(
+ "_ipdltest::TestNestedLoopsParent::BreakNestedLoop", 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 IPC_OK();
+}
+
+void TestNestedLoopsParent::BreakNestedLoop() { mBreakNestedLoop = true; }
+
+//-----------------------------------------------------------------------------
+// child
+
+TestNestedLoopsChild::TestNestedLoopsChild() {
+ MOZ_COUNT_CTOR(TestNestedLoopsChild);
+}
+
+TestNestedLoopsChild::~TestNestedLoopsChild() {
+ MOZ_COUNT_DTOR(TestNestedLoopsChild);
+}
+
+mozilla::ipc::IPCResult TestNestedLoopsChild::RecvStart() {
+ if (!SendNonce()) fail("sending Nonce");
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestNestedLoopsChild::AnswerR() { return IPC_OK(); }
+
+} // 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 0000000000..001b571494
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestNestedLoops.h
@@ -0,0 +1,59 @@
+#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 {
+ friend class PTestNestedLoopsParent;
+
+ public:
+ TestNestedLoopsParent();
+ virtual ~TestNestedLoopsParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+ protected:
+ mozilla::ipc::IPCResult RecvNonce();
+
+ void BreakNestedLoop();
+
+ virtual void ActorDestroy(ActorDestroyReason why) override {
+ if (NormalShutdown != why) fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+
+ bool mBreakNestedLoop;
+};
+
+class TestNestedLoopsChild : public PTestNestedLoopsChild {
+ friend class PTestNestedLoopsChild;
+
+ public:
+ TestNestedLoopsChild();
+ virtual ~TestNestedLoopsChild();
+
+ protected:
+ mozilla::ipc::IPCResult RecvStart();
+
+ mozilla::ipc::IPCResult AnswerR();
+
+ 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/TestOffMainThreadPainting.cpp b/ipc/ipdl/test/cxx/TestOffMainThreadPainting.cpp
new file mode 100644
index 0000000000..7a1bf2cf87
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestOffMainThreadPainting.cpp
@@ -0,0 +1,237 @@
+#include "TestOffMainThreadPainting.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+#include "mozilla/Unused.h"
+#include <prinrval.h>
+#include <prthread.h>
+
+namespace mozilla {
+namespace _ipdltest {
+
+TestOffMainThreadPaintingParent::TestOffMainThreadPaintingParent()
+ : mAsyncMessages(0), mSyncMessages(0) {}
+
+TestOffMainThreadPaintingParent::~TestOffMainThreadPaintingParent() {}
+
+void TestOffMainThreadPaintingParent::Main() {
+ ipc::Endpoint<PTestPaintThreadParent> parentPipe;
+ ipc::Endpoint<PTestPaintThreadChild> childPipe;
+ nsresult rv = PTestPaintThread::CreateEndpoints(
+ base::GetCurrentProcId(), OtherPid(), &parentPipe, &childPipe);
+ if (NS_FAILED(rv)) {
+ fail("create pipes");
+ }
+
+ mPaintActor = new TestPaintThreadParent(this);
+ if (!mPaintActor->Bind(std::move(parentPipe))) {
+ fail("bind parent pipe");
+ }
+
+ if (!SendStartTest(std::move(childPipe))) {
+ fail("sending Start");
+ }
+}
+
+ipc::IPCResult TestOffMainThreadPaintingParent::RecvFinishedLayout(
+ const uint64_t& aTxnId) {
+ if (!mPaintedTxn || mPaintedTxn.value() != aTxnId) {
+ fail("received transaction before receiving paint");
+ }
+ mPaintedTxn = Nothing();
+ mCompletedTxn = Some(aTxnId);
+ return IPC_OK();
+}
+
+void TestOffMainThreadPaintingParent::NotifyFinishedPaint(
+ const uint64_t& aTxnId) {
+ if (mCompletedTxn && mCompletedTxn.value() >= aTxnId) {
+ fail("received paint after receiving transaction");
+ }
+ if (mPaintedTxn) {
+ fail("painted again before completing previous transaction");
+ }
+ mPaintedTxn = Some(aTxnId);
+}
+
+ipc::IPCResult TestOffMainThreadPaintingParent::RecvAsyncMessage(
+ const uint64_t& aTxnId) {
+ if (!mCompletedTxn || mCompletedTxn.value() != aTxnId) {
+ fail("sync message received out of order");
+ return IPC_FAIL_NO_REASON(this);
+ }
+ mAsyncMessages++;
+ return IPC_OK();
+}
+
+ipc::IPCResult TestOffMainThreadPaintingParent::RecvSyncMessage(
+ const uint64_t& aTxnId) {
+ if (!mCompletedTxn || mCompletedTxn.value() != aTxnId) {
+ fail("sync message received out of order");
+ return IPC_FAIL_NO_REASON(this);
+ }
+ if (mSyncMessages >= mAsyncMessages) {
+ fail("sync message received before async message");
+ return IPC_FAIL_NO_REASON(this);
+ }
+ mSyncMessages++;
+ return IPC_OK();
+}
+
+ipc::IPCResult TestOffMainThreadPaintingParent::RecvEndTest() {
+ if (!mCompletedTxn || mCompletedTxn.value() != 1) {
+ fail("expected to complete a transaction");
+ }
+ if (mAsyncMessages != 1) {
+ fail("expected to get 1 async message");
+ }
+ if (mSyncMessages != 1) {
+ fail("expected to get 1 sync message");
+ }
+
+ passed("ok");
+
+ mPaintActor->Close();
+ Close();
+ return IPC_OK();
+}
+
+void TestOffMainThreadPaintingParent::ActorDestroy(ActorDestroyReason aWhy) {
+ if (aWhy != NormalShutdown) {
+ fail("child process aborted");
+ }
+ QuitParent();
+}
+
+/**************************
+ * PTestLayoutThreadChild *
+ **************************/
+
+TestOffMainThreadPaintingChild::TestOffMainThreadPaintingChild()
+ : mNextTxnId(1) {}
+
+TestOffMainThreadPaintingChild::~TestOffMainThreadPaintingChild() {}
+
+ipc::IPCResult TestOffMainThreadPaintingChild::RecvStartTest(
+ ipc::Endpoint<PTestPaintThreadChild>&& aEndpoint) {
+ mPaintThread = MakeUnique<base::Thread>("PaintThread");
+ if (!mPaintThread->Start()) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ mPaintActor = new TestPaintThreadChild(GetIPCChannel());
+ RefPtr<Runnable> task =
+ NewRunnableMethod<ipc::Endpoint<PTestPaintThreadChild>&&>(
+ "TestPaintthreadChild::Bind", mPaintActor,
+ &TestPaintThreadChild::Bind, std::move(aEndpoint));
+ mPaintThread->message_loop()->PostTask(task.forget());
+
+ IssueTransaction();
+ return IPC_OK();
+}
+
+void TestOffMainThreadPaintingChild::ActorDestroy(ActorDestroyReason aWhy) {
+ RefPtr<Runnable> task = NewRunnableMethod(
+ "TestPaintThreadChild::Close", mPaintActor, &TestPaintThreadChild::Close);
+ mPaintThread->message_loop()->PostTask(task.forget());
+ mPaintThread = nullptr;
+
+ QuitChild();
+}
+
+void TestOffMainThreadPaintingChild::ProcessingError(Result aCode,
+ const char* aReason) {
+ MOZ_CRASH("Aborting child due to IPC error");
+}
+
+void TestOffMainThreadPaintingChild::IssueTransaction() {
+ GetIPCChannel()->BeginPostponingSends();
+
+ uint64_t txnId = mNextTxnId++;
+
+ // Start painting before we send the message.
+ RefPtr<Runnable> task = NewRunnableMethod<uint64_t>(
+ "TestPaintThreadChild::BeginPaintingForTxn", mPaintActor,
+ &TestPaintThreadChild::BeginPaintingForTxn, txnId);
+ mPaintThread->message_loop()->PostTask(task.forget());
+
+ // Simulate some gecko main thread stuff.
+ SendFinishedLayout(txnId);
+ SendAsyncMessage(txnId);
+ SendSyncMessage(txnId);
+ SendEndTest();
+}
+
+/**************************
+ * PTestPaintThreadParent *
+ **************************/
+
+TestPaintThreadParent::TestPaintThreadParent(
+ TestOffMainThreadPaintingParent* aMainBridge)
+ : mMainBridge(aMainBridge) {}
+
+TestPaintThreadParent::~TestPaintThreadParent() {}
+
+bool TestPaintThreadParent::Bind(
+ ipc::Endpoint<PTestPaintThreadParent>&& aEndpoint) {
+ if (!aEndpoint.Bind(this)) {
+ return false;
+ }
+
+ AddRef();
+ return true;
+}
+
+ipc::IPCResult TestPaintThreadParent::RecvFinishedPaint(
+ const uint64_t& aTxnId) {
+ mMainBridge->NotifyFinishedPaint(aTxnId);
+ return IPC_OK();
+}
+
+void TestPaintThreadParent::ActorDestroy(ActorDestroyReason aWhy) {}
+
+void TestPaintThreadParent::DeallocPTestPaintThreadParent() { Release(); }
+
+/*************************
+ * PTestPaintThreadChild *
+ *************************/
+
+TestPaintThreadChild::TestPaintThreadChild(MessageChannel* aMainChannel)
+ : mCanSend(false), mMainChannel(aMainChannel) {}
+
+TestPaintThreadChild::~TestPaintThreadChild() {}
+
+void TestPaintThreadChild::Bind(
+ ipc::Endpoint<PTestPaintThreadChild>&& aEndpoint) {
+ if (!aEndpoint.Bind(this)) {
+ MOZ_CRASH("could not bind paint child endpoint");
+ }
+ AddRef();
+ mCanSend = true;
+}
+
+void TestPaintThreadChild::BeginPaintingForTxn(uint64_t aTxnId) {
+ MOZ_RELEASE_ASSERT(!NS_IsMainThread());
+
+ // Sleep for some time to simulate painting being slow.
+ PR_Sleep(PR_MillisecondsToInterval(500));
+ SendFinishedPaint(aTxnId);
+
+ mMainChannel->StopPostponingSends();
+}
+
+void TestPaintThreadChild::ActorDestroy(ActorDestroyReason aWhy) {
+ mCanSend = false;
+}
+
+void TestPaintThreadChild::Close() {
+ MOZ_RELEASE_ASSERT(!NS_IsMainThread());
+
+ if (mCanSend) {
+ PTestPaintThreadChild::Close();
+ }
+}
+
+void TestPaintThreadChild::DeallocPTestPaintThreadChild() { Release(); }
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestOffMainThreadPainting.h b/ipc/ipdl/test/cxx/TestOffMainThreadPainting.h
new file mode 100644
index 0000000000..7cb935a2ee
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestOffMainThreadPainting.h
@@ -0,0 +1,109 @@
+#ifndef mozilla__ipdltest_TestOffMainThreadPainting_h
+#define mozilla__ipdltest_TestOffMainThreadPainting_h
+
+#include "mozilla/Maybe.h"
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+#include "mozilla/_ipdltest/PTestLayoutThreadChild.h"
+#include "mozilla/_ipdltest/PTestLayoutThreadParent.h"
+#include "mozilla/_ipdltest/PTestPaintThreadChild.h"
+#include "mozilla/_ipdltest/PTestPaintThreadParent.h"
+#include "base/thread.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+class TestPaintThreadChild;
+class TestPaintThreadParent;
+
+// Analagous to LayerTransactionParent.
+class TestOffMainThreadPaintingParent final : public PTestLayoutThreadParent {
+ public:
+ static bool RunTestInThreads() { return false; }
+ static bool RunTestInProcesses() { return true; }
+
+ void Main();
+
+ MOZ_IMPLICIT TestOffMainThreadPaintingParent();
+ ~TestOffMainThreadPaintingParent() override;
+
+ ipc::IPCResult RecvFinishedLayout(const uint64_t& aTxnId);
+ ipc::IPCResult RecvAsyncMessage(const uint64_t& aTxnId);
+ ipc::IPCResult RecvSyncMessage(const uint64_t& aTxnId);
+ ipc::IPCResult RecvEndTest();
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ void NotifyFinishedPaint(const uint64_t& aTxnId);
+
+ private:
+ RefPtr<TestPaintThreadParent> mPaintActor;
+ Maybe<uint64_t> mCompletedTxn;
+ Maybe<uint64_t> mPaintedTxn;
+ uint32_t mAsyncMessages;
+ uint32_t mSyncMessages;
+};
+
+// Analagous to LayerTransactionChild.
+class TestOffMainThreadPaintingChild final : public PTestLayoutThreadChild {
+ public:
+ TestOffMainThreadPaintingChild();
+ ~TestOffMainThreadPaintingChild() override;
+
+ ipc::IPCResult RecvStartTest(
+ ipc::Endpoint<PTestPaintThreadChild>&& aEndpoint);
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+ void ProcessingError(Result aCode, const char* aReason) override;
+
+ private:
+ void IssueTransaction();
+
+ private:
+ UniquePtr<base::Thread> mPaintThread;
+ RefPtr<TestPaintThreadChild> mPaintActor;
+ uint64_t mNextTxnId;
+};
+
+/****************
+ * Paint Actors *
+ ****************/
+
+class TestPaintThreadParent final : public PTestPaintThreadParent {
+ public:
+ explicit TestPaintThreadParent(TestOffMainThreadPaintingParent* aMainBridge);
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TestPaintThreadParent);
+
+ bool Bind(ipc::Endpoint<PTestPaintThreadParent>&& aEndpoint);
+ ipc::IPCResult RecvFinishedPaint(const uint64_t& aTxnId);
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+ void DeallocPTestPaintThreadParent() override;
+
+ private:
+ ~TestPaintThreadParent() override;
+
+ private:
+ TestOffMainThreadPaintingParent* mMainBridge;
+};
+
+class TestPaintThreadChild final : public PTestPaintThreadChild {
+ public:
+ explicit TestPaintThreadChild(MessageChannel* aOtherChannel);
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TestPaintThreadChild);
+
+ void Bind(ipc::Endpoint<PTestPaintThreadChild>&& aEndpoint);
+ void BeginPaintingForTxn(uint64_t aTxnId);
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+ void DeallocPTestPaintThreadChild() override;
+ void Close();
+
+ private:
+ ~TestPaintThreadChild() override;
+
+ bool mCanSend;
+ MessageChannel* mMainChannel;
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+#endif // ifndef mozilla__ipdltest_TestOffMainThreadPainting_h
diff --git a/ipc/ipdl/test/cxx/TestRPC.cpp b/ipc/ipdl/test/cxx/TestRPC.cpp
new file mode 100644
index 0000000000..404de01aa8
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestRPC.cpp
@@ -0,0 +1,107 @@
+#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");
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+TestRPCChild::TestRPCChild() { MOZ_COUNT_CTOR(TestRPCChild); }
+
+TestRPCChild::~TestRPCChild() { MOZ_COUNT_DTOR(TestRPCChild); }
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestRPCChild::RecvTest1_NoReenter(uint32_t* aResult) {
+ *aResult = 400;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestRPCChild::RecvTest2_FirstUrgent() {
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestRPCChild::RecvTest2_SecondUrgent() {
+ return IPC_OK();
+}
+
+} // 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 0000000000..907d3fde36
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestRPC.h
@@ -0,0 +1,60 @@
+#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();
+
+ mozilla::ipc::IPCResult RecvTest1_Start(uint32_t* aResult);
+ mozilla::ipc::IPCResult RecvTest1_InnerEvent(uint32_t* aResult);
+ mozilla::ipc::IPCResult RecvTest2_Start();
+ mozilla::ipc::IPCResult RecvTest2_OutOfOrder();
+
+ 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();
+
+ mozilla::ipc::IPCResult RecvStart();
+ mozilla::ipc::IPCResult RecvTest1_InnerQuery(uint32_t* aResult);
+ mozilla::ipc::IPCResult RecvTest1_NoReenter(uint32_t* aResult);
+ mozilla::ipc::IPCResult RecvTest2_FirstUrgent();
+ mozilla::ipc::IPCResult RecvTest2_SecondUrgent();
+
+ 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 0000000000..76defe97a5
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestRaceDeadlock.cpp
@@ -0,0 +1,102 @@
+#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");
+ }
+}
+
+mozilla::ipc::IPCResult TestRaceDeadlockParent::AnswerLose() {
+ return IPC_OK();
+}
+
+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);
+}
+
+mozilla::ipc::IPCResult TestRaceDeadlockParent::RecvStartRace() {
+ if (!CallWin()) {
+ fail("calling Win");
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestRaceDeadlockChild::RecvStartRace() {
+ if (!SendStartRace()) {
+ fail("calling SendStartRace");
+ }
+ if (!CallLose()) {
+ fail("calling Lose");
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestRaceDeadlockChild::AnswerWin() { return IPC_OK(); }
+
+mozilla::ipc::IPCResult TestRaceDeadlockChild::AnswerRpc() { return IPC_OK(); }
+
+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 0000000000..2c5617130d
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestRaceDeadlock.h
@@ -0,0 +1,68 @@
+#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 {
+ friend class PTestRaceDeadlockParent;
+
+ public:
+ TestRaceDeadlockParent();
+ virtual ~TestRaceDeadlockParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+ protected:
+ virtual bool ShouldContinueFromReplyTimeout() override;
+
+ void Test1();
+
+ mozilla::ipc::IPCResult RecvStartRace();
+ mozilla::ipc::IPCResult AnswerLose();
+
+ 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 {
+ friend class PTestRaceDeadlockChild;
+
+ public:
+ TestRaceDeadlockChild();
+ virtual ~TestRaceDeadlockChild();
+
+ protected:
+ mozilla::ipc::IPCResult RecvStartRace();
+
+ mozilla::ipc::IPCResult AnswerWin();
+
+ mozilla::ipc::IPCResult AnswerRpc();
+
+ 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 0000000000..c327b57c16
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestRaceDeferral.cpp
@@ -0,0 +1,84 @@
+#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");
+}
+
+mozilla::ipc::IPCResult TestRaceDeferralParent::AnswerLose() {
+ if (mProcessedLose) fail("processed Lose twice");
+ mProcessedLose = true;
+ return IPC_OK();
+}
+
+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);
+}
+
+mozilla::ipc::IPCResult TestRaceDeferralChild::RecvStartRace() {
+ if (!CallLose()) fail("calling Lose");
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestRaceDeferralChild::AnswerWin() { return IPC_OK(); }
+
+mozilla::ipc::IPCResult TestRaceDeferralChild::AnswerRpc() { return IPC_OK(); }
+
+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 0000000000..b30264fb64
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestRaceDeferral.h
@@ -0,0 +1,67 @@
+#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 {
+ friend class PTestRaceDeferralParent;
+
+ public:
+ TestRaceDeferralParent();
+ virtual ~TestRaceDeferralParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+ protected:
+ void Test1();
+
+ mozilla::ipc::IPCResult AnswerLose();
+
+ 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 {
+ friend class PTestRaceDeferralChild;
+
+ public:
+ TestRaceDeferralChild();
+ virtual ~TestRaceDeferralChild();
+
+ protected:
+ mozilla::ipc::IPCResult RecvStartRace();
+
+ mozilla::ipc::IPCResult AnswerWin();
+
+ mozilla::ipc::IPCResult AnswerRpc();
+
+ 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 0000000000..cda950b1a1
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestRacyInterruptReplies.cpp
@@ -0,0 +1,94 @@
+#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");
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestRacyInterruptRepliesParent::Answer_R(
+ int* replyNum) {
+ *replyNum = ++mReplyNum;
+
+ if (1 == *replyNum)
+ if (!Send_A()) fail("sending _A()");
+
+ return IPC_OK();
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+TestRacyInterruptRepliesChild::TestRacyInterruptRepliesChild() : mReplyNum(0) {
+ MOZ_COUNT_CTOR(TestRacyInterruptRepliesChild);
+}
+
+TestRacyInterruptRepliesChild::~TestRacyInterruptRepliesChild() {
+ MOZ_COUNT_DTOR(TestRacyInterruptRepliesChild);
+}
+
+mozilla::ipc::IPCResult TestRacyInterruptRepliesChild::AnswerR_(int* replyNum) {
+ *replyNum = ++mReplyNum;
+
+ if (1 == *replyNum) SendA_();
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+} // 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 0000000000..182c07f314
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestRacyInterruptReplies.h
@@ -0,0 +1,65 @@
+#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 {
+ friend class PTestRacyInterruptRepliesParent;
+
+ public:
+ TestRacyInterruptRepliesParent();
+ virtual ~TestRacyInterruptRepliesParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+ protected:
+ mozilla::ipc::IPCResult RecvA_();
+
+ mozilla::ipc::IPCResult Answer_R(int* replyNum);
+
+ virtual void ActorDestroy(ActorDestroyReason why) override {
+ if (NormalShutdown != why) fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+
+ private:
+ int mReplyNum;
+};
+
+class TestRacyInterruptRepliesChild : public PTestRacyInterruptRepliesChild {
+ friend class PTestRacyInterruptRepliesChild;
+
+ public:
+ TestRacyInterruptRepliesChild();
+ virtual ~TestRacyInterruptRepliesChild();
+
+ protected:
+ mozilla::ipc::IPCResult AnswerR_(int* replyNum);
+
+ mozilla::ipc::IPCResult RecvChildTest();
+
+ mozilla::ipc::IPCResult Recv_A();
+
+ 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 0000000000..4224018e11
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestRacyReentry.cpp
@@ -0,0 +1,63 @@
+#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");
+}
+
+mozilla::ipc::IPCResult TestRacyReentryParent::AnswerE() {
+ if (!mRecvdE) {
+ mRecvdE = true;
+ return IPC_OK();
+ }
+
+ if (!CallH()) fail("calling H");
+
+ return IPC_OK();
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+TestRacyReentryChild::TestRacyReentryChild() {
+ MOZ_COUNT_CTOR(TestRacyReentryChild);
+}
+
+TestRacyReentryChild::~TestRacyReentryChild() {
+ MOZ_COUNT_DTOR(TestRacyReentryChild);
+}
+
+mozilla::ipc::IPCResult TestRacyReentryChild::RecvStart() {
+ if (!CallE()) fail("calling E");
+
+ Close();
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestRacyReentryChild::RecvN() {
+ if (!CallE()) fail("calling E");
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestRacyReentryChild::AnswerH() { return IPC_OK(); }
+
+} // 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 0000000000..e31f90c0e3
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestRacyReentry.h
@@ -0,0 +1,59 @@
+#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 {
+ friend class PTestRacyReentryParent;
+
+ public:
+ TestRacyReentryParent();
+ virtual ~TestRacyReentryParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+ protected:
+ mozilla::ipc::IPCResult AnswerE();
+
+ virtual void ActorDestroy(ActorDestroyReason why) override {
+ if (NormalShutdown != why) fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+
+ bool mRecvdE;
+};
+
+class TestRacyReentryChild : public PTestRacyReentryChild {
+ friend class PTestRacyReentryChild;
+
+ public:
+ TestRacyReentryChild();
+ virtual ~TestRacyReentryChild();
+
+ protected:
+ mozilla::ipc::IPCResult RecvStart();
+
+ mozilla::ipc::IPCResult RecvN();
+
+ mozilla::ipc::IPCResult AnswerH();
+
+ 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 0000000000..a46db5618e
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestRacyUndefer.cpp
@@ -0,0 +1,83 @@
+#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");
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestRacyUndeferParent::AnswerRaceWinTwice() {
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestRacyUndeferParent::RecvDone() {
+ Close();
+ return IPC_OK();
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+TestRacyUndeferChild::TestRacyUndeferChild() {
+ MOZ_COUNT_CTOR(TestRacyUndeferChild);
+}
+
+TestRacyUndeferChild::~TestRacyUndeferChild() {
+ MOZ_COUNT_DTOR(TestRacyUndeferChild);
+}
+
+mozilla::ipc::IPCResult TestRacyUndeferChild::RecvStart() {
+ if (!CallSpam()) fail("calling Spam");
+
+ if (!SendDone()) fail("sending Done");
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestRacyUndeferChild::RecvAwakenSpam() {
+ if (!CallSpam()) fail("calling Spam");
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestRacyUndeferChild::RecvAwakenRaceWinTwice() {
+ if (!CallRaceWinTwice()) fail("calling RaceWinTwice");
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestRacyUndeferChild::AnswerRace() { return IPC_OK(); }
+
+} // 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 0000000000..1e157a541b
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestRacyUndefer.h
@@ -0,0 +1,62 @@
+#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 {
+ friend class PTestRacyUndeferParent;
+
+ public:
+ TestRacyUndeferParent();
+ virtual ~TestRacyUndeferParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+ protected:
+ mozilla::ipc::IPCResult AnswerSpam();
+
+ mozilla::ipc::IPCResult AnswerRaceWinTwice();
+
+ mozilla::ipc::IPCResult RecvDone();
+
+ virtual void ActorDestroy(ActorDestroyReason why) override {
+ if (NormalShutdown != why) fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+};
+
+class TestRacyUndeferChild : public PTestRacyUndeferChild {
+ friend class PTestRacyUndeferChild;
+
+ public:
+ TestRacyUndeferChild();
+ virtual ~TestRacyUndeferChild();
+
+ protected:
+ mozilla::ipc::IPCResult RecvStart();
+
+ mozilla::ipc::IPCResult RecvAwakenSpam();
+ mozilla::ipc::IPCResult RecvAwakenRaceWinTwice();
+
+ mozilla::ipc::IPCResult AnswerRace();
+
+ 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 0000000000..ba1920fdcc
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestSanity.cpp
@@ -0,0 +1,52 @@
+#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");
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+TestSanityChild::TestSanityChild() { MOZ_COUNT_CTOR(TestSanityChild); }
+
+TestSanityChild::~TestSanityChild() { MOZ_COUNT_DTOR(TestSanityChild); }
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+} // 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 0000000000..ca29d67672
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestSanity.h
@@ -0,0 +1,55 @@
+#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 {
+ friend class PTestSanityParent;
+
+ public:
+ TestSanityParent();
+ virtual ~TestSanityParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+ protected:
+ mozilla::ipc::IPCResult RecvPong(const int& one, const float& zeroPtTwoFive,
+ const uint8_t& dummy);
+
+ virtual void ActorDestroy(ActorDestroyReason why) override {
+ if (NormalShutdown != why) fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+};
+
+class TestSanityChild : public PTestSanityChild {
+ friend class PTestSanityChild;
+
+ public:
+ TestSanityChild();
+ virtual ~TestSanityChild();
+
+ protected:
+ mozilla::ipc::IPCResult RecvPing(const int& zero, const float& zeroPtFive,
+ const int8_t& dummy);
+
+ 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 0000000000..5e09aa4d7e
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestSelfManageRoot.cpp
@@ -0,0 +1,54 @@
+#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 0000000000..5cea09b10d
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestSelfManageRoot.h
@@ -0,0 +1,117 @@
+#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 {
+ friend class PTestSelfManageParent;
+
+ public:
+ MOZ_COUNTED_DEFAULT_CTOR(TestSelfManageParent)
+ MOZ_COUNTED_DTOR_OVERRIDE(TestSelfManageParent)
+
+ ActorDestroyReason mWhy;
+
+ protected:
+ PTestSelfManageParent* AllocPTestSelfManageParent() {
+ return new TestSelfManageParent();
+ }
+
+ bool DeallocPTestSelfManageParent(PTestSelfManageParent* a) { return true; }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override { mWhy = why; }
+};
+
+class TestSelfManageRootParent : public PTestSelfManageRootParent {
+ friend class PTestSelfManageRootParent;
+
+ public:
+ MOZ_COUNTED_DEFAULT_CTOR(TestSelfManageRootParent)
+ virtual ~TestSelfManageRootParent() {
+ MOZ_COUNT_DTOR(TestSelfManageRootParent);
+ }
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+ protected:
+ PTestSelfManageParent* AllocPTestSelfManageParent() {
+ return new TestSelfManageParent();
+ }
+
+ bool DeallocPTestSelfManageParent(PTestSelfManageParent* a) { return true; }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override {
+ if (NormalShutdown != why) fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+};
+
+//-----------------------------------------------------------------------------
+// Child side
+
+class TestSelfManageChild : public PTestSelfManageChild {
+ friend class PTestSelfManageChild;
+
+ public:
+ MOZ_COUNTED_DEFAULT_CTOR(TestSelfManageChild)
+ MOZ_COUNTED_DTOR_OVERRIDE(TestSelfManageChild)
+
+ protected:
+ PTestSelfManageChild* AllocPTestSelfManageChild() {
+ return new TestSelfManageChild();
+ }
+
+ bool DeallocPTestSelfManageChild(PTestSelfManageChild* a) {
+ delete a;
+ return true;
+ }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override {}
+};
+
+class TestSelfManageRootChild : public PTestSelfManageRootChild {
+ friend class PTestSelfManageRootChild;
+
+ public:
+ MOZ_COUNTED_DEFAULT_CTOR(TestSelfManageRootChild)
+ virtual ~TestSelfManageRootChild() {
+ MOZ_COUNT_DTOR(TestSelfManageRootChild);
+ }
+
+ void Main();
+
+ protected:
+ PTestSelfManageChild* AllocPTestSelfManageChild() {
+ return new TestSelfManageChild();
+ }
+
+ bool DeallocPTestSelfManageChild(PTestSelfManageChild* a) {
+ 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 0000000000..dcf7ae2f21
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestShmem.cpp
@@ -0,0 +1,108 @@
+#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(std::move(mem), std::move(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;
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+//-----------------------------------------------------------------------------
+// Child
+
+mozilla::ipc::IPCResult 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(std::move(mem), std::move(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 IPC_OK();
+}
+
+} // 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 0000000000..4ef1f4fd7b
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestShmem.h
@@ -0,0 +1,55 @@
+#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 {
+ friend class PTestShmemParent;
+
+ public:
+ TestShmemParent() {}
+ virtual ~TestShmemParent() {}
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+ protected:
+ mozilla::ipc::IPCResult RecvTake(Shmem&& mem, Shmem&& unsafe,
+ const size_t& expectedSize);
+
+ virtual void ActorDestroy(ActorDestroyReason why) override {
+ if (NormalShutdown != why) fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+};
+
+class TestShmemChild : public PTestShmemChild {
+ friend class PTestShmemChild;
+
+ public:
+ TestShmemChild() {}
+ virtual ~TestShmemChild() {}
+
+ protected:
+ mozilla::ipc::IPCResult RecvGive(Shmem&& mem, Shmem&& unsafe,
+ const size_t& expectedSize);
+
+ 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 0000000000..502695bd88
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestShutdown.cpp
@@ -0,0 +1,185 @@
+#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
+
+mozilla::ipc::IPCResult 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!");
+}
+
+mozilla::ipc::IPCResult TestShutdownSubChild::AnswerStackFrame() {
+ if (!PTestShutdownSubChild::Send__delete__(this))
+ fail("problem sending dtor");
+
+ // WATCH OUT! |this| has just deleted
+
+ return IPC_OK();
+}
+
+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 0000000000..224e42496d
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestShutdown.h
@@ -0,0 +1,168 @@
+#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 {
+ friend class PTestShutdownSubParent;
+
+ public:
+ explicit TestShutdownSubParent(bool expectCrash)
+ : mExpectCrash(expectCrash), mDeletedCount(0) {}
+
+ virtual ~TestShutdownSubParent() {
+ if (2 != mDeletedCount) fail("managees outliving manager!");
+ }
+
+ protected:
+ mozilla::ipc::IPCResult AnswerStackFrame() {
+ if (!CallStackFrame()) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ return IPC_OK();
+ }
+
+ PTestShutdownSubsubParent* AllocPTestShutdownSubsubParent(
+ const bool& expectParentDelete) {
+ return new TestShutdownSubsubParent(expectParentDelete);
+ }
+
+ bool DeallocPTestShutdownSubsubParent(PTestShutdownSubsubParent* actor) {
+ delete actor;
+ ++mDeletedCount;
+ return true;
+ }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ private:
+ bool mExpectCrash;
+ int mDeletedCount;
+};
+
+class TestShutdownParent : public PTestShutdownParent {
+ friend class 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:
+ mozilla::ipc::IPCResult RecvSync() { return IPC_OK(); }
+
+ PTestShutdownSubParent* AllocPTestShutdownSubParent(const bool& expectCrash) {
+ return new TestShutdownSubParent(expectCrash);
+ }
+
+ bool DeallocPTestShutdownSubParent(PTestShutdownSubParent* actor) {
+ 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 {
+ friend class PTestShutdownSubChild;
+
+ public:
+ explicit TestShutdownSubChild(bool expectCrash) : mExpectCrash(expectCrash) {}
+
+ virtual ~TestShutdownSubChild() {}
+
+ protected:
+ mozilla::ipc::IPCResult AnswerStackFrame();
+
+ PTestShutdownSubsubChild* AllocPTestShutdownSubsubChild(
+ const bool& expectParentDelete) {
+ return new TestShutdownSubsubChild(expectParentDelete);
+ }
+
+ bool DeallocPTestShutdownSubsubChild(PTestShutdownSubsubChild* actor) {
+ delete actor;
+ return true;
+ }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ private:
+ bool mExpectCrash;
+};
+
+class TestShutdownChild : public PTestShutdownChild {
+ friend class PTestShutdownChild;
+
+ public:
+ TestShutdownChild() {}
+ virtual ~TestShutdownChild() {}
+
+ protected:
+ mozilla::ipc::IPCResult RecvStart();
+
+ PTestShutdownSubChild* AllocPTestShutdownSubChild(const bool& expectCrash) {
+ return new TestShutdownSubChild(expectCrash);
+ }
+
+ bool DeallocPTestShutdownSubChild(PTestShutdownSubChild* actor) {
+ 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 0000000000..9800ac54e7
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestStackHooks.cpp
@@ -0,0 +1,122 @@
+#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()");
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+TestStackHooksChild::TestStackHooksChild()
+ : mOnStack(false),
+ mEntered(0),
+ mExited(0),
+ mIncallDepth(0),
+ mNumAnswerStackFrame(0) {
+ MOZ_COUNT_CTOR(TestStackHooksChild);
+}
+
+TestStackHooksChild::~TestStackHooksChild() {
+ MOZ_COUNT_DTOR(TestStackHooksChild);
+}
+
+namespace {
+void RunTestsFn() {
+ static_cast<TestStackHooksChild*>(gChildActor)->RunTests();
+}
+} // namespace
+
+mozilla::ipc::IPCResult 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", RunTestsFn));
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestStackHooksChild::AnswerStackFrame() {
+ ++mNumAnswerStackFrame;
+
+ if (!mOnStack) fail("missed stack notification");
+
+ if (1 != mIncallDepth) fail("missed EnteredCall or ExitedCall hook");
+
+ if (mNumAnswerStackFrame == 1) {
+ if (!SendAsync()) fail("sending Async()");
+ } else if (mNumAnswerStackFrame == 2) {
+ if (!SendSync()) fail("sending Sync()");
+ } else {
+ fail("unexpected state");
+ }
+
+ if (!mOnStack) fail("bad stack exit notification");
+
+ return IPC_OK();
+}
+
+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 0000000000..315a3c1ae5
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestStackHooks.h
@@ -0,0 +1,107 @@
+#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 {
+ friend class PTestStackHooksParent;
+
+ public:
+ TestStackHooksParent();
+ virtual ~TestStackHooksParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+ protected:
+ mozilla::ipc::IPCResult RecvAsync() {
+ if (!mOnStack) fail("not on C++ stack?!");
+ return IPC_OK();
+ }
+
+ mozilla::ipc::IPCResult RecvSync() {
+ if (!mOnStack) fail("not on C++ stack?!");
+ return IPC_OK();
+ }
+
+ mozilla::ipc::IPCResult AnswerRpc() {
+ if (!mOnStack) fail("not on C++ stack?!");
+ return IPC_OK();
+ }
+
+ mozilla::ipc::IPCResult AnswerStackFrame();
+
+ 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 {
+ friend class PTestStackHooksChild;
+
+ public:
+ TestStackHooksChild();
+ virtual ~TestStackHooksChild();
+
+ void RunTests();
+
+ protected:
+ mozilla::ipc::IPCResult RecvStart();
+
+ mozilla::ipc::IPCResult AnswerStackFrame();
+
+ 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;
+ int32_t mNumAnswerStackFrame;
+};
+
+} // 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 0000000000..6c21590a87
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestSyncError.cpp
@@ -0,0 +1,45 @@
+#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");
+}
+
+mozilla::ipc::IPCResult TestSyncErrorParent::RecvError() {
+ return IPC_FAIL_NO_REASON(this);
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+TestSyncErrorChild::TestSyncErrorChild() { MOZ_COUNT_CTOR(TestSyncErrorChild); }
+
+TestSyncErrorChild::~TestSyncErrorChild() {
+ MOZ_COUNT_DTOR(TestSyncErrorChild);
+}
+
+mozilla::ipc::IPCResult TestSyncErrorChild::RecvStart() {
+ if (SendError()) fail("Error() should have return false");
+
+ Close();
+
+ return IPC_OK();
+}
+
+} // 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 0000000000..8b84ed5e0b
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestSyncError.h
@@ -0,0 +1,61 @@
+#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 {
+ friend class PTestSyncErrorParent;
+
+ public:
+ TestSyncErrorParent();
+ virtual ~TestSyncErrorParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+ protected:
+ mozilla::ipc::IPCResult RecvError();
+
+ 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 {
+ friend class PTestSyncErrorChild;
+
+ public:
+ TestSyncErrorChild();
+ virtual ~TestSyncErrorChild();
+
+ protected:
+ mozilla::ipc::IPCResult RecvStart();
+
+ 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 0000000000..f53054787b
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestSyncHang.cpp
@@ -0,0 +1,62 @@
+#include "TestSyncHang.h"
+#include "base/task.h"
+#include "mozilla/ipc/GeckoChildProcessHost.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+using std::string;
+using std::vector;
+
+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) {
+ gSyncHangSubprocess->Destroy();
+ gSyncHangSubprocess = nullptr;
+}
+
+void DeferredSyncHangParentShutdown() {
+ // ping to DeleteSubprocess
+ XRE_GetIOMessageLoop()->PostTask(
+ NewRunnableFunction("DeleteSyncHangSubprocess", 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", DeferredSyncHangParentShutdown));
+ Close();
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+TestSyncHangChild::TestSyncHangChild() { MOZ_COUNT_CTOR(TestSyncHangChild); }
+
+TestSyncHangChild::~TestSyncHangChild() { MOZ_COUNT_DTOR(TestSyncHangChild); }
+
+mozilla::ipc::IPCResult TestSyncHangChild::RecvUnusedMessage() {
+ return IPC_OK();
+}
+
+} // 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 0000000000..911f05cd31
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestSyncHang.h
@@ -0,0 +1,50 @@
+#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 {
+ friend class PTestSyncHangChild;
+
+ public:
+ TestSyncHangChild();
+ virtual ~TestSyncHangChild();
+
+ protected:
+ mozilla::ipc::IPCResult RecvUnusedMessage();
+
+ 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 0000000000..4c99aa1bab
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestSyncWakeup.cpp
@@ -0,0 +1,106 @@
+#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()");
+}
+
+mozilla::ipc::IPCResult TestSyncWakeupParent::AnswerStackFrame() {
+ if (!CallStackFrame()) fail("calling StackFrame()");
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestSyncWakeupParent::RecvSync2() {
+ if (!SendNote2()) fail("sending Note2()");
+
+#if defined(OS_POSIX)
+ // see above
+ sleep(5);
+ puts(" (sleeping for 5 seconds. sorry!)");
+#endif
+
+ return IPC_OK();
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+TestSyncWakeupChild::TestSyncWakeupChild() : mDone(false) {
+ MOZ_COUNT_CTOR(TestSyncWakeupChild);
+}
+
+TestSyncWakeupChild::~TestSyncWakeupChild() {
+ MOZ_COUNT_DTOR(TestSyncWakeupChild);
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestSyncWakeupChild::AnswerStackFrame() {
+ if (!SendSync2()) fail("sending Sync()");
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestSyncWakeupChild::RecvNote2() {
+ mDone = true;
+ return IPC_OK();
+}
+
+} // 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 0000000000..5666307929
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestSyncWakeup.h
@@ -0,0 +1,66 @@
+#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 {
+ friend class PTestSyncWakeupParent;
+
+ public:
+ TestSyncWakeupParent();
+ virtual ~TestSyncWakeupParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+ protected:
+ mozilla::ipc::IPCResult AnswerStackFrame();
+
+ mozilla::ipc::IPCResult RecvSync1();
+
+ mozilla::ipc::IPCResult RecvSync2();
+
+ virtual void ActorDestroy(ActorDestroyReason why) override {
+ if (NormalShutdown != why) fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+};
+
+class TestSyncWakeupChild : public PTestSyncWakeupChild {
+ friend class PTestSyncWakeupChild;
+
+ public:
+ TestSyncWakeupChild();
+ virtual ~TestSyncWakeupChild();
+
+ protected:
+ mozilla::ipc::IPCResult RecvStart();
+
+ mozilla::ipc::IPCResult RecvNote1();
+
+ mozilla::ipc::IPCResult AnswerStackFrame();
+
+ mozilla::ipc::IPCResult RecvNote2();
+
+ 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/TestUniquePtrIPC.cpp b/ipc/ipdl/test/cxx/TestUniquePtrIPC.cpp
new file mode 100644
index 0000000000..7d0e8dd6ca
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestUniquePtrIPC.cpp
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TestUniquePtrIPC.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+// ---------------------------------------------------------------------------
+// PARENT PROCESS
+// ---------------------------------------------------------------------------
+
+void TestUniquePtrIPCParent::Main() {
+ UniquePtr<int> a1 = MakeUnique<int>(1);
+ UniquePtr<DummyStruct> a2 = MakeUnique<DummyStruct>(2);
+ DummyStruct a3(3);
+ UniquePtr<int> a4;
+
+ if (!SendTestMessage(std::move(a1), std::move(a2), a3, std::move(a4))) {
+ fail("failed sending UniquePtr items");
+ }
+
+ if (a1 || a2) {
+ fail("did not move TestMessage items in parent");
+ }
+
+ if (a4) {
+ fail("somehow turned null ptr into non-null by sending it");
+ }
+
+ // Pass UniquePtr by reference
+ UniquePtr<DummyStruct> b = MakeUnique<DummyStruct>(1);
+
+ if (!SendTestSendReference(std::move(b))) {
+ fail("failed sending UniquePtr by reference");
+ }
+ if (b) {
+ fail("did not move UniquePtr sent by reference");
+ }
+}
+
+// ---------------------------------------------------------------------------
+// CHILD PROCESS
+// ---------------------------------------------------------------------------
+
+mozilla::ipc::IPCResult TestUniquePtrIPCChild::RecvTestMessage(
+ UniquePtr<int>&& aA1, UniquePtr<DummyStruct>&& aA2, const DummyStruct& aA3,
+ UniquePtr<int>&& aA4) {
+ if ((!aA1) || (!aA2)) {
+ fail("TestMessage received NULL items in child");
+ }
+
+ if (aA4) {
+ fail("TestMessage received non-NULL when expecting NULL");
+ }
+
+ if ((*aA1 != 1) || (aA2->x() != 2) || (aA3.x() != 3)) {
+ fail("TestMessage received incorrect items in child");
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestUniquePtrIPCChild::RecvTestSendReference(
+ UniquePtr<DummyStruct>&& aA) {
+ if (!aA) {
+ fail("TestSendReference received NULL item in child");
+ }
+
+ if (*aA != 1) {
+ fail("TestSendReference received incorrect item in child");
+ }
+
+ Close();
+ return IPC_OK();
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestUniquePtrIPC.h b/ipc/ipdl/test/cxx/TestUniquePtrIPC.h
new file mode 100644
index 0000000000..3c9de33df3
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestUniquePtrIPC.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_TestUniquePtrIPC_h
+#define mozilla_TestUniquePtrIPC_h
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestUniquePtrIPCParent.h"
+#include "mozilla/_ipdltest/PTestUniquePtrIPCChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+class TestUniquePtrIPCParent : public PTestUniquePtrIPCParent {
+ public:
+ MOZ_COUNTED_DEFAULT_CTOR(TestUniquePtrIPCParent)
+ MOZ_COUNTED_DTOR_OVERRIDE(TestUniquePtrIPCParent)
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+
+ bool ShouldContinueFromReplyTimeout() override { return false; }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override {
+ if (NormalShutdown != why) {
+ fail("Abnormal shutdown of parent");
+ }
+ passed("ok");
+ QuitParent();
+ }
+};
+
+class TestUniquePtrIPCChild : public PTestUniquePtrIPCChild {
+ public:
+ MOZ_COUNTED_DEFAULT_CTOR(TestUniquePtrIPCChild)
+ MOZ_COUNTED_DTOR_OVERRIDE(TestUniquePtrIPCChild)
+
+ mozilla::ipc::IPCResult RecvTestMessage(UniquePtr<int>&& aA1,
+ UniquePtr<DummyStruct>&& aA2,
+ const DummyStruct& aA3,
+ UniquePtr<int>&& aA4);
+
+ mozilla::ipc::IPCResult RecvTestSendReference(UniquePtr<DummyStruct>&& aA);
+
+ virtual void ActorDestroy(ActorDestroyReason why) override {
+ if (NormalShutdown != why) {
+ fail("Abnormal shutdown of child");
+ }
+ QuitChild();
+ }
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+#endif // mozilla_TestUniquePtrIPC_h
diff --git a/ipc/ipdl/test/cxx/TestUrgency.cpp b/ipc/ipdl/test/cxx/TestUrgency.cpp
new file mode 100644
index 0000000000..5845f8210d
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestUrgency.cpp
@@ -0,0 +1,117 @@
+#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");
+}
+
+mozilla::ipc::IPCResult TestUrgencyParent::RecvTest1(uint32_t* value) {
+ if (!SendReply1(value)) fail("sending Reply1");
+ if (*value != 99) fail("bad value");
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestUrgencyParent::RecvTest2() {
+ uint32_t value;
+ inreply_ = true;
+ if (!SendReply2(&value)) fail("sending Reply2");
+ inreply_ = false;
+ if (value != 500) fail("bad value");
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestUrgencyParent::RecvTest3(uint32_t* value) {
+ if (inreply_) fail("nested non-urgent on top of urgent rpc");
+ *value = 1000;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestUrgencyParent::RecvFinalTest_Begin() {
+ return IPC_OK();
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+enum {
+ kFirstTestBegin = 1,
+ kFirstTestGotReply,
+ kSecondTestBegin,
+ kSecondTestGotReply,
+};
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestUrgencyChild::RecvReply1(uint32_t* reply) {
+ if (test_ != kFirstTestBegin) fail("wrong test # in RecvReply1");
+
+ *reply = 99;
+ test_ = kFirstTestGotReply;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+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 0000000000..6af73177ee
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestUrgency.h
@@ -0,0 +1,57 @@
+#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();
+
+ mozilla::ipc::IPCResult RecvTest1(uint32_t* value);
+ mozilla::ipc::IPCResult RecvTest2();
+ mozilla::ipc::IPCResult RecvTest3(uint32_t* value);
+ mozilla::ipc::IPCResult RecvTest4_Begin();
+ mozilla::ipc::IPCResult RecvTest4_NestedSync();
+ mozilla::ipc::IPCResult 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();
+
+ mozilla::ipc::IPCResult RecvStart();
+ mozilla::ipc::IPCResult RecvReply1(uint32_t* reply);
+ mozilla::ipc::IPCResult 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 0000000000..e6139e38c5
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestUrgentHangs.cpp
@@ -0,0 +1,174 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: sw=2 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(
+ "_ipdltest::TestUrgentHangsParent::SecondStage", 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("_ipdltest::TestUrgentHangsParent::ThirdStage",
+ 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("ipc::IToplevelProtocol::Close", this,
+ &TestUrgentHangsParent::Close),
+ 3000);
+}
+
+mozilla::ipc::IPCResult TestUrgentHangsParent::RecvTest1_2() {
+ if (!SendTest1_3()) fail("sending Test1_3");
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestUrgentHangsParent::RecvTestInner() {
+ mInnerCount++;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestUrgentHangsParent::RecvTestInnerUrgent() {
+ mInnerUrgentCount++;
+ return IPC_OK();
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+mozilla::ipc::IPCResult TestUrgentHangsChild::RecvTest1_1() {
+ if (!SendTest1_2()) fail("sending Test1_2");
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestUrgentHangsChild::RecvTest1_3() {
+ PR_Sleep(PR_SecondsToInterval(2));
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestUrgentHangsChild::RecvTest2() {
+ PR_Sleep(PR_SecondsToInterval(2));
+
+ // Should fail because of the timeout.
+ if (SendTestInner()) fail("sending TestInner");
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestUrgentHangsChild::RecvTest3() {
+ fail("RecvTest3 should never be called");
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult TestUrgentHangsChild::RecvTest4_1() {
+ // This should fail because Test4_1 timed out and hasn't gotten a response
+ // yet.
+ if (SendTestInner()) fail("sending TestInner");
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+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 0000000000..facda522bf
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestUrgentHangs.h
@@ -0,0 +1,64 @@
+#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();
+
+ mozilla::ipc::IPCResult RecvTest1_2();
+ mozilla::ipc::IPCResult RecvTestInner();
+ mozilla::ipc::IPCResult 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();
+
+ mozilla::ipc::IPCResult RecvTest1_1();
+ mozilla::ipc::IPCResult RecvTest1_3();
+ mozilla::ipc::IPCResult RecvTest2();
+ mozilla::ipc::IPCResult RecvTest3();
+ mozilla::ipc::IPCResult RecvTest4();
+ mozilla::ipc::IPCResult RecvTest4_1();
+ mozilla::ipc::IPCResult RecvTest5();
+ mozilla::ipc::IPCResult 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/TestIPDL.cpp b/ipc/ipdl/test/cxx/app/TestIPDL.cpp
new file mode 100644
index 0000000000..3891aead62
--- /dev/null
+++ b/ipc/ipdl/test/cxx/app/TestIPDL.cpp
@@ -0,0 +1,24 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#define MOZ_IPDL_TESTS
+#include "mozilla/Bootstrap.h"
+
+#if defined(XP_WIN)
+# include <windows.h>
+# include "nsWindowsWMain.cpp"
+#endif
+
+using namespace mozilla;
+
+int main(int argc, char** argv) {
+ // the first argument specifies which IPDL test case/suite to load
+ if (argc < 2) return 1;
+
+ Bootstrap::UniquePtr bootstrap = GetBootstrap();
+ if (!bootstrap) {
+ return 2;
+ }
+ return bootstrap->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 0000000000..909360062a
--- /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["CC_TYPE"] == "clang-cl":
+ 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 0000000000..3636ec0416
--- /dev/null
+++ b/ipc/ipdl/test/cxx/genIPDLUnitTests.py
@@ -0,0 +1,195 @@
+# 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/.
+
+from __future__ import print_function, unicode_literals
+
+import string
+import sys
+
+
+def usage():
+ print(
+ """
+%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]),
+ file=sys.stderr,
+ )
+ 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", encoding="utf-8")
+ 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 0000000000..591fa269a9
--- /dev/null
+++ b/ipc/ipdl/test/cxx/moz.build
@@ -0,0 +1,173 @@
+# -*- 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",
+ "TestActorPunning.h",
+ "TestAsyncReturns.h",
+ "TestBadActor.h",
+ "TestCancel.h",
+ "TestCrashCleanup.h",
+ "TestDataStructures.h",
+ "TestDemon.h",
+ "TestDesc.h",
+ "TestEndpointBridgeMain.h",
+ "TestEndpointOpens.h",
+ "TestFailedCtor.h",
+ "TestHangs.h",
+ "TestHighestPrio.h",
+ "TestInterruptErrorCleanup.h",
+ "TestInterruptRaces.h",
+ "TestInterruptShutdownRace.h",
+ "TestJSON.h",
+ "TestLatency.h",
+ "TestManyChildAllocs.h",
+ "TestMultiMgrs.h",
+ "TestNestedLoops.h",
+ "TestOffMainThreadPainting.h",
+ "TestRaceDeadlock.h",
+ "TestRaceDeferral.h",
+ "TestRacyInterruptReplies.h",
+ "TestRacyReentry.h",
+ "TestRacyUndefer.h",
+ "TestRPC.h",
+ "TestSanity.h",
+ "TestSelfManageRoot.h",
+ "TestShmem.h",
+ "TestShutdown.h",
+ "TestStackHooks.h",
+ "TestSyncError.h",
+ "TestSyncHang.h",
+ "TestSyncWakeup.h",
+ "TestUniquePtrIPC.h",
+ "TestUrgency.h",
+ "TestUrgentHangs.h",
+]
+
+SOURCES += [
+ "TestActorPunning.cpp",
+ "TestAsyncReturns.cpp",
+ "TestBadActor.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",
+ "TestOffMainThreadPainting.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",
+ "TestUniquePtrIPC.cpp",
+ "TestUrgency.cpp",
+ "TestUrgentHangs.cpp",
+]
+
+SOURCES += [
+ "!IPDLUnitTests.cpp",
+ "IPDLUnitTestProcessChild.cpp",
+ "IPDLUnitTestSubprocess.cpp",
+]
+
+IPDL_SOURCES += [
+ "PTestActorPunning.ipdl",
+ "PTestActorPunningPunned.ipdl",
+ "PTestActorPunningSub.ipdl",
+ "PTestAsyncReturns.ipdl",
+ "PTestBadActor.ipdl",
+ "PTestBadActorSub.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",
+ "PTestLayoutThread.ipdl",
+ "PTestManyChildAllocs.ipdl",
+ "PTestManyChildAllocsSub.ipdl",
+ "PTestMultiMgrs.ipdl",
+ "PTestMultiMgrsBottom.ipdl",
+ "PTestMultiMgrsLeft.ipdl",
+ "PTestMultiMgrsRight.ipdl",
+ "PTestNestedLoops.ipdl",
+ "PTestPaintThread.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",
+ "PTestUniquePtrIPC.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 0000000000..af45a70faa
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/IPDLCompile.py
@@ -0,0 +1,78 @@
+import copy
+import re
+import os
+import subprocess
+import 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.specfilename| 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,
+ universal_newlines=True,
+ )
+ 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, expectedError):
+ """Return True iff compiling self.specstring resulted in an
+ IPDL compiler error."""
+ assert self.completed()
+
+ errorRe = re.compile(re.escape(expectedError))
+ return None is not re.search(errorRe, 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("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 0000000000..67f241775b
--- /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::
+ @$(PYTHON3) $(srcdir)/runtests.py \
+ $(srcdir)/ok $(srcdir)/error \
+ $(PYTHON3) $(topsrcdir)/ipc/ipdl/ipdl.py \
+ --sync-msg-list=$(srcdir)/sync-messages.ini \
+ --msg-metadata=$(srcdir)/message-metadata.ini \
+ OKTESTS $(OKTESTS) \
+ ERRORTESTS $(ERRORTESTS)
diff --git a/ipc/ipdl/test/ipdl/error/AsyncCtorReturns.ipdl b/ipc/ipdl/test/ipdl/error/AsyncCtorReturns.ipdl
new file mode 100644
index 0000000000..831d4d1159
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/AsyncCtorReturns.ipdl
@@ -0,0 +1,10 @@
+//error: asynchronous ctor/dtor message `AsyncCtorReturnsManageeConstructor' declares return values
+
+include protocol AsyncCtorReturnsManagee;
+
+protocol AsyncCtorReturns {
+ manages AsyncCtorReturnsManagee;
+
+child:
+ async AsyncCtorReturnsManagee() returns (bool unused);
+};
diff --git a/ipc/ipdl/test/ipdl/error/AsyncCtorReturnsManagee.ipdl b/ipc/ipdl/test/ipdl/error/AsyncCtorReturnsManagee.ipdl
new file mode 100644
index 0000000000..f38e2bd0ea
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/AsyncCtorReturnsManagee.ipdl
@@ -0,0 +1,10 @@
+//error: asynchronous ctor/dtor message `AsyncCtorReturnsManageeConstructor' declares return values
+
+include protocol AsyncCtorReturns;
+
+protocol AsyncCtorReturnsManagee {
+ manager AsyncCtorReturns;
+
+parent:
+ async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/error/AsyncInsideSync.ipdl b/ipc/ipdl/test/ipdl/error/AsyncInsideSync.ipdl
new file mode 100644
index 0000000000..d5358d260e
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/AsyncInsideSync.ipdl
@@ -0,0 +1,9 @@
+// inside_sync nested messages must be sync
+
+//error: inside_sync nested messages must be sync (here, message `Msg' in protocol `AsyncInsideSync')
+//error: message `Msg' requires more powerful send semantics than its protocol `AsyncInsideSync' provides
+
+protocol AsyncInsideSync {
+child:
+ nested(inside_sync) async Msg();
+};
diff --git a/ipc/ipdl/test/ipdl/error/ForgottenManagee.ipdl b/ipc/ipdl/test/ipdl/error/ForgottenManagee.ipdl
new file mode 100644
index 0000000000..35f6f02e4d
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/ForgottenManagee.ipdl
@@ -0,0 +1,12 @@
+//error: |manager| declaration in protocol `ForgottenManagee' does not match any |manages| declaration in protocol `ManagerForgot'
+
+include protocol ManagerForgot;
+
+// This protocol says ManagerForgot manages it,
+// but ManagerForgot does not manage it.
+
+protocol ForgottenManagee {
+ manager ManagerForgot;
+child:
+ async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/error/ForgottenManager.ipdl b/ipc/ipdl/test/ipdl/error/ForgottenManager.ipdl
new file mode 100644
index 0000000000..4335dc8dc7
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/ForgottenManager.ipdl
@@ -0,0 +1,11 @@
+//error: |manages| declaration in protocol `ForgottenManager' does not match any |manager| declaration in protocol `ManageeForgot'
+
+include protocol ManageeForgot;
+
+// ManageeForgot should have this protocol as its manager.
+
+protocol ForgottenManager {
+ manages ManageeForgot;
+child:
+ async ManageeForgot();
+};
diff --git a/ipc/ipdl/test/ipdl/error/InsideCpowToChild.ipdl b/ipc/ipdl/test/ipdl/error/InsideCpowToChild.ipdl
new file mode 100644
index 0000000000..a28ab2c6d7
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/InsideCpowToChild.ipdl
@@ -0,0 +1,9 @@
+// inside_cpow nested parent-to-child messages are verboten
+
+//error: inside_cpow nested parent-to-child messages are verboten (here, message `Msg' in protocol `InsideCpowToChild')
+//error: message `Msg' requires more powerful send semantics than its protocol `InsideCpowToChild' provides
+
+protocol InsideCpowToChild {
+child:
+ nested(inside_cpow) sync Msg();
+};
diff --git a/ipc/ipdl/test/ipdl/error/IntrAsyncManagee.ipdl b/ipc/ipdl/test/ipdl/error/IntrAsyncManagee.ipdl
new file mode 100644
index 0000000000..527c007ec4
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/IntrAsyncManagee.ipdl
@@ -0,0 +1,9 @@
+//error: protocol `IntrAsyncManagee' requires more powerful send semantics than its manager `IntrAsyncManager' provides
+
+include protocol IntrAsyncManager;
+
+intr protocol IntrAsyncManagee {
+ manager IntrAsyncManager;
+child:
+ async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/error/IntrAsyncManager.ipdl b/ipc/ipdl/test/ipdl/error/IntrAsyncManager.ipdl
new file mode 100644
index 0000000000..7bf413ef61
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/IntrAsyncManager.ipdl
@@ -0,0 +1,9 @@
+//error: protocol `IntrAsyncManagee' requires more powerful send semantics than its manager `IntrAsyncManager' provides
+
+include protocol IntrAsyncManagee;
+
+async protocol IntrAsyncManager {
+ manages IntrAsyncManagee;
+parent:
+ async IntrAsyncManagee();
+};
diff --git a/ipc/ipdl/test/ipdl/error/IntrSyncManagee.ipdl b/ipc/ipdl/test/ipdl/error/IntrSyncManagee.ipdl
new file mode 100644
index 0000000000..d0b1462e86
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/IntrSyncManagee.ipdl
@@ -0,0 +1,9 @@
+//error: protocol `IntrSyncManagee' requires more powerful send semantics than its manager `IntrSyncManager' provides
+
+include protocol IntrSyncManager;
+
+intr protocol IntrSyncManagee {
+ manager IntrSyncManager;
+child:
+ async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/error/IntrSyncManager.ipdl b/ipc/ipdl/test/ipdl/error/IntrSyncManager.ipdl
new file mode 100644
index 0000000000..80a82ab6d8
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/IntrSyncManager.ipdl
@@ -0,0 +1,9 @@
+//error: protocol `IntrSyncManagee' requires more powerful send semantics than its manager `IntrSyncManager' provides
+
+include protocol IntrSyncManagee;
+
+sync protocol IntrSyncManager {
+ manages IntrSyncManagee;
+parent:
+ async IntrSyncManagee();
+};
diff --git a/ipc/ipdl/test/ipdl/error/ManageeForgot.ipdl b/ipc/ipdl/test/ipdl/error/ManageeForgot.ipdl
new file mode 100644
index 0000000000..babe2f0658
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/ManageeForgot.ipdl
@@ -0,0 +1,11 @@
+//error: |manages| declaration in protocol `ForgottenManager' does not match any |manager| declaration in protocol `ManageeForgot'
+
+include protocol ForgottenManager;
+
+// See ForgottenManager. This includes ForgottenManager to ensure that
+// loading this file fails.
+
+protocol ManageeForgot {
+child:
+ async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/error/ManagerForgot.ipdl b/ipc/ipdl/test/ipdl/error/ManagerForgot.ipdl
new file mode 100644
index 0000000000..00c08ebe87
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/ManagerForgot.ipdl
@@ -0,0 +1,11 @@
+//error: |manager| declaration in protocol `ForgottenManagee' does not match any |manages| declaration in protocol `ManagerForgot'
+
+include protocol ForgottenManagee;
+
+// See ForgottenManagee.ipdl. This includes ForgottenManagee to
+// ensure that loading this file fails.
+
+protocol ManagerForgot {
+child:
+ async Msg();
+};
diff --git a/ipc/ipdl/test/ipdl/error/Nullable.ipdl b/ipc/ipdl/test/ipdl/error/Nullable.ipdl
new file mode 100644
index 0000000000..e1dd6fd55e
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/Nullable.ipdl
@@ -0,0 +1,6 @@
+//error: `nullable' qualifier for type `int' makes no sense
+
+protocol PNullable {
+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 0000000000..41c8b190b7
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/Nullable2.ipdl
@@ -0,0 +1,10 @@
+//error: `nullable' qualifier for type `int' makes no sense
+
+union Union {
+ nullable int;
+};
+
+protocol Nullable2 {
+child:
+ async Msg(Union i);
+};
diff --git a/ipc/ipdl/test/ipdl/error/PBadArrayBase.ipdl b/ipc/ipdl/test/ipdl/error/PBadArrayBase.ipdl
new file mode 100644
index 0000000000..4afdcb4c8e
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/PBadArrayBase.ipdl
@@ -0,0 +1,7 @@
+//error: argument typename `X' of message `Test' has not been declared
+
+protocol PBadArrayBase {
+child:
+ async Test(X[] x);
+ async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/error/PBadNestedManagee.ipdl b/ipc/ipdl/test/ipdl/error/PBadNestedManagee.ipdl
new file mode 100644
index 0000000000..43ee446bd7
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/PBadNestedManagee.ipdl
@@ -0,0 +1,9 @@
+//error: protocol `PBadNestedManagee' requires more powerful send semantics than its manager `PBadNestedManager' provides
+
+include protocol PBadNestedManager;
+
+nested(upto inside_sync) async protocol PBadNestedManagee {
+ manager PBadNestedManager;
+child:
+ async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/error/PBadNestedManager.ipdl b/ipc/ipdl/test/ipdl/error/PBadNestedManager.ipdl
new file mode 100644
index 0000000000..84a4fe491e
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/PBadNestedManager.ipdl
@@ -0,0 +1,9 @@
+//error: protocol `PBadNestedManagee' requires more powerful send semantics than its manager `PBadNestedManager' provides
+
+include protocol PBadNestedManagee;
+
+nested(upto not) async protocol PBadNestedManager {
+ manages PBadNestedManagee;
+parent:
+ async PBadNestedManagee();
+};
diff --git a/ipc/ipdl/test/ipdl/error/PBadUniquePtrBase.ipdl b/ipc/ipdl/test/ipdl/error/PBadUniquePtrBase.ipdl
new file mode 100644
index 0000000000..b93b20742d
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/PBadUniquePtrBase.ipdl
@@ -0,0 +1,7 @@
+//error: argument typename `UndeclaredType' of message `Test' has not been declared
+
+protocol PBadUniquePtrBase {
+child:
+ async Test(UniquePtr<UndeclaredType> x);
+ async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/error/PDouble.ipdl b/ipc/ipdl/test/ipdl/error/PDouble.ipdl
new file mode 100644
index 0000000000..e08ff1d063
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/PDouble.ipdl
@@ -0,0 +1,11 @@
+//error: Trying to load `PDouble' from a file when we'd already seen it in file
+
+// This will load extra/PDouble.ipdl because extra/ is earlier
+// in the list of include directories than the current working
+// directory. Loading the same protocol from two files is
+// obviously bad.
+include protocol PDouble;
+
+protocol PDouble {
+child: async Msg();
+};
diff --git a/ipc/ipdl/test/ipdl/error/PInconsistentMoveOnly.ipdl b/ipc/ipdl/test/ipdl/error/PInconsistentMoveOnly.ipdl
new file mode 100644
index 0000000000..0efd3c840c
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/PInconsistentMoveOnly.ipdl
@@ -0,0 +1,9 @@
+//error: inconsistent moveonly status of type `mozilla::ipc::SomeMoveonlyType`
+
+using moveonly class mozilla::ipc::SomeMoveonlyType from "SomeFile.h";
+using class mozilla::ipc::SomeMoveonlyType from "SomeFile.h";
+
+protocol PInconsistentMoveOnly {
+child:
+ async SomeMessage();
+};
diff --git a/ipc/ipdl/test/ipdl/error/PUniquePtrRecursive.ipdl b/ipc/ipdl/test/ipdl/error/PUniquePtrRecursive.ipdl
new file mode 100644
index 0000000000..83c9459ec6
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/PUniquePtrRecursive.ipdl
@@ -0,0 +1,5 @@
+//error: bad syntax near `UniquePtr'
+
+protocol PUniquePtrRecursive {
+child: async Msg( UniquePtr< UniquePtr<int> > aa);
+};
diff --git a/ipc/ipdl/test/ipdl/error/PUniquePtrSelfRecStruct.ipdl b/ipc/ipdl/test/ipdl/error/PUniquePtrSelfRecStruct.ipdl
new file mode 100644
index 0000000000..c9a54b22b2
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/PUniquePtrSelfRecStruct.ipdl
@@ -0,0 +1,9 @@
+//error: struct `X' is only partially defined
+
+struct X {
+ UniquePtr<X> x;
+};
+
+protocol PUniquePtrSelfRecStruct {
+child: async Msg(UniquePtr<X> aa);
+};
diff --git a/ipc/ipdl/test/ipdl/error/PUniquePtrSelfRecUnion.ipdl b/ipc/ipdl/test/ipdl/error/PUniquePtrSelfRecUnion.ipdl
new file mode 100644
index 0000000000..5dba38263f
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/PUniquePtrSelfRecUnion.ipdl
@@ -0,0 +1,7 @@
+//error: union `X' is only partially defined
+
+union X { UniquePtr<X>; };
+
+protocol PUniquePtrSelfRecUnion {
+child: async Msg(UniquePtr<X> aa);
+};
diff --git a/ipc/ipdl/test/ipdl/error/PasyncMessageListed.ipdl b/ipc/ipdl/test/ipdl/error/PasyncMessageListed.ipdl
new file mode 100644
index 0000000000..6a3cdad0ee
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/PasyncMessageListed.ipdl
@@ -0,0 +1,6 @@
+//error: IPC message PasyncMessageListed::Msg is async, can be delisted
+
+protocol PasyncMessageListed {
+parent:
+ async Msg();
+};
diff --git a/ipc/ipdl/test/ipdl/error/SyncAsyncManagee.ipdl b/ipc/ipdl/test/ipdl/error/SyncAsyncManagee.ipdl
new file mode 100644
index 0000000000..166881ae1e
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/SyncAsyncManagee.ipdl
@@ -0,0 +1,9 @@
+//error: protocol `SyncAsyncManagee' requires more powerful send semantics than its manager `SyncAsyncManager' provides
+
+include protocol SyncAsyncManager;
+
+sync protocol SyncAsyncManagee {
+ manager SyncAsyncManager;
+child:
+ async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/error/SyncAsyncManager.ipdl b/ipc/ipdl/test/ipdl/error/SyncAsyncManager.ipdl
new file mode 100644
index 0000000000..9dbc7a6318
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/SyncAsyncManager.ipdl
@@ -0,0 +1,9 @@
+//error: protocol `SyncAsyncManagee' requires more powerful send semantics than its manager `SyncAsyncManager' provides
+
+include protocol SyncAsyncManagee;
+
+async protocol SyncAsyncManager {
+ manages SyncAsyncManagee;
+parent:
+ async SyncAsyncManagee();
+};
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 0000000000..f5027e2299
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/array_Recursive.ipdl
@@ -0,0 +1,5 @@
+//error: bad syntax near `['
+
+protocol array_Recursive {
+child: async 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 0000000000..7a45d8066c
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/badProtocolInclude.ipdl
@@ -0,0 +1,9 @@
+//error: can't locate include file `IDONTEXIST.ipdl'
+
+include protocol IDONTEXIST;
+
+// error: nonexistent protocol ^^^
+
+protocol badProtocolInclude {
+child: Msg();
+};
diff --git a/ipc/ipdl/test/ipdl/error/compressCtor.ipdl b/ipc/ipdl/test/ipdl/error/compressCtor.ipdl
new file mode 100644
index 0000000000..cc8d7e3d7c
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/compressCtor.ipdl
@@ -0,0 +1,11 @@
+//error: destructor messages can't use compression (here, in protocol `compressCtorManagee')
+//error: constructor messages can't use compression (here, in protocol `compressCtor')
+
+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 0000000000..23586fe104
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/compressCtorManagee.ipdl
@@ -0,0 +1,11 @@
+//error: constructor messages can't use compression (here, in protocol `compressCtor')
+//error: destructor messages can't use compression (here, in protocol `compressCtorManagee')
+
+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 0000000000..b223dab783
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/conflictProtocolMsg.ipdl
@@ -0,0 +1,9 @@
+//error: ctor for protocol `conflictProtocolMsg', which is not managed by protocol `conflictProtocolMsg'
+
+protocol conflictProtocolMsg {
+
+ // it's an error to re-use the protocol name as a message ID; these
+ // are reserved
+child: async 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 0000000000..dd27586d8b
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/cyclecheck_Child.ipdl
@@ -0,0 +1,15 @@
+//error: cycle(s) detected in manager/manages hierarchy: `cyclecheck_Parent -> cyclecheck_Child -> cyclecheck_Grandchild -> cyclecheck_Parent'
+//error: |manages| declaration in protocol `cyclecheck_Grandchild' does not match any |manager| declaration in protocol `cyclecheck_Parent'
+
+include protocol cyclecheck_Parent;
+include protocol cyclecheck_Grandchild;
+
+protocol cyclecheck_Child {
+ manager cyclecheck_Parent;
+ manages cyclecheck_Grandchild;
+
+child:
+ async 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 0000000000..9152a56a16
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/cyclecheck_Grandchild.ipdl
@@ -0,0 +1,15 @@
+//error: cycle(s) detected in manager/manages hierarchy: `cyclecheck_Parent -> cyclecheck_Child -> cyclecheck_Grandchild -> cyclecheck_Parent'
+//error: |manages| declaration in protocol `cyclecheck_Grandchild' does not match any |manager| declaration in protocol `cyclecheck_Parent'
+
+include protocol cyclecheck_Child;
+include protocol cyclecheck_Parent;
+
+protocol cyclecheck_Grandchild {
+ manager cyclecheck_Child;
+ manages cyclecheck_Parent;
+
+child:
+ async 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 0000000000..f2eb060641
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/cyclecheck_Parent.ipdl
@@ -0,0 +1,12 @@
+//error: cycle(s) detected in manager/manages hierarchy: `cyclecheck_Parent -> cyclecheck_Child -> cyclecheck_Grandchild -> cyclecheck_Parent'
+
+include protocol cyclecheck_Child;
+
+protocol cyclecheck_Parent {
+ manages cyclecheck_Child;
+
+child:
+ async 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 0000000000..51833489be
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/dtorReserved.ipdl
@@ -0,0 +1,10 @@
+//error: lexically invalid characters `~SomeMsg();
+
+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 0000000000..c6249c280b
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/empty.ipdl
@@ -0,0 +1 @@
+//error: bad syntax near `???' \ No newline at end of file
diff --git a/ipc/ipdl/test/ipdl/error/extra/PDouble.ipdl b/ipc/ipdl/test/ipdl/error/extra/PDouble.ipdl
new file mode 100644
index 0000000000..2efae8fff4
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/extra/PDouble.ipdl
@@ -0,0 +1,5 @@
+// This is a valid file.
+
+protocol PDouble {
+child: async Msg();
+};
diff --git a/ipc/ipdl/test/ipdl/error/inconsistentRC.ipdl b/ipc/ipdl/test/ipdl/error/inconsistentRC.ipdl
new file mode 100644
index 0000000000..8ce0618a83
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/inconsistentRC.ipdl
@@ -0,0 +1,9 @@
+//error: inconsistent refcounted status of type `mozilla::ipc::SomeRefcountedType`
+
+using refcounted class mozilla::ipc::SomeRefcountedType from "SomeFile.h";
+using class mozilla::ipc::SomeRefcountedType from "SomeFile.h";
+
+protocol inconsistentRC {
+child:
+ async SomeMessage();
+};
diff --git a/ipc/ipdl/test/ipdl/error/intrMessageCompress.ipdl b/ipc/ipdl/test/ipdl/error/intrMessageCompress.ipdl
new file mode 100644
index 0000000000..f76a0620df
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/intrMessageCompress.ipdl
@@ -0,0 +1,9 @@
+//error: message `foo' in protocol `intrMessageCompress' requests compression but is not async
+//error: message `bar' in protocol `intrMessageCompress' requests compression but is not async
+
+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 0000000000..a00629cbca
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/lex1.ipdl
@@ -0,0 +1,2 @@
+//error: bad syntax near `slkdjfl'
+slkdjfl*&^*
diff --git a/ipc/ipdl/test/ipdl/error/manageSelfToplevel.ipdl b/ipc/ipdl/test/ipdl/error/manageSelfToplevel.ipdl
new file mode 100644
index 0000000000..4540c2c2f6
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/manageSelfToplevel.ipdl
@@ -0,0 +1,10 @@
+//error: top-level protocol `manageSelfToplevel' cannot manage itself
+
+protocol manageSelfToplevel {
+ manager manageSelfToplevel;
+ manages manageSelfToplevel;
+
+child:
+ async manageSelfToplevel();
+ async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/error/managedNoDtor.ipdl b/ipc/ipdl/test/ipdl/error/managedNoDtor.ipdl
new file mode 100644
index 0000000000..41bbd76689
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/managedNoDtor.ipdl
@@ -0,0 +1,7 @@
+//error: destructor declaration `__delete__(...)' required for managed protocol `managedNoDtor'
+include protocol managerNoDtor;
+
+protocol managedNoDtor {
+ manager managerNoDtor;
+ // empty
+};
diff --git a/ipc/ipdl/test/ipdl/error/managerNoDtor.ipdl b/ipc/ipdl/test/ipdl/error/managerNoDtor.ipdl
new file mode 100644
index 0000000000..0417cc5ace
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/managerNoDtor.ipdl
@@ -0,0 +1,11 @@
+//error: destructor declaration `__delete__(...)' required for managed protocol `managedNoDtor'
+
+include protocol managedNoDtor;
+
+protocol managerNoDtor {
+ manages managedNoDtor;
+
+parent:
+ async managedNoDtor();
+ // error: no ctor defined
+};
diff --git a/ipc/ipdl/test/ipdl/error/maybe_Recursive.ipdl b/ipc/ipdl/test/ipdl/error/maybe_Recursive.ipdl
new file mode 100644
index 0000000000..b039924199
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/maybe_Recursive.ipdl
@@ -0,0 +1,5 @@
+//error: bad syntax near `?'
+
+protocol maybe_Recursive {
+child: async Msg(int?? aa);
+};
diff --git a/ipc/ipdl/test/ipdl/error/maybe_SelfRecStruct.ipdl b/ipc/ipdl/test/ipdl/error/maybe_SelfRecStruct.ipdl
new file mode 100644
index 0000000000..a13cdc3647
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/maybe_SelfRecStruct.ipdl
@@ -0,0 +1,9 @@
+//error: struct `X' is only partially defined
+
+struct X {
+ X? x;
+};
+
+protocol maybe_SelfRecStruct {
+child: async Msg(X? aa);
+};
diff --git a/ipc/ipdl/test/ipdl/error/maybe_SelfRecUnion.ipdl b/ipc/ipdl/test/ipdl/error/maybe_SelfRecUnion.ipdl
new file mode 100644
index 0000000000..3cd5cde22a
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/maybe_SelfRecUnion.ipdl
@@ -0,0 +1,7 @@
+//error: union `X' is only partially defined
+
+union X { X?; };
+
+protocol maybe_SelfRecUnion {
+child: async Msg(X? aa);
+};
diff --git a/ipc/ipdl/test/ipdl/error/messageNoDirection.ipdl b/ipc/ipdl/test/ipdl/error/messageNoDirection.ipdl
new file mode 100644
index 0000000000..199bb089c3
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/messageNoDirection.ipdl
@@ -0,0 +1,5 @@
+//error: missing message direction
+
+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 0000000000..805a9f905e
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/multimanDupMgrs.ipdl
@@ -0,0 +1,10 @@
+//error: manager `multimanDupMgrsMgr' appears multiple times
+
+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 0000000000..ff96fe91a3
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/multimanDupMgrsMgr.ipdl
@@ -0,0 +1,11 @@
+//error: manager `multimanDupMgrsMgr' appears multiple times
+
+include protocol multimanDupMgrs;
+
+protocol multimanDupMgrsMgr {
+ manages multimanDupMgrs;
+
+child:
+ async 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 0000000000..24dafb8163
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/multimanNonexistentMgrs.ipdl
@@ -0,0 +1,10 @@
+//error: protocol `Starsky' referenced as |manager| of `multimanNonexistentMgrs' has not been declared
+//error: protocol `Hutch' referenced as |manager| of `multimanNonexistentMgrs' has not been declared
+
+protocol multimanNonexistentMgrs {
+ 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 0000000000..9df16543bf
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/mutualRecStruct.ipdl
@@ -0,0 +1,24 @@
+//error: struct `X' is only partially defined
+//error: struct `Y' is only partially defined
+//error: struct `Z' is only partially defined
+
+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 0000000000..cd193f9f29
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/mutualRecStructUnion.ipdl
@@ -0,0 +1,24 @@
+//error: struct `X' is only partially defined
+//error: union `Y' is only partially defined
+//error: struct `Z' is only partially defined
+
+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 0000000000..bc36cead42
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/noEmptyToplevel.ipdl
@@ -0,0 +1,7 @@
+//error: top-level protocol `noEmptyToplevel' cannot be empty
+
+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 0000000000..8351adf4cc
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/noProtocolInHeader.ipdlh
@@ -0,0 +1,6 @@
+//error: can't define a protocol in a header. Do it in a protocol spec instead.
+
+protocol noProtocolInHeader {
+child:
+ async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/error/oldIncludeSyntax.ipdl b/ipc/ipdl/test/ipdl/error/oldIncludeSyntax.ipdl
new file mode 100644
index 0000000000..cb17c60a40
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/oldIncludeSyntax.ipdl
@@ -0,0 +1,7 @@
+//error: bad syntax near `Foo.ipdl'
+
+include protocol "Foo.ipdl";
+
+protocol oldIncludeSyntax {
+child: async __delete__(X x);
+};
diff --git a/ipc/ipdl/test/ipdl/error/parser.ipdl b/ipc/ipdl/test/ipdl/error/parser.ipdl
new file mode 100644
index 0000000000..8bfd63546a
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/parser.ipdl
@@ -0,0 +1,3 @@
+//error: bad syntax near `???'
+
+protocol parser {
diff --git a/ipc/ipdl/test/ipdl/error/redeclMessage.ipdl b/ipc/ipdl/test/ipdl/error/redeclMessage.ipdl
new file mode 100644
index 0000000000..1247aa41cf
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/redeclMessage.ipdl
@@ -0,0 +1,12 @@
+//error: message name `Msg' already declared as `MessageType'
+//error: redeclaration of symbol `Msg', first declared at
+
+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 0000000000..ccdcbbe0d1
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/redeclParamReturn.ipdl
@@ -0,0 +1,9 @@
+//error: redeclaration of symbol `f', first declared at
+
+sync protocol redeclParamReturn {
+
+ // it's an error to name a parameter with the same id as a return
+
+parent: async Msg(int f) returns (bool f);
+
+};
diff --git a/ipc/ipdl/test/ipdl/error/redeclScope.ipdlh b/ipc/ipdl/test/ipdl/error/redeclScope.ipdlh
new file mode 100644
index 0000000000..a64439b599
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/redeclScope.ipdlh
@@ -0,0 +1,12 @@
+//error: redeclaration of symbol `Foo', first declared at
+
+struct Foo {
+ bool b;
+};
+
+struct Bar {
+ // This should produce an error saying that Foo is a redeclaration,
+ // even though the initial declaration was in a different frame of
+ // the symbol table.
+ bool Foo;
+};
diff --git a/ipc/ipdl/test/ipdl/error/shmem.ipdl b/ipc/ipdl/test/ipdl/error/shmem.ipdl
new file mode 100644
index 0000000000..b2dda1adf7
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/shmem.ipdl
@@ -0,0 +1,8 @@
+//error: redeclaration of symbol `Shmem', first declared at
+//error: redeclaration of symbol `mozilla::ipc::Shmem', first declared at
+
+using class mozilla::ipc::Shmem from "mozilla/ipc/Shmem.h"; // redeclaration
+
+protocol shmem {
+child: async Msg(Shmem s);
+};
diff --git a/ipc/ipdl/test/ipdl/error/structRedecl.ipdl b/ipc/ipdl/test/ipdl/error/structRedecl.ipdl
new file mode 100644
index 0000000000..03a5557877
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/structRedecl.ipdl
@@ -0,0 +1,10 @@
+//error: redeclaration of symbol `a', first declared at
+
+struct Redecl {
+ int a;
+ double a;
+};
+
+protocol structRedecl {
+child: async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/error/structUnknownField.ipdl b/ipc/ipdl/test/ipdl/error/structUnknownField.ipdl
new file mode 100644
index 0000000000..ac92edee23
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/structUnknownField.ipdl
@@ -0,0 +1,9 @@
+//error: field `i' of struct `S' has unknown type `Foobers'
+
+struct S {
+ Foobers i;
+};
+
+protocol structUnknownField {
+child: async __delete__(S s);
+};
diff --git a/ipc/ipdl/test/ipdl/error/syncMessageCompress.ipdl b/ipc/ipdl/test/ipdl/error/syncMessageCompress.ipdl
new file mode 100644
index 0000000000..72c9ef40ee
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/syncMessageCompress.ipdl
@@ -0,0 +1,6 @@
+//error: message `foo' in protocol `syncMessageCompress' requests compression but is not async
+
+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 0000000000..bd6c8bc5b1
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/syncParentToChild.ipdl
@@ -0,0 +1,8 @@
+//error: sync parent-to-child messages are verboten (here, message `Msg' in protocol `syncParentToChild')
+
+intr protocol syncParentToChild {
+
+ // can't declare sync parent-to-child messages
+child: sync Msg();
+
+};
diff --git a/ipc/ipdl/test/ipdl/error/tooWeakIntrAsync.ipdl b/ipc/ipdl/test/ipdl/error/tooWeakIntrAsync.ipdl
new file mode 100644
index 0000000000..b85b5af600
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/tooWeakIntrAsync.ipdl
@@ -0,0 +1,9 @@
+//error: message `Msg' requires more powerful send semantics than its protocol `tooWeakIntrAsync' provides
+
+protocol tooWeakIntrAsync {
+
+ // it's an error to declare an async protocol with an intr message
+
+parent: intr Msg();
+
+};
diff --git a/ipc/ipdl/test/ipdl/error/tooWeakIntrSync.ipdl b/ipc/ipdl/test/ipdl/error/tooWeakIntrSync.ipdl
new file mode 100644
index 0000000000..9137586c90
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/tooWeakIntrSync.ipdl
@@ -0,0 +1,8 @@
+//error: message `Msg' requires more powerful send semantics than its protocol `tooWeakIntrSync' provides
+
+sync protocol tooWeakIntrSync {
+
+ // it's an error to declare a sync protocol with an interrupt message
+parent:
+ intr Msg();
+};
diff --git a/ipc/ipdl/test/ipdl/error/tooWeakSyncAsync.ipdl b/ipc/ipdl/test/ipdl/error/tooWeakSyncAsync.ipdl
new file mode 100644
index 0000000000..49e4d3b75b
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/tooWeakSyncAsync.ipdl
@@ -0,0 +1,9 @@
+//error: message `Msg' requires more powerful send semantics than its protocol `tooWeakSyncAsync' provides
+
+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/twoprotocols.ipdl b/ipc/ipdl/test/ipdl/error/twoprotocols.ipdl
new file mode 100644
index 0000000000..7e2a51a61a
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/twoprotocols.ipdl
@@ -0,0 +1,11 @@
+// it's an error to define two protocols in the same file
+
+//error: only one protocol definition per file
+
+protocol p1 {
+child: async Msg();
+};
+
+protocol p2 {
+child: async Msg();
+};
diff --git a/ipc/ipdl/test/ipdl/error/undeclParamType.ipdl b/ipc/ipdl/test/ipdl/error/undeclParamType.ipdl
new file mode 100644
index 0000000000..3f93e747e1
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/undeclParamType.ipdl
@@ -0,0 +1,7 @@
+//error: argument typename `FARGLEGARGLE' of message `Msg' has not been declared
+
+protocol undeclParamType {
+
+child: async 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 0000000000..cddf57267f
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/undeclProtocol.ipdl
@@ -0,0 +1,8 @@
+//error: protocol `undeclared', managed by `undeclProtocol', has not been declared
+
+protocol undeclProtocol {
+ manages undeclared;
+
+child:
+ async undeclared();
+};
diff --git a/ipc/ipdl/test/ipdl/error/undeclReturnType.ipdl b/ipc/ipdl/test/ipdl/error/undeclReturnType.ipdl
new file mode 100644
index 0000000000..2f353a1957
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/undeclReturnType.ipdl
@@ -0,0 +1,7 @@
+//error: argument typename `FARGLEGARGLE' of message `Msg' has not been declared
+
+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 0000000000..5ede1d8299
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/undefMutualRecStruct.ipdl
@@ -0,0 +1,11 @@
+//error: struct `X' is only partially defined
+//error: struct `Y' is only partially defined
+//error: struct `Z' is only partially defined
+
+struct X { Y y; };
+struct Y { Z z; };
+struct Z { X x; };
+
+protocol undefMutualRecStruct {
+child: async __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 0000000000..0e88c126e0
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/undefMutualRecStructUnion.ipdl
@@ -0,0 +1,11 @@
+//error: struct `X' is only partially defined
+//error: union `Y' is only partially defined
+//error: struct `Z' is only partially defined
+
+struct X { Y y; };
+union Y { Z; };
+struct Z { X x; };
+
+protocol undefMutualRecStructUnion {
+child: async __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 0000000000..3fc99bbb95
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/undefMutualRecUnion.ipdl
@@ -0,0 +1,11 @@
+//error: union `X' is only partially defined
+//error: union `Y' is only partially defined
+//error: union `Z' is only partially defined
+
+union X { Y; };
+union Y { Z; };
+union Z { X; };
+
+protocol undefMutualRecUnion {
+child: async __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 0000000000..15fc45abcd
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/undefSelfRecStruct.ipdl
@@ -0,0 +1,7 @@
+//error: struct `X' is only partially defined
+
+struct X { X x; };
+
+protocol undefSelfRecStruct {
+child: async __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 0000000000..951f916c57
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/undefSelfRecUnion.ipdl
@@ -0,0 +1,7 @@
+//error: union `X' is only partially defined
+
+union X { X; };
+
+protocol undefSelfRecUnion {
+child: async __delete__(X x);
+};
diff --git a/ipc/ipdl/test/ipdl/error/unknownIntrMessage.ipdl b/ipc/ipdl/test/ipdl/error/unknownIntrMessage.ipdl
new file mode 100644
index 0000000000..6d668f01ea
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/unknownIntrMessage.ipdl
@@ -0,0 +1,6 @@
+//error: Unknown sync IPC message unknownIntrMessage::Msg
+
+intr protocol unknownIntrMessage {
+parent:
+ intr Msg();
+};
diff --git a/ipc/ipdl/test/ipdl/error/unknownSyncMessage.ipdl b/ipc/ipdl/test/ipdl/error/unknownSyncMessage.ipdl
new file mode 100644
index 0000000000..be2cfad0cc
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/unknownSyncMessage.ipdl
@@ -0,0 +1,6 @@
+//error: Unknown sync IPC message unknownSyncMessage::Msg
+
+sync protocol unknownSyncMessage {
+parent:
+ sync Msg();
+};
diff --git a/ipc/ipdl/test/ipdl/message-metadata.ini b/ipc/ipdl/test/ipdl/message-metadata.ini
new file mode 100644
index 0000000000..02ec1a992f
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/message-metadata.ini
@@ -0,0 +1,2 @@
+# The IPDL parser expects this file to exist, even though we don't
+# need it for parsing tests, so here's an empty file. \ No newline at end of file
diff --git a/ipc/ipdl/test/ipdl/moz.build b/ipc/ipdl/test/ipdl/moz.build
new file mode 100644
index 0000000000..568f361a54
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/moz.build
@@ -0,0 +1,5 @@
+# -*- 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/MutRecHeader1.ipdlh b/ipc/ipdl/test/ipdl/ok/MutRecHeader1.ipdlh
new file mode 100644
index 0000000000..56135b2197
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/MutRecHeader1.ipdlh
@@ -0,0 +1,34 @@
+include MutRecHeader2;
+
+/* MutRecHeader1 (H1) includes MutRecHeader2 (H2), and uses a struct from H2.
+ H2 includes MutRecHeader3 (H3).
+ H3 includes H1.
+
+When type checking H1, GatherDecls::visitInclude will recursively
+cause us to first check H2, which in turn will cause us to first check
+H3.
+
+H3 only includes H1, so when we check it, we do not have any
+declarations from H2 in the context. There used to be code in
+GatherDecls::visitTranslationUnit that would, as part of the "second
+pass", check the validity of all included structures. This would check
+Struct1, and fail, because Struct2 is not declared.
+
+Fundamentally, it doesn't make sense to check anything declared in an
+included file in the context of the file that included it.
+
+Note that this error did not show up when either H2 or H3 was
+checked. This is because in those cases we are not in the middle of
+checking H1 when we check H3, so we end up fully checking H1 before we
+get to the end of checking H3. This means the "visited" tag gets put
+on Struct1 before we get to the end of that troublesome block of code
+in visitTranslationUnit, and visitStructDecl doesn't do anything if
+that tag is set, so we don't end up actually checking H1 in the
+context of H3.
+
+*/
+
+struct Struct1
+{
+ Struct2 b;
+};
diff --git a/ipc/ipdl/test/ipdl/ok/MutRecHeader2.ipdlh b/ipc/ipdl/test/ipdl/ok/MutRecHeader2.ipdlh
new file mode 100644
index 0000000000..142979dc45
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/MutRecHeader2.ipdlh
@@ -0,0 +1,8 @@
+include MutRecHeader3;
+
+// See MutRecHeader1.ipdlh for explanation.
+
+struct Struct2
+{
+ bool b;
+};
diff --git a/ipc/ipdl/test/ipdl/ok/MutRecHeader3.ipdlh b/ipc/ipdl/test/ipdl/ok/MutRecHeader3.ipdlh
new file mode 100644
index 0000000000..2e303ae444
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/MutRecHeader3.ipdlh
@@ -0,0 +1,8 @@
+include MutRecHeader1;
+
+// See MutRecHeader1.ipdlh for explanation.
+
+struct Struct3
+{
+ bool b;
+};
diff --git a/ipc/ipdl/test/ipdl/ok/PAsyncReturn.ipdl b/ipc/ipdl/test/ipdl/ok/PAsyncReturn.ipdl
new file mode 100644
index 0000000000..89c2a7c647
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PAsyncReturn.ipdl
@@ -0,0 +1,8 @@
+// Async messages are not allowed to return values.
+
+//error: asynchronous message `Msg' declares return values
+
+protocol PAsyncReturn {
+child:
+ async Msg() returns(int32_t aNumber);
+};
diff --git a/ipc/ipdl/test/ipdl/ok/PDelete.ipdl b/ipc/ipdl/test/ipdl/ok/PDelete.ipdl
new file mode 100644
index 0000000000..f2f85fd7fa
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PDelete.ipdl
@@ -0,0 +1,8 @@
+include protocol PDeleteSub;
+
+sync protocol PDelete {
+ manages PDeleteSub;
+
+child:
+ async PDeleteSub();
+};
diff --git a/ipc/ipdl/test/ipdl/ok/PDeleteSub.ipdl b/ipc/ipdl/test/ipdl/ok/PDeleteSub.ipdl
new file mode 100644
index 0000000000..12b4c677eb
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PDeleteSub.ipdl
@@ -0,0 +1,10 @@
+include protocol PDelete;
+
+sync protocol PDeleteSub {
+ manager PDelete;
+
+parent:
+ sync __delete__(int x) returns (double d);
+
+};
+
diff --git a/ipc/ipdl/test/ipdl/ok/PEndpointDecl.ipdl b/ipc/ipdl/test/ipdl/ok/PEndpointDecl.ipdl
new file mode 100644
index 0000000000..25f58f3f63
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PEndpointDecl.ipdl
@@ -0,0 +1,18 @@
+
+// Basic test that Endpoint types are declared for protocols, within
+// that protocol.
+
+struct Whatever {
+ Endpoint<PEndpointDeclParent> par;
+ Endpoint<PEndpointDeclChild> chi;
+};
+
+namespace mozilla {
+
+protocol PEndpointDecl {
+ child:
+ async Message(Endpoint<PEndpointDeclParent> aEndpointParent,
+ Endpoint<PEndpointDeclChild> aEndpointChild);
+};
+
+}
diff --git a/ipc/ipdl/test/ipdl/ok/PEndpointUse.ipdl b/ipc/ipdl/test/ipdl/ok/PEndpointUse.ipdl
new file mode 100644
index 0000000000..0c776b359c
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PEndpointUse.ipdl
@@ -0,0 +1,9 @@
+include protocol PEndpointDecl;
+
+// Basic test that Endpoint types are declared for included protocols.
+
+protocol PEndpointUse {
+ child:
+ async Message(Endpoint<PEndpointDeclParent> aEndpointParent,
+ Endpoint<PEndpointDeclChild> aEndpointChild);
+};
diff --git a/ipc/ipdl/test/ipdl/ok/PManagedEndpointDecl.ipdl b/ipc/ipdl/test/ipdl/ok/PManagedEndpointDecl.ipdl
new file mode 100644
index 0000000000..13eb10fcc4
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PManagedEndpointDecl.ipdl
@@ -0,0 +1,23 @@
+include protocol PManagedEndpointManager;
+
+// Basic test that ManagedEndpoint types are declared for protocols, within
+// that protocol.
+
+struct ManagedWhatever {
+ ManagedEndpoint<PManagedEndpointDeclParent> par;
+ ManagedEndpoint<PManagedEndpointDeclChild> chi;
+};
+
+namespace mozilla {
+
+protocol PManagedEndpointDecl {
+ manager PManagedEndpointManager;
+
+child:
+ async Message(ManagedEndpoint<PManagedEndpointDeclParent> aEndpointParent,
+ ManagedEndpoint<PManagedEndpointDeclChild> aEndpointChild);
+
+ async __delete__();
+};
+
+}
diff --git a/ipc/ipdl/test/ipdl/ok/PManagedEndpointManager.ipdl b/ipc/ipdl/test/ipdl/ok/PManagedEndpointManager.ipdl
new file mode 100644
index 0000000000..4c95b7880c
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PManagedEndpointManager.ipdl
@@ -0,0 +1,14 @@
+include protocol PManagedEndpointDecl;
+
+namespace mozilla {
+
+protocol PManagedEndpointManager {
+ manages PManagedEndpointDecl;
+
+child:
+ async Message(ManagedEndpoint<PManagedEndpointDeclParent> aEndpointParent,
+ ManagedEndpoint<PManagedEndpointDeclChild> aEndpointChild);
+};
+
+}
+
diff --git a/ipc/ipdl/test/ipdl/ok/PMessageTainted.ipdl b/ipc/ipdl/test/ipdl/ok/PMessageTainted.ipdl
new file mode 100644
index 0000000000..d6791fd693
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PMessageTainted.ipdl
@@ -0,0 +1,4 @@
+intr protocol PMessageTainted {
+child:
+ async foo() tainted;
+};
diff --git a/ipc/ipdl/test/ipdl/ok/PNullable.ipdl b/ipc/ipdl/test/ipdl/ok/PNullable.ipdl
new file mode 100644
index 0000000000..bd83c0c436
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PNullable.ipdl
@@ -0,0 +1,11 @@
+union Union {
+ nullable PNullable;
+ nullable PNullable[];
+};
+
+protocol PNullable {
+child:
+ async Msg(nullable PNullable n);
+ async Msg2(nullable PNullable[] N);
+ async Msg3(Union u);
+};
diff --git a/ipc/ipdl/test/ipdl/ok/PRefcounted.ipdl b/ipc/ipdl/test/ipdl/ok/PRefcounted.ipdl
new file mode 100644
index 0000000000..678f2ccf86
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PRefcounted.ipdl
@@ -0,0 +1,4 @@
+async refcounted protocol PRefcounted {
+child:
+ async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/ok/PStruct.ipdl b/ipc/ipdl/test/ipdl/ok/PStruct.ipdl
new file mode 100644
index 0000000000..c670a83b0b
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PStruct.ipdl
@@ -0,0 +1,10 @@
+struct S {
+ int i;
+ double d;
+};
+
+sync protocol PStruct {
+parent:
+ sync test(S s) returns (S ss);
+ async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/ok/PStructComparable.ipdl b/ipc/ipdl/test/ipdl/ok/PStructComparable.ipdl
new file mode 100644
index 0000000000..dfc1a2bf67
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PStructComparable.ipdl
@@ -0,0 +1,10 @@
+comparable struct S {
+ int i;
+ double d;
+};
+
+sync protocol PStructComparable {
+parent:
+ sync test(S s) returns (S ss);
+ async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/ok/PSyncSyncManagee.ipdl b/ipc/ipdl/test/ipdl/ok/PSyncSyncManagee.ipdl
new file mode 100644
index 0000000000..458f0c83c0
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PSyncSyncManagee.ipdl
@@ -0,0 +1,7 @@
+include protocol PSyncSyncManager;
+
+sync protocol PSyncSyncManagee {
+ manager PSyncSyncManager;
+child:
+ async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/ok/PSyncSyncManager.ipdl b/ipc/ipdl/test/ipdl/ok/PSyncSyncManager.ipdl
new file mode 100644
index 0000000000..40ed425a36
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PSyncSyncManager.ipdl
@@ -0,0 +1,11 @@
+include protocol PSyncSyncManagee;
+
+/* The main reason for this test is that it would have caught a bug
+ * in the Rust IPDL parser that was treating "sync" like "async" in the
+ * nested case.
+ */
+nested(upto not) sync protocol PSyncSyncManager {
+ manages PSyncSyncManagee;
+parent:
+ async PSyncSyncManagee();
+};
diff --git a/ipc/ipdl/test/ipdl/ok/PUniquePtrBasic.ipdl b/ipc/ipdl/test/ipdl/ok/PUniquePtrBasic.ipdl
new file mode 100644
index 0000000000..caffd0dbbc
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PUniquePtrBasic.ipdl
@@ -0,0 +1,4 @@
+protocol PUniquePtrBasic {
+child:
+ async Msg(UniquePtr<int> maybe);
+};
diff --git a/ipc/ipdl/test/ipdl/ok/PUniquePtrOfActors.ipdl b/ipc/ipdl/test/ipdl/ok/PUniquePtrOfActors.ipdl
new file mode 100644
index 0000000000..ce67f23c68
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PUniquePtrOfActors.ipdl
@@ -0,0 +1,10 @@
+include protocol PUniquePtrOfActorsSub;
+
+protocol PUniquePtrOfActors {
+ manages PUniquePtrOfActorsSub;
+
+child:
+ async Msg(UniquePtr<PUniquePtrOfActorsSub> p);
+
+ async PUniquePtrOfActorsSub();
+};
diff --git a/ipc/ipdl/test/ipdl/ok/PUniquePtrOfActorsSub.ipdl b/ipc/ipdl/test/ipdl/ok/PUniquePtrOfActorsSub.ipdl
new file mode 100644
index 0000000000..a60c8e475f
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PUniquePtrOfActorsSub.ipdl
@@ -0,0 +1,7 @@
+include protocol PUniquePtrOfActors;
+
+protocol PUniquePtrOfActorsSub {
+ manager PUniquePtrOfActors;
+
+child: async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/ok/PUniquePtrRecUnion.ipdl b/ipc/ipdl/test/ipdl/ok/PUniquePtrRecUnion.ipdl
new file mode 100644
index 0000000000..4a0b9e7299
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PUniquePtrRecUnion.ipdl
@@ -0,0 +1,21 @@
+union X {
+ int;
+ Y[];
+ UniquePtr<Y>;
+};
+
+union Y {
+ X;
+ Z;
+};
+
+union Z {
+ double;
+ X;
+};
+
+protocol PUniquePtrRecUnion {
+child:
+ async Test(X x, Y y, Z z);
+ async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/ok/PUniquePtrUnion.ipdl b/ipc/ipdl/test/ipdl/ok/PUniquePtrUnion.ipdl
new file mode 100644
index 0000000000..d756339980
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PUniquePtrUnion.ipdl
@@ -0,0 +1,10 @@
+union UniquePtrUnion {
+ int[];
+ int;
+ double;
+};
+
+sync protocol PUniquePtrUnion {
+parent:
+ async Msg(UniquePtrUnion u, UniquePtr<UniquePtrUnion> au) returns (UniquePtrUnion r);
+};
diff --git a/ipc/ipdl/test/ipdl/ok/Pactorparam.ipdl b/ipc/ipdl/test/ipdl/ok/Pactorparam.ipdl
new file mode 100644
index 0000000000..0cba299cf5
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/Pactorparam.ipdl
@@ -0,0 +1,5 @@
+protocol Pactorparam {
+
+child: async Msg(Pactorparam p);
+
+};
diff --git a/ipc/ipdl/test/ipdl/ok/Pactorreturn.ipdl b/ipc/ipdl/test/ipdl/ok/Pactorreturn.ipdl
new file mode 100644
index 0000000000..99d0d36225
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/Pactorreturn.ipdl
@@ -0,0 +1,6 @@
+sync protocol Pactorreturn {
+
+parent:
+ sync Msg(Pactorreturn p) returns (Pactorreturn r);
+
+};
diff --git a/ipc/ipdl/test/ipdl/ok/Parray_Basic.ipdl b/ipc/ipdl/test/ipdl/ok/Parray_Basic.ipdl
new file mode 100644
index 0000000000..811c01f324
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/Parray_Basic.ipdl
@@ -0,0 +1,4 @@
+protocol Parray_Basic {
+child:
+ async Msg(int[] array);
+};
diff --git a/ipc/ipdl/test/ipdl/ok/Parray_OfActors.ipdl b/ipc/ipdl/test/ipdl/ok/Parray_OfActors.ipdl
new file mode 100644
index 0000000000..b084c2c464
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/Parray_OfActors.ipdl
@@ -0,0 +1,10 @@
+include protocol Parray_OfActorsSub;
+
+protocol Parray_OfActors {
+ manages Parray_OfActorsSub;
+
+child:
+ async Msg(Parray_OfActorsSub[] p);
+
+ async Parray_OfActorsSub();
+};
diff --git a/ipc/ipdl/test/ipdl/ok/Parray_OfActorsSub.ipdl b/ipc/ipdl/test/ipdl/ok/Parray_OfActorsSub.ipdl
new file mode 100644
index 0000000000..6769e98abb
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/Parray_OfActorsSub.ipdl
@@ -0,0 +1,7 @@
+include protocol Parray_OfActors;
+
+protocol Parray_OfActorsSub {
+ manager Parray_OfActors;
+
+child: async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/ok/Parray_Union.ipdl b/ipc/ipdl/test/ipdl/ok/Parray_Union.ipdl
new file mode 100644
index 0000000000..33c94e7ae5
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/Parray_Union.ipdl
@@ -0,0 +1,10 @@
+union Union {
+ int[];
+ int;
+ double;
+};
+
+sync protocol Parray_Union {
+parent:
+ sync Msg(Union u, Union[] au) returns (Union r);
+};
diff --git a/ipc/ipdl/test/ipdl/ok/PbasicUsing.ipdl b/ipc/ipdl/test/ipdl/ok/PbasicUsing.ipdl
new file mode 100644
index 0000000000..2de73dab14
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PbasicUsing.ipdl
@@ -0,0 +1,36 @@
+using SomeType from "SomeFile.h";
+using class SomeClass from "SomeFile.h";
+using struct SomeStruct from "SomeFile.h";
+
+using refcounted SomeRefcountedType from "SomeFile.h";
+using refcounted class SomeRefcountedClass from "SomeFile.h";
+using refcounted struct SomeRefcountedStruct from "SomeFile.h";
+
+using moveonly SomeMoveonlyType from "SomeFile.h";
+using moveonly class SomeMoveonlyClass from "SomeFile.h";
+using moveonly struct SomeMoveonlyStruct from "SomeFile.h";
+
+using refcounted moveonly SomeRefcountedMoveonlyType from "SomeFile.h";
+using refcounted moveonly class SomeRefcountedMoveonlyClass from "SomeFile.h";
+using refcounted moveonly struct SomeRefcountedMoveonlyStruct from "SomeFile.h";
+
+union SomeUnion
+{
+ SomeType;
+ SomeClass;
+ SomeStruct;
+ SomeRefcountedType;
+ SomeRefcountedClass;
+ SomeRefcountedStruct;
+ SomeMoveonlyType;
+ SomeMoveonlyClass;
+ SomeMoveonlyStruct;
+ SomeRefcountedMoveonlyType;
+ SomeRefcountedMoveonlyClass;
+ SomeRefcountedMoveonlyStruct;
+};
+
+protocol PbasicUsing {
+child:
+ async Msg(SomeUnion foo);
+};
diff --git a/ipc/ipdl/test/ipdl/ok/Pbuiltins.ipdl b/ipc/ipdl/test/ipdl/ok/Pbuiltins.ipdl
new file mode 100644
index 0000000000..4085dc6d95
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/Pbuiltins.ipdl
@@ -0,0 +1,21 @@
+protocol Pbuiltins {
+
+ // sanity-check that "essential" builtins are being declared
+
+child: async 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/Pbytebuf.ipdl b/ipc/ipdl/test/ipdl/ok/Pbytebuf.ipdl
new file mode 100644
index 0000000000..1e7dd49f8d
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/Pbytebuf.ipdl
@@ -0,0 +1,13 @@
+union Foo {
+ int;
+ ByteBuf;
+};
+
+intr protocol Pbytebuf {
+parent:
+ async Msg(ByteBuf s, Foo f);
+ sync SyncMsg(ByteBuf s, Foo f)
+ returns (ByteBuf t, Foo g);
+ intr InterruptMsg(ByteBuf s, Foo f)
+ returns (ByteBuf t, Foo g);
+};
diff --git a/ipc/ipdl/test/ipdl/ok/Pempty.ipdl b/ipc/ipdl/test/ipdl/ok/Pempty.ipdl
new file mode 100644
index 0000000000..61b511bec6
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/Pempty.ipdl
@@ -0,0 +1,3 @@
+protocol Pempty {
+child: async Msg();
+};
diff --git a/ipc/ipdl/test/ipdl/ok/PemptyStruct.ipdl b/ipc/ipdl/test/ipdl/ok/PemptyStruct.ipdl
new file mode 100644
index 0000000000..a067bf71da
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PemptyStruct.ipdl
@@ -0,0 +1,3 @@
+struct empty { };
+
+protocol PemptyStruct { child: async __delete__(); };
diff --git a/ipc/ipdl/test/ipdl/ok/PheaderProto.ipdl b/ipc/ipdl/test/ipdl/ok/PheaderProto.ipdl
new file mode 100644
index 0000000000..7ae285042e
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PheaderProto.ipdl
@@ -0,0 +1,10 @@
+include header;
+
+namespace c {
+
+protocol PheaderProto {
+child:
+ async __delete__(foo a, baz b, Inner1 c, Inner2 d, X x);
+};
+
+}
diff --git a/ipc/ipdl/test/ipdl/ok/PintrProtocol.ipdl b/ipc/ipdl/test/ipdl/ok/PintrProtocol.ipdl
new file mode 100644
index 0000000000..190d30abf5
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PintrProtocol.ipdl
@@ -0,0 +1,13 @@
+intr protocol PintrProtocol {
+
+ // 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/Pjetpack.ipdl b/ipc/ipdl/test/ipdl/ok/Pjetpack.ipdl
new file mode 100644
index 0000000000..190732485d
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/Pjetpack.ipdl
@@ -0,0 +1,5 @@
+sync protocol Pjetpack {
+child:
+ async __delete__();
+
+};
diff --git a/ipc/ipdl/test/ipdl/ok/PmanageSelf.ipdl b/ipc/ipdl/test/ipdl/ok/PmanageSelf.ipdl
new file mode 100644
index 0000000000..992f374a58
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PmanageSelf.ipdl
@@ -0,0 +1,10 @@
+include protocol PmanageSelf_Toplevel;
+
+protocol PmanageSelf {
+ manager PmanageSelf_Toplevel or PmanageSelf;
+ manages PmanageSelf;
+
+child:
+ async PmanageSelf();
+ async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/ok/PmanageSelf_Toplevel.ipdl b/ipc/ipdl/test/ipdl/ok/PmanageSelf_Toplevel.ipdl
new file mode 100644
index 0000000000..70b5fe8a9b
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PmanageSelf_Toplevel.ipdl
@@ -0,0 +1,9 @@
+include protocol PmanageSelf;
+
+protocol PmanageSelf_Toplevel {
+ manages PmanageSelf;
+
+child:
+ async PmanageSelf();
+ async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/ok/PmanagedProtocol.ipdl b/ipc/ipdl/test/ipdl/ok/PmanagedProtocol.ipdl
new file mode 100644
index 0000000000..cab07d06e7
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PmanagedProtocol.ipdl
@@ -0,0 +1,8 @@
+include protocol PmanagerProtocol;
+
+protocol PmanagedProtocol {
+ manager PmanagerProtocol;
+
+child:
+ async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/ok/PmanagerProtocol.ipdl b/ipc/ipdl/test/ipdl/ok/PmanagerProtocol.ipdl
new file mode 100644
index 0000000000..40d1de31b0
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PmanagerProtocol.ipdl
@@ -0,0 +1,11 @@
+include protocol PmanagedProtocol;
+
+// sanity check of managed/manager protocols
+
+protocol PmanagerProtocol {
+ manages PmanagedProtocol;
+
+parent:
+ async PmanagedProtocol(int i);
+
+};
diff --git a/ipc/ipdl/test/ipdl/ok/Pmaybe_Basic.ipdl b/ipc/ipdl/test/ipdl/ok/Pmaybe_Basic.ipdl
new file mode 100644
index 0000000000..a0bbccd874
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/Pmaybe_Basic.ipdl
@@ -0,0 +1,4 @@
+protocol Pmaybe_Basic {
+child:
+ async Msg(int? maybe);
+};
diff --git a/ipc/ipdl/test/ipdl/ok/Pmaybe_OfActors.ipdl b/ipc/ipdl/test/ipdl/ok/Pmaybe_OfActors.ipdl
new file mode 100644
index 0000000000..430faa9ae7
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/Pmaybe_OfActors.ipdl
@@ -0,0 +1,10 @@
+include protocol Pmaybe_OfActorsSub;
+
+protocol Pmaybe_OfActors {
+ manages Pmaybe_OfActorsSub;
+
+child:
+ async Msg(Pmaybe_OfActorsSub? p);
+
+ async Pmaybe_OfActorsSub();
+};
diff --git a/ipc/ipdl/test/ipdl/ok/Pmaybe_OfActorsSub.ipdl b/ipc/ipdl/test/ipdl/ok/Pmaybe_OfActorsSub.ipdl
new file mode 100644
index 0000000000..6a4759953f
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/Pmaybe_OfActorsSub.ipdl
@@ -0,0 +1,7 @@
+include protocol Pmaybe_OfActors;
+
+protocol Pmaybe_OfActorsSub {
+ manager Pmaybe_OfActors;
+
+child: async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/ok/Pmaybe_Union.ipdl b/ipc/ipdl/test/ipdl/ok/Pmaybe_Union.ipdl
new file mode 100644
index 0000000000..67f24cb794
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/Pmaybe_Union.ipdl
@@ -0,0 +1,10 @@
+union MaybeUnion {
+ int[];
+ int;
+ double;
+};
+
+sync protocol Pmaybe_Union {
+parent:
+ async Msg(MaybeUnion u, MaybeUnion? au) returns (MaybeUnion r);
+};
diff --git a/ipc/ipdl/test/ipdl/ok/Pmedia.ipdl b/ipc/ipdl/test/ipdl/ok/Pmedia.ipdl
new file mode 100644
index 0000000000..0a8076a433
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/Pmedia.ipdl
@@ -0,0 +1,5 @@
+sync protocol Pmedia {
+child:
+ async __delete__();
+
+};
diff --git a/ipc/ipdl/test/ipdl/ok/PmessageCompress.ipdl b/ipc/ipdl/test/ipdl/ok/PmessageCompress.ipdl
new file mode 100644
index 0000000000..d74c5626bd
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PmessageCompress.ipdl
@@ -0,0 +1,4 @@
+intr protocol PmessageCompress {
+child:
+ async foo() compress;
+};
diff --git a/ipc/ipdl/test/ipdl/ok/PmessageVerify.ipdl b/ipc/ipdl/test/ipdl/ok/PmessageVerify.ipdl
new file mode 100644
index 0000000000..ee7adfe8d0
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PmessageVerify.ipdl
@@ -0,0 +1,14 @@
+include protocol PmessageVerifyTopLevel;
+
+intr protocol PmessageVerify {
+ manager PmessageVerifyTopLevel;
+
+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/PmessageVerifyTopLevel.ipdl b/ipc/ipdl/test/ipdl/ok/PmessageVerifyTopLevel.ipdl
new file mode 100644
index 0000000000..72115a0338
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PmessageVerifyTopLevel.ipdl
@@ -0,0 +1,7 @@
+include protocol PmessageVerify;
+
+intr protocol PmessageVerifyTopLevel{
+ manages PmessageVerify;
+ parent:
+ sync PmessageVerify(uint32_t aParam1) returns (double rv1) verify;
+};
diff --git a/ipc/ipdl/test/ipdl/ok/PmultiManaged.ipdl b/ipc/ipdl/test/ipdl/ok/PmultiManaged.ipdl
new file mode 100644
index 0000000000..d8c47fc9e3
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PmultiManaged.ipdl
@@ -0,0 +1,9 @@
+include protocol PmultiManager1;
+include protocol PmultiManager2;
+
+protocol PmultiManaged {
+ manager PmultiManager1 or PmultiManager2;
+
+child:
+ async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/ok/PmultiManager1.ipdl b/ipc/ipdl/test/ipdl/ok/PmultiManager1.ipdl
new file mode 100644
index 0000000000..b0654a8c4f
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PmultiManager1.ipdl
@@ -0,0 +1,8 @@
+include protocol PmultiManaged;
+
+protocol PmultiManager1 {
+ manages PmultiManaged;
+
+child:
+ async PmultiManaged();
+};
diff --git a/ipc/ipdl/test/ipdl/ok/PmultiManager2.ipdl b/ipc/ipdl/test/ipdl/ok/PmultiManager2.ipdl
new file mode 100644
index 0000000000..c03f05a173
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PmultiManager2.ipdl
@@ -0,0 +1,8 @@
+include protocol PmultiManaged;
+
+protocol PmultiManager2 {
+ manages PmultiManaged;
+
+child:
+ async PmultiManaged();
+};
diff --git a/ipc/ipdl/test/ipdl/ok/PmultipleUsingCxxTypes.ipdl b/ipc/ipdl/test/ipdl/ok/PmultipleUsingCxxTypes.ipdl
new file mode 100644
index 0000000000..fa63b5b4b0
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PmultipleUsingCxxTypes.ipdl
@@ -0,0 +1,7 @@
+using struct mozilla::void_t from "mozilla/ipc/IPCCore.h";
+using struct mozilla::void_t from "mozilla/ipc/IPCCore.h";
+
+protocol PmultipleUsingCxxTypes {
+child:
+ async Msg(void_t foo);
+};
diff --git a/ipc/ipdl/test/ipdl/ok/PmutualRecStructUnion.ipdl b/ipc/ipdl/test/ipdl/ok/PmutualRecStructUnion.ipdl
new file mode 100644
index 0000000000..0fe5501a09
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PmutualRecStructUnion.ipdl
@@ -0,0 +1,21 @@
+struct X {
+ int i;
+ Y[] y;
+};
+
+union Y {
+ double;
+ X;
+ Z;
+};
+
+struct Z {
+ X x;
+ Y y;
+};
+
+protocol PmutualRecStructUnion {
+child:
+ async Test(X x, Y y, Z z);
+ async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/ok/PmutualRecUnion.ipdl b/ipc/ipdl/test/ipdl/ok/PmutualRecUnion.ipdl
new file mode 100644
index 0000000000..304847e003
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PmutualRecUnion.ipdl
@@ -0,0 +1,21 @@
+union X {
+ int;
+ Y[];
+ Y?;
+};
+
+union Y {
+ X;
+ Z;
+};
+
+union Z {
+ double;
+ X;
+};
+
+protocol PmutualRecUnion {
+child:
+ async Test(X x, Y y, Z z);
+ async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/ok/Pnamespace_Basic.ipdl b/ipc/ipdl/test/ipdl/ok/Pnamespace_Basic.ipdl
new file mode 100644
index 0000000000..4f996d9744
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/Pnamespace_Basic.ipdl
@@ -0,0 +1,12 @@
+namespace basic {
+
+// sanity check of namespaced protocols
+
+protocol Pnamespace_Basic {
+
+child:
+ async Msg();
+
+};
+
+} // namespace basic
diff --git a/ipc/ipdl/test/ipdl/ok/PnoRedeclCrossMessage.ipdl b/ipc/ipdl/test/ipdl/ok/PnoRedeclCrossMessage.ipdl
new file mode 100644
index 0000000000..d353a8c07c
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PnoRedeclCrossMessage.ipdl
@@ -0,0 +1,9 @@
+protocol PnoRedeclCrossMessage {
+
+ // 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/Pplugin.ipdl b/ipc/ipdl/test/ipdl/ok/Pplugin.ipdl
new file mode 100644
index 0000000000..29c001e67b
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/Pplugin.ipdl
@@ -0,0 +1,5 @@
+intr protocol Pplugin {
+child:
+ async __delete__();
+
+};
diff --git a/ipc/ipdl/test/ipdl/ok/Prio.ipdl b/ipc/ipdl/test/ipdl/ok/Prio.ipdl
new file mode 100644
index 0000000000..d2690935e9
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/Prio.ipdl
@@ -0,0 +1,8 @@
+async protocol Prio
+{
+child:
+ prio(normal) async NormalPrio();
+ prio(high) async HighPrio();
+ prio(input) async InputPrio();
+ prio(mediumhigh) async MediumHighPrio();
+};
diff --git a/ipc/ipdl/test/ipdl/ok/PselfRecUnion.ipdl b/ipc/ipdl/test/ipdl/ok/PselfRecUnion.ipdl
new file mode 100644
index 0000000000..7d76c34542
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PselfRecUnion.ipdl
@@ -0,0 +1,11 @@
+union R {
+ int;
+ double;
+ R;
+};
+
+protocol PselfRecUnion {
+child:
+ async Test(R r);
+ async __delete__();
+};
diff --git a/ipc/ipdl/test/ipdl/ok/Pshmem.ipdl b/ipc/ipdl/test/ipdl/ok/Pshmem.ipdl
new file mode 100644
index 0000000000..326fb60c97
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/Pshmem.ipdl
@@ -0,0 +1,13 @@
+union Foo {
+ int;
+ Shmem;
+};
+
+intr protocol Pshmem {
+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/PsyncProtocol.ipdl b/ipc/ipdl/test/ipdl/ok/PsyncProtocol.ipdl
new file mode 100644
index 0000000000..377671b2d6
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PsyncProtocol.ipdl
@@ -0,0 +1,11 @@
+sync protocol PsyncProtocol {
+
+ // sanity check of sync protocols
+
+child:
+ async AsyncMsg();
+
+parent:
+ sync SyncMsg() returns (int i);
+
+};
diff --git a/ipc/ipdl/test/ipdl/ok/PthreeDirections.ipdl b/ipc/ipdl/test/ipdl/ok/PthreeDirections.ipdl
new file mode 100644
index 0000000000..81c8cf6041
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/PthreeDirections.ipdl
@@ -0,0 +1,13 @@
+protocol PthreeDirections {
+
+ // 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/Punion_Basic.ipdl b/ipc/ipdl/test/ipdl/ok/Punion_Basic.ipdl
new file mode 100644
index 0000000000..697b86e088
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/Punion_Basic.ipdl
@@ -0,0 +1,11 @@
+union Basic {
+ int;
+ double;
+};
+
+sync protocol Punion_Basic {
+
+parent:
+ sync Msg(Basic p) returns (Basic r);
+
+};
diff --git a/ipc/ipdl/test/ipdl/ok/Punion_Comparable.ipdl b/ipc/ipdl/test/ipdl/ok/Punion_Comparable.ipdl
new file mode 100644
index 0000000000..b63043f377
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/Punion_Comparable.ipdl
@@ -0,0 +1,11 @@
+comparable union Basic {
+ int;
+ double;
+};
+
+sync protocol Punion_Comparable {
+
+parent:
+ sync Msg(Basic p) returns (Basic r);
+
+};
diff --git a/ipc/ipdl/test/ipdl/ok/Punion_Namespaced.ipdl b/ipc/ipdl/test/ipdl/ok/Punion_Namespaced.ipdl
new file mode 100644
index 0000000000..9453a561c8
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/Punion_Namespaced.ipdl
@@ -0,0 +1,18 @@
+namespace kitties {
+
+union Socks {
+ int;
+ double;
+};
+
+} // namespace kitties
+
+
+namespace puppies {
+
+protocol Punion_Namespaced {
+child:
+ async Msg(Socks s);
+};
+
+} // namespace puppies
diff --git a/ipc/ipdl/test/ipdl/ok/header.ipdlh b/ipc/ipdl/test/ipdl/ok/header.ipdlh
new file mode 100644
index 0000000000..fc3f8c8279
--- /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/runtests.py b/ipc/ipdl/test/ipdl/runtests.py
new file mode 100644
index 0000000000..7338c7087a
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/runtests.py
@@ -0,0 +1,116 @@
+import os
+import 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 mkCustomMsg(self, msg):
+ return """
+### Command: %s
+### %s
+### stderr:
+%s""" % (
+ " ".join(self.compile.argv),
+ msg,
+ self.compile.stderr,
+ )
+
+ 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)
+
+ # Look for expected errors in the input file.
+ f = open(filename, "r")
+ self.expectedErrorMessage = []
+ for l in f:
+ if l.startswith("//error:"):
+ self.expectedErrorMessage.append(l[2:-1])
+ f.close()
+
+ def checkPassed(self):
+ self.assertNotEqual(
+ self.expectedErrorMessage,
+ [],
+ self.mkCustomMsg(
+ "Error test should contain at least "
+ + "one line starting with //error: "
+ + "that indicates the expected failure."
+ ),
+ )
+
+ for e in self.expectedErrorMessage:
+ self.assertTrue(
+ self.compile.error(e),
+ self.mkCustomMsg('Did not see expected error "' + e + '"'),
+ )
+
+
+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 = False, False
+ for arg in sys.argv[3:]:
+ if errortests:
+ # The extra subdirectory is used for non-failing files we want
+ # to include from failing files.
+ errorIncludes = ["-I", os.path.join(errordir, "extra"), "-I", errordir]
+ errorsuite.addTest(ErrorTestCase(ipdlargv + errorIncludes, arg))
+ elif oktests:
+ if "ERRORTESTS" == arg:
+ errortests = True
+ continue
+ oksuite.addTest(OkTestCase(ipdlargv + ["-I", okdir], arg))
+ else:
+ if "OKTESTS" == arg:
+ oktests = True
+ continue
+ ipdlargv.append(arg)
+
+ (unittest.TextTestRunner()).run(unittest.TestSuite([oksuite, errorsuite]))
diff --git a/ipc/ipdl/test/ipdl/sync-messages.ini b/ipc/ipdl/test/ipdl/sync-messages.ini
new file mode 100644
index 0000000000..f7034efe08
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/sync-messages.ini
@@ -0,0 +1,52 @@
+[Pactorreturn::Msg]
+description = test only
+[Parray_Union::Msg]
+description = test only
+[Punion_Basic::Msg]
+description = test only
+[PStruct::test]
+description = test only
+[PStructComparable::test]
+description = test only
+[Punion_Comparable::Msg]
+description = test only
+[PintrProtocol::SyncMsg]
+description = test only
+[PintrProtocol::InterruptMsg]
+description = test only
+[Pshmem::SyncMsg]
+description = test only
+[Pshmem::InterruptMsg]
+description = test only
+[Pbytebuf::SyncMsg]
+description = test only
+[Pbytebuf::InterruptMsg]
+description = test only
+[PsyncProtocol::SyncMsg]
+description = test only
+[PmessageVerify::__delete__]
+description = test only
+[PmessageVerify::msg3]
+description = test only
+[PmessageVerify::msg4]
+description = test only
+[PmessageVerifyTopLevel::PmessageVerify]
+description = test only
+[PDeleteSub::__delete__]
+description = test only
+[PintrMessageCompress::foo]
+description = test only
+[PintrMessageCompress::bar]
+description = test only
+[PsyncMessageCompress::foo]
+description = test only
+[PsyncParentToChild::Msg]
+description = test only
+[PtooWeakIntrSync::Msg]
+description = test only
+[PtooWeakSyncAsync::Msg]
+description = test only
+[PundeclReturnType::Msg]
+description = test only
+[PasyncMessageListed::Msg]
+description = test only
diff --git a/ipc/ipdl/test/moz.build b/ipc/ipdl/test/moz.build
new file mode 100644
index 0000000000..7c0986edb1
--- /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"]