<?xml version="1.0"?>
<rss version="2.0">
<channel>
	<title>Planet Lisp</title>
	<link>http://planet.lisp.org/</link>
	<description>Planet Lisp</description>
	<language>en</language>


<item>
	<title>Eugene Zaikonnikov: The Net is Too Damn Fast</title>
	<guid isPermaLink="true">http://blog.funcall.org//lisp/2026/07/06/net-is-too-damn-fast/</guid>
	<link>http://blog.funcall.org//lisp/2026/07/06/net-is-too-damn-fast/</link>
	
	<description>&lt;p&gt;Recently I stumbled on a funny kind of race in distributed systems. I believe even the &lt;a href="https://en.wikipedia.org/wiki/Fallacies_of_distributed_computing"&gt;classic texts&lt;/a&gt; don't cover that.&lt;/p&gt;

&lt;p&gt;Say we have a system &lt;code class="language-plaintext highlighter-rouge"&gt;S&lt;/code&gt; sending commands to a receiver &lt;code class="language-plaintext highlighter-rouge"&gt;R&lt;/code&gt; over network. &lt;code class="language-plaintext highlighter-rouge"&gt;S&lt;/code&gt; maintains network outbox and inbox handled by separate threads. A command is expected to complete with certain result sent back before the deadline, or else &lt;code class="language-plaintext highlighter-rouge"&gt;S&lt;/code&gt; would assume a request timed out. Very basic so far.&lt;/p&gt;

&lt;p&gt;However a certain class of commands (and only it) was timing out. They would fail regularly on some networks, sporadically on others and seemingly never on some. Perplexingly they were really simple commands, amounting to little else than sending back a reading of &lt;code class="language-plaintext highlighter-rouge"&gt;R&lt;/code&gt;'s internal state. Increasing the command deadline had no apparent effect. Adding logging in places helped little and in fact often resulted in the issue disappearing.&lt;/p&gt;

&lt;p&gt;Simplifying it quite a bit the &lt;code class="language-plaintext highlighter-rouge"&gt;S&lt;/code&gt; side looked something like this:&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-lisp"&gt;&lt;table class="rouge-table"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class="gutter gl"&gt;&lt;pre class="lineno"&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;handle-outbox&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;socket&lt;/span&gt; &lt;span class="nv"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;command&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;pop&lt;/span&gt; &lt;span class="nv"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;handler-case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;send-command&lt;/span&gt; &lt;span class="nv"&gt;socket&lt;/span&gt; &lt;span class="nv"&gt;command&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;network-error&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
	&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;log-network-error&lt;/span&gt; &lt;span class="nv"&gt;c&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:no-error&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
	&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;register-command-awaiting-response&lt;/span&gt; &lt;span class="nv"&gt;command&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;handle-inbox&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let*&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;response&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;receive-incoming&lt;/span&gt; &lt;span class="nv"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
	 &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;corresponding-command&lt;/span&gt;
	  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;command-awaiting-response&lt;/span&gt; &lt;span class="nv"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;when&lt;/span&gt; &lt;span class="nv"&gt;corresponding-command&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;process-reply-for-command&lt;/span&gt; &lt;span class="nv"&gt;command&lt;/span&gt; &lt;span class="nv"&gt;response&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;You see now what was happening: &lt;code class="language-plaintext highlighter-rouge"&gt;S&lt;/code&gt; was sending the command, &lt;code class="language-plaintext highlighter-rouge"&gt;R&lt;/code&gt; processing it, sending the reply and &lt;code class="language-plaintext highlighter-rouge"&gt;S&lt;/code&gt; handling the reply &lt;em&gt;before&lt;/em&gt; the bookkeeping of outbox process would record the command. Then the response wouldn't match anything and be discarded as orphaned. Later the inbox would timeout the command (not shown).&lt;/p&gt;

&lt;p&gt;The bookkeeping wasn't even that heavyweight: just storing some structures and perhaps couple expensive calls to set up a condition variable but that was enough.&lt;/p&gt;

&lt;p&gt;Naturally any kind of latency (be it due to network load or extraneous syslog calls) would alleviate that. The fix was to make the code inelegant but correct by registering the command before the send attempt and un-registering in the event of failure.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;tl;dr a network can be way too fast&lt;/em&gt;&lt;/p&gt;</description>
	
	<pubDate>Mon, 06 Jul 2026 00:00:00 GMT</pubDate>
</item>

<item>
	<title>Joe Marshall: New chatbot</title>
	<guid isPermaLink="true">http://funcall.blogspot.com/2026/06/new-chatbot.html</guid>
	<link>http://funcall.blogspot.com/2026/06/new-chatbot.html</link>
	
	<description>&lt;p&gt;Lately I've been playing with writing a &lt;a href="http://github.com/jrm-code-project/chatbot"&gt;chatbot&lt;/a&gt; library in Common Lisp.&lt;/p&gt;

&lt;p&gt;My previous gemini bindings were getting unweildy.  I wanted to add the ability to run LLMs on my local machine but it turned out
  to be really kind of kludgy, so I decided to start from scratch with multiple back ends in mind.&lt;/p&gt;

&lt;p&gt;I've got it to the point where in supports multiple back ends, so now I can prompt local LLMs from Lisp.&lt;/p&gt;

&lt;p&gt;Recently I added the ability to recursively launch chatbots that can call each other.  Since the chatbots do not share 
  their contexts, this greatly reduces the context bloat of thet main chat because it can spawn off subtasks to a minion and not 
  pollute the main context.  This also allows you to create a federation of chatbots, each of which specializes in some topic and is overseen by a controlling chatbot that talks to the user.&lt;/p&gt;

&lt;p&gt;Chatbots can be serialized and checkpointed, so if one is carrying out an agentic task and Lisp crashes, when we restart
  the agentic tasks are restarted as well and pick up where they left off.&lt;/p&gt;

&lt;p&gt;IT turns out that recursive chats are a useful abstraction once you figure out how to use them.  Basically any prompt you may issue may also want to be issued by an llm and this enables that to happen.  It allows you to run subprocesses that would otherwise put junk in your context, for example reading the contents of a lange number of files.  If you put that into a rocursive chatbot, it could
  slurp up the files into its context without adding tokens to the parent chat.&lt;/p&gt;

