Bug 1488808 Part 8 - Redirection changes for supporting layout and painting after diverging from the recording, r=froydnj.
authorBrian Hackett <bhackett1024@gmail.com>
Wed, 17 Oct 2018 10:04:01 -0600
changeset 500497 0130de663e9f9006da9381d89967758af9974224
parent 500496 b6ca168b7e52bff9f36a6ca07c1dab6750c87882
child 500498 0ce807f9d1f679c848341e4858df243a5c127bdf
push id1864
push userffxbld-merge
push dateMon, 03 Dec 2018 15:51:40 +0000
treeherdermozilla-release@f040763d99ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1488808
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1488808 Part 8 - Redirection changes for supporting layout and painting after diverging from the recording, r=froydnj.
toolkit/recordreplay/ProcessRedirectDarwin.cpp
--- a/toolkit/recordreplay/ProcessRedirectDarwin.cpp
+++ b/toolkit/recordreplay/ProcessRedirectDarwin.cpp
@@ -8,16 +8,17 @@
 
 #include "mozilla/Maybe.h"
 
 #include "HashTable.h"
 #include "Lock.h"
 #include "MemorySnapshot.h"
 #include "ProcessRecordReplay.h"
 #include "ProcessRewind.h"
+#include "base/eintr_wrapper.h"
 
 #include <dlfcn.h>
 #include <fcntl.h>
 #include <signal.h>
 
 #include <bsm/audit.h>
 #include <bsm/audit_session.h>
 #include <mach/clock.h>
@@ -73,53 +74,59 @@ namespace recordreplay {
   MACRO(lseek, RR_SaveRvalHadErrorNegative)                      \
   MACRO(socketpair, RR_SaveRvalHadErrorNegative<RR_WriteBufferFixedSize<3, 2 * sizeof(int)>>) \
   MACRO(fileport_makeport,                                       \
         RR_SaveRvalHadErrorNegative<RR_WriteBufferFixedSize<1, sizeof(size_t)>>) \
   MACRO(getsockopt, RR_SaveRvalHadErrorNegative<RR_getsockopt>)  \
   MACRO(gettimeofday, RR_SaveRvalHadErrorNegative<RR_Compose<    \
                         RR_WriteOptionalBufferFixedSize<0, sizeof(struct timeval)>, \
                         RR_WriteOptionalBufferFixedSize<1, sizeof(struct timezone)>>>, \
-                      Preamble_gettimeofday)                     \
+        nullptr, nullptr, Preamble_PassThrough)                  \
   MACRO(getuid, RR_ScalarRval)                                   \
   MACRO(geteuid, RR_ScalarRval)                                  \
   MACRO(getgid, RR_ScalarRval)                                   \
   MACRO(getegid, RR_ScalarRval)                                  \
   MACRO(issetugid, RR_ScalarRval)                                \
   MACRO(__gettid, RR_ScalarRval)                                 \
-  MACRO(getpid, RR_ScalarRval)                                   \
+  MACRO(getpid, nullptr, Preamble_getpid)                        \
   MACRO(fcntl, RR_SaveRvalHadErrorNegative, Preamble_fcntl)      \
   MACRO(getattrlist, RR_SaveRvalHadErrorNegative<RR_WriteBuffer<2, 3>>) \
   MACRO(fstat$INODE64,                                           \
-        RR_SaveRvalHadErrorNegative<RR_WriteBufferFixedSize<1, sizeof(struct stat)>>) \
+        RR_SaveRvalHadErrorNegative<RR_WriteBufferFixedSize<1, sizeof(struct stat)>>, \
+        nullptr, nullptr, Preamble_SetError)                     \
   MACRO(lstat$INODE64,                                           \
-        RR_SaveRvalHadErrorNegative<RR_WriteBufferFixedSize<1, sizeof(struct stat)>>) \
+        RR_SaveRvalHadErrorNegative<RR_WriteBufferFixedSize<1, sizeof(struct stat)>>, \
+        nullptr, nullptr, Preamble_SetError)                     \
   MACRO(stat$INODE64,                                            \
-        RR_SaveRvalHadErrorNegative<RR_WriteBufferFixedSize<1, sizeof(struct stat)>>) \
+        RR_SaveRvalHadErrorNegative<RR_WriteBufferFixedSize<1, sizeof(struct stat)>>, \
+        nullptr, nullptr, Preamble_SetError)                     \
   MACRO(statfs$INODE64,                                          \
-        RR_SaveRvalHadErrorNegative<RR_WriteBufferFixedSize<1, sizeof(struct statfs)>>) \
+        RR_SaveRvalHadErrorNegative<RR_WriteBufferFixedSize<1, sizeof(struct statfs)>>, \
+        nullptr, nullptr, Preamble_SetError)                     \
   MACRO(fstatfs$INODE64,                                         \
-        RR_SaveRvalHadErrorNegative<RR_WriteBufferFixedSize<1, sizeof(struct statfs)>>) \
+        RR_SaveRvalHadErrorNegative<RR_WriteBufferFixedSize<1, sizeof(struct statfs)>>, \
+        nullptr, nullptr, Preamble_SetError)                     \
   MACRO(readlink, RR_SaveRvalHadErrorNegative<RR_WriteBuffer<1, 2>>) \
   MACRO(__getdirentries64, RR_SaveRvalHadErrorNegative<RR_Compose< \
                              RR_WriteBuffer<1, 2>,               \
                              RR_WriteBufferFixedSize<3, sizeof(size_t)>>>) \
   MACRO(getdirentriesattr, RR_SaveRvalHadErrorNegative<RR_Compose< \
                              RR_WriteBufferFixedSize<1, sizeof(struct attrlist)>, \
                              RR_WriteBuffer<2, 3>,               \
                              RR_WriteBufferFixedSize<4, sizeof(size_t)>, \
                              RR_WriteBufferFixedSize<5, sizeof(size_t)>, \
                              RR_WriteBufferFixedSize<6, sizeof(size_t)>>>) \
   MACRO(getrusage,                                               \
         RR_SaveRvalHadErrorNegative<RR_WriteBufferFixedSize<1, sizeof(struct rusage)>>) \
   MACRO(__getrlimit,                                             \
         RR_SaveRvalHadErrorNegative<RR_WriteBufferFixedSize<1, sizeof(struct rlimit)>>) \
   MACRO(__setrlimit, RR_SaveRvalHadErrorNegative)                \
   MACRO(sigprocmask,                                             \
-        RR_SaveRvalHadErrorNegative<RR_WriteOptionalBufferFixedSize<2, sizeof(sigset_t)>>) \
+        RR_SaveRvalHadErrorNegative<RR_WriteOptionalBufferFixedSize<2, sizeof(sigset_t)>>, \
+        nullptr, nullptr, Preamble_PassThrough)                  \
   MACRO(sigaltstack,                                             \
         RR_SaveRvalHadErrorNegative<RR_WriteOptionalBufferFixedSize<2, sizeof(stack_t)>>) \
   MACRO(sigaction,                                               \
         RR_SaveRvalHadErrorNegative<RR_WriteOptionalBufferFixedSize<2, sizeof(struct sigaction)>>) \
   MACRO(__pthread_sigmask,                                       \
         RR_SaveRvalHadErrorNegative<RR_WriteOptionalBufferFixedSize<2, sizeof(sigset_t)>>) \
   MACRO(__fsgetpath, RR_SaveRvalHadErrorNegative<RR_WriteBuffer<0, 1>>) \
   MACRO(__disable_threadsignal, nullptr, Preamble___disable_threadsignal) \
