Generate SSD crash logging from typed Jerboa

ober

8a847bdd3743baa0b58d71244e2d6a9ac029f7a7

diff --git a/templates/ssd-review.ss b/templates/ssd-review.ss
index b15e067..6617b91 100644
--- a/templates/ssd-review.ss
+++ b/templates/ssd-review.ss
@@ -8053,6 +8053,121 @@
               crashingThread
               error)))))
 
+    (typed-kotlin-file "com/sfb/ssdreview/ClientCrashInstall.kt"
+      (kotlin-imports (java lang Runnable)
+                      (java lang Thread)
+                      (java lang Thread UncaughtExceptionHandler)
+                      (org json JSONObject))
+      (typed-library (com sfb ssdreview)
+        (export mainActivityInstallClientCrashLoggerHandler)
+        (type Int)
+        (type Int32)
+        (type Exception)
+        (type Runnable)
+        (type Thread)
+        (type Throwable)
+        (type UncaughtExceptionHandler)
+        (type JSONObject)
+        (type MainActivity)
+        (extern (threadDefaultUncaughtExceptionHandlerRaw)
+            : (Nullable UncaughtExceptionHandler)
+          (kotlin-call Thread getDefaultUncaughtExceptionHandler))
+        (extern (threadSetDefaultUncaughtExceptionHandlerRaw
+                  (handler : UncaughtExceptionHandler)) : Unit
+          (kotlin-call Thread setDefaultUncaughtExceptionHandler))
+        (extern (makeCrashThread (runnable : Runnable)) : Thread
+          (kotlin-call Thread))
+        (extern (threadStartCrashRaw (thread : Thread)) : Unit
+          (kotlin-member-call start))
+        (extern (threadJoinCrashRaw (thread : Thread)
+                                    (timeoutMillis : Int)) : Unit
+          (kotlin-member-call join))
+        (extern (threadNameRaw (thread : Thread)) : String
+          (kotlin-member-get name))
+        (extern (mainActivityCrashLogPayloadRaw
+                  (activity : MainActivity)
+                  (event : String)
+                  (fields : JSONObject)) : JSONObject
+          (kotlin-member-call clientLogPayload))
+        (extern (mainActivityCrashSendClientLogRaw
+                  (activity : MainActivity)
+                  (payload : JSONObject)
+                  (readTimeoutMillis : Int32)) : Unit
+          (kotlin-member-call sendClientLog))
+        (extern (mainActivityThrowableLogRaw
+                  (activity : MainActivity)
+                  (error : Throwable)) : JSONObject
+          (kotlin-member-call throwableLog))
+        (def (startCrashSenderThread
+               (activity : MainActivity)
+               (payload : JSONObject)
+               (readTimeoutMillis : Int32)) : Thread
+          (let ((sender
+                  (makeCrashThread
+                    (object Runnable
+                      (override (run) : Unit
+                        (mainActivityCrashSendClientLogRaw
+                          activity
+                          payload
+                          readTimeoutMillis))))))
+            (begin
+              (threadStartCrashRaw sender)
+              sender)))
+        (def (crashPayloadFor
+               (activity : MainActivity)
+               (crashingThread : Thread)
+               (throwable : Throwable)) : JSONObject
+          (try
+            (mainActivityCrashLogPayloadRaw
+              activity
+              "ssd-uncaught-exception"
+              (clientLogCrashFields
+                (nullableTextOrEmpty
+                  (nullable-some (threadNameRaw crashingThread)))
+                (mainActivityThrowableLogRaw activity throwable)))
+            (catch (error : Exception)
+              (clientLogFallbackPayload "ssd-uncaught-exception"))))
+        (def (sendCrashPayloadBlocking
+               (activity : MainActivity)
+               (payload : JSONObject)
+               (readTimeoutMillis : Int32)
+               (joinTimeoutMillis : Int)) : Unit
+          (try
+            (let ((sender
+                    (startCrashSenderThread
+                      activity
+                      payload
+                      readTimeoutMillis)))
+              (threadJoinCrashRaw sender joinTimeoutMillis))
+            (catch (error : Exception)
+              (begin))))
+        (def (mainActivityInstallClientCrashLoggerHandler
+               (activity : MainActivity)
+               (readTimeoutMillis : Int32)
+               (joinTimeoutMillis : Int)) : Unit
+          (let ((previous (threadDefaultUncaughtExceptionHandlerRaw)))
+            (threadSetDefaultUncaughtExceptionHandlerRaw
+              (object UncaughtExceptionHandler
+                (override (uncaughtException
+                            (crashingThread : Thread)
+                            (throwable : Throwable)) : Unit
+                  (let ((payload
+                          (crashPayloadFor
+                            activity
+                            crashingThread
+                            throwable)))
+                    (begin
+                      (sendCrashPayloadBlocking
+                        activity
+                        payload
+                        readTimeoutMillis
+                        joinTimeoutMillis)
+                      (finishUncaughtException
+                        previous
+                        crashingThread
+                        throwable
+                        (int32 2)))))))))))
+
     (typed-kotlin-file "com/sfb/ssdreview/TruthIndexSelect.kt"
       (typed-library (com sfb ssdreview)
         (export bestTruthIndexForSession truthIndexEntryPresent)
@@ -9879,22 +9994,11 @@
        "    private fun installClientCrashLogger() {"
        "        if (clientCrashLoggerInstalled) return"
        "        clientCrashLoggerInstalled = true"
-       "        val previous = Thread.getDefaultUncaughtExceptionHandler()"
-       "        Thread.setDefaultUncaughtExceptionHandler { crashingThread, throwable ->"
-       "            val payload = try {"
-       "                clientLogPayload("
-       "                    \"ssd-uncaught-exception\","
-       "                    clientLogCrashFields(nullableTextOrEmpty(crashingThread.name), throwableLog(throwable))"
-       "                )"
-       "            } catch (_: Exception) { clientLogFallbackPayload(\"ssd-uncaught-exception\") }"
-       "            try {"
-       "                val sender = thread(start = true) { sendClientLog(payload, CLIENT_LOG_CRASH_TIMEOUT_MS) }"
-       "                sender.join((CLIENT_LOG_CRASH_TIMEOUT_MS + 500).toLong())"
-       "            } catch (_: Exception) {"
-       "                // Diagnostics must not change Android crash handling."
-       "            }"
-       "            finishUncaughtException(previous, crashingThread, throwable, 2)"
-       "        }"
+       "        mainActivityInstallClientCrashLoggerHandler("
+       "            this,"
+       "            CLIENT_LOG_CRASH_TIMEOUT_MS,"
+       "            (CLIENT_LOG_CRASH_TIMEOUT_MS + 500).toLong()"
+       "        )"
        "    }"
        ""
        "    private fun logClientEvent(event: String, fields: JSONObject = emptyJsonObject()) {"
@@ -9975,7 +10079,7 @@
        "            nullableTextOrEmpty(Build.MODEL)"
        "        )"
        ""
-       "    private fun throwableLog(error: Throwable): JSONObject {"
+       "    internal fun throwableLog(error: Throwable): JSONObject {"
        "        val writer = StringWriter()"
        "        error.printStackTrace(PrintWriter(writer))"
        "        val stack = writer.toString()"