&lt;p&gt;You can use a recursive chat as a `smart component'.  The recursive chat can have a specialized system instruction and can preload its context with relevant information specific to it.  It's context doesn't get diluted by the caller's context&lt;/p&gt;</description>
	
	<pubDate>Sun, 28 Jun 2026 22:52:33 GMT</pubDate>
</item>

<item>
	<title>Joe Marshall: Anecdote or data point</title>
	<guid isPermaLink="true">http://funcall.blogspot.com/2026/06/anecdote-or-data-point.html</guid>
	<link>http://funcall.blogspot.com/2026/06/anecdote-or-data-point.html</link>
	
	<description>&lt;p&gt;I saw that there was some argument over how much slower slot access is than struct access, so
I just decided to measure it naively.  I made a two slot sruct and a CLOS version of a CONS cell
with &lt;code&gt;car&lt;/code&gt; and &lt;code&gt;cdr&lt;/code&gt; slots
and I ran LTAK using regular lists, `lists' made from CLOS conses, and `lists' made from structs.
Here are the results:&lt;/p&gt;

&lt;pre&gt;
D:\repositories\clos-benchmark&amp;gt;sbcl --script run-benchmarks.lisp
Benchmark: ltak over native cons cells, CLOS my-cons nodes, and my-cons-struct nodes
Inputs: x=15 y=9 z=4 repeats=35

Scenario                   min-ms     mean-ms      max-ms      ratio
--------------------------------------------------------------------
native standard               0.129      0.146      0.186
clos standard                 1.346      1.365      1.475       9.37x
struct standard               0.172      0.175      0.179       1.20x
native optimized              0.068      0.069      0.073
clos optimized                0.411      0.414      0.419       6.04x
struct optimized              0.068      0.069      0.073       1.01x&lt;/pre&gt;

&lt;p&gt;In this naive use case, structs are same as native cons cells, but CLOS objects are one ninth the speed of a struct or cons cell if you just use it unoptimized, and one sixth the speed if optimizations are turned on.&lt;/p&gt;
  
&lt;p&gt;But the CLOS instance is more functional than the cons cell in mimics.  For instance, I could add a slot to the class and all the instances would be lazily updated with the new slot.  I can also subclass the CLOS class and the selector functions will continue to work.  Finally, I can redefine the CLOS closs while I'm developing it and all the instances will be uppdated.
  THe machinery to keep all this running is costing us our factor of 9.&lt;/p&gt;
  
  &lt;p&gt;But this might be worth the cost if we are running on a network where the bulk of the time will be transmitting the answer down the pipe once it is computed.  Taking a few extra milliseconds to compute the answer might be worth the convenience features of CLOS.&lt;/p&gt;</description>
	
	<pubDate>Thu, 25 Jun 2026 16:11:51 GMT</pubDate>
</item>

<item>
	<title>Joe Marshall: Controlled Unclassified Information</title>
	<guid isPermaLink="true">http://funcall.blogspot.com/2026/06/controlled-unclassified-information.html</guid>
	<link>http://funcall.blogspot.com/2026/06/controlled-unclassified-information.html</link>
	
	<description>&lt;p&gt;Back in the day, the US government had a program called SBIR (Small
  Business Innovation Research) that funded small businesses to do
  research and development.  I recall sitting in our dorm in college,
  reading through a giant printed catalog of SBIR grants just to amuse
  ourselves by brainstorming solutions over bad pizza.&lt;/p&gt;.

&lt;p&gt;So, I got curious the other day: what does the SBIR landscape look like now?&lt;/p&gt;

&lt;p&gt;I can tell you right now: do &lt;em&gt;not&lt;/em&gt; even try to &lt;em&gt;read&lt;/em&gt;
  an SBIR solicitation on your local machine. You are opening yourself
  up to a world of absolute, unmitigated pain.&lt;/p&gt;


&lt;p&gt;You might think, what harm could there be in simply opening a
  file?&lt;/p&gt;

&lt;p&gt;Well, in the modern compliance panopticon, any manipulation of digital information that comes from
  the govenment has the potential to spawn CUI (Controlled
  Unclassified Information).  CUI  is basically a digital pathogen;
  once you download that file, *anything whatsover*
  derived from it, including notes and metadata, instantly becomes CUI by
association. The moment you
  read an SBIR on your computer, you've infected your
system, rendering you subject to a nightmare of  Byzantine federal regulations.&lt;/p&gt;

&lt;p&gt;These days, the amount of beurocratic red tape surrounding 
  CUI is insane.  To
even look at the file legally, you need a dedicated, air-gapped machine
completely disconnected from the internet, conforming to a massive, expensive
slew of NIST standards covering everything from hardware-level encryption to
strict access controls. 
  Alternatively you could contract with a cloud company that offers a
  pre-certified "CUI-compliant" environment.&lt;/p&gt;

&lt;p&gt;And assuming you actually shell out the cash and jump through the hoops to set
up this digital containment zone just to read a PDF, you must meticulously audit
and account for every single action you take in its presence.   Under current
federal auditing logic, you are explicitly assumed to be attempting to defraud
the government unless you can produce a mountain of paper proving otherwise.
Want to bring in a partner to bounce ideas around? You can't just "know a guy."
You have to navigate a labyrinth of federal subcontracting regulations.&lt;/p&gt;



&lt;p&gt;I had intended on amusing myself by reading some SBIRs and
  daydreaming about solutions that might involve Lisp (an impossibility in the
modern enterprise stack for entirely separate, depressing reasons).
  Instead, I quickly discovered I did not
  even own  the physical hardware required to even &lt;em&gt;read&lt;/em&gt; an SBIR without running
  afoul of federal regulations.&lt;/p&gt;

&lt;p&gt;I wanted to read some clever and inspiring engineering proposals. I ended up reading a lot of very dry and boring
  compliance regulations.&lt;/p&gt;</description>
	
	<pubDate>Thu, 18 Jun 2026 11:48:58 GMT</pubDate>
</item>

<item>
	<title>Joe Marshall: Regression</title>
	<guid isPermaLink="true">http://funcall.blogspot.com/2026/06/regression.html</guid>
	<link>http://funcall.blogspot.com/2026/06/regression.html</link>
	
	<description>&lt;p&gt;Last year I wrote some Lisp related AI apps.  There was a syntax highlighter that used the LLM to determine how to colorize and highlight syntax, and a prompt refiner that takes a wimpy LLM prompt and creates more elaborate prompt from them.&lt;/p&gt;