@@ -140,48 +147,49 @@ namespace recordreplay {
   MACRO(csops, RR_SaveRvalHadErrorNegative<RR_WriteBuffer<2, 3>>) \
   MACRO(__getlogin, RR_SaveRvalHadErrorNegative<RR_WriteBuffer<0, 1>>) \
   MACRO(__workq_kernreturn, nullptr, Preamble___workq_kernreturn) \
   MACRO(start_wqthread, nullptr, Preamble_start_wqthread)        \
   /* pthreads interface functions */                             \
   MACRO(pthread_cond_wait, nullptr, Preamble_pthread_cond_wait)  \
   MACRO(pthread_cond_timedwait, nullptr, Preamble_pthread_cond_timedwait) \
   MACRO(pthread_cond_timedwait_relative_np, nullptr, Preamble_pthread_cond_timedwait_relative_np) \
-  MACRO(pthread_create, nullptr, Preamble_pthread_create)        \
+  MACRO(pthread_create, nullptr, Preamble_pthread_create, nullptr, Preamble_SetError) \
   MACRO(pthread_join, nullptr, Preamble_pthread_join)            \
   MACRO(pthread_mutex_init, nullptr, Preamble_pthread_mutex_init) \
   MACRO(pthread_mutex_destroy, nullptr, Preamble_pthread_mutex_destroy) \
   MACRO(pthread_mutex_lock, nullptr, Preamble_pthread_mutex_lock) \
   MACRO(pthread_mutex_trylock, nullptr, Preamble_pthread_mutex_trylock) \
   MACRO(pthread_mutex_unlock, nullptr, Preamble_pthread_mutex_unlock) \
   /* C Library functions */                                      \
   MACRO(dlclose, nullptr, Preamble_Veto<0>)                      \
   MACRO(dlopen, nullptr, Preamble_PassThrough)                   \
   MACRO(dlsym, nullptr, Preamble_PassThrough)                    \
   MACRO(fclose, RR_SaveRvalHadErrorNegative)                     \
   MACRO(fopen, RR_SaveRvalHadErrorZero)                          \
   MACRO(fread, RR_Compose<RR_ScalarRval, RR_fread>)              \
   MACRO(fseek, RR_SaveRvalHadErrorNegative)                      \
   MACRO(ftell, RR_SaveRvalHadErrorNegative)                      \
   MACRO(fwrite, RR_ScalarRval)                                   \
-  MACRO(getenv, RR_CStringRval)                                  \
+  MACRO(getenv, RR_CStringRval, nullptr, nullptr, Preamble_Veto<0>) \
   MACRO(localtime_r, RR_SaveRvalHadErrorZero<RR_Compose<         \
                        RR_WriteBufferFixedSize<1, sizeof(struct tm)>, \
                        RR_RvalIsArgument<1>>>)                   \
   MACRO(gmtime_r, RR_SaveRvalHadErrorZero<RR_Compose<            \
                     RR_WriteBufferFixedSize<1, sizeof(struct tm)>, \
                     RR_RvalIsArgument<1>>>)                      \
   MACRO(localtime, nullptr, Preamble_localtime)                  \
   MACRO(gmtime, nullptr, Preamble_gmtime)                        \
   MACRO(mktime, RR_Compose<RR_ScalarRval, RR_WriteBufferFixedSize<0, sizeof(struct tm)>>) \
   MACRO(setlocale, RR_CStringRval)                               \
   MACRO(strftime, RR_Compose<RR_ScalarRval, RR_WriteBufferViaRval<0, 1, 1>>) \
   MACRO(arc4random, RR_ScalarRval)                               \
-  MACRO(mach_absolute_time, RR_ScalarRval, Preamble_mach_absolute_time) \
-  MACRO(mach_msg, RR_Compose<RR_OrderCall, RR_ScalarRval, RR_WriteBuffer<0, 3>>) \
+  MACRO(mach_absolute_time, RR_ScalarRval, Preamble_mach_absolute_time, \
+        nullptr, Preamble_PassThrough)                           \
+  MACRO(mach_msg, RR_Compose<RR_ScalarRval, RR_WriteBuffer<0, 3>>) \
   MACRO(mach_timebase_info,                                      \
         RR_Compose<RR_ScalarRval, RR_WriteBufferFixedSize<0, sizeof(mach_timebase_info_data_t)>>) \
   MACRO(mach_vm_allocate, nullptr, Preamble_mach_vm_allocate)    \
   MACRO(mach_vm_deallocate, nullptr, Preamble_mach_vm_deallocate) \
   MACRO(mach_vm_protect, nullptr, Preamble_mach_vm_protect)      \
   MACRO(realpath,                                                \
         RR_SaveRvalHadErrorZero<RR_Compose<RR_CStringRval,       \
                                            RR_WriteOptionalBufferFixedSize<1, PATH_MAX>>>) \
@@ -204,96 +212,116 @@ namespace recordreplay {
   MACRO(PL_NewHashTable, nullptr, Preamble_PL_NewHashTable)      \
   MACRO(PL_HashTableDestroy, nullptr, Preamble_PL_HashTableDestroy) \
   /* Objective C functions */                                    \
   MACRO(class_getClassMethod, RR_ScalarRval)                     \
   MACRO(class_getInstanceMethod, RR_ScalarRval)                  \
   MACRO(method_exchangeImplementations)                          \
   MACRO(objc_autoreleasePoolPop)                                 \
   MACRO(objc_autoreleasePoolPush, RR_ScalarRval)                 \
-  MACRO(objc_msgSend, nullptr, Preamble_objc_msgSend)            \
+  MACRO(objc_msgSend, RR_objc_msgSend, Preamble_objc_msgSend,    \
+        Middleman_objc_msgSend, MiddlemanPreamble_objc_msgSend)  \
   /* Cocoa functions */                                          \
   MACRO(AcquireFirstMatchingEventInQueue, RR_ScalarRval)         \
   MACRO(CFArrayAppendValue)                                      \
-  MACRO(CFArrayCreate, RR_ScalarRval)                            \
-  MACRO(CFArrayGetCount, RR_ScalarRval)                          \
-  MACRO(CFArrayGetValueAtIndex, RR_ScalarRval)                   \
+  MACRO(CFArrayCreate, RR_ScalarRval, nullptr, Middleman_CFArrayCreate) \
+  MACRO(CFArrayGetCount, RR_ScalarRval, nullptr, Middleman_CFTypeArg<0>) \
+  MACRO(CFArrayGetValueAtIndex, RR_ScalarRval, nullptr, Middleman_CFArrayGetValueAtIndex) \
   MACRO(CFArrayRemoveValueAtIndex)                               \
-  MACRO(CFAttributedStringCreate, RR_ScalarRval)                 \
+  MACRO(CFAttributedStringCreate, RR_ScalarRval, nullptr,        \
+        Middleman_Compose<Middleman_CFTypeArg<1>, Middleman_CFTypeArg<2>, Middleman_CreateCFTypeRval>) \
   MACRO(CFBundleCopyExecutableURL, RR_ScalarRval)                \
   MACRO(CFBundleCopyInfoDictionaryForURL, RR_ScalarRval)         \
   MACRO(CFBundleCreate, RR_ScalarRval)                           \
   MACRO(CFBundleGetBundleWithIdentifier, RR_ScalarRval)          \
   MACRO(CFBundleGetDataPointerForName, nullptr, Preamble_VetoIfNotPassedThrough<0>) \
   MACRO(CFBundleGetFunctionPointerForName, nullptr, Preamble_VetoIfNotPassedThrough<0>) \
   MACRO(CFBundleGetIdentifier, RR_ScalarRval)                    \
   MACRO(CFBundleGetInfoDictionary, RR_ScalarRval)                \
   MACRO(CFBundleGetMainBundle, RR_ScalarRval)                    \
   MACRO(CFBundleGetValueForInfoDictionaryKey, RR_ScalarRval)     \
-  MACRO(CFDataGetBytePtr, RR_CFDataGetBytePtr)                   \
-  MACRO(CFDataGetLength, RR_ScalarRval)                          \
+  MACRO(CFDataGetBytePtr, RR_CFDataGetBytePtr, nullptr, Middleman_CFDataGetBytePtr) \
+  MACRO(CFDataGetLength, RR_ScalarRval, nullptr, Middleman_CFTypeArg<0>) \
   MACRO(CFDateFormatterCreate, RR_ScalarRval)                    \
   MACRO(CFDateFormatterGetFormat, RR_ScalarRval)                 \
-  MACRO(CFDictionaryAddValue)                                    \
-  MACRO(CFDictionaryCreate, RR_ScalarRval)                       \
-  MACRO(CFDictionaryCreateMutable, RR_ScalarRval)                \
-  MACRO(CFDictionaryCreateMutableCopy, RR_ScalarRval)            \
-  MACRO(CFDictionaryGetValue, RR_ScalarRval)                     \
+  MACRO(CFDictionaryAddValue, nullptr, nullptr,                  \
+        Middleman_Compose<Middleman_UpdateCFTypeArg<0>,          \
+                          Middleman_CFTypeArg<1>,                \
+                          Middleman_CFTypeArg<2>>)               \
+  MACRO(CFDictionaryCreate, RR_ScalarRval, nullptr, Middleman_CFDictionaryCreate) \
+  MACRO(CFDictionaryCreateMutable, RR_ScalarRval, nullptr, Middleman_CreateCFTypeRval) \
+  MACRO(CFDictionaryCreateMutableCopy, RR_ScalarRval, nullptr,   \
+        Middleman_Compose<Middleman_CFTypeArg<2>, Middleman_CreateCFTypeRval>) \
+  MACRO(CFDictionaryGetValue, RR_ScalarRval, nullptr,            \
+        Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CFTypeArg<1>, Middleman_CFTypeRval>) \
   MACRO(CFDictionaryGetValueIfPresent,                           \
-        RR_Compose<RR_ScalarRval, RR_WriteBufferFixedSize<2, sizeof(const void*)>>) \
-  MACRO(CFDictionaryReplaceValue)                                \
-  MACRO(CFEqual, RR_ScalarRval)                                  \
-  MACRO(CFGetTypeID, RR_ScalarRval)                              \
+        RR_Compose<RR_ScalarRval, RR_WriteBufferFixedSize<2, sizeof(const void*)>>, nullptr, \
+        Middleman_Compose<Middleman_CFTypeArg<0>,                \
+                          Middleman_CFTypeArg<1>,                \
+                          Middleman_CFTypeOutputArg<2>>)         \
+  MACRO(CFDictionaryReplaceValue, nullptr, nullptr,              \
+        Middleman_Compose<Middleman_UpdateCFTypeArg<0>,          \
+                          Middleman_CFTypeArg<1>,                \
+                          Middleman_CFTypeArg<2>>)               \
+  MACRO(CFEqual, RR_ScalarRval, nullptr,                         \
+        Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CFTypeArg<1>>) \
+  MACRO(CFGetTypeID, RR_ScalarRval, nullptr, Middleman_CFTypeArg<0>) \
   MACRO(CFLocaleCopyCurrent, RR_ScalarRval)                      \
   MACRO(CFLocaleCopyPreferredLanguages, RR_ScalarRval)           \
   MACRO(CFLocaleCreate, RR_ScalarRval)                           \
   MACRO(CFLocaleGetIdentifier, RR_ScalarRval)                    \
   MACRO(CFNotificationCenterAddObserver, nullptr, Preamble_CFNotificationCenterAddObserver) \
   MACRO(CFNotificationCenterGetLocalCenter, RR_ScalarRval)       \
   MACRO(CFNotificationCenterRemoveObserver)                      \
-  MACRO(CFNumberCreate, RR_ScalarRval)                           \
-  MACRO(CFNumberGetValue, RR_Compose<RR_ScalarRval, RR_CFNumberGetValue>) \
-  MACRO(CFNumberIsFloatType, RR_ScalarRval)                      \
+  MACRO(CFNumberCreate, RR_ScalarRval, nullptr, Middleman_CFNumberCreate) \
+  MACRO(CFNumberGetValue, RR_Compose<RR_ScalarRval, RR_CFNumberGetValue>, nullptr, \
+        Middleman_CFNumberGetValue)                              \
+  MACRO(CFNumberIsFloatType, RR_ScalarRval, nullptr, Middleman_CFTypeArg<0>) \
   MACRO(CFPreferencesAppValueIsForced, RR_ScalarRval)            \
   MACRO(CFPreferencesCopyAppValue, RR_ScalarRval)                \
   MACRO(CFPreferencesCopyValue, RR_ScalarRval)                   \
   MACRO(CFPropertyListCreateFromXMLData, RR_ScalarRval)          \
   MACRO(CFPropertyListCreateWithStream, RR_ScalarRval)           \
   MACRO(CFReadStreamClose)                                       \
   MACRO(CFReadStreamCreateWithFile, RR_ScalarRval)               \
   MACRO(CFReadStreamOpen, RR_ScalarRval)                         \
-  MACRO(CFRelease, RR_ScalarRval)                                \
-  MACRO(CFRetain, RR_ScalarRval)                                 \
+  /* Don't handle release/retain calls explicitly in the middleman, all resources */ \
+  /* will be cleaned up when its calls are reset. */             \
+  MACRO(CFRelease, RR_ScalarRval, nullptr, nullptr, Preamble_Veto<0>) \
+  MACRO(CFRetain, RR_ScalarRval, nullptr, nullptr, MiddlemanPreamble_CFRetain) \
   MACRO(CFRunLoopAddSource)                                      \
   MACRO(CFRunLoopGetCurrent, RR_ScalarRval)                      \
   MACRO(CFRunLoopRemoveSource)                                   \
   MACRO(CFRunLoopSourceCreate, RR_ScalarRval, Preamble_CFRunLoopSourceCreate) \
   MACRO(CFRunLoopSourceSignal)                                   \
   MACRO(CFRunLoopWakeUp)                                         \
   MACRO(CFStringAppendCharacters)                                \
-  MACRO(CFStringCompare, RR_ScalarRval)                          \
+  MACRO(CFStringCompare, RR_ScalarRval, nullptr,                 \
+        Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CFTypeArg<1>>) \
   MACRO(CFStringCreateArrayBySeparatingStrings, RR_ScalarRval)   \
   MACRO(CFStringCreateMutable, RR_ScalarRval)                    \
   MACRO(CFStringCreateWithBytes, RR_ScalarRval)                  \
   MACRO(CFStringCreateWithBytesNoCopy, RR_ScalarRval)            \
-  MACRO(CFStringCreateWithCharactersNoCopy, RR_ScalarRval)       \
+  MACRO(CFStringCreateWithCharactersNoCopy, RR_ScalarRval, nullptr, \
+        Middleman_Compose<Middleman_Buffer<1, 2, UniChar>, Middleman_CreateCFTypeRval>) \
   MACRO(CFStringCreateWithCString, RR_ScalarRval)                \
   MACRO(CFStringCreateWithFormat, RR_ScalarRval)                 \
   /* Argument indexes are off by one here as the CFRange argument uses two slots. */ \
   MACRO(CFStringGetBytes, RR_Compose<                            \
                             RR_ScalarRval,                       \
                             RR_WriteOptionalBuffer<6, 7>,        \
                             RR_WriteOptionalBufferFixedSize<8, sizeof(CFIndex)>>) \
   /* Argument indexes are off by one here as the CFRange argument uses two slots. */ \
   /* We also need to specify the argument register with the range's length here. */ \
-  MACRO(CFStringGetCharacters, RR_WriteBuffer<3, 2, UniChar>)    \
+  MACRO(CFStringGetCharacters, RR_WriteBuffer<3, 2, UniChar>, nullptr, \
+        Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_WriteBuffer<3, 2, UniChar>>) \
   MACRO(CFStringGetCString, RR_Compose<RR_ScalarRval, RR_WriteBuffer<1, 2>>) \
   MACRO(CFStringGetCStringPtr, nullptr, Preamble_VetoIfNotPassedThrough<0>) \
   MACRO(CFStringGetIntValue, RR_ScalarRval)                      \
-  MACRO(CFStringGetLength, RR_ScalarRval)                        \
+  MACRO(CFStringGetLength, RR_ScalarRval, nullptr, Middleman_CFTypeArg<0>) \
   MACRO(CFStringGetMaximumSizeForEncoding, RR_ScalarRval)        \
   MACRO(CFStringHasPrefix, RR_ScalarRval)                        \
   MACRO(CFStringTokenizerAdvanceToNextToken, RR_ScalarRval)      \
   MACRO(CFStringTokenizerCreate, RR_ScalarRval)                  \
   MACRO(CFStringTokenizerGetCurrentTokenRange, RR_ComplexScalarRval) \
   MACRO(CFURLCreateFromFileSystemRepresentation, RR_ScalarRval)  \
   MACRO(CFURLCreateFromFSRef, RR_ScalarRval)                     \
   MACRO(CFURLCreateWithFileSystemPath, RR_ScalarRval)            \
@@ -301,140 +329,218 @@ namespace recordreplay {
   MACRO(CFURLGetFileSystemRepresentation, RR_Compose<RR_ScalarRval, RR_WriteBuffer<2, 3>>) \
   MACRO(CFURLGetFSRef, RR_Compose<RR_ScalarRval, RR_WriteBufferFixedSize<1, sizeof(FSRef)>>) \
   MACRO(CFUUIDCreate, RR_ScalarRval)                             \
   MACRO(CFUUIDCreateString, RR_ScalarRval)                       \
   MACRO(CFUUIDGetUUIDBytes, RR_ComplexScalarRval)                \
   MACRO(CGAffineTransformConcat, RR_OversizeRval<sizeof(CGAffineTransform)>) \
   MACRO(CGBitmapContextCreateImage, RR_ScalarRval)               \
   MACRO(CGBitmapContextCreateWithData,                           \
-        RR_Compose<RR_ScalarRval, RR_CGBitmapContextCreateWithData>) \
+        RR_Compose<RR_ScalarRval, RR_CGBitmapContextCreateWithData>, nullptr, \
+        Middleman_CGBitmapContextCreateWithData)                 \
   MACRO(CGBitmapContextGetBytesPerRow, RR_ScalarRval)            \
   MACRO(CGBitmapContextGetHeight, RR_ScalarRval)                 \
   MACRO(CGBitmapContextGetWidth, RR_ScalarRval)                  \
   MACRO(CGColorRelease, RR_ScalarRval)                           \
   MACRO(CGColorSpaceCopyICCProfile, RR_ScalarRval)               \
-  MACRO(CGColorSpaceCreateDeviceRGB, RR_ScalarRval)              \
+  MACRO(CGColorSpaceCreateDeviceRGB, RR_ScalarRval, nullptr, Middleman_CreateCFTypeRval) \
   MACRO(CGColorSpaceCreatePattern, RR_ScalarRval)                \
   MACRO(CGColorSpaceRelease, RR_ScalarRval)                      \
   MACRO(CGContextBeginTransparencyLayerWithRect)                 \
-  MACRO(CGContextClipToRects, RR_ScalarRval)                     \
-  MACRO(CGContextConcatCTM)                                      \
+  MACRO(CGContextClipToRects, RR_ScalarRval, nullptr,            \
+        Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_Buffer<1, 2, CGRect>>) \
+  MACRO(CGContextConcatCTM, nullptr, nullptr,                    \
+        Middleman_Compose<Middleman_CFTypeArg<0>,                \
+                          Middleman_StackArgumentData<sizeof(CGAffineTransform)>>) \
   MACRO(CGContextDrawImage, RR_FlushCGContext<0>)                \
   MACRO(CGContextEndTransparencyLayer)                           \
-  MACRO(CGContextFillRect, RR_FlushCGContext<0>)                 \
+  MACRO(CGContextFillRect, RR_FlushCGContext<0>, nullptr,        \
+        Middleman_Compose<Middleman_CFTypeArg<0>,                \
+                          Middleman_StackArgumentData<sizeof(CGRect)>, \
+                          Middleman_FlushCGContext<0>>)          \
   MACRO(CGContextGetClipBoundingBox, RR_OversizeRval<sizeof(CGRect)>) \
   MACRO(CGContextGetCTM, RR_OversizeRval<sizeof(CGAffineTransform)>) \
   MACRO(CGContextGetType, RR_ScalarRval)                         \
   MACRO(CGContextGetUserSpaceToDeviceSpaceTransform, RR_OversizeRval<sizeof(CGAffineTransform)>) \
-  MACRO(CGContextRestoreGState, nullptr, Preamble_CGContextRestoreGState) \
-  MACRO(CGContextSaveGState)                                     \
-  MACRO(CGContextSetAllowsFontSubpixelPositioning)               \
-  MACRO(CGContextSetAllowsFontSubpixelQuantization)              \
-  MACRO(CGContextSetAlpha)                                       \
-  MACRO(CGContextSetBaseCTM)                                     \
-  MACRO(CGContextSetCTM)                                         \
-  MACRO(CGContextSetGrayFillColor)                               \
+  MACRO(CGContextRestoreGState, nullptr, Preamble_CGContextRestoreGState, \
+        Middleman_UpdateCFTypeArg<0>)                            \
+  MACRO(CGContextSaveGState, nullptr, nullptr, Middleman_UpdateCFTypeArg<0>) \
+  MACRO(CGContextSetAllowsFontSubpixelPositioning, nullptr, nullptr, \
+        Middleman_UpdateCFTypeArg<0>)                            \
+  MACRO(CGContextSetAllowsFontSubpixelQuantization, nullptr, nullptr, \
+        Middleman_UpdateCFTypeArg<0>)                            \
+  MACRO(CGContextSetAlpha, nullptr, nullptr, Middleman_UpdateCFTypeArg<0>) \
+  MACRO(CGContextSetBaseCTM, nullptr, nullptr,                   \
+        Middleman_Compose<Middleman_UpdateCFTypeArg<0>,          \
+                          Middleman_StackArgumentData<sizeof(CGAffineTransform)>>) \
+  MACRO(CGContextSetCTM, nullptr, nullptr,                       \
+        Middleman_Compose<Middleman_UpdateCFTypeArg<0>,          \
+                          Middleman_StackArgumentData<sizeof(CGAffineTransform)>>) \
+  MACRO(CGContextSetGrayFillColor, nullptr, nullptr, Middleman_UpdateCFTypeArg<0>) \
   MACRO(CGContextSetRGBFillColor)                                \
-  MACRO(CGContextSetShouldAntialias)                             \
-  MACRO(CGContextSetShouldSmoothFonts)                           \
-  MACRO(CGContextSetShouldSubpixelPositionFonts)                 \
-  MACRO(CGContextSetShouldSubpixelQuantizeFonts)                 \
-  MACRO(CGContextSetTextDrawingMode)                             \
-  MACRO(CGContextSetTextMatrix)                                  \
-  MACRO(CGContextScaleCTM)                                       \
-  MACRO(CGContextTranslateCTM)                                   \
+  MACRO(CGContextSetShouldAntialias, nullptr, nullptr, Middleman_UpdateCFTypeArg<0>) \
+  MACRO(CGContextSetShouldSmoothFonts, nullptr, nullptr, Middleman_UpdateCFTypeArg<0>) \
+  MACRO(CGContextSetShouldSubpixelPositionFonts, nullptr, nullptr, Middleman_UpdateCFTypeArg<0>) \
+  MACRO(CGContextSetShouldSubpixelQuantizeFonts, nullptr, nullptr, Middleman_UpdateCFTypeArg<0>) \
+  MACRO(CGContextSetTextDrawingMode, nullptr, nullptr, Middleman_UpdateCFTypeArg<0>) \
+  MACRO(CGContextSetTextMatrix, nullptr, nullptr,                \
+        Middleman_Compose<Middleman_UpdateCFTypeArg<0>,          \
+                          Middleman_StackArgumentData<sizeof(CGAffineTransform)>>) \
+  MACRO(CGContextScaleCTM, nullptr, nullptr, Middleman_UpdateCFTypeArg<0>) \
+  MACRO(CGContextTranslateCTM, nullptr, nullptr, Middleman_UpdateCFTypeArg<0>) \
   MACRO(CGDataProviderCreateWithData, RR_Compose<RR_ScalarRval, RR_CGDataProviderCreateWithData>) \
   MACRO(CGDataProviderRelease)                                   \
   MACRO(CGDisplayCopyColorSpace, RR_ScalarRval)                  \
   MACRO(CGDisplayIOServicePort, RR_ScalarRval)                   \
   MACRO(CGEventSourceCounterForEventType, RR_ScalarRval)         \
-  MACRO(CGFontCopyTableForTag, RR_ScalarRval)                    \
-  MACRO(CGFontCopyTableTags, RR_ScalarRval)                      \
-  MACRO(CGFontCopyVariations, RR_ScalarRval)                     \
+  MACRO(CGFontCopyTableForTag, RR_ScalarRval, nullptr,           \
+        Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CreateCFTypeRval>) \
+  MACRO(CGFontCopyTableTags, RR_ScalarRval, nullptr,             \
+        Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CreateCFTypeRval>) \
+  MACRO(CGFontCopyVariations, RR_ScalarRval, nullptr,            \
+        Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CreateCFTypeRval>) \
   MACRO(CGFontCreateCopyWithVariations, RR_ScalarRval)           \
   MACRO(CGFontCreateWithDataProvider, RR_ScalarRval)             \
-  MACRO(CGFontCreateWithFontName, RR_ScalarRval)                 \
+  MACRO(CGFontCreateWithFontName, RR_ScalarRval, nullptr,        \
+        Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CreateCFTypeRval>) \
   MACRO(CGFontCreateWithPlatformFont, RR_ScalarRval)             \
-  MACRO(CGFontGetAscent, RR_ScalarRval)                          \
-  MACRO(CGFontGetCapHeight, RR_ScalarRval)                       \
-  MACRO(CGFontGetDescent, RR_ScalarRval)                         \
-  MACRO(CGFontGetFontBBox, RR_OversizeRval<sizeof(CGRect)>)      \
-  MACRO(CGFontGetGlyphAdvances, RR_Compose<RR_ScalarRval, RR_WriteBuffer<3, 2, int>>) \
-  MACRO(CGFontGetGlyphBBoxes, RR_Compose<RR_ScalarRval, RR_WriteBuffer<3, 2, CGRect>>) \
+  MACRO(CGFontGetAscent, RR_ScalarRval, nullptr, Middleman_CFTypeArg<0>) \
+  MACRO(CGFontGetCapHeight, RR_ScalarRval, nullptr, Middleman_CFTypeArg<0>) \
+  MACRO(CGFontGetDescent, RR_ScalarRval, nullptr, Middleman_CFTypeArg<0>) \
+  MACRO(CGFontGetFontBBox, RR_OversizeRval<sizeof(CGRect)>, nullptr, \
+        Middleman_Compose<Middleman_CFTypeArg<1>, Middleman_OversizeRval<sizeof(CGRect)>>) \
+  MACRO(CGFontGetGlyphAdvances, RR_Compose<RR_ScalarRval, RR_WriteBuffer<3, 2, int>>, nullptr, \
+        Middleman_Compose<Middleman_CFTypeArg<0>,                \
+                          Middleman_Buffer<1, 2, CGGlyph>,       \
+                          Middleman_WriteBuffer<3, 2, int>>)     \
+  MACRO(CGFontGetGlyphBBoxes, RR_Compose<RR_ScalarRval, RR_WriteBuffer<3, 2, CGRect>>, nullptr, \
+        Middleman_Compose<Middleman_CFTypeArg<0>,                \
+                          Middleman_Buffer<1, 2, CGGlyph>,       \
+                          Middleman_WriteBuffer<3, 2, CGRect>>)  \
   MACRO(CGFontGetGlyphPath, RR_ScalarRval)                       \
-  MACRO(CGFontGetLeading, RR_ScalarRval)                         \
-  MACRO(CGFontGetUnitsPerEm, RR_ScalarRval)                      \
-  MACRO(CGFontGetXHeight, RR_ScalarRval)                         \
+  MACRO(CGFontGetLeading, RR_ScalarRval, nullptr, Middleman_CFTypeArg<0>) \
+  MACRO(CGFontGetUnitsPerEm, RR_ScalarRval, nullptr, Middleman_CFTypeArg<0>) \
+  MACRO(CGFontGetXHeight, RR_ScalarRval, nullptr, Middleman_CFTypeArg<0>) \
   MACRO(CGImageGetHeight, RR_ScalarRval)                         \
   MACRO(CGImageGetWidth, RR_ScalarRval)                          \
   MACRO(CGImageRelease, RR_ScalarRval)                           \
   MACRO(CGMainDisplayID, RR_ScalarRval)                          \
   MACRO(CGPathAddPath)                                           \
   MACRO(CGPathApply, nullptr, Preamble_CGPathApply)              \
   MACRO(CGPathContainsPoint, RR_ScalarRval)                      \
   MACRO(CGPathCreateMutable, RR_ScalarRval)                      \
   MACRO(CGPathGetBoundingBox, RR_OversizeRval<sizeof(CGRect)>)   \
   MACRO(CGPathGetCurrentPoint, RR_ComplexFloatRval)              \
   MACRO(CGPathIsEmpty, RR_ScalarRval)                            \
   MACRO(CGSSetDebugOptions, RR_ScalarRval)                       \
   MACRO(CGSShutdownServerConnections)                            \
-  MACRO(CTFontCopyFamilyName, RR_ScalarRval)                     \
-  MACRO(CTFontCopyFeatures, RR_ScalarRval)                       \
-  MACRO(CTFontCopyFontDescriptor, RR_ScalarRval)                 \
-  MACRO(CTFontCopyGraphicsFont, RR_ScalarRval)                   \
-  MACRO(CTFontCopyTable, RR_ScalarRval)                          \
+  MACRO(CTFontCopyFamilyName, RR_ScalarRval, nullptr,            \
+        Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CreateCFTypeRval>) \
+  MACRO(CTFontCopyFeatures, RR_ScalarRval, nullptr,              \
+        Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CreateCFTypeRval>) \
+  MACRO(CTFontCopyFontDescriptor, RR_ScalarRval, nullptr,        \
+        Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CreateCFTypeRval>) \
+  MACRO(CTFontCopyGraphicsFont, RR_ScalarRval, nullptr,          \
+        Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CreateCFTypeRval>) \
+  MACRO(CTFontCopyTable, RR_ScalarRval, nullptr,                 \
+        Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CreateCFTypeRval>) \
   MACRO(CTFontCopyVariationAxes, RR_ScalarRval)                  \
-  MACRO(CTFontCreateForString, RR_ScalarRval)                    \
+  MACRO(CTFontCreateForString, RR_ScalarRval, nullptr,           \
+        Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CFTypeArg<1>, Middleman_CreateCFTypeRval>) \
   MACRO(CTFontCreatePathForGlyph, RR_ScalarRval)                 \
-  MACRO(CTFontCreateWithFontDescriptor, RR_ScalarRval)           \
-  MACRO(CTFontCreateWithGraphicsFont, RR_ScalarRval)             \
-  MACRO(CTFontCreateWithName, RR_ScalarRval)                     \
-  MACRO(CTFontDescriptorCopyAttribute, RR_ScalarRval)            \
-  MACRO(CTFontDescriptorCreateCopyWithAttributes, RR_ScalarRval) \
-  MACRO(CTFontDescriptorCreateMatchingFontDescriptors, RR_ScalarRval) \
-  MACRO(CTFontDescriptorCreateWithAttributes, RR_ScalarRval)     \
-  MACRO(CTFontDrawGlyphs, RR_FlushCGContext<4>)                  \
+  MACRO(CTFontCreateWithFontDescriptor, RR_ScalarRval, nullptr,  \
+        Middleman_Compose<Middleman_CFTypeArg<0>,                \
+                          Middleman_BufferFixedSize<1, sizeof(CGAffineTransform)>, \
+                          Middleman_CreateCFTypeRval>)                 \
+  MACRO(CTFontCreateWithGraphicsFont, RR_ScalarRval, nullptr,    \
+        Middleman_Compose<Middleman_CFTypeArg<0>,                \
+                          Middleman_BufferFixedSize<1, sizeof(CGAffineTransform)>, \
+                          Middleman_CFTypeArg<2>,                \
+                          Middleman_CreateCFTypeRval>)                 \
+  MACRO(CTFontCreateWithName, RR_ScalarRval, nullptr,            \
+        Middleman_Compose<Middleman_CFTypeArg<0>,                \
+                          Middleman_BufferFixedSize<1, sizeof(CGAffineTransform)>, \
+                          Middleman_CreateCFTypeRval>)                 \
+  MACRO(CTFontDescriptorCopyAttribute, RR_ScalarRval, nullptr,   \
+        Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CFTypeArg<1>, Middleman_CreateCFTypeRval>) \
+  MACRO(CTFontDescriptorCreateCopyWithAttributes, RR_ScalarRval, nullptr, \
+        Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CFTypeArg<1>, Middleman_CreateCFTypeRval>) \
+  MACRO(CTFontDescriptorCreateMatchingFontDescriptors, RR_ScalarRval, nullptr, \
+        Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CFTypeArg<1>, Middleman_CreateCFTypeRval>) \
+  MACRO(CTFontDescriptorCreateWithAttributes, RR_ScalarRval, nullptr, \
+        Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CreateCFTypeRval>) \
+  MACRO(CTFontDrawGlyphs, RR_FlushCGContext<4>, nullptr,         \
+        Middleman_Compose<Middleman_CFTypeArg<0>,                \
+                          Middleman_CFTypeArg<4>,                \
+                          Middleman_Buffer<1, 3, CGGlyph>,       \
+                          Middleman_Buffer<2, 3, CGPoint>,       \
+                          Middleman_FlushCGContext<4>>)          \
   MACRO(CTFontGetAdvancesForGlyphs,                              \
-        RR_Compose<RR_FloatRval, RR_WriteOptionalBuffer<3, 4, CGSize>>) \
-  MACRO(CTFontGetAscent, RR_FloatRval)                           \
-  MACRO(CTFontGetBoundingBox, RR_OversizeRval<sizeof(CGRect)>)   \
+        RR_Compose<RR_FloatRval, RR_WriteOptionalBuffer<3, 4, CGSize>>, nullptr, \
+        Middleman_Compose<Middleman_CFTypeArg<0>,                \
+                          Middleman_Buffer<2, 4, CGGlyph>,       \
+                          Middleman_WriteBuffer<3, 4, CGSize>>)  \
+  MACRO(CTFontGetAscent, RR_FloatRval, nullptr, Middleman_CFTypeArg<0>)   \
+  MACRO(CTFontGetBoundingBox, RR_OversizeRval<sizeof(CGRect)>, nullptr, \
+        Middleman_Compose<Middleman_CFTypeArg<1>, Middleman_OversizeRval<sizeof(CGRect)>>) \
   MACRO(CTFontGetBoundingRectsForGlyphs,                         \
         /* Argument indexes here are off by one due to the oversize rval. */ \
-        RR_Compose<RR_OversizeRval<sizeof(CGRect)>, RR_WriteOptionalBuffer<4, 5, CGRect>>) \
-  MACRO(CTFontGetCapHeight, RR_FloatRval)                        \
-  MACRO(CTFontGetDescent, RR_FloatRval)                          \
-  MACRO(CTFontGetGlyphCount, RR_ScalarRval)                      \
+        RR_Compose<RR_OversizeRval<sizeof(CGRect)>,              \
+                   RR_WriteOptionalBuffer<4, 5, CGRect>>, nullptr, \
+        Middleman_Compose<Middleman_CFTypeArg<1>,                \
+                          Middleman_Buffer<3, 5, CGGlyph>,       \
+                          Middleman_OversizeRval<sizeof(CGRect)>, \
+                          Middleman_WriteBuffer<4, 5, CGRect>>)  \
+  MACRO(CTFontGetCapHeight, RR_FloatRval, nullptr, Middleman_CFTypeArg<0>) \
+  MACRO(CTFontGetDescent, RR_FloatRval, nullptr, Middleman_CFTypeArg<0>) \
+  MACRO(CTFontGetGlyphCount, RR_ScalarRval, nullptr, Middleman_CFTypeArg<0>) \
   MACRO(CTFontGetGlyphsForCharacters,                            \
-        RR_Compose<RR_ScalarRval, RR_WriteBuffer<2, 3, CGGlyph>>) \
-  MACRO(CTFontGetLeading, RR_FloatRval)                          \
-  MACRO(CTFontGetSize, RR_FloatRval)                             \
-  MACRO(CTFontGetSymbolicTraits, RR_ScalarRval)                  \
-  MACRO(CTFontGetUnderlinePosition, RR_FloatRval)                \
-  MACRO(CTFontGetUnderlineThickness, RR_FloatRval)               \
-  MACRO(CTFontGetUnitsPerEm, RR_ScalarRval)                      \
-  MACRO(CTFontGetXHeight, RR_FloatRval)                           \
+        RR_Compose<RR_ScalarRval, RR_WriteBuffer<2, 3, CGGlyph>>, nullptr, \
+        Middleman_Compose<Middleman_CFTypeArg<0>,                \
+                          Middleman_Buffer<1, 3, UniChar>,       \
+                          Middleman_WriteBuffer<2, 3, CGGlyph>>) \
+  MACRO(CTFontGetLeading, RR_FloatRval, nullptr, Middleman_CFTypeArg<0>) \
+  MACRO(CTFontGetSize, RR_FloatRval, nullptr, Middleman_CFTypeArg<0>) \
+  MACRO(CTFontGetSymbolicTraits, RR_ScalarRval, nullptr, Middleman_CFTypeArg<0>) \
+  MACRO(CTFontGetUnderlinePosition, RR_FloatRval, nullptr, Middleman_CFTypeArg<0>) \
+  MACRO(CTFontGetUnderlineThickness, RR_FloatRval, nullptr, Middleman_CFTypeArg<0>) \
+  MACRO(CTFontGetUnitsPerEm, RR_ScalarRval, nullptr, Middleman_CFTypeArg<0>) \
+  MACRO(CTFontGetXHeight, RR_FloatRval, nullptr, Middleman_CFTypeArg<0>) \
   MACRO(CTFontManagerCopyAvailableFontFamilyNames, RR_ScalarRval) \
   MACRO(CTFontManagerRegisterFontsForURLs, RR_ScalarRval)        \
   MACRO(CTFontManagerSetAutoActivationSetting)                   \
