ValueRender

internal object ValueRender

The => type = value line, for both the single-tick result and every cross-tick McpScope.yield — one function, so the two cannot drift apart.

toString() is the fallback and is right for almost everything. It is replaced only where the JVM representation is an address or a lie (an unsigned array prints its signed storage). Value classes are reachable by is here only when they arrive boxed, which yield's reified parameter guarantees; a single-tick value-class RESULT does not, since it is read back off an unboxed backing field.

A container is walked only when the walk CHANGES something, so a type keeps its own toString() unless it holds something opaque. A container reached from inside itself renders as CYCLE, and that counts as a change — so a cyclic value never falls back to the toString() that would follow the cycle forever.

Everything appends into ONE builder and answers with a Boolean, so the payload is materialized exactly once. A String per node costs one copy per level of nesting plus one for the head — at depth 1, the whole payload.

Types

Link copied to clipboard
private class Sink(val sb: StringBuilder)

One line call's state: the buffer and the ancestors on the way down — one lifetime, so they travel as fields rather than through every signature.

Properties

Link copied to clipboard
private const val CYCLE: String

A container reached from inside itself; the spelling Arrays.deepToString uses for the same thing.

Link copied to clipboard
private const val MAX_NESTING: Int = 64

A hard stop on nesting. This walk spends several frames per level — where the contentDeepToString it replaced spent one — and it runs on the game thread, so depth has to be bounded somewhere. The chain is the ancestor list, so its size IS the depth; no counter to thread through.

Link copied to clipboard
private const val TOO_DEEP: String

Nesting past MAX_NESTING: there is more here, it just isn't worth the stack. Angle-bracketed like <lambda> and threw — this file's mark for the renderer speaking rather than showing a value.

Functions

Link copied to clipboard
private fun ambiguousBare(v: String): Boolean

Whether v read bare would say something it is not — nothing at all, or a null. Not every string: quoting costs two characters per element inside a container. Edges tested directly, not through trim(), which copies the whole string exactly when the check matches. isEmpty first, or v[0] throws.

Link copied to clipboard
fun line(type: String, value: Any?): StringBuilder

The builder, not a String: the head is already in it when the payload lands, so it costs no copy of its own. Both callers consume it at once — one toString(), one append.

Link copied to clipboard
private fun overridesToString(v: Any): Boolean

Whether v's class says anything of its own, or inherits Object's Type@hexHash. Any::class.java IS java.lang.Object.class — the comparison is against the real declaring class, not a Kotlin alias.

Link copied to clipboard
private fun threw(v: Any, t: Throwable): String

toString() is caller code and may throw; a value that cannot render must not fail the eval that made it. Per ELEMENT so one bad entry costs its slot, not the line — Sink.render backstops what walking can't reach.