< Summary - Kestrun — Combined Coverage

Information
Class: Kestrun.Languages.CSharpDelegateBuilder
Assembly: Kestrun
File(s): /home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun/Languages/CSharpDelegateBuilder.cs
Tag: Kestrun/Kestrun@0d738bf294e6281b936d031e1979d928007495ff
Line coverage
82%
Covered lines: 201
Uncovered lines: 44
Coverable lines: 245
Total lines: 677
Line coverage: 82%
Branch coverage
66%
Covered branches: 121
Total branches: 182
Branch coverage: 66.4%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 08/26/2025 - 14:53:17 Line coverage: 83.8% (145/173) Branch coverage: 66.9% (83/124) Total lines: 443 Tag: Kestrun/Kestrun@78d1e497d8ba989d121b57aa39aa3c6b22de743108/26/2025 - 18:44:21 Line coverage: 83.8% (145/173) Branch coverage: 66.1% (82/124) Total lines: 443 Tag: Kestrun/Kestrun@d4eca24e86ad16771e391821efd443d6dc96606b08/27/2025 - 16:39:45 Line coverage: 83.8% (145/173) Branch coverage: 66.9% (83/124) Total lines: 443 Tag: Kestrun/Kestrun@4c6ca8f799d2de93b1edf9355074f3da4297dc6208/28/2025 - 13:31:25 Line coverage: 83.8% (145/173) Branch coverage: 66.1% (82/124) Total lines: 443 Tag: Kestrun/Kestrun@c90b3fa538340336a4939f46ad7235e29212eb5908/29/2025 - 15:33:49 Line coverage: 83.8% (145/173) Branch coverage: 66.9% (83/124) Total lines: 443 Tag: Kestrun/Kestrun@d302841c78e142037a1aae3165383d903a00b5f408/29/2025 - 16:01:24 Line coverage: 83.8% (145/173) Branch coverage: 66.1% (82/124) Total lines: 443 Tag: Kestrun/Kestrun@ad4a22f390590bbcb102eb1a8fa13cdf7ebd5c5b08/29/2025 - 19:42:14 Line coverage: 83.8% (145/173) Branch coverage: 66.9% (83/124) Total lines: 443 Tag: Kestrun/Kestrun@3c9ce084a1c53128813d4002ea9ff6c197712a0e08/30/2025 - 03:33:49 Line coverage: 83.8% (145/173) Branch coverage: 66.1% (82/124) Total lines: 443 Tag: Kestrun/Kestrun@1211de166c0177afac78a94a8f91658b8dd4981809/01/2025 - 04:08:24 Line coverage: 83.8% (145/173) Branch coverage: 66.9% (83/124) Total lines: 443 Tag: Kestrun/Kestrun@d6f26a131219b7a7fcb4e129af3193ec2ec4892909/01/2025 - 15:55:00 Line coverage: 83.8% (145/173) Branch coverage: 66.1% (82/124) Total lines: 443 Tag: Kestrun/Kestrun@19d70630c420f79cdedeb90ba1187c4ef9ff547409/03/2025 - 13:55:51 Line coverage: 83.8% (145/173) Branch coverage: 66.9% (83/124) Total lines: 443 Tag: Kestrun/Kestrun@80ce2a54be2f719c7be1c21a92a8156bfdc48eb409/03/2025 - 18:37:02 Line coverage: 83.8% (145/173) Branch coverage: 66.1% (82/124) Total lines: 443 Tag: Kestrun/Kestrun@86fc5b3bca9d694f07b5e51c954ccc23c5594b4909/04/2025 - 17:02:01 Line coverage: 80.9% (187/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@f3880b25ea131298aa2f8b1e0d0a8d55eb160bc009/04/2025 - 17:09:47 Line coverage: 80.9% (187/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@2afb92fed8453d222b7af7c048460fff3c488fbc09/04/2025 - 18:11:31 Line coverage: 80.9% (187/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@de99e24698289f3f61ac7b73e96092732ae12b0509/04/2025 - 20:03:06 Line coverage: 80.9% (187/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@aac43c4a9d431237cc28e06bebdc8b9fca48b30609/05/2025 - 01:20:27 Line coverage: 80.9% (187/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@307328b32323080f64e2bbfdbf3f7087235cf22d09/05/2025 - 16:51:52 Line coverage: 80.9% (187/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@c672b8f05e89891aff034d6863debc0ea95b234d09/06/2025 - 04:23:39 Line coverage: 80.9% (187/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@a13e6122733c75e6e50f39500972849d262774e509/06/2025 - 18:30:33 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@aeddbedb8a96e9137aac94c2d5edd011b57ac87109/07/2025 - 18:41:40 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@2192d4ccb46312ce89b7f7fda1aa8c915bfa228409/08/2025 - 14:52:50 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@819d430c57fbf6a6ffbecb11f265d9b5cbe754a909/09/2025 - 05:44:24 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@a26a91936c400a7f2324671b2222643fb772438109/09/2025 - 20:36:29 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@eec6e531f6ea893bb4939db76943e009d9fd963c09/09/2025 - 21:56:59 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@739093f321f10605cc4d1029da7300e3bb4dcba909/10/2025 - 17:50:34 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@5f41b7385e6492ec5892c0c7887658952f6fb87e09/10/2025 - 19:58:59 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@9d3a582b2d63930269564a7591aa77ef297cadeb09/12/2025 - 13:06:50 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@0cd8aff9570206f0b23cbe71d96e965f66ee14fa09/12/2025 - 13:32:05 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@63ea5841fe73fd164406accba17a956e8c08357f09/12/2025 - 13:43:03 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@bd4d239cd63303706ae393e27d54f868ef64f9ff09/12/2025 - 16:20:13 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@bd014be0a15f3c9298922d2ff67068869adda2a009/12/2025 - 17:01:20 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@383c1560c6be4027d79f183f7ed839edb887efea09/12/2025 - 17:39:52 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@7189783cf8fc454ea15d22b08b77bebbd01f968709/12/2025 - 21:57:57 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@7f3035804f966a691bd6936a199a8086730a784509/13/2025 - 17:19:56 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@ea635f1ee1937c260a89d1a43a3c203cd8767c7b09/13/2025 - 21:11:10 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@c00c04d65edffc6840698a5c67a70cae1ad411d909/14/2025 - 21:23:16 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@c9d2f0b3dd164d7dc0dc2407a9f006293d92422309/15/2025 - 19:16:35 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@bfb58693b9baaed61644ace5b29e014d9ffacbc909/16/2025 - 04:01:29 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@e5263347b0baba68d9fd62ffbf60a7dd87f994bb09/16/2025 - 16:28:42 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@d5c0d6132e97ca542441289c02a4c9e9d0364d4909/16/2025 - 18:38:10 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@1ec65c49ba24bea275273220d19054072659b62a09/16/2025 - 19:34:38 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@cd031acff6ee2a77514aa4ff9c66847f9e475e6010/13/2025 - 16:52:37 Line coverage: 83.1% (202/243) Branch coverage: 68.3% (123/180) Total lines: 665 Tag: Kestrun/Kestrun@10d476bee71c71ad215bb8ab59f219887b5b4a5e10/15/2025 - 01:01:18 Line coverage: 83.1% (202/243) Branch coverage: 68.8% (124/180) Total lines: 665 Tag: Kestrun/Kestrun@7c4ce528870211ad6c2d2398c31ec13097fc584010/16/2025 - 12:37:55 Line coverage: 83.1% (202/243) Branch coverage: 68.3% (123/180) Total lines: 665 Tag: Kestrun/Kestrun@fe9b0d5b0046c725ac092fdc6c0e022ea3ddbd0610/17/2025 - 15:48:30 Line coverage: 83.1% (202/243) Branch coverage: 68.8% (124/180) Total lines: 665 Tag: Kestrun/Kestrun@b8199aff869a847b75e185d0527ba45e04a43d8610/17/2025 - 17:13:00 Line coverage: 83.1% (202/243) Branch coverage: 68.3% (123/180) Total lines: 665 Tag: Kestrun/Kestrun@72ffc26b6676ccf0f60d50c94e4bb52de23743be10/27/2025 - 15:14:42 Line coverage: 83.1% (202/243) Branch coverage: 68.8% (124/180) Total lines: 665 Tag: Kestrun/Kestrun@29b7a3fdf10e67fea44e784a49929d1eb2a8874611/14/2025 - 12:29:34 Line coverage: 82% (201/245) Branch coverage: 67% (122/182) Total lines: 677 Tag: Kestrun/Kestrun@5e12b09a6838e68e704cd3dc975331b9e680a62612/12/2025 - 17:27:19 Line coverage: 82% (201/245) Branch coverage: 66.4% (121/182) Total lines: 677 Tag: Kestrun/Kestrun@826bf9dcf9db118c5de4c78a3259bce9549f0dcd 08/26/2025 - 14:53:17 Line coverage: 83.8% (145/173) Branch coverage: 66.9% (83/124) Total lines: 443 Tag: Kestrun/Kestrun@78d1e497d8ba989d121b57aa39aa3c6b22de743108/26/2025 - 18:44:21 Line coverage: 83.8% (145/173) Branch coverage: 66.1% (82/124) Total lines: 443 Tag: Kestrun/Kestrun@d4eca24e86ad16771e391821efd443d6dc96606b08/27/2025 - 16:39:45 Line coverage: 83.8% (145/173) Branch coverage: 66.9% (83/124) Total lines: 443 Tag: Kestrun/Kestrun@4c6ca8f799d2de93b1edf9355074f3da4297dc6208/28/2025 - 13:31:25 Line coverage: 83.8% (145/173) Branch coverage: 66.1% (82/124) Total lines: 443 Tag: Kestrun/Kestrun@c90b3fa538340336a4939f46ad7235e29212eb5908/29/2025 - 15:33:49 Line coverage: 83.8% (145/173) Branch coverage: 66.9% (83/124) Total lines: 443 Tag: Kestrun/Kestrun@d302841c78e142037a1aae3165383d903a00b5f408/29/2025 - 16:01:24 Line coverage: 83.8% (145/173) Branch coverage: 66.1% (82/124) Total lines: 443 Tag: Kestrun/Kestrun@ad4a22f390590bbcb102eb1a8fa13cdf7ebd5c5b08/29/2025 - 19:42:14 Line coverage: 83.8% (145/173) Branch coverage: 66.9% (83/124) Total lines: 443 Tag: Kestrun/Kestrun@3c9ce084a1c53128813d4002ea9ff6c197712a0e08/30/2025 - 03:33:49 Line coverage: 83.8% (145/173) Branch coverage: 66.1% (82/124) Total lines: 443 Tag: Kestrun/Kestrun@1211de166c0177afac78a94a8f91658b8dd4981809/01/2025 - 04:08:24 Line coverage: 83.8% (145/173) Branch coverage: 66.9% (83/124) Total lines: 443 Tag: Kestrun/Kestrun@d6f26a131219b7a7fcb4e129af3193ec2ec4892909/01/2025 - 15:55:00 Line coverage: 83.8% (145/173) Branch coverage: 66.1% (82/124) Total lines: 443 Tag: Kestrun/Kestrun@19d70630c420f79cdedeb90ba1187c4ef9ff547409/03/2025 - 13:55:51 Line coverage: 83.8% (145/173) Branch coverage: 66.9% (83/124) Total lines: 443 Tag: Kestrun/Kestrun@80ce2a54be2f719c7be1c21a92a8156bfdc48eb409/03/2025 - 18:37:02 Line coverage: 83.8% (145/173) Branch coverage: 66.1% (82/124) Total lines: 443 Tag: Kestrun/Kestrun@86fc5b3bca9d694f07b5e51c954ccc23c5594b4909/04/2025 - 17:02:01 Line coverage: 80.9% (187/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@f3880b25ea131298aa2f8b1e0d0a8d55eb160bc009/04/2025 - 17:09:47 Line coverage: 80.9% (187/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@2afb92fed8453d222b7af7c048460fff3c488fbc09/04/2025 - 18:11:31 Line coverage: 80.9% (187/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@de99e24698289f3f61ac7b73e96092732ae12b0509/04/2025 - 20:03:06 Line coverage: 80.9% (187/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@aac43c4a9d431237cc28e06bebdc8b9fca48b30609/05/2025 - 01:20:27 Line coverage: 80.9% (187/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@307328b32323080f64e2bbfdbf3f7087235cf22d09/05/2025 - 16:51:52 Line coverage: 80.9% (187/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@c672b8f05e89891aff034d6863debc0ea95b234d09/06/2025 - 04:23:39 Line coverage: 80.9% (187/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@a13e6122733c75e6e50f39500972849d262774e509/06/2025 - 18:30:33 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@aeddbedb8a96e9137aac94c2d5edd011b57ac87109/07/2025 - 18:41:40 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@2192d4ccb46312ce89b7f7fda1aa8c915bfa228409/08/2025 - 14:52:50 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@819d430c57fbf6a6ffbecb11f265d9b5cbe754a909/09/2025 - 05:44:24 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@a26a91936c400a7f2324671b2222643fb772438109/09/2025 - 20:36:29 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@eec6e531f6ea893bb4939db76943e009d9fd963c09/09/2025 - 21:56:59 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@739093f321f10605cc4d1029da7300e3bb4dcba909/10/2025 - 17:50:34 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@5f41b7385e6492ec5892c0c7887658952f6fb87e09/10/2025 - 19:58:59 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@9d3a582b2d63930269564a7591aa77ef297cadeb09/12/2025 - 13:06:50 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@0cd8aff9570206f0b23cbe71d96e965f66ee14fa09/12/2025 - 13:32:05 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@63ea5841fe73fd164406accba17a956e8c08357f09/12/2025 - 13:43:03 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@bd4d239cd63303706ae393e27d54f868ef64f9ff09/12/2025 - 16:20:13 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@bd014be0a15f3c9298922d2ff67068869adda2a009/12/2025 - 17:01:20 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@383c1560c6be4027d79f183f7ed839edb887efea09/12/2025 - 17:39:52 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@7189783cf8fc454ea15d22b08b77bebbd01f968709/12/2025 - 21:57:57 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@7f3035804f966a691bd6936a199a8086730a784509/13/2025 - 17:19:56 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@ea635f1ee1937c260a89d1a43a3c203cd8767c7b09/13/2025 - 21:11:10 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@c00c04d65edffc6840698a5c67a70cae1ad411d909/14/2025 - 21:23:16 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@c9d2f0b3dd164d7dc0dc2407a9f006293d92422309/15/2025 - 19:16:35 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@bfb58693b9baaed61644ace5b29e014d9ffacbc909/16/2025 - 04:01:29 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@e5263347b0baba68d9fd62ffbf60a7dd87f994bb09/16/2025 - 16:28:42 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@d5c0d6132e97ca542441289c02a4c9e9d0364d4909/16/2025 - 18:38:10 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@1ec65c49ba24bea275273220d19054072659b62a09/16/2025 - 19:34:38 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@cd031acff6ee2a77514aa4ff9c66847f9e475e6010/13/2025 - 16:52:37 Line coverage: 83.1% (202/243) Branch coverage: 68.3% (123/180) Total lines: 665 Tag: Kestrun/Kestrun@10d476bee71c71ad215bb8ab59f219887b5b4a5e10/15/2025 - 01:01:18 Line coverage: 83.1% (202/243) Branch coverage: 68.8% (124/180) Total lines: 665 Tag: Kestrun/Kestrun@7c4ce528870211ad6c2d2398c31ec13097fc584010/16/2025 - 12:37:55 Line coverage: 83.1% (202/243) Branch coverage: 68.3% (123/180) Total lines: 665 Tag: Kestrun/Kestrun@fe9b0d5b0046c725ac092fdc6c0e022ea3ddbd0610/17/2025 - 15:48:30 Line coverage: 83.1% (202/243) Branch coverage: 68.8% (124/180) Total lines: 665 Tag: Kestrun/Kestrun@b8199aff869a847b75e185d0527ba45e04a43d8610/17/2025 - 17:13:00 Line coverage: 83.1% (202/243) Branch coverage: 68.3% (123/180) Total lines: 665 Tag: Kestrun/Kestrun@72ffc26b6676ccf0f60d50c94e4bb52de23743be10/27/2025 - 15:14:42 Line coverage: 83.1% (202/243) Branch coverage: 68.8% (124/180) Total lines: 665 Tag: Kestrun/Kestrun@29b7a3fdf10e67fea44e784a49929d1eb2a8874611/14/2025 - 12:29:34 Line coverage: 82% (201/245) Branch coverage: 67% (122/182) Total lines: 677 Tag: Kestrun/Kestrun@5e12b09a6838e68e704cd3dc975331b9e680a62612/12/2025 - 17:27:19 Line coverage: 82% (201/245) Branch coverage: 66.4% (121/182) Total lines: 677 Tag: Kestrun/Kestrun@826bf9dcf9db118c5de4c78a3259bce9549f0dcd

Metrics

File(s)

/home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun/Languages/CSharpDelegateBuilder.cs

#LineLine coverage
 1using System.Collections.Immutable;
 2using System.Reflection;
 3using System.Text;
 4using Kestrun.SharedState;
 5using Microsoft.CodeAnalysis;
 6using Microsoft.CodeAnalysis.CSharp;
 7using Microsoft.CodeAnalysis.CSharp.Scripting;
 8using Microsoft.CodeAnalysis.Scripting;
 9using Serilog.Events;
 10using Kestrun.Logging;
 11using Kestrun.Hosting;
 12
 13namespace Kestrun.Languages;
 14
 15
 16internal static class CSharpDelegateBuilder
 17{
 18    /// <summary>
 19    /// Builds a C# delegate for handling HTTP requests.
 20    /// </summary>
 21    /// <param name="host">The Kestrun host instance.</param>
 22    /// <param name="code">The C# code to execute.</param>
 23    /// <param name="args">Arguments to inject as variables into the script.</param>
 24    /// <param name="extraImports">Additional namespaces to import.</param>
 25    /// <param name="extraRefs">Additional assemblies to reference.</param>
 26    /// <param name="languageVersion">The C# language version to use.</param>
 27    /// <returns>A delegate that handles HTTP requests.</returns>
 28    /// <exception cref="ArgumentNullException">Thrown if the code is null or whitespace.</exception>
 29    /// <exception cref="CompilationErrorException">Thrown if the C# code compilation fails.</exception>
 30    /// <remarks>
 31    /// This method compiles the provided C# code into a script and returns a delegate that can be used to handle HTTP r
 32    /// It supports additional imports and references, and can inject global variables into the script.
 33    /// The delegate will execute the provided C# code within the context of an HTTP request, allowing access to the req
 34    /// </remarks>
 35    internal static RequestDelegate Build(KestrunHost host,
 36            string code, Dictionary<string, object?>? args, string[]? extraImports,
 37            Assembly[]? extraRefs, LanguageVersion languageVersion = LanguageVersion.CSharp12)
 38    {
 3739        if (host.Logger.IsEnabled(LogEventLevel.Debug))
 40        {
 2641            host.Logger.Debug("Building C# delegate, script length={Length}, imports={ImportsCount}, refs={RefsCount}, l
 2642                code?.Length, extraImports?.Length ?? 0, extraRefs?.Length ?? 0, languageVersion);
 43        }
 44
 45        // Validate inputs
 3746        if (string.IsNullOrWhiteSpace(code))
 47        {
 148            throw new ArgumentNullException(nameof(code), "C# code cannot be null or whitespace.");
 49        }
 50        // 1. Compile the C# code into a script
 51        //    - Use CSharpScript.Create() to create a script with the provided code
 52        //    - Use ScriptOptions to specify imports, references, and language version
 53        //    - Inject the provided arguments into the globals
 3654        var script = Compile(host: host, code: code, extraImports: extraImports, extraRefs: extraRefs, null, languageVer
 55
 56        // 2. Return a delegate that executes the script
 57        //    - The delegate takes an HttpContext and returns a Task
 58        //    - It creates a KestrunContext and KestrunResponse from the HttpContext
 59        //    - It executes the script with the provided globals and locals
 60        //    - It applies the response to the HttpContext
 3661        if (host.Logger.IsEnabled(LogEventLevel.Debug))
 62        {
 2563            host.Logger.Debug("C# delegate built successfully, script length={Length}, imports={ImportsCount}, refs={Ref
 2564                code?.Length, extraImports?.Length ?? 0, extraRefs?.Length ?? 0, languageVersion);
 65        }
 66
 3667        return async ctx =>
 3668        {
 3669            try
 3670            {
 1571                if (host.Logger.IsEnabled(LogEventLevel.Debug))
 3672                {
 373                    host.Logger.DebugSanitized("Preparing execution for C# script at {Path}", ctx.Request.Path);
 3674                }
 3675
 1576                var (Globals, Response, Context) = await DelegateBuilder.PrepareExecutionAsync(host, ctx, args).Configur
 3677
 3678                // Execute the script with the current context and shared state
 1579                if (host.Logger.IsEnabled(LogEventLevel.Debug))
 3680                {
 381                    host.Logger.DebugSanitized("Executing C# script for {Path}", ctx.Request.Path);
 3682                }
 3683
 1584                _ = await script.RunAsync(Globals).ConfigureAwait(false);
 1085                if (host.Logger.IsEnabled(LogEventLevel.Debug))
 3686                {
 387                    host.Logger.DebugSanitized("C# script executed successfully for {Path}", ctx.Request.Path);
 3688                }
 3689
 3690                // Apply the response to the Kestrun context
 1091                await DelegateBuilder.ApplyResponseAsync(ctx, Response, host.Logger).ConfigureAwait(false);
 1092            }
 3693            finally
 3694            {
 3695                // Intentionally do not call Response.CompleteAsync here to keep the pipeline open
 3696                // for middleware like StatusCodePages to generate bodies for status-only responses.
 3697            }
 4698        };
 99    }
 100
 101    /// <summary>
 102    /// Compiles the provided C# code into a script.
 103    /// This method supports additional imports and references, and can inject global variables into the script.
 104    /// It returns a compiled script that can be executed later.
 105    /// </summary>
 106    /// <param name="host">The Kestrun host instance.</param>
 107    /// <param name="code">The C# code to compile.</param>
 108    /// <param name="extraImports">Additional namespaces to import.</param>
 109    /// <param name="extraRefs">Additional assembly references.</param>
 110    /// <param name="locals">Local variables to inject into the script.</param>
 111    /// <param name="languageVersion">The C# language version to use.</param>
 112    /// <returns>A compiled script that can be executed later.</returns>
 113    /// <exception cref="ArgumentNullException">Thrown when the code is null or whitespace.</exception>
 114    /// <exception cref="CompilationErrorException">Thrown when there are compilation errors.</exception>
 115    /// <remarks>
 116    /// This method compiles the provided C# code into a script using Roslyn.
 117    /// It supports additional imports and references, and can inject global variables into the script.
 118    /// The script can be executed later with the provided globals and locals.
 119    /// It is useful for scenarios where dynamic C# code execution is required, such as in web applications or scripting
 120    /// </remarks>
 121    internal static Script<object> Compile(
 122            KestrunHost host,
 123            string? code, string[]? extraImports,
 124            Assembly[]? extraRefs, IReadOnlyDictionary<string, object?>? locals, LanguageVersion languageVersion = Langu
 125            )
 126    {
 59127        var log = host.Logger;
 59128        if (log.IsEnabled(LogEventLevel.Debug))
 129        {
 48130            log.Debug("Compiling C# script, length={Length}, imports={ImportsCount}, refs={RefsCount}, lang={Lang}",
 48131                code?.Length, extraImports?.Length ?? 0, extraRefs?.Length ?? 0, languageVersion);
 132        }
 133
 134        // Validate inputs
 59135        if (string.IsNullOrWhiteSpace(code))
 136        {
 0137            throw new ArgumentNullException(nameof(code), "C# code cannot be null or whitespace.");
 138        }
 139
 140        // References and imports
 59141        var coreRefs = DelegateBuilder.BuildBaselineReferences();
 142        // Core references + Kestrun + extras
 143        // Note: Order matters, Kestrun must come after core to avoid conflicts
 59144        var kestrunAssembly = typeof(KestrunHost).Assembly; // Kestrun.dll
 59145        var kestrunRef = MetadataReference.CreateFromFile(kestrunAssembly.Location);
 59146        var kestrunNamespaces = CollectKestrunNamespaces(kestrunAssembly);
 147        // Create script options
 59148        var opts = CreateScriptOptions(DelegateBuilder.PlatformImports, kestrunNamespaces, coreRefs, kestrunRef);
 59149        opts = AddExtraImports(opts, extraImports);
 59150        opts = AddExtraReferences(opts, extraRefs, log);
 151
 152        // Include currently loaded assemblies (deduplicated) to minimize missing reference issues.
 59153        opts = AddLoadedAssemblyReferences(opts, log);
 154
 155        // Globals/locals injection plus dynamic discovery of namespaces & assemblies needed
 59156        var (CodeWithPreamble, DynamicImports, DynamicReferences) = BuildGlobalsAndLocalsPreamble(host, code, locals);
 59157        code = CodeWithPreamble;
 158
 59159        if (DynamicImports.Count > 0)
 160        {
 22161            var newImports = DynamicImports.Except(opts.Imports, StringComparer.Ordinal).ToArray();
 22162            if (newImports.Length > 0)
 163            {
 0164                opts = opts.WithImports(opts.Imports.Concat(newImports));
 0165                if (log.IsEnabled(LogEventLevel.Debug))
 166                {
 0167                    log.Debug("Added {ImportCount} dynamic imports derived from globals/locals: {Imports}", newImports.L
 168                }
 169            }
 170        }
 171
 59172        if (DynamicReferences.Count > 0)
 173        {
 174            // Avoid duplicates by location
 22175            var existingRefPaths = new HashSet<string>(opts.MetadataReferences
 22176                .OfType<PortableExecutableReference>()
 5460177                .Select(r => r.FilePath ?? string.Empty)
 5482178                .Where(p => !string.IsNullOrEmpty(p)), StringComparer.OrdinalIgnoreCase);
 179
 22180            var newRefs = DynamicReferences
 22181                .Where(r => !string.IsNullOrEmpty(r.Location) && File.Exists(r.Location) && !existingRefPaths.Contains(r
 0182                .Select(r => MetadataReference.CreateFromFile(r.Location))
 22183                .ToArray();
 184
 22185            if (newRefs.Length > 0)
 186            {
 0187                opts = opts.WithReferences(opts.MetadataReferences.Concat(newRefs));
 0188                if (log.IsEnabled(LogEventLevel.Debug))
 189                {
 0190                    log.Debug("Added {RefCount} dynamic assembly reference(s) derived from globals/locals.", newRefs.Len
 191                }
 192            }
 193        }
 194
 195        // Compile
 59196        var script = CSharpScript.Create(code, opts, typeof(CsGlobals));
 59197        var diagnostics = CompileAndGetDiagnostics(script, log);
 59198        ThrowIfDiagnosticsNull(diagnostics);
 59199        ThrowOnErrors(diagnostics, log);
 58200        LogWarnings(diagnostics, log);
 58201        LogSuccessIfNoWarnings(diagnostics, log);
 202
 58203        return script;
 204    }
 205
 206    /// <summary>Collects metadata references for all non-dynamic loaded assemblies with a physical location.</summary>
 207    /// <param name="log">Logger.</param>
 208    /// <returns>Tuple of references and total count considered.</returns>
 209    private static (IEnumerable<MetadataReference> Refs, int Total) CollectLoadedAssemblyReferences(Serilog.ILogger log)
 210    {
 211        try
 212        {
 59213            var loaded = AppDomain.CurrentDomain.GetAssemblies();
 59214            var refs = new List<MetadataReference>(loaded.Length);
 59215            var considered = 0;
 35220216            foreach (var a in loaded)
 217            {
 17551218                considered++;
 17551219                if (a.IsDynamic)
 220                {
 221                    continue;
 222                }
 17329223                if (string.IsNullOrEmpty(a.Location) || !File.Exists(a.Location))
 224                {
 225                    continue;
 226                }
 227                try
 228                {
 14765229                    refs.Add(MetadataReference.CreateFromFile(a.Location));
 14765230                }
 0231                catch (Exception ex)
 232                {
 0233                    if (log.IsEnabled(LogEventLevel.Debug))
 234                    {
 0235                        log.Debug(ex, "Failed to add loaded assembly reference: {Assembly}", a.FullName);
 236                    }
 0237                }
 238            }
 59239            return (refs, considered);
 240        }
 0241        catch (Exception ex)
 242        {
 0243            log.Warning(ex, "Failed to enumerate loaded assemblies for dynamic references.");
 0244            return (Array.Empty<MetadataReference>(), 0);
 245        }
 59246    }
 247
 248    /// <summary>
 249    /// Builds the core assembly references for the script.
 250    /// </summary>
 251    /// <returns>The core assembly references.</returns>
 252
 253    /// <summary>
 254    /// Collects the namespaces from the Kestrun assembly.
 255    /// </summary>
 256    /// <param name="kestrunAssembly">The Kestrun assembly.</param>
 257    /// <returns>The collected namespaces.</returns>
 258    private static string[] CollectKestrunNamespaces(Assembly kestrunAssembly)
 259    {
 59260        return [.. kestrunAssembly
 59261            .GetExportedTypes()
 10974262            .Select(t => t.Namespace)
 10974263            .Where(ns => !string.IsNullOrEmpty(ns) && ns!.StartsWith("Kestrun", StringComparison.Ordinal))
 10974264            .Select(ns => ns!)
 59265            .Distinct()];
 266    }
 267
 268    /// <summary>
 269    /// Creates script options for the VB.NET script.
 270    /// </summary>
 271    /// <param name="platformImports">The platform-specific namespaces to import.</param>
 272    /// <param name="kestrunNamespaces">The Kestrun-specific namespaces to import.</param>
 273    /// <param name="coreRefs">The core assembly references to include.</param>
 274    /// <param name="kestrunRef">The Kestrun assembly reference to include.</param>
 275    /// <returns>The created script options.</returns>
 276    private static ScriptOptions CreateScriptOptions(
 277        IEnumerable<string> platformImports,
 278        IEnumerable<string> kestrunNamespaces,
 279        IEnumerable<MetadataReference> coreRefs,
 280        MetadataReference kestrunRef)
 281    {
 59282        var allImports = platformImports.Concat(kestrunNamespaces) ?? [];
 283        // Keep default references then add our core + Kestrun to avoid losing essential BCL assemblies
 59284        var opts = ScriptOptions.Default
 59285            .WithImports(allImports)
 59286            .AddReferences(coreRefs)
 59287            .AddReferences(kestrunRef);
 59288        return opts;
 289    }
 290
 291    /// <summary>
 292    /// Adds extra using directives to the script options.
 293    /// </summary>
 294    /// <param name="opts">The script options to modify.</param>
 295    /// <param name="extraImports">The extra using directives to add.</param>
 296    /// <returns>The modified script options.</returns>
 297    private static ScriptOptions AddExtraImports(ScriptOptions opts, string[]? extraImports)
 298    {
 59299        extraImports ??= ["Kestrun"];
 59300        if (!extraImports.Contains("Kestrun"))
 301        {
 1302            var importsList = extraImports.ToList();
 1303            importsList.Add("Kestrun");
 1304            extraImports = [.. importsList];
 305        }
 59306        return extraImports.Length > 0
 59307            ? opts.WithImports(opts.Imports.Concat(extraImports))
 59308            : opts;
 309    }
 310
 311    /// <summary>
 312    /// Adds extra assembly references to the script options.
 313    /// </summary>
 314    /// <param name="opts">The script options to modify.</param>
 315    /// <param name="extraRefs">The extra assembly references to add.</param>
 316    /// <param name="log">The logger to use for logging.</param>
 317    /// <returns>The modified script options.</returns>
 318    private static ScriptOptions AddExtraReferences(ScriptOptions opts, Assembly[]? extraRefs, Serilog.ILogger log)
 319    {
 59320        if (extraRefs is not { Length: > 0 })
 321        {
 59322            return opts;
 323        }
 324
 0325        foreach (var r in extraRefs)
 326        {
 0327            if (string.IsNullOrEmpty(r.Location))
 328            {
 0329                log.Warning("Skipping dynamic assembly with no location: {Assembly}", r.FullName);
 330            }
 0331            else if (!File.Exists(r.Location))
 332            {
 0333                log.Warning("Skipping missing assembly file: {Location}", r.Location);
 334            }
 335        }
 336
 0337        var safeRefs = extraRefs
 0338            .Where(r => !string.IsNullOrEmpty(r.Location) && File.Exists(r.Location))
 0339            .Select(r => MetadataReference.CreateFromFile(r.Location));
 340
 0341        return opts.WithReferences(opts.MetadataReferences.Concat(safeRefs));
 342    }
 343
 344    /// <summary>
 345    /// Adds references for all currently loaded (non-duplicate) assemblies to the script options.
 346    /// </summary>
 347    /// <param name="opts">Current script options.</param>
 348    /// <param name="log">Logger.</param>
 349    /// <returns>Updated script options.</returns>
 350    private static ScriptOptions AddLoadedAssemblyReferences(ScriptOptions opts, Serilog.ILogger log)
 351    {
 352        // Optionally include all currently loaded assemblies to reduce missing reference issues.
 353        // Roslyn will de-duplicate by file path internally but we still filter to avoid redundant work.
 59354        var (loadedRefs, loadedCount) = CollectLoadedAssemblyReferences(log);
 59355        if (loadedCount <= 0)
 356        {
 0357            return opts;
 358        }
 359
 59360        var existingPaths = new HashSet<string>(opts.MetadataReferences
 59361            .OfType<PortableExecutableReference>()
 10637362            .Select(r => r.FilePath ?? string.Empty)
 10696363            .Where(p => !string.IsNullOrEmpty(p)), StringComparer.OrdinalIgnoreCase);
 364
 59365        var newLoadedRefs = loadedRefs
 14765366            .Where(r => r is PortableExecutableReference pe && !string.IsNullOrEmpty(pe.FilePath) && !existingPaths.Cont
 59367            .ToArray();
 368
 59369        if (newLoadedRefs.Length == 0)
 370        {
 0371            return opts;
 372        }
 373
 59374        var updated = opts.WithReferences(opts.MetadataReferences.Concat(newLoadedRefs));
 59375        if (log.IsEnabled(LogEventLevel.Debug))
 376        {
 48377            log.Debug("Added {RefCount} loaded assembly reference(s) (of {TotalLoaded}) for dynamic script compilation."
 378        }
 59379        return updated;
 380    }
 381
 382    /// <summary>
 383    /// Prepends global and local variable declarations to the provided code.
 384    /// </summary>
 385    ///<param name="host">The Kestrun host instance.</param>
 386    /// <param name="code">The original code to modify.</param>
 387    /// <param name="locals">The local variables to include.</param>
 388    /// <returns>The modified code with global and local variable declarations.</returns>
 389    /// <summary>Builds the preamble variable declarations for globals &amp; locals and discovers required namespaces an
 390    /// <returns>Tuple containing code with preamble, dynamic imports, dynamic references.</returns>
 391    private static (string CodeWithPreamble, List<string> DynamicImports, List<Assembly> DynamicReferences) BuildGlobals
 392        KestrunHost host,
 393        string? code,
 394        IReadOnlyDictionary<string, object?>? locals)
 395    {
 396        // Merge globals + locals
 59397        var merged = MergeGlobalsAndLocals(host, locals);
 398
 399        // Build preamble & discover dynamic imports/refs
 59400        var (preamble, imports, refs) = GeneratePreambleAndDiscover(merged);
 401
 402        // Append original code
 59403        var finalCode = preamble.Length > 0 ? preamble + (code ?? string.Empty) : code ?? string.Empty;
 404
 405        // Filter references to only those with locations
 81406        var filteredRefs = refs.Where(r => !string.IsNullOrEmpty(r.Location)).ToList();
 407
 59408        LogDynamicDiscovery(imports, filteredRefs, host.Logger);
 409
 59410        return (finalCode, imports.ToList(), filteredRefs);
 411    }
 412
 413    /// <summary>
 414    /// Creates a merged dictionary of global shared state and the supplied <paramref name="locals"/>.
 415    /// Local values override globals when a key collision occurs (case-insensitive).
 416    /// </summary>
 417    /// <param name="host">The Kestrun host instance.</param>
 418    /// <param name="locals">Optional locals dictionary passed in at compile time.</param>
 419    /// <returns>A mutable dictionary keyed by variable name mapping to its source store name and value.</returns>
 420    private static Dictionary<string, (string Dict, object? Value)> MergeGlobalsAndLocals(
 421        KestrunHost host,
 422        IReadOnlyDictionary<string, object?>? locals)
 423    {
 59424        var merged = new Dictionary<string, (string Dict, object? Value)>(StringComparer.OrdinalIgnoreCase);
 118425        foreach (var g in GlobalStore.Snapshot())
 426        {
 0427            merged[g.Key] = ("Globals", g.Value);
 428        }
 429        // Also include host-level shared state
 430        // if host shared state has same key as global, host takes precedence
 126431        foreach (var g in host.SharedState.Snapshot())
 432        {
 4433            merged[g.Key] = ("Globals", g.Value);
 434        }
 435        // Now overlay locals
 436        // if local has same key as global, local takes precedence
 59437        if (locals is { Count: > 0 })
 438        {
 80439            foreach (var l in locals)
 440            {
 21441                merged[l.Key] = ("Locals", l.Value);
 442            }
 443        }
 59444        return merged;
 445    }
 446
 447    /// <summary>
 448    /// Iterates all merged global + local variables and builds a textual preamble of variable declarations.
 449    /// While building, collects the required namespace imports and assembly references inferred from the runtime value 
 450    /// </summary>
 451    /// <param name="merged">Merged globals + locals produced by <see cref="MergeGlobalsAndLocals"/>.</param>
 452    /// <returns>Tuple containing the preamble text builder, discovered namespace import set, and assembly reference set
 453    private static (StringBuilder Preamble, HashSet<string> Imports, HashSet<Assembly> Refs) GeneratePreambleAndDiscover
 454        Dictionary<string, (string Dict, object? Value)> merged)
 455    {
 59456        var preambleBuilder = new StringBuilder();
 59457        var dynamicImports = new HashSet<string>(StringComparer.Ordinal);
 59458        var dynamicRefs = new HashSet<Assembly>();
 459
 166460        foreach (var kvp in merged)
 461        {
 24462            AppendVariableDeclarationAndCollect(kvp, preambleBuilder, dynamicImports, dynamicRefs);
 463        }
 464
 59465        return (preambleBuilder, dynamicImports, dynamicRefs);
 466    }
 467
 468    /// <summary>
 469    /// Appends a single variable declaration for the provided key/value and gathers any dynamic imports &amp; reference
 470    /// </summary>
 471    /// <param name="kvp">The merged key and its (source dictionary name, value).</param>
 472    /// <param name="preambleBuilder">Builder accumulating declaration lines.</param>
 473    /// <param name="dynamicImports">Set capturing namespaces to import.</param>
 474    /// <param name="dynamicRefs">Set capturing assemblies that must be referenced.</param>
 475    private static void AppendVariableDeclarationAndCollect(
 476        KeyValuePair<string, (string Dict, object? Value)> kvp,
 477        StringBuilder preambleBuilder,
 478        HashSet<string> dynamicImports,
 479        HashSet<Assembly> dynamicRefs)
 480    {
 24481        var valueType = kvp.Value.Value?.GetType();
 24482        var typeName = FormatTypeName(valueType);
 24483        _ = preambleBuilder.AppendLine($"var {kvp.Key} = ({typeName}){kvp.Value.Dict}[\"{kvp.Key}\"]; ");
 484
 24485        if (valueType == null)
 486        {
 0487            return;
 488        }
 489
 24490        TryAddNamespace(dynamicImports, valueType.Namespace);
 24491        CollectGenericArguments(valueType, dynamicImports, dynamicRefs);
 24492        _ = dynamicRefs.Add(valueType.Assembly);
 24493    }
 494
 495    /// <summary>
 496    /// Adds a namespace string to the imports set if it is non-empty.
 497    /// </summary>
 498    /// <param name="imports">Namespace accumulator set.</param>
 499    /// <param name="ns">Namespace candidate.</param>
 500    private static void TryAddNamespace(HashSet<string> imports, string? ns)
 501    {
 27502        if (!string.IsNullOrEmpty(ns))
 503        {
 27504            _ = imports.Add(ns);
 505        }
 27506    }
 507
 508    /// <summary>
 509    /// For a generic type, collects namespaces and assemblies for each generic argument.
 510    /// </summary>
 511    /// <param name="valueType">The possibly generic value type.</param>
 512    /// <param name="imports">Namespace accumulator set.</param>
 513    /// <param name="refs">Assembly reference accumulator set.</param>
 514    private static void CollectGenericArguments(Type valueType, HashSet<string> imports, HashSet<Assembly> refs)
 515    {
 24516        if (!valueType.IsGenericType)
 517        {
 22518            return;
 519        }
 10520        foreach (var ga in valueType.GetGenericArguments())
 521        {
 3522            TryAddNamespace(imports, ga.Namespace);
 3523            _ = refs.Add(ga.Assembly);
 524        }
 2525    }
 526
 527    /// <summary>
 528    /// Emits a debug log summarizing the dynamic imports and assembly references that were discovered.
 529    /// </summary>
 530    /// <param name="imports">Collected imports.</param>
 531    /// <param name="refs">Collected assembly references (filtered to those with physical locations).</param>
 532    /// <param name="log">Logger.</param>
 533    private static void LogDynamicDiscovery(HashSet<string> imports, List<Assembly> refs, Serilog.ILogger log)
 534    {
 59535        if (log.IsEnabled(LogEventLevel.Debug) && (imports.Count > 0 || refs.Count > 0))
 536        {
 22537            log.Debug("Discovered {ImportCount} dynamic import(s) and {RefCount} reference(s) from globals/locals.", imp
 538        }
 59539    }
 540
 541    // Produces a C# friendly type name for reflection types (handles generics, arrays, nullable, and fallbacks).
 542    private static string FormatTypeName(Type? t)
 543    {
 28544        if (t == null)
 545        {
 0546            return "object";
 547        }
 28548        if (t.IsGenericParameter)
 549        {
 0550            return "object";
 551        }
 28552        if (t.IsArray)
 553        {
 1554            return FormatTypeName(t.GetElementType()) + "[]";
 555        }
 556        // Nullable<T>
 27557        if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>))
 558        {
 0559            return FormatTypeName(t.GetGenericArguments()[0]) + "?";
 560        }
 27561        if (t.IsGenericType)
 562        {
 563            try
 564            {
 2565                var genericDefName = t.Name;
 2566                var tickIndex = genericDefName.IndexOf('`');
 2567                if (tickIndex > 0)
 568                {
 2569                    genericDefName = genericDefName[..tickIndex];
 570                }
 2571                var args = t.GetGenericArguments().Select(FormatTypeName);
 2572                return (t.Namespace != null ? t.Namespace + "." : string.Empty) + genericDefName + "<" + string.Join(","
 573            }
 0574            catch
 575            {
 0576                return "object";
 577            }
 578        }
 579        // Non generic
 25580        return t.FullName ?? t.Name ?? "object";
 2581    }
 582
 583    /// <summary>
 584    /// Compiles the provided VB.NET script and returns any diagnostics.
 585    /// </summary>
 586    /// <param name="script">The VB.NET script to compile.</param>
 587    /// <param name="log">The logger to use for logging.</param>
 588    /// <returns>A collection of diagnostics produced during compilation, or null if compilation failed.</returns>
 589    private static ImmutableArray<Diagnostic>? CompileAndGetDiagnostics(Script<object> script, Serilog.ILogger log)
 590    {
 591        try
 592        {
 59593            return script.Compile();
 594        }
 0595        catch (CompilationErrorException ex)
 596        {
 0597            log.Error(ex, "C# script compilation failed with errors.");
 0598            return null;
 599        }
 59600    }
 601
 602    private static void ThrowIfDiagnosticsNull(ImmutableArray<Diagnostic>? diagnostics)
 603    {
 59604        if (diagnostics == null)
 605        {
 0606            throw new CompilationErrorException("C# script compilation failed with no diagnostics.", []);
 607        }
 59608    }
 609
 610    /// <summary>
 611    /// Throws a CompilationErrorException if the diagnostics are null.
 612    /// </summary>
 613    /// <param name="diagnostics">The compilation diagnostics.</param>
 614    /// <param name="log">The logger to use for logging.</param>
 615    /// <exception cref="CompilationErrorException"></exception>
 616    private static void ThrowOnErrors(ImmutableArray<Diagnostic>? diagnostics, Serilog.ILogger log)
 617    {
 60618        if (diagnostics?.Any(d => d.Severity == DiagnosticSeverity.Error) != true)
 619        {
 58620            return;
 621        }
 622
 2623        var errors = diagnostics?.Where(d => d.Severity == DiagnosticSeverity.Error).ToArray();
 1624        if (errors is not { Length: > 0 })
 625        {
 0626            return;
 627        }
 628
 1629        var sb = new StringBuilder();
 1630        _ = sb.AppendLine($"C# script compilation completed with {errors.Length} error(s):");
 4631        foreach (var error in errors)
 632        {
 1633            var location = error.Location.IsInSource
 1634                ? $" at line {error.Location.GetLineSpan().StartLinePosition.Line + 1}"
 1635                : string.Empty;
 1636            var msg = $"  Error [{error.Id}]: {error.GetMessage()}{location}";
 1637            log.Error(msg);
 1638            _ = sb.AppendLine(msg);
 639        }
 1640        throw new CompilationErrorException("C# route code compilation failed\n" + sb.ToString(), diagnostics ?? []);
 641    }
 642
 643    /// <summary>
 644    /// Logs warning messages if the compilation succeeded with warnings.
 645    /// </summary>
 646    /// <param name="diagnostics">The compilation diagnostics.</param>
 647    /// <param name="log">The logger to use for logging.</param>
 648    private static void LogWarnings(ImmutableArray<Diagnostic>? diagnostics, Serilog.ILogger log)
 649    {
 58650        var warnings = diagnostics?.Where(d => d.Severity == DiagnosticSeverity.Warning).ToArray();
 58651        if (warnings is not null && warnings.Length != 0)
 652        {
 0653            log.Warning($"C# script compilation completed with {warnings.Length} warning(s):");
 0654            foreach (var warning in warnings)
 655            {
 0656                var location = warning.Location.IsInSource
 0657                    ? $" at line {warning.Location.GetLineSpan().StartLinePosition.Line + 1}"
 0658                    : string.Empty;
 0659                log.Warning($"  Warning [{warning.Id}]: {warning.GetMessage()}{location}");
 660            }
 661        }
 58662    }
 663
 664    /// <summary>
 665    /// Logs a success message if the compilation succeeded without warnings.
 666    /// </summary>
 667    /// <param name="diagnostics">The compilation diagnostics.</param>
 668    /// <param name="log">The logger to use for logging.</param>
 669    private static void LogSuccessIfNoWarnings(ImmutableArray<Diagnostic>? diagnostics, Serilog.ILogger log)
 670    {
 58671        var warnings = diagnostics?.Where(d => d.Severity == DiagnosticSeverity.Warning).ToArray();
 58672        if (warnings != null && warnings.Length == 0 && log.IsEnabled(LogEventLevel.Debug))
 673        {
 47674            log.Debug("C# script compiled successfully with no warnings.");
 675        }
 58676    }
 677}

Methods/Properties

Build(Kestrun.Hosting.KestrunHost,System.String,System.Collections.Generic.Dictionary`2<System.String,System.Object>,System.String[],System.Reflection.Assembly[],Microsoft.CodeAnalysis.CSharp.LanguageVersion)
Compile(Kestrun.Hosting.KestrunHost,System.String,System.String[],System.Reflection.Assembly[],System.Collections.Generic.IReadOnlyDictionary`2<System.String,System.Object>,Microsoft.CodeAnalysis.CSharp.LanguageVersion)
CollectLoadedAssemblyReferences(Serilog.ILogger)
CollectKestrunNamespaces(System.Reflection.Assembly)
CreateScriptOptions(System.Collections.Generic.IEnumerable`1<System.String>,System.Collections.Generic.IEnumerable`1<System.String>,System.Collections.Generic.IEnumerable`1<Microsoft.CodeAnalysis.MetadataReference>,Microsoft.CodeAnalysis.MetadataReference)
AddExtraImports(Microsoft.CodeAnalysis.Scripting.ScriptOptions,System.String[])
AddExtraReferences(Microsoft.CodeAnalysis.Scripting.ScriptOptions,System.Reflection.Assembly[],Serilog.ILogger)
AddLoadedAssemblyReferences(Microsoft.CodeAnalysis.Scripting.ScriptOptions,Serilog.ILogger)
BuildGlobalsAndLocalsPreamble(Kestrun.Hosting.KestrunHost,System.String,System.Collections.Generic.IReadOnlyDictionary`2<System.String,System.Object>)
MergeGlobalsAndLocals(Kestrun.Hosting.KestrunHost,System.Collections.Generic.IReadOnlyDictionary`2<System.String,System.Object>)
GeneratePreambleAndDiscover(System.Collections.Generic.Dictionary`2<System.String,System.ValueTuple`2<System.String,System.Object>>)
AppendVariableDeclarationAndCollect(System.Collections.Generic.KeyValuePair`2<System.String,System.ValueTuple`2<System.String,System.Object>>,System.Text.StringBuilder,System.Collections.Generic.HashSet`1<System.String>,System.Collections.Generic.HashSet`1<System.Reflection.Assembly>)
TryAddNamespace(System.Collections.Generic.HashSet`1<System.String>,System.String)
CollectGenericArguments(System.Type,System.Collections.Generic.HashSet`1<System.String>,System.Collections.Generic.HashSet`1<System.Reflection.Assembly>)
LogDynamicDiscovery(System.Collections.Generic.HashSet`1<System.String>,System.Collections.Generic.List`1<System.Reflection.Assembly>,Serilog.ILogger)
FormatTypeName(System.Type)
CompileAndGetDiagnostics(Microsoft.CodeAnalysis.Scripting.Script`1<System.Object>,Serilog.ILogger)
ThrowIfDiagnosticsNull(System.Nullable`1<System.Collections.Immutable.ImmutableArray`1<Microsoft.CodeAnalysis.Diagnostic>>)
ThrowOnErrors(System.Nullable`1<System.Collections.Immutable.ImmutableArray`1<Microsoft.CodeAnalysis.Diagnostic>>,Serilog.ILogger)
LogWarnings(System.Nullable`1<System.Collections.Immutable.ImmutableArray`1<Microsoft.CodeAnalysis.Diagnostic>>,Serilog.ILogger)
LogSuccessIfNoWarnings(System.Nullable`1<System.Collections.Immutable.ImmutableArray`1<Microsoft.CodeAnalysis.Diagnostic>>,Serilog.ILogger)