-  MACRO(CTLineCreateWithAttributedString, RR_ScalarRval)         \
-  MACRO(CTLineGetGlyphRuns, RR_ScalarRval)                       \
-  MACRO(CTRunGetAttributes, RR_ScalarRval)                       \
-  MACRO(CTRunGetGlyphCount, RR_ScalarRval)                       \
-  MACRO(CTRunGetGlyphsPtr, RR_CTRunGetElements<CGGlyph, CTRunGetGlyphs>) \
-  MACRO(CTRunGetPositionsPtr, RR_CTRunGetElements<CGPoint, CTRunGetPositions>) \
-  MACRO(CTRunGetStringIndicesPtr, RR_CTRunGetElements<CFIndex, CTRunGetStringIndices>) \
-  MACRO(CTRunGetStringRange, RR_ComplexScalarRval)               \
+  MACRO(CTLineCreateWithAttributedString, RR_ScalarRval, nullptr, \
+        Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CreateCFTypeRval>) \
+  MACRO(CTLineGetGlyphRuns, RR_ScalarRval, nullptr,              \
+        Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CFTypeRval>) \
+  MACRO(CTRunGetAttributes, RR_ScalarRval, nullptr,              \
+        Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CFTypeRval>) \
+  MACRO(CTRunGetGlyphCount, RR_ScalarRval, nullptr, Middleman_CFTypeArg<0>) \
+  MACRO(CTRunGetGlyphsPtr, RR_CTRunGetElements<CGGlyph, CTRunGetGlyphs>, nullptr, \
+        Middleman_CTRunGetElements<CGGlyph, CTRunGetGlyphs>)     \
+  MACRO(CTRunGetPositionsPtr, RR_CTRunGetElements<CGPoint, CTRunGetPositions>, nullptr, \
+        Middleman_CTRunGetElements<CGPoint, CTRunGetPositions>)  \
+  MACRO(CTRunGetStringIndicesPtr, RR_CTRunGetElements<CFIndex, CTRunGetStringIndices>, nullptr, \
+        Middleman_CTRunGetElements<CFIndex, CTRunGetStringIndices>) \
+  MACRO(CTRunGetStringRange, RR_ComplexScalarRval, nullptr, Middleman_CFTypeArg<0>) \
   /* Argument indexes are off by one here as the CFRange argument uses two slots. */ \