&lt;p&gt;I took the apps down last week.  They were `vibe coded' and therefore approximate and had bugs (but that's to be expected), but they had a security hole where you could hijack the LLM processing with your own prompt turning my app into an open relay using my API key.
  Last week I discovered that my AI spend on video creation was becoming serious.  This is odd because I never create AI video.  It turned out that my app was being hijacked by a proxy in Luxembourg and was generating videos on my dime.&lt;/p&gt;

&lt;p&gt;So I shut down the apps.  I knew they had the potential of being abused, and I was willing to tolerate a small amount of abuse, but it didn't occur to me that syntax highlighter could be hijacked to generate gigabytes of video at my expense.  Future applications will be careful to obtain the API key from the user.&lt;/p&gt;</description>
	
	<pubDate>Mon, 01 Jun 2026 07:00:00 GMT</pubDate>
</item>

<item>
	<title>Joe Marshall: CLRHack: Meta-object Protocol</title>
	<guid isPermaLink="true">http://funcall.blogspot.com/2026/05/clrhack-meta-object-protocol.html</guid>
	<link>http://funcall.blogspot.com/2026/05/clrhack-meta-object-protocol.html</link>
	
	<description>&lt;h3&gt;Metaobject Protocol (MOP) Implementation in CLRHack&lt;/h3&gt;

  &lt;p&gt;The Metaobject Protocol in CLRHack is a high-performance implementation of the Common Lisp Object System (CLOS) integrated into
  the .NET 8.0 Common Language Runtime (CLR). It provides a complete meta-compilation pipeline that bridges the gap between dynamic
  Lisp semantics and the static CIL (Common Intermediate Language) execution model.&lt;/p&gt;

  &lt;h4&gt;Core Architecture&lt;/h4&gt;
  &lt;p&gt;The MOP is implemented through three primary layers:&lt;/p&gt;
  &lt;ol&gt;
      &lt;li&gt;&lt;strong&gt;The Metaobject Hierarchy (C#):&lt;/strong&gt; A set of foundational classes in &lt;code&gt;LispBase&lt;/code&gt; representing
  classes, methods, generic functions, and slot definitions.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;The Runtime Engine (&lt;code&gt;MopRuntime&lt;/code&gt;):&lt;/strong&gt; A centralized orchestrator that manages class finalization,
  method combination, dispatch caching, and instance allocation.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;The Compiler Bridge (Lisp):&lt;/strong&gt; Transformations in &lt;code&gt;ast.lisp&lt;/code&gt; that translate high-level CLOS forms
  (&lt;code&gt;defclass&lt;/code&gt;, &lt;code&gt;defmethod&lt;/code&gt;) into optimized runtime calls.&lt;/li&gt;
  &lt;/ol&gt;

  &lt;h4&gt;Instance Representation&lt;/h4&gt;
  &lt;p&gt;Because the CLR type system is strictly single-inheritance and statically defined, CLRHack decouples Lisp-level inheritance from
  C# inheritance. All CLOS instances are represented by the &lt;code&gt;StandardObjectInstance&lt;/code&gt; class, which contains:&lt;/p&gt;
  &lt;ul&gt;
      &lt;li&gt;A reference to its &lt;code&gt;ClassMetaobject&lt;/code&gt;.&lt;/li&gt;
      &lt;li&gt;A private &lt;code&gt;object[] storage&lt;/code&gt; array for instance slots, indexed by locations calculated during class
  finalization.&lt;/li&gt;
  &lt;/ul&gt;

  &lt;h4&gt;The Dispatch Pipeline&lt;/h4&gt;
  &lt;p&gt;Generic function invocation is the most complex part of the implementation. When a generic function is called:&lt;/p&gt;
  &lt;ol&gt;
      &lt;li&gt;&lt;strong&gt;Cache Lookup:&lt;/strong&gt; The &lt;code&gt;DiscriminatingFunction&lt;/code&gt; first checks a thread-safe
  &lt;code&gt;dispatchCache&lt;/code&gt; using an &lt;code&gt;InvocationCacheKey&lt;/code&gt; (a stack-allocated &lt;code&gt;struct&lt;/code&gt;) to find a previously
  computed effective method.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Applicability &amp; Precedence:&lt;/strong&gt; If the cache misses, the runtime computes all applicable methods and sorts
  them based on specializer specificity and the Class Precedence List (CPL).&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Method Combination:&lt;/strong&gt; The &lt;code&gt;ComputeEffectiveMethod&lt;/code&gt; logic builds a nested execution chain
  following the &lt;strong&gt;Standard Method Combination&lt;/strong&gt; rules:
          &lt;ul&gt;
              &lt;li&gt;&lt;code&gt;:around&lt;/code&gt; methods are called first, with &lt;code&gt;call-next-method&lt;/code&gt; progressing to the next around
  method or the main chain.&lt;/li&gt;
              &lt;li&gt;The main chain executes all &lt;code&gt;:before&lt;/code&gt; methods, the primary method, and finally all &lt;code&gt;:after&lt;/code&gt;
  methods in reverse order.&lt;/li&gt;
          &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Fast Invocation:&lt;/strong&gt; The resulting effective method is compiled into a &lt;code&gt;Func&amp;lt;object[],
  object&amp;gt;&lt;/code&gt; that uses direct delegate invocation to minimize overhead.&lt;/li&gt;
  &lt;/ol&gt;

  &lt;h4&gt;Challenges and Solutions&lt;/h4&gt;

  &lt;h5&gt;1. Thread-Safe Non-Local Exits (call-next-method)&lt;/h5&gt;
  &lt;p&gt;&lt;strong&gt;Challenge:&lt;/strong&gt; &lt;code&gt;call-next-method&lt;/code&gt; and &lt;code&gt;next-method-p&lt;/code&gt; require access to the current
  invocation's state (the remaining methods and original arguments). Passing this state through every function call would break
  compatibility with standard Lisp function signatures.&lt;/p&gt;
  &lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; CLRHack utilizes &lt;code&gt;[ThreadStatic]&lt;/code&gt; fields in &lt;code&gt;MopRuntime&lt;/code&gt; to store the
  &lt;code&gt;currentNextMethods&lt;/code&gt; and &lt;code&gt;currentArguments&lt;/code&gt;. This ensures that even in highly concurrent environments (like a
  web server), each OS thread has its own isolated invocation context, allowing &lt;code&gt;call-next-method&lt;/code&gt; to function correctly
  without state leakage.&lt;/p&gt;

  &lt;h5&gt;2. Forward References and Lazy Finalization&lt;/h5&gt;
  &lt;p&gt;&lt;strong&gt;Challenge:&lt;/strong&gt; Lisp allows classes to refer to superclasses that haven't been defined yet. The runtime must handle
  these "zombie" classes without crashing the JIT compiler.&lt;/p&gt;
  &lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; The system implements a &lt;code&gt;ForwardReferencedClassMetaobject&lt;/code&gt;. When a class is defined, it is
  automatically finalized (computing its CPL and slot layout). If a superclass is missing, a forward reference is created. The
  &lt;code&gt;EnsureFinalized&lt;/code&gt; protocol ensures that inheritance is resolved and slot locations are assigned the moment the class is
  first instantiated or used in dispatch.&lt;/p&gt;

  &lt;h5&gt;3. Performance Overhead of the "MOP Bridge"&lt;/h5&gt;
  &lt;p&gt;&lt;strong&gt;Challenge:&lt;/strong&gt; A naive implementation of &lt;code&gt;slot-value&lt;/code&gt; or generic dispatch using C# reflection or linear
  searches is orders of magnitude slower than native C# member access.&lt;/p&gt;
  &lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Three distinct optimizations were applied:
      &lt;ul&gt;
          &lt;li&gt;&lt;strong&gt;O(1) Slot Access:&lt;/strong&gt; Each &lt;code&gt;ClassMetaobject&lt;/code&gt; maintains a &lt;code&gt;SlotDictionary&lt;/code&gt;. Slot
  names are mapped to physical array indices during finalization, allowing &lt;code&gt;slot-value&lt;/code&gt; to perform a direct array access
  after a single dictionary lookup.&lt;/li&gt;
          &lt;li&gt;&lt;strong&gt;Compiler Primitives:&lt;/strong&gt; The compiler identifies &lt;code&gt;SLOT-VALUE&lt;/code&gt; and &lt;code&gt;MAKE-INSTANCE&lt;/code&gt;
  calls and emits direct CIL &lt;code&gt;call&lt;/code&gt; instructions to optimized &lt;code&gt;Lisp.MopRuntime&lt;/code&gt; methods, bypassing the general
  &lt;code&gt;Funcall&lt;/code&gt; path.&lt;/li&gt;
          &lt;li&gt;&lt;strong&gt;Zero-Allocation Cache Hits:&lt;/strong&gt; By making &lt;code&gt;InvocationCacheKey&lt;/code&gt; a &lt;code&gt;readonly struct&lt;/code&gt;
  and avoiding the cloning of the argument array during cache probes, the hot-path for generic function dispatch generates zero
  garbage for the .NET Collector.&lt;/li&gt;
      &lt;/ul&gt;
  &lt;/p&gt;

  &lt;h5&gt;4. Bootstrapping the COMMON-LISP Package&lt;/h5&gt;
  &lt;p&gt;&lt;strong&gt;Challenge:&lt;/strong&gt; Core CLOS functions like &lt;code&gt;make-instance&lt;/code&gt; must be available as symbols in the
  &lt;code&gt;COMMON-LISP&lt;/code&gt; package before user code runs, but they rely on the MOP runtime being fully initialized.&lt;/p&gt;
  &lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; A &lt;code&gt;MopRuntime.Initialize()&lt;/code&gt; method is injected into the entry point (&lt;code&gt;Main&lt;/code&gt;) of
  every generated assembly. This method interns the necessary symbols and binds them to &lt;code&gt;GenericFunctionClosureAdapter&lt;/code&gt;
  objects, ensuring that the MOP is "alive" before the first line of Lisp code executes.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Vibe coding the MOP basically involved feeding chapters 4 and 5 of the &lt;i&gt;Art of the Meta-Object Protocol&lt;/i&gt; into the LLM and telling it to make an implementation plan.  It came up with a twenty-step plan to bootstrap CLOS.  I then spent the rest of the day instructing an agent to take on each task of the twenty-step plan in sequential order.  At the end of the day, I had a working MOP&lt;/p&gt;

&lt;p&gt;This is the end of my series of posts on CLRHack.&lt;/p&gt;</description>
	
	<pubDate>Sun, 31 May 2026 07:00:00 GMT</pubDate>
</item>

<item>
	<title>Joe Marshall: CLRHack: signal and error</title>
	<guid isPermaLink="true">http://funcall.blogspot.com/2026/05/clrhack-signal-and-error.html</guid>
	<link>http://funcall.blogspot.com/2026/05/clrhack-signal-and-error.html</link>
	
	<description>&lt;h3&gt;Implementation of SIGNAL and ERROR in CLRHack&lt;/h3&gt;

  &lt;p&gt;In CLRHack, the condition signaling system is implemented in the &lt;code&gt;Lisp.HandlerControl&lt;/code&gt; class within the
  &lt;code&gt;LispBase&lt;/code&gt; library. It leverages .NET's &lt;code&gt;[ThreadStatic]&lt;/code&gt; storage to maintain a per-thread dynamic stack of
  active condition handlers.&lt;/p&gt;

  &lt;h4&gt;SIGNAL Implementation&lt;/h4&gt;
  &lt;p&gt;The &lt;code&gt;Signal(object condition)&lt;/code&gt; method performs the following logic:&lt;/p&gt;
  &lt;ol&gt;
    &lt;li&gt;&lt;strong&gt;Retrieval:&lt;/strong&gt; It fetches the &lt;code&gt;activeHandlers&lt;/code&gt; list for the current thread. This list is a chain of
  &lt;code&gt;[LispBase]Lisp.Handler&lt;/code&gt; objects maintained by &lt;code&gt;handler-bind&lt;/code&gt;.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Iteration:&lt;/strong&gt; It iterates linearly through the list from the most recently bound handler to the oldest.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Type Matching:&lt;/strong&gt; For each handler, it calls &lt;code&gt;IsType(condition, handler.ConditionType)&lt;/code&gt;.
      &lt;ul&gt;
        &lt;li&gt;If the condition is a &lt;strong&gt;symbol&lt;/strong&gt;, it checks for symbol equality (supporting simple symbol-based
  conditions).&lt;/li&gt;
        &lt;li&gt;If the condition is a &lt;strong&gt;.NET object&lt;/strong&gt;, it checks if the handler's type is assignable from the condition's
  runtime type (supporting interop with system exceptions).&lt;/li&gt;
        &lt;li&gt;It treats the symbols &lt;code&gt;T&lt;/code&gt; or &lt;code&gt;EXCEPTION&lt;/code&gt; as catch-all types.&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Handler Invocation:&lt;/strong&gt; If a match is found:
      &lt;ul&gt;
        &lt;li&gt;&lt;strong&gt;Recursive Signal Protection:&lt;/strong&gt; Before calling the handler function, the current handler list is
  temporarily shadowed. &lt;code&gt;activeHandlers&lt;/code&gt; is set to &lt;code&gt;cell.rest&lt;/code&gt; (the handlers bound &lt;em&gt;outside&lt;/em&gt; the current
  one). This ensures that if the handler itself calls &lt;code&gt;signal&lt;/code&gt;, it won't trigger itself recursively.&lt;/li&gt;
        &lt;li&gt;&lt;strong&gt;Execution:&lt;/strong&gt; The handler's &lt;code&gt;Closure&lt;/code&gt; is invoked with the condition object as its argument.&lt;/li&gt;
        &lt;li&gt;&lt;strong&gt;Restoration:&lt;/strong&gt; A &lt;code&gt;finally&lt;/code&gt; block ensures the original &lt;code&gt;activeHandlers&lt;/code&gt; list is
  restored if the handler returns normally.&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/li&gt;
  &lt;h4&gt;ERROR Implementation&lt;/h4&gt;
  &lt;p&gt;The &lt;code&gt;Error(object condition)&lt;/code&gt; method build upon &lt;code&gt;Signal&lt;/code&gt;:&lt;/p&gt;
  &lt;ol&gt;
    &lt;li&gt;&lt;strong&gt;Signaling Pass:&lt;/strong&gt; It first invokes &lt;code&gt;Signal(condition)&lt;/code&gt;. If a handler performs a non-local exit
  (e.g., via &lt;code&gt;handler-case&lt;/code&gt;), the &lt;code&gt;Error&lt;/code&gt; method never returns.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Debugger Entry:&lt;/strong&gt; If &lt;code&gt;Signal&lt;/code&gt; returns normally (meaning all handlers declined), &lt;code&gt;Error&lt;/code&gt;
  calls &lt;code&gt;EnterDebugger(condition)&lt;/code&gt;.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Interactive Debugging:&lt;/strong&gt; The debugger:
      &lt;ul&gt;
        &lt;li&gt;Prints the condition and a list of available restarts (retrieved via
  &lt;code&gt;RestartControl.GetActiveRestarts()&lt;/code&gt;).&lt;/li&gt;
        &lt;li&gt;Provides a prompt for the user to select a restart, launch the system-level debugger (Visual Studio/Rider), or
  abort.&lt;/li&gt;
        &lt;li&gt;If a restart is selected, it is invoked interactively (potentially gathering arguments from the user).&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Final Fallback:&lt;/strong&gt; If the debugger is exited without invoking a restart, &lt;code&gt;Error&lt;/code&gt; throws a C#
  &lt;code&gt;Exception&lt;/code&gt; to ensure that execution does not continue on an invalid path.&lt;/li&gt;
  &lt;/ol&gt;

  &lt;h4&gt;Notable Implementation Decisions and Edge Cases&lt;/h4&gt;
  &lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;Handler Shadowing:&lt;/strong&gt; The decision to pop the handler list during invocation is critical for maintaining Common
  Lisp semantics. It prevents infinite loops and ensures that "outer" handlers can handle errors raised within "inner" handlers.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Unified Exception Model:&lt;/strong&gt; CLRHack attempts to unify Lisp conditions and .NET exceptions. &lt;code&gt;IsType&lt;/code&gt;
  allows Lisp handlers to catch C# exceptions by their class name or Type object.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Thread Isolation:&lt;/strong&gt; By using &lt;code&gt;[ThreadStatic]&lt;/code&gt; for &lt;code&gt;activeHandlers&lt;/code&gt;, CLRHack ensures that
  condition signaling is thread-safe. One thread signaling an error will not interfere with the handler state of another thread.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Debugger Capability:&lt;/strong&gt; The &lt;code&gt;SYSTEM-DEBUGGER&lt;/code&gt; option in &lt;code&gt;EnterDebugger&lt;/code&gt; is a bridge to
  the underlying .NET environment, allowing developers to use professional IDE tools to inspect the state of the Lisp VM when an
  unhandled error occurs.&lt;/li&gt;
  &lt;/ul&gt;
    
    &lt;p&gt;signal and error complete the Common Lisp condition system implementation for CLRHack&lt;/p&gt;</description>
	
	<pubDate>Sat, 30 May 2026 07:00:00 GMT</pubDate>
</item>

<item>
	<title>Joe Marshall: CLRHack: handler-bind and handler-case</title>
	<guid isPermaLink="true">http://funcall.blogspot.com/2026/05/clrhack-handler-bind-and-handler-case.html</guid>
	<link>http://funcall.blogspot.com/2026/05/clrhack-handler-bind-and-handler-case.html</link>
	
	<description>&lt;p&gt;In the CLRHack compiler, &lt;strong&gt;handler-bind&lt;/strong&gt; is a primitive form used to register condition handlers in the dynamic
  environment. It operates by managing a thread-local list of active handler objects, ensuring that condition signaling follows the
  standard Common Lisp search and execution rules.&lt;/p&gt;

  &lt;h3&gt;Handling of handler-bind&lt;/h3&gt;
  &lt;p&gt;When the compiler processes a &lt;code&gt;handler-bind&lt;/code&gt; form, it generates CIL code that performs the following steps:&lt;/p&gt;
  &lt;ol&gt;
    &lt;li&gt;&lt;strong&gt;Capture Previous State:&lt;/strong&gt; It calls &lt;code&gt;Lisp.HandlerControl::GetActiveHandlers()&lt;/code&gt; to retrieve the
  current list of active handlers and stores it in a frame-local variable.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Construct New List:&lt;/strong&gt; For each binding, it evaluates the condition type and the handler function (which is
  typically a closure). It instantiates a new &lt;code&gt;[LispBase]Lisp.Handler&lt;/code&gt; object and conses it onto the current handler
  list.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Install New State:&lt;/strong&gt; It calls &lt;code&gt;Lisp.HandlerControl::SetActiveHandlers(new_list)&lt;/code&gt; to update the
  dynamic environment for the current thread.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Protected Execution:&lt;/strong&gt; The body of the &lt;code&gt;handler-bind&lt;/code&gt; is wrapped in a CIL &lt;code&gt;.try&lt;/code&gt;
  block.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Restoration:&lt;/strong&gt; A &lt;code&gt;finally&lt;/code&gt; block is emitted that calls &lt;code&gt;SetActiveHandlers&lt;/code&gt; with the
  saved list. This ensures that handlers are properly uninstalled, regardless of whether the body completes normally, signals an
  error, or performs a non-local exit.&lt;/li&gt;
  &lt;/ol&gt;

  &lt;h3&gt;Lexical Non-Local Exits&lt;/h3&gt;
  &lt;p&gt;Handlers in Common Lisp are executed in the dynamic environment of the signaller but have lexical access to the environment
  where they were defined. In CLRHack, if a handler function performs a non-local exit (such as a &lt;code&gt;throw&lt;/code&gt; or
  &lt;code&gt;return-from&lt;/code&gt;), the compiler utilizes its exception-based jump mechanism:&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;If the exit is a &lt;code&gt;throw&lt;/code&gt;, it uses the standard &lt;code&gt;CatchThrowException&lt;/code&gt; mechanism.&lt;/li&gt;
    &lt;li&gt;If the exit is a &lt;code&gt;return-from&lt;/code&gt; to a block outside the handler closure, the compiler identifies this as a non-local
  exit during &lt;code&gt;analyze-environment&lt;/code&gt;. It compiles the &lt;code&gt;return-from&lt;/code&gt; into a &lt;code&gt;throw&lt;/code&gt; of a
  &lt;code&gt;BlockExitException&lt;/code&gt;, which is subsequently caught by the &lt;code&gt;try/catch&lt;/code&gt; frame established by the target
  &lt;code&gt;block&lt;/code&gt;.&lt;/li&gt;
  &lt;/ul&gt;

  &lt;h3&gt;Handler Search&lt;/h3&gt;
  &lt;p&gt;The handler search is performed at runtime by the &lt;code&gt;signal&lt;/code&gt; or &lt;code&gt;error&lt;/code&gt; functions. These functions retrieve
  the active handlers list via &lt;code&gt;HandlerControl.GetActiveHandlers()&lt;/code&gt; and iterate through them. For each handler, the
  runtime checks if the signaled condition is of the type (or a subtype of the type) the handler was registered for. If a match is
  found, the handler function is invoked. If the handler returns normally (declines), the search continues with the next applicable
  handler.&lt;/p&gt;

  &lt;h3&gt;Dynamic Tags&lt;/h3&gt;
  &lt;p&gt;The &lt;code&gt;handler-bind&lt;/code&gt; implementation itself relies on the dynamic state of the thread-local &lt;code&gt;activeHandlers&lt;/code&gt;
  list. However, when used in conjunction with &lt;code&gt;handler-case&lt;/code&gt;, unique dynamic tags (typically fresh &lt;code&gt;ListCell&lt;/code&gt;
  objects) are generated. These tags are used as the "target" for the &lt;code&gt;throw&lt;/code&gt; performed by the handler, ensuring that the
  control flow returns exactly to the correct &lt;code&gt;handler-case&lt;/code&gt; frame and doesn't conflict with other active handler or catch
  frames.&lt;/p&gt;

  &lt;h3&gt;handler-case as an Extension of handler-bind&lt;/h3&gt;
  &lt;p&gt;In CLRHack, &lt;strong&gt;handler-case&lt;/strong&gt; is not a primitive but a macro that expands into a combination of &lt;code&gt;block&lt;/code&gt;,
  &lt;code&gt;catch&lt;/code&gt;, and &lt;code&gt;handler-bind&lt;/code&gt;. It extends &lt;code&gt;handler-bind&lt;/code&gt; by providing a mechanism to automatically
  exit the signaling context and execute a specific branch of code based on the condition caught.&lt;/p&gt;
  &lt;p&gt;The implementation details of the expansion are as follows:&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;Exit Block:&lt;/strong&gt; The entire form is wrapped in a &lt;code&gt;block&lt;/code&gt; with a unique exit tag to allow the normal
  path to return immediately upon completion of the protected expression.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Dynamic Setup:&lt;/strong&gt; A unique dynamic tag is created for the &lt;code&gt;catch&lt;/code&gt; frame. Local variables are
  established to store the captured condition and a unique ID identifying which clause was triggered.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;The Binding:&lt;/strong&gt; A &lt;code&gt;handler-bind&lt;/code&gt; is generated where each handler function is a closure that, when
  called:
      &lt;ol&gt;
        &lt;li&gt;Saves the signaled condition into the local &lt;code&gt;condition-var&lt;/code&gt;.&lt;/li&gt;
        &lt;li&gt;Sets the &lt;code&gt;id-var&lt;/code&gt; to a unique GENSYM representing that specific clause.&lt;/li&gt;
        &lt;li&gt;Performs a &lt;code&gt;throw&lt;/code&gt; to the dynamic tag.&lt;/li&gt;
      &lt;/ol&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;The Catch and Dispatch:&lt;/strong&gt; A &lt;code&gt;catch&lt;/code&gt; block surrounds the protected expression. If a handler performs
  the &lt;code&gt;throw&lt;/code&gt;, the &lt;code&gt;catch&lt;/code&gt; returns, and a &lt;code&gt;cond&lt;/code&gt; statement (the dispatcher) checks the
  &lt;code&gt;id-var&lt;/code&gt;. It then executes the body of the matching &lt;code&gt;handler-case&lt;/code&gt; clause with the condition variable bound
  to the clause's parameter.&lt;/li&gt;
  &lt;/ul&gt;</description>
	
	<pubDate>Fri, 29 May 2026 07:00:00 GMT</pubDate>
</item>

<item>
	<title>Joe Marshall: CLRHack: restarts</title>
	<guid isPermaLink="true">http://funcall.blogspot.com/2026/05/clrhack-restarts.html</guid>
	<link>http://funcall.blogspot.com/2026/05/clrhack-restarts.html</link>
	
	<description>&lt;p&gt;In the CLRHack compiler, &lt;strong&gt;restart-bind&lt;/strong&gt; is a primitive form that manages the dynamic lifecycle of Common Lisp
  restarts by manipulating a thread-local stack of active restart objects.&lt;/p&gt;

  &lt;h3&gt;Handling of restart-bind&lt;/h3&gt;
  &lt;p&gt;When the compiler encounters a &lt;code&gt;restart-bind&lt;/code&gt; form, it generates CIL code that performs the following steps:&lt;/p&gt;
  &lt;ol&gt;
    &lt;li&gt;&lt;strong&gt;Capture Previous State:&lt;/strong&gt; It calls &lt;code&gt;Lisp.RestartControl::GetActiveRestarts()&lt;/code&gt; to retrieve the
  current list of active restarts and stores it in a frame-local variable.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Construct New List:&lt;/strong&gt; For each binding, it evaluates the restart name, handler function, and optional keyword
  arguments (&lt;code&gt;:report-function&lt;/code&gt;, &lt;code&gt;:interactive-function&lt;/code&gt;, &lt;code&gt;:test-function&lt;/code&gt;). It then instantiates a
  new &lt;code&gt;[LispBase]Lisp.Restart&lt;/code&gt; object and conses it onto the existing list.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Install New State:&lt;/strong&gt; It calls &lt;code&gt;Lisp.RestartControl::SetActiveRestarts(new_list)&lt;/code&gt; to update the
  dynamic environment.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Protected Execution:&lt;/strong&gt; The body of the &lt;code&gt;restart-bind&lt;/code&gt; is wrapped in a CIL &lt;code&gt;.try&lt;/code&gt;
  block.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Restoration:&lt;/strong&gt; A &lt;code&gt;finally&lt;/code&gt; block is emitted that restores the previously saved restart list using
  &lt;code&gt;SetActiveRestarts&lt;/code&gt;, ensuring that restarts are properly uninstalled even if the body performs a non-local exit.&lt;/li&gt;
  &lt;/ol&gt;

  &lt;h3&gt;Lexical Non-Local Exits&lt;/h3&gt;
  &lt;p&gt;The CLRHack compiler supports lexical non-local exits (e.g., &lt;code&gt;return-from&lt;/code&gt; or &lt;code&gt;go&lt;/code&gt;) through an
  exception-based mechanism. During the &lt;code&gt;analyze-environment&lt;/code&gt; pass, the compiler identifies if a &lt;code&gt;return-from&lt;/code&gt;
  target block is "non-local" (i.e., the return occurs within a nested closure). If so:&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;The target &lt;strong&gt;block&lt;/strong&gt; is wrapped in a &lt;code&gt;try/catch&lt;/code&gt; for
  &lt;code&gt;[LispBase]Lisp.BlockExitException&lt;/code&gt;.&lt;/li&gt;
    &lt;li&gt;The &lt;strong&gt;block&lt;/strong&gt; is assigned a unique string ID.&lt;/li&gt;
    &lt;li&gt;The &lt;strong&gt;return-from&lt;/strong&gt; form is compiled into a &lt;code&gt;throw&lt;/code&gt; of a &lt;code&gt;BlockExitException&lt;/code&gt;, which
  carries the target ID, the return value, and a captured array of multiple return values (retrieved via
  &lt;code&gt;Lisp.Values::CaptureValues()&lt;/code&gt;).&lt;/li&gt;
    &lt;li&gt;The &lt;code&gt;catch&lt;/code&gt; handler verifies the target ID. If it matches, it restores any captured multiple values and resumes
  normal execution; otherwise, it rethrows the exception.&lt;/li&gt;
  &lt;/ul&gt;

  &lt;h3&gt;Restart Search&lt;/h3&gt;
  &lt;p&gt;The search for an applicable restart is handled at runtime by &lt;code&gt;Lisp.RestartControl::FindRestart&lt;/code&gt;. It performs a
  linear search through the current thread's &lt;code&gt;activeRestarts&lt;/code&gt; list (stored in a &lt;code&gt;[ThreadStatic]&lt;/code&gt; field). It can
  accept either a symbol name or a &lt;code&gt;Restart&lt;/code&gt; object itself. If a name is provided, the search respects shadowing,
  returning the innermost (most recently bound) restart with that name.&lt;/p&gt;

  &lt;h3&gt;Dynamic Tags&lt;/h3&gt;
  &lt;p&gt;Dynamic tags are required for the &lt;code&gt;catch&lt;/code&gt; and &lt;code&gt;throw&lt;/code&gt; forms used in non-local control flow. In CLRHack, a
  dynamic tag is simply a fresh object (typically a &lt;code&gt;ListCell&lt;/code&gt; or a new &lt;code&gt;System.Object&lt;/code&gt;) used as a unique
  token. This ensures that a &lt;code&gt;throw&lt;/code&gt; only matches the specific &lt;code&gt;catch&lt;/code&gt; frame it was intended for, avoiding
  collisions between different invocations of the same function or different &lt;code&gt;restart-case&lt;/code&gt; blocks.&lt;/p&gt;

  &lt;h3&gt;restart-case as an Extension of restart-bind&lt;/h3&gt;
  &lt;p&gt;In CLRHack, &lt;strong&gt;restart-case&lt;/strong&gt; is implemented as a macro that expands into a combination of &lt;code&gt;block&lt;/code&gt;,
  &lt;code&gt;catch&lt;/code&gt;, and &lt;code&gt;restart-bind&lt;/code&gt;. It extends the basic binding functionality by providing a built-in mechanism to
  jump back to the site of the &lt;code&gt;restart-case&lt;/code&gt; when a restart is invoked.&lt;/p&gt;
  &lt;p&gt;The implementation details are as follows:&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;Exit Block:&lt;/strong&gt; The entire expansion is wrapped in a &lt;code&gt;(block exit_tag ...)&lt;/code&gt; to allow normal
  completion of the expression.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Dynamic Tag:&lt;/strong&gt; A unique dynamic tag is created (e.g., &lt;code&gt;(let ((tag (list nil))) ...)&lt;/code&gt;).&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Catch Frame:&lt;/strong&gt; A &lt;code&gt;(catch tag ...)&lt;/code&gt; is established around the &lt;code&gt;restart-bind&lt;/code&gt; and the
  expression.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Binding:&lt;/strong&gt; The &lt;code&gt;restart-bind&lt;/code&gt; creates restarts whose handler functions are closures. When invoked,
  these closures capture their arguments into local variables, set a unique clause ID, and then &lt;code&gt;throw&lt;/code&gt; to the dynamic
  tag.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Dispatch:&lt;/strong&gt; When the &lt;code&gt;throw&lt;/code&gt; is caught, the &lt;code&gt;restart-case&lt;/code&gt; body executes a
  &lt;code&gt;cond&lt;/code&gt; or &lt;code&gt;case&lt;/code&gt; statement. This dispatcher checks the clause ID set by the handler and executes the
  corresponding forms provided in the &lt;code&gt;restart-case&lt;/code&gt; clause, eventually returning the result from the
  &lt;code&gt;exit_tag&lt;/code&gt; block.&lt;/li&gt;
  &lt;/ul&gt;</description>
	
	<pubDate>Thu, 28 May 2026 07:00:00 GMT</pubDate>
</item>

<item>
	<title>Joe Marshall: CLRHack: unwind-protect and catch-throw</title>
	<guid isPermaLink="true">http://funcall.blogspot.com/2026/05/clrhack-unwind-protect-and-catch-throw.html</guid>
	<link>http://funcall.blogspot.com/2026/05/clrhack-unwind-protect-and-catch-throw.html</link>
	
	<description>&lt;h2&gt;Handling of &lt;code&gt;unwind-protect&lt;/code&gt;&lt;/h2&gt;

  &lt;p&gt;The CLRHack compiler maps Lisp &lt;code&gt;unwind-protect&lt;/code&gt; semantics directly onto the &lt;strong&gt;Structured Exception Handling
  (SEH)&lt;/strong&gt; infrastructure of the .NET Common Language Runtime (CLR). Specifically, it utilizes the &lt;code&gt;try...finally&lt;/code&gt;
  construct provided by the Common Intermediate Language (CIL).&lt;/p&gt;

  &lt;p&gt;Lisp semantics require that the cleanup forms in an &lt;code&gt;unwind-protect&lt;/code&gt; block be executed regardless of how control
  leaves the protected form&amp;mdash;whether via normal return, a non-local &lt;code&gt;throw&lt;/code&gt;, or a lexical exit like
  &lt;code&gt;return-from&lt;/code&gt;. The CLR guarantees that a &lt;code&gt;finally&lt;/code&gt; block will execute during stack unwinding, which is
  exactly the hook required for Lisp. The implementation details are as follows:&lt;/p&gt;

  &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;Protected Form:&lt;/strong&gt; The compiler generates the code for the protected form inside a CIL &lt;code&gt;try&lt;/code&gt;
  block. Upon successful completion, the primary return value is stored in a local variable, and a &lt;code&gt;leave&lt;/code&gt; instruction is
  used to exit the &lt;code&gt;try&lt;/code&gt; block, which automatically triggers the transition to the &lt;code&gt;finally&lt;/code&gt; block.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Side-Channel Preservation:&lt;/strong&gt; A unique challenge in Lisp is that &lt;code&gt;unwind-protect&lt;/code&gt; must return the
  values of the protected form, but cleanup forms may themselves perform operations that alter the Multiple Return Value (MRV)
  side-channel. CLRHack exploits method-local variables to save the &lt;code&gt;ReturnCount&lt;/code&gt; and the contents of &lt;code&gt;Value1&lt;/code&gt;
  through &lt;code&gt;Value63&lt;/code&gt; at the very beginning of the &lt;code&gt;finally&lt;/code&gt; block and restore them at the very end.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Unwinding:&lt;/strong&gt; If a &lt;code&gt;throw&lt;/code&gt; or other exception occurs within the &lt;code&gt;try&lt;/code&gt; block, the CLR
  stack walker identifies the &lt;code&gt;finally&lt;/code&gt; block and executes it before propagating the exception further. This ensures
  Lisp's "cleanup guarantee" is maintained even during catastrophic or non-local control transfers.&lt;/li&gt;
  &lt;/ul&gt;

  &lt;h2&gt;Handling of &lt;code&gt;catch&lt;/code&gt; and &lt;code&gt;throw&lt;/code&gt;&lt;/h2&gt;

  &lt;p&gt;Lisp's &lt;code&gt;catch&lt;/code&gt; and &lt;code&gt;throw&lt;/code&gt; are implemented as a &lt;strong&gt;Dynamic Non-Local Exit&lt;/strong&gt; system built on
  top of .NET's exception propagation mechanism. While CLR exceptions are typically filtered by type, Lisp requires filtering by a
  dynamic "tag" object (compared via &lt;code&gt;eq&lt;/code&gt;).&lt;/p&gt;

  &lt;h3&gt;The &lt;code&gt;throw&lt;/code&gt; Mechanism&lt;/h3&gt;
  &lt;p&gt;When a &lt;code&gt;(throw tag value)&lt;/code&gt; is evaluated, CLRHack does not simply perform a jump. Instead, it performs the following
  steps:&lt;/p&gt;
  &lt;ol&gt;
      &lt;li&gt;Evaluates the &lt;code&gt;tag&lt;/code&gt; and the primary &lt;code&gt;value&lt;/code&gt;.&lt;/li&gt;
      &lt;li&gt;Captures the current state of the MRV side-channel into an &lt;code&gt;object[]&lt;/code&gt;.&lt;/li&gt;
      &lt;li&gt;Instantiates a specialized exception class: &lt;code&gt;[LispBase]Lisp.CatchThrowException&lt;/code&gt;. This object acts as a carrier
  for the tag, the primary value, and the captured MRV array.&lt;/li&gt;
      &lt;li&gt;Executes the CIL &lt;code&gt;throw&lt;/code&gt; instruction. This initiates the CLR's SEH stack walk.&lt;/li&gt;
  &lt;/ol&gt;

  &lt;h3&gt;The &lt;code&gt;catch&lt;/code&gt; Mechanism&lt;/h3&gt;
  &lt;p&gt;The &lt;code&gt;(catch tag body)&lt;/code&gt; form is compiled into a &lt;code&gt;try...catch&lt;/code&gt; block where the catch handler specifically
  targets &lt;code&gt;CatchThrowException&lt;/code&gt;:&lt;/p&gt;
  &lt;ol&gt;
      &lt;li&gt;&lt;strong&gt;Tag Setup:&lt;/strong&gt; The &lt;code&gt;catch&lt;/code&gt; tag is evaluated and stored in a method-local variable.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Body Execution:&lt;/strong&gt; The body forms are executed within a &lt;code&gt;try&lt;/code&gt; block.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;The Catch Handler:&lt;/strong&gt; When a &lt;code&gt;CatchThrowException&lt;/code&gt; is intercepted, the handler performs a "Dynamic
  Filter":
          &lt;ul&gt;
              &lt;li&gt;It extracts the tag from the exception object and compares it to the local &lt;code&gt;catch&lt;/code&gt; tag using
  &lt;code&gt;System.Object.Equals&lt;/code&gt; (simulating Lisp's &lt;code&gt;eq&lt;/code&gt; for reference types).&lt;/li&gt;
              &lt;li&gt;&lt;strong&gt;Match:&lt;/strong&gt; If the tags match, the handler "claims" the exception. It extracts the primary value and
  the MRV array from the exception, restores them to the thread-local side-channel, and resumes normal execution after the
  &lt;code&gt;catch&lt;/code&gt; block.&lt;/li&gt;
              &lt;li&gt;&lt;strong&gt;Mismatch:&lt;/strong&gt; If the tags do not match, the handler executes the CIL &lt;code&gt;rethrow&lt;/code&gt; instruction.
  This allows the exception to continue up the stack to find a matching &lt;code&gt;catch&lt;/code&gt; tag in a higher frame.&lt;/li&gt;
          &lt;/ul&gt;
      &lt;/li&gt;
  &lt;/ol&gt;

  &lt;h2&gt;Exploiting SEH for Lisp Semantics&lt;/h2&gt;

  &lt;p&gt;CLRHack exploits the CLR's SEH in three fundamental ways to bridge the gap between .NET and Lisp:&lt;/p&gt;
  &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;Automatic Stack Unwinding:&lt;/strong&gt; By using &lt;code&gt;throw&lt;/code&gt; and &lt;code&gt;try...catch&lt;/code&gt;, the compiler
  delegates the complex task of cleaning up stack frames, registers, and intermediate states to the highly optimized .NET
  runtime.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Guaranteed Cleanup:&lt;/strong&gt; The &lt;code&gt;finally&lt;/code&gt; block is the "silicon reality" of Lisp's
  &lt;code&gt;unwind-protect&lt;/code&gt;. The CLR ensures it runs even if an exception is re-thrown multiple times or if a thread is being
  terminated.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Payload-Heavy Exceptions:&lt;/strong&gt; Unlike standard .NET exceptions which often carry only metadata,
  &lt;code&gt;CatchThrowException&lt;/code&gt; is exploited as a transport mechanism. It carries the entire "return state" of a Lisp expression
  (primary value + MRV side-channel) across an arbitrary number of stack frames, allowing a &lt;code&gt;throw&lt;/code&gt; to behave exactly like
  a multi-valued return to a dynamic point.&lt;/li&gt;
  &lt;/ul&gt;</description>
	
	<pubDate>Wed, 27 May 2026 07:00:00 GMT</pubDate>
</item>


</channel>
</rss>
