Bug: free-play C/C++ code execution never dispatches to the Clang executor #453

Closed
opened 2026-07-07 16:15:46 +00:00 by spikerj · 1 comment
Owner

What's wrong

Discovered while writing Phase 5 of the Lessons integration test suite (spikersoft-backend#177 / spikersoft-issues#452).

CodeExecutionWorkerService.ExecuteCodeAsync in SpikerSoft.EventHandlers.CodeExecution decides how to run a free-play (no LessonId) submission with:

var usePython = gradingRuntime == LessonGradingRuntime.CPython
    || (!request.LessonId.HasValue && CodeExecutionMetadataHelper.IsPythonLanguage(request.Metadata));
var useJavaScript = gradingRuntime == LessonGradingRuntime.NodeJavaScript
    || (!request.LessonId.HasValue && CodeExecutionMetadataHelper.IsJavaScriptLanguage(request.Metadata));
...
if (request.LessonId.HasValue) { response = await ExecuteLessonCodeAsync(request, cancellationToken); }
else if (usePython || useJavaScript) { response = await ExecuteNonCsharpFreePlayAsync(request, cancellationToken); }
else { response = await ExecuteGeneralCodeAsync(request); } // <- Roslyn C#

There is no useC/useCpp (or general "use the language-aware free-play factory whenever metadata says so") branch. So a free-play submission through POST /api/CCodeRunner/run or POST /api/CppCodeRunner/run (which correctly stamp metadata.language = "c" / "cpp") falls through to ExecuteGeneralCodeAsync, which always compiles the submission as C# via Roslyn. It never reaches the registered CFreePlayCodeExecutor / CppFreePlayCodeExecutor (both exist and are wired up in SpikerSoft.EventHandlers.CodeExecution/Program.cs - they're just dead code for this path).

Practical effect: any real C/C++ free-play submission fails immediately with a C# compile error (e.g. #include isn't valid C#), regardless of whether the code is actually correct C/C++.

Lesson submissions are unaffected - ExecuteLessonCodeAsync dispatches via _gradingExecutorFactory.For(gradingRuntime), keyed off the lesson strategy's GradingRuntime (ClangC/ClangCpp), which correctly resolves ClangLessonGradingExecutor. Only the free-play/"run" endpoints are affected.

Suggested fix

Generalize the free-play dispatch condition to also cover C/C++ (e.g. check CodeExecutionMetadataHelper.IsCLanguage/IsCppLanguage alongside Python/JS, or better: always prefer _freePlayExecutorFactory when a non-C# language is present in metadata, falling back to ExecuteGeneralCodeAsync only for the C#/unset default).

Why not fixed alongside Phase 5 integration tests

Out of scope for a test-writing pass, and the in-process test fixture never registers the Clang executors anyway (needs wasi-sdk + wasmtime baked into the worker's Docker image), so this bug isn't observable from that fixture regardless - it was found by reading the dispatch code while designing the C/C++ coverage, not from a failing test.

## What's wrong Discovered while writing Phase 5 of the Lessons integration test suite (spikersoft-backend#177 / spikersoft-issues#452). `CodeExecutionWorkerService.ExecuteCodeAsync` in `SpikerSoft.EventHandlers.CodeExecution` decides how to run a **free-play** (no `LessonId`) submission with: ```csharp var usePython = gradingRuntime == LessonGradingRuntime.CPython || (!request.LessonId.HasValue && CodeExecutionMetadataHelper.IsPythonLanguage(request.Metadata)); var useJavaScript = gradingRuntime == LessonGradingRuntime.NodeJavaScript || (!request.LessonId.HasValue && CodeExecutionMetadataHelper.IsJavaScriptLanguage(request.Metadata)); ... if (request.LessonId.HasValue) { response = await ExecuteLessonCodeAsync(request, cancellationToken); } else if (usePython || useJavaScript) { response = await ExecuteNonCsharpFreePlayAsync(request, cancellationToken); } else { response = await ExecuteGeneralCodeAsync(request); } // <- Roslyn C# ``` There is no `useC`/`useCpp` (or general "use the language-aware free-play factory whenever metadata says so") branch. So a free-play submission through `POST /api/CCodeRunner/run` or `POST /api/CppCodeRunner/run` (which correctly stamp `metadata.language = "c"` / `"cpp"`) falls through to `ExecuteGeneralCodeAsync`, which **always compiles the submission as C# via Roslyn**. It never reaches the registered `CFreePlayCodeExecutor` / `CppFreePlayCodeExecutor` (both exist and are wired up in `SpikerSoft.EventHandlers.CodeExecution/Program.cs` - they're just dead code for this path). Practical effect: any real C/C++ free-play submission fails immediately with a C# compile error (e.g. `#include` isn't valid C#), regardless of whether the code is actually correct C/C++. **Lesson submissions are unaffected** - `ExecuteLessonCodeAsync` dispatches via `_gradingExecutorFactory.For(gradingRuntime)`, keyed off the lesson strategy's `GradingRuntime` (`ClangC`/`ClangCpp`), which correctly resolves `ClangLessonGradingExecutor`. Only the free-play/`"run"` endpoints are affected. ## Suggested fix Generalize the free-play dispatch condition to also cover C/C++ (e.g. check `CodeExecutionMetadataHelper.IsCLanguage`/`IsCppLanguage` alongside Python/JS, or better: always prefer `_freePlayExecutorFactory` when a non-C# `language` is present in metadata, falling back to `ExecuteGeneralCodeAsync` only for the C#/unset default). ## Why not fixed alongside Phase 5 integration tests Out of scope for a test-writing pass, and the in-process test fixture never registers the Clang executors anyway (needs wasi-sdk + wasmtime baked into the worker's Docker image), so this bug isn't observable from that fixture regardless - it was found by reading the dispatch code while designing the C/C++ coverage, not from a failing test.
Author
Owner

Resolved in spikersoft-backend PR #178 (merged to master). Added a useClang arm (keyed off CodeExecutionMetadataHelper.IsClangLanguage, free-play only) to the free-play dispatch in CodeExecutionWorkerService.ExecuteCodeAsync, so C/C++ submissions now reach the language-aware free-play factory (CFreePlayCodeExecutor/CppFreePlayCodeExecutor) instead of falling through to Roslyn C#. ExecuteNonCsharpFreePlayAsync already resolved the executor via _freePlayExecutorFactory.For(metadata), so no deeper change was needed. Keyed off metadata.language rather than a grading-runtime member since LessonGradingRuntime has no ClangC/ClangCpp. New CodeExecutionWorkerServiceTests theory (c, cpp) pins the routing; dotnet test SpikerSoft.Tests.Unit green. Closing.

Resolved in spikersoft-backend PR #178 (merged to `master`). Added a `useClang` arm (keyed off `CodeExecutionMetadataHelper.IsClangLanguage`, free-play only) to the free-play dispatch in `CodeExecutionWorkerService.ExecuteCodeAsync`, so C/C++ submissions now reach the language-aware free-play factory (`CFreePlayCodeExecutor`/`CppFreePlayCodeExecutor`) instead of falling through to Roslyn C#. `ExecuteNonCsharpFreePlayAsync` already resolved the executor via `_freePlayExecutorFactory.For(metadata)`, so no deeper change was needed. Keyed off `metadata.language` rather than a grading-runtime member since `LessonGradingRuntime` has no `ClangC`/`ClangCpp`. New `CodeExecutionWorkerServiceTests` theory (`c`, `cpp`) pins the routing; `dotnet test SpikerSoft.Tests.Unit` green. Closing.
Sign in to join this conversation.