-  MACRO(CTRunGetTypographicBounds, RR_Compose<                   \
-                                     RR_FloatRval,               \
-                                     RR_WriteOptionalBufferFixedSize<3, sizeof(CGFloat)>, \
-                                     RR_WriteOptionalBufferFixedSize<4, sizeof(CGFloat)>, \
-                                     RR_WriteOptionalBufferFixedSize<5, sizeof(CGFloat)>>) \
-  MACRO(CUIDraw)                                                 \
+  MACRO(CTRunGetTypographicBounds,                               \
+        RR_Compose<RR_FloatRval,                                 \
+                   RR_WriteOptionalBufferFixedSize<3, sizeof(CGFloat)>, \
+                   RR_WriteOptionalBufferFixedSize<4, sizeof(CGFloat)>, \
+                   RR_WriteOptionalBufferFixedSize<5, sizeof(CGFloat)>>, nullptr, \
+        Middleman_Compose<Middleman_CFTypeArg<0>,                \
+                          Middleman_WriteBufferFixedSize<3, sizeof(CGFloat)>, \
+                          Middleman_WriteBufferFixedSize<4, sizeof(CGFloat)>, \
+                          Middleman_WriteBufferFixedSize<5, sizeof(CGFloat)>>) \
+  MACRO(CUIDraw, nullptr, nullptr,                               \
+        Middleman_Compose<Middleman_CFTypeArg<0>,                \
+                          Middleman_CFTypeArg<1>,                \
+                          Middleman_CFTypeArg<2>,                \
+                          Middleman_StackArgumentData<sizeof(CGRect)>>) \
   MACRO(FSCompareFSRefs, RR_ScalarRval)                          \
   MACRO(FSGetVolumeInfo, RR_Compose<                             \
                            RR_ScalarRval,                        \
                            RR_WriteBufferFixedSize<5, sizeof(HFSUniStr255)>, \
                            RR_WriteBufferFixedSize<6, sizeof(FSRef)>>) \
   MACRO(FSFindFolder, RR_Compose<RR_ScalarRval, RR_WriteBufferFixedSize<3, sizeof(FSRef)>>) \
   MACRO(Gestalt, RR_Compose<RR_ScalarRval, RR_WriteBufferFixedSize<1, sizeof(SInt32)>>) \
   MACRO(GetEventClass, RR_ScalarRval)                            \
