Skip to content

fix: avoid byref delegate parameters incompatible with Mono/Xamarin (issue #458)#627

Merged
rexm merged 2 commits into
masterfrom
worktree-agent-a7c056b778f0ae5da
Jun 20, 2026
Merged

fix: avoid byref delegate parameters incompatible with Mono/Xamarin (issue #458)#627
rexm merged 2 commits into
masterfrom
worktree-agent-a7c056b778f0ae5da

Conversation

@rexm

@rexm rexm commented Jun 20, 2026

Copy link
Copy Markdown
Member

Fixes #458

Root Cause

Mono's AOT/JIT compiler (used by Xamarin.iOS and other Mono-based platforms) does not support delegates whose parameter list includes byref types (ref/in/out). The in keyword on a value-type parameter compiles to a managed byref, so any delegate type that uses in is a "byref delegate."

In v2, two internal delegate types gained in EncodedTextWriter parameters:

  • TemplateDelegatedelegate void TemplateDelegate(in EncodedTextWriter writer, BindingContext context)
  • DecoratorDelegatedelegate TemplateDelegate DecoratorDelegate(in EncodedTextWriter writer, ...)

CompilationContext also created the writer parameter as typeof(EncodedTextWriter).MakeByRefType(), so every Expression.Lambda<TemplateDelegate> call produced a byref delegate, throwing System.NotImplementedException: byref delegate at template-compile time on Mono.

Fix

Remove the in modifier from the two delegate type definitions so EncodedTextWriter is passed by value instead of by readonly reference:

  • TemplateDelegatedelegate void TemplateDelegate(EncodedTextWriter writer, BindingContext context)
  • DecoratorDelegatedelegate TemplateDelegate DecoratorDelegate(EncodedTextWriter writer, ...)
  • CompilationContext.EncodedWriterExpression.Parameter(typeof(EncodedTextWriter), "writer")

EncodedTextWriter is a small readonly struct containing three managed-reference fields (~24 bytes on 64-bit). Passing it by value incurs a negligible copy and produces no observable behaviour difference on standard .NET runtimes.

The same in-removal is applied to all lambda literals that match these delegate types, and to the two affected test helpers.

Tests

Two regression tests added to IssueTests.cs:

  • Issue458_BasicTemplateCompilationAndRender — compile and render {{input}}
  • Issue458_BlockHelperTemplateCompilationAndRender — compile and render {{#if show}}visible{{/if}}

All 1748 existing tests continue to pass.

🤖 Generated with Claude Code

rexm and others added 2 commits June 19, 2026 20:58
…o/Xamarin (#458)

Mono's AOT/JIT compiler does not support delegates with byref parameters
(ref/in). TemplateDelegate and DecoratorDelegate used `in EncodedTextWriter`
which caused System.NotImplementedException: byref delegate at runtime on
Xamarin.iOS and other Mono-based platforms.

- Change `TemplateDelegate` from `delegate void TemplateDelegate(in EncodedTextWriter, ...)` to pass the struct by value
- Change `DecoratorDelegate` similarly
- Update `CompilationContext.EncodedWriter` to use `typeof(EncodedTextWriter)` instead of `MakeByRefType()` so Expression.Lambda<TemplateDelegate> no longer generates a byref parameter
- Update all lambda literals in HandlebarsCompiler, FunctionBuilder, DecoratorDefinition and tests to drop the `in` modifier

EncodedTextWriter is a small readonly struct (3 reference-type fields) so
the by-value copy is cheap and there is no observable behaviour change on
standard .NET runtimes.

Adds two regression tests: Issue458_BasicTemplateCompilationAndRender and
Issue458_BlockHelperTemplateCompilationAndRender.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@rexm rexm enabled auto-merge June 20, 2026 01:20
@rexm rexm merged commit ecad20c into master Jun 20, 2026
7 checks passed
@rexm rexm deleted the worktree-agent-a7c056b778f0ae5da branch June 20, 2026 01:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Trying to compile a template in Xamarin.iOS results in a System.NotImplementedException

1 participant