@@ -464,21 +570,22 @@ namespace recordreplay {
   MACRO(LSCopyItemAttribute,                                     \
         RR_Compose<RR_ScalarRval, RR_WriteOptionalBufferFixedSize<3, sizeof(CFTypeRef)>>) \
   MACRO(LSCopyKindStringForMIMEType,                             \
         RR_Compose<RR_ScalarRval, RR_WriteOptionalBufferFixedSize<1, sizeof(CFStringRef)>>) \
   MACRO(LSGetApplicationForInfo, RR_Compose<                     \
                                    RR_ScalarRval,                \
                                    RR_WriteOptionalBufferFixedSize<4, sizeof(FSRef)>, \
                                    RR_WriteOptionalBufferFixedSize<5, sizeof(CFURLRef)>>) \
-  MACRO(LSGetApplicationForURL, RR_Compose<                     \
+  MACRO(LSGetApplicationForURL, RR_Compose<                      \
                                    RR_ScalarRval,                \
                                    RR_WriteOptionalBufferFixedSize<2, sizeof(FSRef)>, \
                                    RR_WriteOptionalBufferFixedSize<3, sizeof(CFURLRef)>>) \
-  MACRO(NSClassFromString, RR_ScalarRval)                        \
+  MACRO(NSClassFromString, RR_ScalarRval, nullptr,               \
+        Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CFTypeRval>) \
   MACRO(NSRectFill)                                              \
   MACRO(NSSearchPathForDirectoriesInDomains, RR_ScalarRval)      \
   MACRO(NSSetFocusRingStyle, RR_ScalarRval)                      \
   MACRO(NSTemporaryDirectory, RR_ScalarRval)                     \
   MACRO(OSSpinLockLock, nullptr, Preamble_OSSpinLockLock)        \
   MACRO(ReleaseEvent, RR_ScalarRval)                             \
   MACRO(RemoveEventFromQueue, RR_ScalarRval)                     \
   MACRO(RetainEvent, RR_ScalarRval)                              \
@@ -574,16 +681,213 @@ ReplayInvokeCallback(size_t aCallbackId)
     CGPathApplierFunctionWrapper(nullptr, nullptr);
     break;
   default:
     MOZ_CRASH();
   }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
+// Middleman Call Helpers
+///////////////////////////////////////////////////////////////////////////////
+
+static bool
+TestObjCObjectClass(id aObj, const char* aName)
+{
+  Class cls = object_getClass(aObj);
+  while (cls) {
+    const char* className = class_getName(cls);
+    if (!strcmp(className, aName)) {
+      return true;
+    }
+    cls = class_getSuperclass(cls);
+  }
+  return false;
+}
+
+// Inputs that originate from static data in the replaying process itself
+// rather than from previous middleman calls.
+enum class ObjCInputKind {
+  StaticClass,
+  ConstantString,
+};
+
+// Capture an Objective C or CoreFoundation input to a call, which may come
+// either from another middleman call, or from static data in the replaying
+// process.
+static void
+Middleman_ObjCInput(MiddlemanCallContext& aCx, id* aThingPtr)
+{
+  MOZ_RELEASE_ASSERT(aCx.AccessPreface());
+
+  if (Middleman_SystemInput(aCx, (const void**) aThingPtr)) {
+    // This value came from a previous middleman call.
+    return;
+  }
+
+  MOZ_RELEASE_ASSERT(aCx.AccessInput());
+
+  if (aCx.mPhase == MiddlemanCallPhase::ReplayInput) {
+    // Try to determine where this object came from.
+
+    // List of the Objective C classes which messages might be sent to directly.
+    static const char* gStaticClasses[] = {
+      "NSAutoreleasePool",
+      "NSColor",
+      "NSDictionary",
+      "NSFont",
+      "NSFontManager",
+      "NSNumber",
+      "NSString",
+      "NSWindow",
+    };
+
+    // Watch for messages sent to particular classes.
+    for (const char* className : gStaticClasses) {
+      Class cls = objc_lookUpClass(className);
+      if (cls == (Class) *aThingPtr) {
+        aCx.WriteInputScalar((size_t) ObjCInputKind::StaticClass);
+        size_t len = strlen(className) + 1;
+        aCx.WriteInputScalar(len);
+        aCx.WriteInputBytes(className, len);
+        return;
+      }
+    }
+
+    // Watch for constant compile time strings baked into the generated code or
+    // stored in system libraries. We can crash here if the object came from
+    // e.g. a replayed pointer from the recording, as can happen if not enough
+    // redirections have middleman call hooks. We could do better here to make
+    // sure the pointer looks like it could be a constant string, but it seems
+    // better and simpler to crash more reliably here than mask problems due to
+    // missing middleman call hooks.
+    if (TestObjCObjectClass(*aThingPtr, "NSString")) {
+      AutoPassThroughThreadEvents pt;
+      CFIndex len = CFStringGetLength((CFStringRef)*aThingPtr);
+      InfallibleVector<UniChar> buffer;
+      buffer.appendN(0, len);
+      CFStringGetCharacters((CFStringRef)*aThingPtr, { 0, len }, buffer.begin());
+      aCx.WriteInputScalar((size_t) ObjCInputKind::ConstantString);
+      aCx.WriteInputScalar(len);
+      aCx.WriteInputBytes(buffer.begin(), len * sizeof(UniChar));
+      return;
+    }
+
+    aCx.MarkAsFailed();
+    return;
+  }
+
+  switch ((ObjCInputKind) aCx.ReadInputScalar()) {
+  case ObjCInputKind::StaticClass: {
+    size_t len = aCx.ReadInputScalar();
+    UniquePtr<char[]> className(new char[len]);
+    aCx.ReadInputBytes(className.get(), len);
+    *aThingPtr = (id) objc_lookUpClass(className.get());
+    break;
+  }
+  case ObjCInputKind::ConstantString: {
+    size_t len = aCx.ReadInputScalar();
+    UniquePtr<UniChar[]> contents(new UniChar[len]);
+    aCx.ReadInputBytes(contents.get(), len * sizeof(UniChar));
+    *aThingPtr = (id) CFStringCreateWithCharacters(kCFAllocatorDefault, contents.get(), len);
+    break;
+  }
+  default:
+    MOZ_CRASH();
+  }
+}
+
+template <size_t Argument>
+static void
+Middleman_CFTypeArg(MiddlemanCallContext& aCx)
+{
+  if (aCx.AccessPreface()) {
+    auto& object = aCx.mArguments->Arg<Argument, id>();
+    Middleman_ObjCInput(aCx, &object);
+  }
+}
+
+static void
+Middleman_CFTypeOutput(MiddlemanCallContext& aCx, CFTypeRef* aOutput, bool aOwnsReference)
+{
+  Middleman_SystemOutput(aCx, (const void**) aOutput);
+
+  if (*aOutput) {
+    switch (aCx.mPhase) {
+    case MiddlemanCallPhase::MiddlemanOutput:
+      if (!aOwnsReference) {
+        CFRetain(*aOutput);
+      }
+      break;
+    case MiddlemanCallPhase::MiddlemanRelease:
+      CFRelease(*aOutput);
+      break;
+    default:
+      break;
+    }
+  }
+}
+
+// For APIs using the 'Get' rule: no reference is held on the returned value.
+static void
+Middleman_CFTypeRval(MiddlemanCallContext& aCx)
+{
+  auto& rval = aCx.mArguments->Rval<CFTypeRef>();
+  Middleman_CFTypeOutput(aCx, &rval, /* aOwnsReference = */ false);
+}
+
+// For APIs using the 'Create' rule: a reference is held on the returned
+// value which must be released.
+static void
+Middleman_CreateCFTypeRval(MiddlemanCallContext& aCx)
+{
+  auto& rval = aCx.mArguments->Rval<CFTypeRef>();
+  Middleman_CFTypeOutput(aCx, &rval, /* aOwnsReference = */ true);
+}
+
+template <size_t Argument>
+static void
+Middleman_CFTypeOutputArg(MiddlemanCallContext& aCx)
+{
+  Middleman_WriteBufferFixedSize<Argument, sizeof(const void*)>(aCx);
+
+  auto arg = aCx.mArguments->Arg<Argument, const void**>();
+  Middleman_CFTypeOutput(aCx, arg, /* aOwnsReference = */ false);
+}
+
+// For APIs whose result will be released by the middleman's autorelease pool.
+static void
+Middleman_AutoreleaseCFTypeRval(MiddlemanCallContext& aCx)
+{
+  auto& rval = aCx.mArguments->Rval<const void*>();
+  Middleman_SystemOutput(aCx, &rval);
+}
+
+// For functions which have an input CFType value and also have side effects on
+// that value, this associates the call with its own input value so that this
+// will be treated as a dependent for any future calls using the value.
+template <size_t Argument>
+static void
+Middleman_UpdateCFTypeArg(MiddlemanCallContext& aCx)
+{
+  auto arg = aCx.mArguments->Arg<Argument, const void*>();
+
+  Middleman_CFTypeArg<Argument>(aCx);
+  Middleman_SystemOutput(aCx, &arg, /* aUpdating = */ true);
+}
+
+static PreambleResult
+Preamble_SetError(CallArguments* aArguments)
+{
+  aArguments->Rval<ssize_t>() = -1;
+  errno = EAGAIN;
+  return PreambleResult::Veto;
+}
+
+///////////////////////////////////////////////////////////////////////////////
 // system call redirections
 ///////////////////////////////////////////////////////////////////////////////
 
 static void
 RR_recvmsg(Stream& aEvents, CallArguments* aArguments, ErrorType* aError)
 {
   auto& msg = aArguments->Arg<1, struct msghdr*>();
 
@@ -697,23 +1001,23 @@ RR_getsockopt(Stream& aEvents, CallArgum
   int initLen = *optlen;
   aEvents.RecordOrReplayValue(optlen);
   MOZ_RELEASE_ASSERT(*optlen <= initLen);
 
   aEvents.RecordOrReplayBytes(optval, *optlen);
 }
 
 static PreambleResult
-Preamble_gettimeofday(CallArguments* aArguments)
+Preamble_getpid(CallArguments* aArguments)
 {
-  // If we have diverged from the recording, just get the actual current time
-  // rather than causing the current debugger operation to fail. This function
-  // is frequently called via e.g. JS natives which the debugger will execute.
-  if (HasDivergedFromRecording()) {
-    return PreambleResult::PassThrough;
+  if (!AreThreadEventsPassedThrough()) {
+    // Don't involve the recording with getpid calls, so that this can be used
+    // after diverging from the recording.
+    aArguments->Rval<size_t>() = GetRecordingPid();
+    return PreambleResult::Veto;
   }
   return PreambleResult::Redirect;
 }
 
 static PreambleResult
 Preamble_fcntl(CallArguments* aArguments)
 {
   // Make sure fcntl is only used with a limited set of commands.
@@ -1150,36 +1454,16 @@ Preamble_PL_HashTableDestroy(CallArgumen
   DestroyPLHashTableCallbacks(priv);
   return PreambleResult::Veto;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // Objective C redirections
 ///////////////////////////////////////////////////////////////////////////////
 
-static bool
-TestObjCObjectClass(id aObj, const char* aName)
-{
-  // When recording we can test to see what the class of an object is, but we
-  // have to record the result of the test because the object "pointers" we
-  // have when replaying are not valid.
-  bool found = false;
-  if (IsRecording()) {
-    Class cls = object_getClass(aObj);
-    while (cls) {
-      if (!strcmp(class_getName(cls), aName)) {
-        found = true;
-        break;
-      }
-      cls = class_getSuperclass(cls);
-    }
-  }
-  return RecordReplayValue(found);
-}
-
 // From Foundation.h, which has lots of Objective C declarations and can't be
 // included here :(
 struct NSFastEnumerationState
 {
   unsigned long state;
   id* itemsPtr;
   unsigned long* mutationsPtr;
   unsigned long extra[5];
@@ -1187,112 +1471,363 @@ struct NSFastEnumerationState
 
 // Emulation of NSFastEnumeration on arrays does not replay any exceptions
 // thrown by mutating the array while it is being iterated over.
 static unsigned long gNeverChange;
 
 static PreambleResult
 Preamble_objc_msgSend(CallArguments* aArguments)
 {
-  Thread* thread = Thread::Current();
-  if (!thread || thread->PassThroughEvents()) {
-    return PreambleResult::Redirect;
+  if (!AreThreadEventsPassedThrough()) {
+    auto message = aArguments->Arg<1, const char*>();
+
+    // Watch for some top level NSApplication messages that can cause Gecko
+    // events to be processed.
+    if (!strcmp(message, "run") ||
+        !strcmp(message, "nextEventMatchingMask:untilDate:inMode:dequeue:"))
+    {
+      PassThroughThreadEventsAllowCallbacks([&]() {
+          RecordReplayInvokeCall(CallEvent_objc_msgSend, aArguments);
+        });
+      RecordReplayBytes(&aArguments->Rval<size_t>(), sizeof(size_t));
+      return PreambleResult::Veto;
+    }
   }
-  EnsureNotDivergedFromRecording();
+  return PreambleResult::Redirect;
+}
 
+static void
+RR_objc_msgSend(Stream& aEvents, CallArguments* aArguments, ErrorType* aError)
+{
   auto& object = aArguments->Arg<0, id>();
   auto& message = aArguments->Arg<1, const char*>();
 
-  thread->Events().RecordOrReplayThreadEvent(CallIdToThreadEvent(CallEvent_objc_msgSend));
-  thread->Events().CheckInput(message);
-
-  bool handled = false;
+  aEvents.CheckInput(message);
 
-  // Watch for some top level NSApplication messages that can cause Gecko
-  // events to be processed.
-  if ((!strcmp(message, "run") ||
-       !strcmp(message, "nextEventMatchingMask:untilDate:inMode:dequeue:")) &&
-      TestObjCObjectClass(object, "NSApplication"))
-  {
-    PassThroughThreadEventsAllowCallbacks([&]() {
-        RecordReplayInvokeCall(CallEvent_objc_msgSend, aArguments);
-      });
-    handled = true;
-  }
-
-  // Other messages are performed as normal.
-  if (!handled && IsRecording()) {
-    AutoPassThroughThreadEvents pt;
-    RecordReplayInvokeCall(CallEvent_objc_msgSend, aArguments);
-  }
-
-  RecordReplayBytes(&aArguments->Rval<size_t>(), sizeof(size_t));
-  RecordReplayBytes(&aArguments->FloatRval(), sizeof(double));
+  aEvents.RecordOrReplayValue(&aArguments->Rval<size_t>());
+  aEvents.RecordOrReplayBytes(&aArguments->FloatRval(), sizeof(double));
 
   // Do some extra recording on messages that return additional state.
 
-  if (!strcmp(message, "countByEnumeratingWithState:objects:count:") &&
-      TestObjCObjectClass(object, "NSArray"))
-  {
+  if (!strcmp(message, "countByEnumeratingWithState:objects:count:")) {
     auto& state = aArguments->Arg<2, NSFastEnumerationState*>();
     auto& rval = aArguments->Rval<size_t>();
     if (IsReplaying()) {
       state->itemsPtr = NewLeakyArray<id>(rval);
       state->mutationsPtr = &gNeverChange;
     }
-    RecordReplayBytes(state->itemsPtr, rval * sizeof(id));
+    aEvents.RecordOrReplayBytes(state->itemsPtr, rval * sizeof(id));
   }
 
-  if (!strcmp(message, "getCharacters:") &&
-      TestObjCObjectClass(object, "NSString"))
-  {
+  if (!strcmp(message, "getCharacters:")) {
     size_t len = 0;
     if (IsRecording()) {
       AutoPassThroughThreadEvents pt;
       len = CFStringGetLength((CFStringRef) object);
     }
-    len = RecordReplayValue(len);
-    RecordReplayBytes(aArguments->Arg<2, void*>(), len * sizeof(wchar_t));
+    aEvents.RecordOrReplayValue(&len);
+    aEvents.RecordOrReplayBytes(aArguments->Arg<2, void*>(), len * sizeof(wchar_t));
   }
 
-  if ((!strcmp(message, "UTF8String") ||
-       !strcmp(message, "cStringUsingEncoding:")) &&
-      TestObjCObjectClass(object, "NSString"))
-  {
+  if (!strcmp(message, "UTF8String") || !strcmp(message, "cStringUsingEncoding:")) {
     auto& rval = aArguments->Rval<char*>();
-    size_t len = RecordReplayValue(IsRecording() ? strlen(rval) : 0);
+    size_t len = IsRecording() ? strlen(rval) : 0;
+    aEvents.RecordOrReplayValue(&len);
     if (IsReplaying()) {
       rval = NewLeakyArray<char>(len + 1);
     }
-    RecordReplayBytes(rval, len + 1);
+    aEvents.RecordOrReplayBytes(rval, len + 1);
+  }
+}
+
+static PreambleResult
+MiddlemanPreamble_objc_msgSend(CallArguments* aArguments)
+{
+  auto message = aArguments->Arg<1, const char*>();
+
+  // Ignore uses of NSAutoreleasePool after diverging from the recording.
+  // These are not performed in the middleman because the middleman has its
+  // own autorelease pool, and because the middleman can process calls from
+  // multiple threads which will cause these messages to behave differently.
+  if (!strcmp(message, "alloc") ||
+      !strcmp(message, "drain") ||
+      !strcmp(message, "init") ||
+      !strcmp(message, "release")) {
+    // Fake a return value in case the caller null checks it.
+    aArguments->Rval<size_t>() = 1;
+    return PreambleResult::Veto;
+  }
+
+  // Other messages will be handled by Middleman_objc_msgSend.
+  return PreambleResult::Redirect;
+}
+
+static void
+Middleman_PerformSelector(MiddlemanCallContext& aCx)
+{
+  Middleman_CString<2>(aCx);
+  Middleman_CFTypeArg<3>(aCx);
+
+  // The behavior of performSelector:withObject: varies depending on the
+  // selector used, so use a whitelist here.
+  if (aCx.mPhase == MiddlemanCallPhase::ReplayPreface) {
+    auto str = aCx.mArguments->Arg<2, const char*>();
+    if (strcmp(str, "appearanceNamed:")) {
+      aCx.MarkAsFailed();
+      return;
+    }
+  }
+
+  Middleman_AutoreleaseCFTypeRval(aCx);
+}
+
+static void
+Middleman_DictionaryWithObjects(MiddlemanCallContext& aCx)
+{
+  Middleman_Buffer<2, 4, const void*>(aCx);
+  Middleman_Buffer<3, 4, const void*>(aCx);
+
+  if (aCx.AccessPreface()) {
+    auto objects = aCx.mArguments->Arg<2, const void**>();
+    auto keys = aCx.mArguments->Arg<3, const void**>();
+    auto count = aCx.mArguments->Arg<4, CFIndex>();
+
+    for (CFIndex i = 0; i < count; i++) {
+      Middleman_ObjCInput(aCx, (id*) &objects[i]);
+      Middleman_ObjCInput(aCx, (id*) &keys[i]);
+    }
+  }
+
+  Middleman_AutoreleaseCFTypeRval(aCx);
+}
+
+static void
+Middleman_NSStringGetCharacters(MiddlemanCallContext& aCx)
+{
+  auto string = aCx.mArguments->Arg<0, CFStringRef>();
+  auto& buffer = aCx.mArguments->Arg<2, void*>();
+
+  if (aCx.mPhase == MiddlemanCallPhase::MiddlemanInput) {
+    size_t len = CFStringGetLength(string);
+    buffer = aCx.AllocateBytes(len * sizeof(UniChar));
   }
 
-  return PreambleResult::Veto;
+  if (aCx.AccessOutput()) {
+    size_t len =
+      (aCx.mPhase == MiddlemanCallPhase::MiddlemanOutput) ? CFStringGetLength(string) : 0;
+    aCx.ReadOrWriteOutputBytes(&len, sizeof(len));
+    if (aCx.mReplayOutputIsOld) {
+      buffer = aCx.AllocateBytes(len * sizeof(UniChar));
+    }
+    aCx.ReadOrWriteOutputBytes(buffer, len * sizeof(UniChar));
+  }
+}
+
+struct ObjCMessageInfo
+{
+  const char* mMessage;
+  MiddlemanCallFn mMiddlemanCall;
+};
+
+// All Objective C messages that can be called in the middleman, and hooks for
+// capturing any inputs and outputs other than the object and message.
+static ObjCMessageInfo gObjCMiddlemanCallMessages[] = {
+  { "performSelector:withObject:", Middleman_PerformSelector },
+  { "respondsToSelector:", Middleman_CString<2> },
+
+  // NSArray
+  { "count" },
+  { "objectAtIndex:", Middleman_AutoreleaseCFTypeRval },
+
+  // NSColor
+  { "currentControlTint" },
+
+  // NSDictionary
+  { "dictionaryWithObjects:forKeys:count:", Middleman_DictionaryWithObjects },
+
+  // NSFont
+  { "boldSystemFontOfSize:", Middleman_AutoreleaseCFTypeRval },
+  { "controlContentFontOfSize:", Middleman_AutoreleaseCFTypeRval },
+  { "familyName", Middleman_AutoreleaseCFTypeRval },
+  { "fontDescriptor", Middleman_AutoreleaseCFTypeRval },
+  { "menuBarFontOfSize:", Middleman_AutoreleaseCFTypeRval },
+  { "pointSize" },
+  { "smallSystemFontSize" },
+  { "systemFontOfSize:", Middleman_AutoreleaseCFTypeRval },
+  { "toolTipsFontOfSize:", Middleman_AutoreleaseCFTypeRval },
+  { "userFontOfSize:", Middleman_AutoreleaseCFTypeRval },
+
+  // NSFontManager
+  { "availableMembersOfFontFamily:", Middleman_Compose<Middleman_CFTypeArg<2>, Middleman_AutoreleaseCFTypeRval> },
+  { "sharedFontManager", Middleman_AutoreleaseCFTypeRval },
+
+  // NSNumber
+  { "numberWithBool:", Middleman_AutoreleaseCFTypeRval },
+  { "unsignedIntValue" },
+
+  // NSString
+  { "getCharacters:", Middleman_NSStringGetCharacters },
+  { "hasSuffix:", Middleman_CFTypeArg<2> },
+  { "isEqualToString:", Middleman_CFTypeArg<2> },
+  { "length" },
+  { "rangeOfString:options:", Middleman_CFTypeArg<2> },
+  { "stringWithCharacters:length:",
+    Middleman_Compose<Middleman_Buffer<2, 3, UniChar>, Middleman_AutoreleaseCFTypeRval> },
+
+  // NSWindow
+  { "coreUIRenderer", Middleman_AutoreleaseCFTypeRval },
+
+  // UIFontDescriptor
+  { "symbolicTraits" },
+};
+
+static void
+Middleman_objc_msgSend(MiddlemanCallContext& aCx)
+{
+  auto& object = aCx.mArguments->Arg<0, id>();
+  auto message = aCx.mArguments->Arg<1, const char*>();
+
+  for (const ObjCMessageInfo& info : gObjCMiddlemanCallMessages) {
+    if (!strcmp(info.mMessage, message)) {
+      if (aCx.AccessPreface()) {
+        Middleman_ObjCInput(aCx, &object);
+      }
+      if (info.mMiddlemanCall && !aCx.mFailed) {
+        info.mMiddlemanCall(aCx);
+      }
+      return;
+    }
+  }
+
+  if (aCx.mPhase == MiddlemanCallPhase::ReplayPreface) {
+    aCx.MarkAsFailed();
+  }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // Cocoa redirections
 ///////////////////////////////////////////////////////////////////////////////
 
 static void
+Middleman_CFArrayCreate(MiddlemanCallContext& aCx)
+{
+  Middleman_Buffer<1, 2, const void*>(aCx);
+
+  if (aCx.AccessPreface()) {
+    auto values = aCx.mArguments->Arg<1, const void**>();
+    auto numValues = aCx.mArguments->Arg<2, CFIndex>();
+    auto& callbacks = aCx.mArguments->Arg<3, const CFArrayCallBacks*>();
+
+    // For now we only support creating arrays with CFType elements.
+    if (aCx.mPhase == MiddlemanCallPhase::MiddlemanInput) {
+      callbacks = &kCFTypeArrayCallBacks;
+    } else {
+      MOZ_RELEASE_ASSERT(callbacks == &kCFTypeArrayCallBacks);
+    }
+
+    for (CFIndex i = 0; i < numValues; i++) {
+      Middleman_ObjCInput(aCx, (id*) &values[i]);
+    }
+  }
+
+  Middleman_CreateCFTypeRval(aCx);
+}
+
+static void
+Middleman_CFArrayGetValueAtIndex(MiddlemanCallContext& aCx)
+{
+  Middleman_CFTypeArg<0>(aCx);
+
+  auto array = aCx.mArguments->Arg<0, id>();
+
+  // We can't probe the array to see what callbacks it uses, so look at where
+  // it came from to see whether its elements should be treated as CFTypes.
+  MiddlemanCall* call = LookupMiddlemanCall(array);
+  bool isCFTypeRval = false;
+  if (call) {
+    switch (call->mCallId) {
+    case CallEvent_CTLineGetGlyphRuns:
+    case CallEvent_CTFontDescriptorCreateMatchingFontDescriptors:
+      isCFTypeRval = true;
+      break;
+    default:
+      break;
+    }
+  }
+
+  if (isCFTypeRval) {
+    Middleman_CFTypeRval(aCx);
+  }
+}
+
+static void
 RR_CFDataGetBytePtr(Stream& aEvents, CallArguments* aArguments, ErrorType* aError)
 {
   auto& rval = aArguments->Rval<UInt8*>();
 
   size_t len = 0;
   if (IsRecording()) {
     len = OriginalCall(CFDataGetLength, size_t, aArguments->Arg<0, CFDataRef>());
   }
   aEvents.RecordOrReplayValue(&len);
   if (IsReplaying()) {
     rval = NewLeakyArray<UInt8>(len);
   }
   aEvents.RecordOrReplayBytes(rval, len);
 }
 
+static void
+Middleman_CFDataGetBytePtr(MiddlemanCallContext& aCx)
+{
+  Middleman_CFTypeArg<0>(aCx);
+
+  auto data = aCx.mArguments->Arg<0, CFDataRef>();
+  auto& buffer = aCx.mArguments->Rval<void*>();
+
+  if (aCx.AccessOutput()) {
+    size_t len = (aCx.mPhase == MiddlemanCallPhase::MiddlemanOutput) ? CFDataGetLength(data) : 0;
+    aCx.ReadOrWriteOutputBytes(&len, sizeof(len));
+    if (aCx.mPhase == MiddlemanCallPhase::ReplayOutput) {
+      buffer = aCx.AllocateBytes(len);
+    }
+    aCx.ReadOrWriteOutputBytes(buffer, len);
+  }
+}
+
+static void
+Middleman_CFDictionaryCreate(MiddlemanCallContext& aCx)
+{
+  Middleman_Buffer<1, 3, const void*>(aCx);
+  Middleman_Buffer<2, 3, const void*>(aCx);
+
+  if (aCx.AccessPreface()) {
+    auto keys = aCx.mArguments->Arg<1, const void**>();
+    auto values = aCx.mArguments->Arg<2, const void**>();
+    auto numValues = aCx.mArguments->Arg<3, CFIndex>();
+    auto& keyCallbacks = aCx.mArguments->Arg<4, const CFDictionaryKeyCallBacks*>();
+    auto& valueCallbacks = aCx.mArguments->Arg<5, const CFDictionaryValueCallBacks*>();
+
+    // For now we only support creating dictionaries with CFType keys and values.
+    if (aCx.mPhase == MiddlemanCallPhase::MiddlemanInput) {
+      keyCallbacks = &kCFTypeDictionaryKeyCallBacks;
+      valueCallbacks = &kCFTypeDictionaryValueCallBacks;
+    } else {
+      MOZ_RELEASE_ASSERT(keyCallbacks == &kCFTypeDictionaryKeyCallBacks);
+      MOZ_RELEASE_ASSERT(valueCallbacks == &kCFTypeDictionaryValueCallBacks);
+    }
+
+    for (CFIndex i = 0; i < numValues; i++) {
+      Middleman_ObjCInput(aCx, (id*) &keys[i]);
+      Middleman_ObjCInput(aCx, (id*) &values[i]);
+    }
+  }
+
+  Middleman_CreateCFTypeRval(aCx);
+}
+
 static void DummyCFNotificationCallback(CFNotificationCenterRef aCenter, void* aObserver,
                                         CFStringRef aName, const void* aObject,
                                         CFDictionaryRef aUserInfo)
 {
   // FIXME
   //MOZ_CRASH();
 }
 
@@ -1326,25 +1861,54 @@ CFNumberTypeBytes(CFNumberType aType)
   case kCFNumberCFIndexType: return sizeof(CFIndex);
   case kCFNumberNSIntegerType: return sizeof(long);
   case kCFNumberCGFloatType: return sizeof(CGFloat);
   default: MOZ_CRASH();
   }
 }
 
 static void
+Middleman_CFNumberCreate(MiddlemanCallContext& aCx)
+{
+  if (aCx.AccessPreface()) {
+    auto numberType = aCx.mArguments->Arg<1, CFNumberType>();
+    auto& valuePtr = aCx.mArguments->Arg<2, void*>();
+    aCx.ReadOrWritePrefaceBuffer(&valuePtr, CFNumberTypeBytes(numberType));
+  }
+
+  Middleman_CreateCFTypeRval(aCx);
+}
+
+static void
 RR_CFNumberGetValue(Stream& aEvents, CallArguments* aArguments, ErrorType* aError)
 {
   auto& type = aArguments->Arg<1, CFNumberType>();
   auto& value = aArguments->Arg<2, void*>();
 
   aEvents.CheckInput(type);
   aEvents.RecordOrReplayBytes(value, CFNumberTypeBytes(type));
 }
 
+static void
+Middleman_CFNumberGetValue(MiddlemanCallContext& aCx)
+{
+  Middleman_CFTypeArg<0>(aCx);
+
+  auto& buffer = aCx.mArguments->Arg<2, void*>();
+  auto type = aCx.mArguments->Arg<1, CFNumberType>();
+  aCx.ReadOrWriteOutputBuffer(&buffer, CFNumberTypeBytes(type));
+}
+
+static PreambleResult
+MiddlemanPreamble_CFRetain(CallArguments* aArguments)
+{
+  aArguments->Rval<size_t>() = aArguments->Arg<0, size_t>();
+  return PreambleResult::Veto;
+}
+
 static PreambleResult
 Preamble_CFRunLoopSourceCreate(CallArguments* aArguments)
 {
   if (!AreThreadEventsPassedThrough()) {
     auto& cx = aArguments->Arg<2, CFRunLoopSourceContext*>();
 
     RegisterCallbackData(BitwiseCast<void*>(cx->perform));
     RegisterCallbackData(cx->info);
@@ -1375,32 +1939,91 @@ static void
 RR_CGBitmapContextCreateWithData(Stream& aEvents, CallArguments* aArguments, ErrorType* aError)
 {
   auto& data = aArguments->Arg<0, void*>();
   auto& height = aArguments->Arg<2, size_t>();
   auto& bytesPerRow = aArguments->Arg<4, size_t>();
   auto& rval = aArguments->Rval<CGContextRef>();
 
   MOZ_RELEASE_ASSERT(Thread::CurrentIsMainThread());
-  gContextData.emplaceBack(rval, data, height * bytesPerRow);
+
+  // When replaying, Middleman_CGBitmapContextCreateWithData will take care of
+  // updating gContextData with the right context pointer (after being mangled
+  // in Middleman_SystemOutput).
+  if (IsRecording()) {
+    gContextData.emplaceBack(rval, data, height * bytesPerRow);
+  }
+}
+
+static void
+Middleman_CGBitmapContextCreateWithData(MiddlemanCallContext& aCx)
+{
+  Middleman_CFTypeArg<5>(aCx);
+  Middleman_StackArgumentData<3 * sizeof(size_t)>(aCx);
+  Middleman_CreateCFTypeRval(aCx);
+
+  auto& data = aCx.mArguments->Arg<0, void*>();
+  auto height = aCx.mArguments->Arg<2, size_t>();
+  auto bytesPerRow = aCx.mArguments->Arg<4, size_t>();
+  auto rval = aCx.mArguments->Rval<CGContextRef>();
+
+  if (aCx.mPhase == MiddlemanCallPhase::MiddlemanInput) {
+    data = aCx.AllocateBytes(height * bytesPerRow);
+  }
+
+  if ((aCx.mPhase == MiddlemanCallPhase::ReplayPreface && !HasDivergedFromRecording()) ||
+      (aCx.AccessOutput() && !aCx.mReplayOutputIsOld)) {
+    gContextData.emplaceBack(rval, data, height * bytesPerRow);
+  }
 }
 
 template <size_t ContextArgument>
 static void
 RR_FlushCGContext(Stream& aEvents, CallArguments* aArguments, ErrorType* aError)
 {
   auto& context = aArguments->Arg<ContextArgument, CGContextRef>();
 
   for (int i = gContextData.length() - 1; i >= 0; i--) {
     if (context == gContextData[i].mContext) {
-      RecordReplayBytes(gContextData[i].mData, gContextData[i].mDataSize);
+      aEvents.RecordOrReplayBytes(gContextData[i].mData, gContextData[i].mDataSize);
       return;
     }
   }
-  MOZ_CRASH();
+  MOZ_CRASH("RR_FlushCGContext");
+}
+
+template <size_t ContextArgument>
+static void
+Middleman_FlushCGContext(MiddlemanCallContext& aCx)
+{
+  auto context = aCx.mArguments->Arg<ContextArgument, CGContextRef>();
+
+  // Update the contents of the target buffer in the middleman process to match
+  // the current contents in the replaying process.
+  if (aCx.AccessInput()) {
+    for (int i = gContextData.length() - 1; i >= 0; i--) {
+      if (context == gContextData[i].mContext) {
+        aCx.ReadOrWriteInputBytes(gContextData[i].mData, gContextData[i].mDataSize);
+        return;
+      }
+    }
+    MOZ_CRASH("Middleman_FlushCGContext");
+  }
+
+  // After performing the call, the buffer in the replaying process is updated
+  // to match any data written by the middleman.
+  if (aCx.AccessOutput()) {
+    for (int i = gContextData.length() - 1; i >= 0; i--) {
+      if (context == gContextData[i].mContext) {
+        aCx.ReadOrWriteOutputBytes(gContextData[i].mData, gContextData[i].mDataSize);
+        return;
+      }
+    }
+    MOZ_CRASH("Middleman_FlushCGContext");
+  }
 }
 
 static PreambleResult
 Preamble_CGContextRestoreGState(CallArguments* aArguments)
 {
   return IsRecording() ? PreambleResult::PassThrough : PreambleResult::Veto;
 }
 
@@ -1465,16 +2088,42 @@ RR_CTRunGetElements(Stream& aEvents, Cal
   }
   aEvents.RecordOrReplayValue(&count);
   if (IsReplaying()) {
     rval = NewLeakyArray<ElemType>(count);
   }
   aEvents.RecordOrReplayBytes(rval, count * sizeof(ElemType));
 }
 
+template <typename ElemType, void (*GetElemsFn)(CTRunRef, CFRange, ElemType*)>
+static void
+Middleman_CTRunGetElements(MiddlemanCallContext& aCx)
+{
+  Middleman_CFTypeArg<0>(aCx);
+
+  if (aCx.AccessOutput()) {
+    auto run = aCx.mArguments->Arg<0, CTRunRef>();
+    auto& rval = aCx.mArguments->Rval<ElemType*>();
+
+    size_t count = 0;
+    if (IsMiddleman()) {
+      count = CTRunGetGlyphCount(run);
+      if (!rval) {
+        rval = (ElemType*) aCx.AllocateBytes(count * sizeof(ElemType));
+        GetElemsFn(run, CFRangeMake(0, 0), rval);
+      }
+    }
+    aCx.ReadOrWriteOutputBytes(&count, sizeof(count));
+    if (IsReplaying()) {
+      rval = (ElemType*) aCx.AllocateBytes(count * sizeof(ElemType));
+    }
+    aCx.ReadOrWriteOutputBytes(rval, count * sizeof(ElemType));
+  }
+}
+
 static PreambleResult
 Preamble_OSSpinLockLock(CallArguments* aArguments)
 {
   auto& lock = aArguments->Arg<0, OSSpinLock*>();
 
   // These spin locks never need to be recorded, but they are used by malloc
   // and can end up calling other recorded functions like mach_absolute_time,
   // so make sure events are passed through here. Note that we don't have to
@@ -1540,25 +2189,16 @@ void
 DirectUnprotectMemory(void* aAddress, size_t aSize, bool aExecutable,
                       bool aIgnoreFailures /* = false */)
 {
   ssize_t rv = OriginalCall(mprotect, int, aAddress, aSize,
                             PROT_READ | PROT_WRITE | (aExecutable ? PROT_EXEC : 0));
   MOZ_RELEASE_ASSERT(aIgnoreFailures || rv == 0);
 }
 
-// From chromium/src/base/eintr_wrapper.h
-#define HANDLE_EINTR(x) ({ \
-  typeof(x) __eintr_result__; \
-  do { \
-    __eintr_result__ = x; \
-  } while (__eintr_result__ == -1 && errno == EINTR); \
-  __eintr_result__;\
-})
-
 void
 DirectSeekFile(FileHandle aFd, uint64_t aOffset)
 {
   static_assert(sizeof(uint64_t) == sizeof(off_t), "off_t should have 64 bits");
   ssize_t rv = HANDLE_EINTR(OriginalCall(lseek, int, aFd, aOffset, SEEK_SET));
   MOZ_RELEASE_ASSERT(rv >= 0);
 }