{"id":3037,"date":"2020-12-09T16:58:39","date_gmt":"2020-12-09T04:58:39","guid":{"rendered":"https:\/\/www.deltics.co.nz\/blog\/?p=3037"},"modified":"2020-12-09T17:52:21","modified_gmt":"2020-12-09T05:52:21","slug":"reraise-exception-bug-in-10-4-1-x86-compiler","status":"publish","type":"post","link":"https:\/\/www.deltics.co.nz\/blog\/posts\/3037\/","title":{"rendered":"ReRaise Exception Bug in 10.4.1 x86 Compiler"},"content":{"rendered":"<span class=\"span-reading-time rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\"> 4<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span>\n<p class=\"wp-block-paragraph\">I just ran into a very frustrating issue in Delphi 10.4.1 with long-standing, basic functionality that is now broken in certain circumstances.  Fortunately there is a work-around in those certain circumstances, but it&#8217;s not pretty.<\/p>\n\n\n\n<!--more-->\n\n\n\n<p class=\"has-text-color has-background has-medium-font-size has-very-light-gray-color has-vivid-red-background-color wp-block-paragraph\"><strong>The Problem: <\/strong>Re-Raising an exception causes an immediate Access Violation error at the <code class=\"\" data-line=\"\">raise<\/code> statement (in certain circumstances).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">An MCVE (Minimum, Complete\/Compilable, Verifiable Example) is difficult to provide, as a stand-alone example.  But the real-world scenario in which I ran into this is itself a CVE, if not entirely &#8220;Minimal&#8221;.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This scenario was in a test case in the <a href=\"https:\/\/github.com\/deltics\/deltics.smoketest\">Smoketest<\/a> unit tests for my <a href=\"https:\/\/github.com\/deltics\/deltics.multicast\">Multicast<\/a> event library, specifically in this test:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-pascal\" data-line=\"\">  procedure TMulticastNotifyTests.AllHandlersAreCalledIfAnExceptionIsRaisedByOne;\n  begin\n    Test.RaisesException(EHandlerExceptions);\n\n    sut.Add(NotifyException);\n    sut.Add(NotifyA);\n\n    try\n      try\n        sut.DoEvent;\n      except\n        on e: EHandlerExceptions do\n        begin\n          Test(&#039;e.Count&#039;).Assert(e.Count).Equals(1);\n          Test(&#039;e.Exceptions[0] is Exception&#039;).Assert(e[0] is Exception);\n\n          raise;\n        end;\n      end;\n\n    finally\n      Test(&#039;fCallCountA&#039;).Assert(fCallCountA).Equals(1);\n    end;\n  end;<\/code><\/pre>\n\n\n\n<p class=\"has-background has-luminous-vivid-amber-background-color wp-block-paragraph\"><code class=\"\" data-line=\"\">sut<\/code> is a unit variable referencing a <code class=\"\" data-line=\"\">TMulticastNotify<\/code> event instance, initialised (and reset) by <code class=\"\" data-line=\"\">Setup<\/code> and <code class=\"\" data-line=\"\">Teardown<\/code> methods that executing before and after each test.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The intent of this test is as follows:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>ARRANGE<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Add two handlers to the sut (subject under test), which is a multicast <code class=\"\" data-line=\"\">TNotifyEvent<\/code> (<code class=\"\" data-line=\"\">TMulticastNotify<\/code>).<ul><li>The first handler raises an exception when called<\/li><li>The second handler increments a call count variable<\/li><\/ul><\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>ACT<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>The event is fired<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>ASSERT:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>An <code class=\"\" data-line=\"\">EHandlerExceptions<\/code> exception is raised by the multicast event.  (This is an aggregate exception type that captures any exceptions raised by any handlers).<\/li><li>The <code class=\"\" data-line=\"\">EHandlerExceptions<\/code> exception has captured a single exception of class <code class=\"\" data-line=\"\">Exception<\/code>.<\/li><li>All handlers are called, despite the exception raised by the first handler (tested by ensuring that <code class=\"\" data-line=\"\">fCallCountA<\/code> has been incremented).<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">For the first of these test outcomes, the test uses a <a href=\"https:\/\/github.com\/deltics\/deltics.smoketest\">new feature in Smoketest 2.1<\/a> that describes an expected exception in the initial arrange phase.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The test must then <em>catch<\/em> that exception in order to test it for the expected properties.  The exception is then <em>re-raised<\/em> to allow the stated, expected exception to be satisfied.  The <code class=\"\" data-line=\"\">Test.RaisesException()<\/code> could have been replaced by tests in the exception handler, avoiding the re-raise, but at the expense of (a little) greater complexity (imho).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Finally, the call count is tested to ensure that even though the first handler raised an exception, the second handler was also successfully called.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">And this test executes and passes successfully in all versions of Delphi from 7 thru 10.3.  It also executes and passes in an x64 build with 10.4.1.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">But in an x86 build with 10.4.1, the test fails because the <code class=\"\" data-line=\"\">raise<\/code> statement in the <code class=\"\" data-line=\"\">e: EHandlerExceptions<\/code> handler crashes with an <em>Access Violation<\/em>.  <\/p>\n\n\n\n<figure class=\"wp-block-image\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"640\" height=\"269\" src=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/image.png?resize=640%2C269&#038;ssl=1\" alt=\"\" class=\"wp-image-3038\" srcset=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/image.png?resize=1024%2C431&amp;ssl=1 1024w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/image.png?resize=300%2C126&amp;ssl=1 300w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/image.png?resize=768%2C323&amp;ssl=1 768w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/image.png?resize=380%2C160&amp;ssl=1 380w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/image.png?w=1446&amp;ssl=1 1446w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/image.png?w=1280&amp;ssl=1 1280w\" sizes=\"auto, (max-width: 640px) 100vw, 640px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">We can see in this log output from the Azure DevOps build pipeline, that two tests exhibit this behaviour.  The second test case is similar and suffers the same problem as a result.<\/p>\n\n\n\n<p class=\"has-background has-luminous-vivid-amber-background-color wp-block-paragraph\">Ordinarily this would be an <code class=\"\" data-line=\"\">ERROR<\/code> result, rather than <code class=\"\" data-line=\"\">FAILED<\/code>, but because the test has the expectation of <em>some<\/em> exception being raised, the test is considered to have failed because although an exception was indeed raised, it was not of the expected class.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I have not yet gotten to the bottom of why this happens.  However, I came upon <a href=\"https:\/\/en.delphipraxis.net\/topic\/3515-help-needed-re-raising-exception-gives-av\/\">this discussion thread<\/a> in which someone experiencing the same problem mentioned that passing the captured exception as a parameter to a procedure &#8216;fixed&#8217; the problem.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So I applied that to my case as follows:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-pascal\" data-line=\"\">  procedure TMulticastNotifyTests.AllHandlersAreCalledIfAnExceptionIsRaisedByOne;\n\n  {$ifdef DELPHI10_4}\n    \/\/ In 10.4.1, re-raising the exception crashes immediately with an AV exception.\n    \/\/  This is a compiler bug that can be avoided by passing the capture exception (e)\n    \/\/  to a procedure (for some reason).\n    procedure DoTestsInProcToAvoidCompilerBugWhenReraisingTheException(e: EHandlerExceptions);\n    begin\n      Test(&#039;e.Count&#039;).Assert(e.Count).Equals(1);\n      Test(&#039;e.Exceptions[0] is Exception&#039;).Assert(e[0] is Exception);\n    end;\n  {$endif}\n\n  begin\n    Test.RaisesException(EHandlerExceptions);\n\n    sut.Add(NotifyException);\n    sut.Add(NotifyA);\n\n    try\n      try\n        sut.DoEvent;\n      except\n        on e: EHandlerExceptions do\n        begin\n        {$ifdef DELPHI10_4}\n          DoTestsInProcToAvoidCompilerBugWhenReraisingTheException(e);\n        {$else}\n          Test(&#039;e.Count&#039;).Assert(e.Count).Equals(1);\n          Test(&#039;e.Exceptions[0] is Exception&#039;).Assert(e[0] is Exception);\n        {$endif}\n\n          raise;\n        end;\n      end;\n\n    finally\n      Test(&#039;fCallCountA&#039;).Assert(fCallCountA).Equals(1);\n    end;\n  end;<\/code><\/pre>\n\n\n\n<p class=\"has-medium-font-size wp-block-paragraph\">And it worked!  \\o\/<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Of course, this is far from ideal, resulting in duplication of the test conditions not to mention behaviour specific to a particular compiler version.  I could have adopted a consistent approach for <em>all<\/em> compiler versions of course, but decided not to as providing the best opportunity to identify whether the problem persists in Delphi 10.5 (or a compiler update to 10.4 with a different version identifier).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Reproducability<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Frustratingly, an MCVE console app that simply raises an exception, catches it and re-raises it does not exhibit the behaviour.  So is it something about my Smoketest framework?  The multicast event implementation?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Well, the person experiencing this problem in that praxis thread isn&#8217;t using either of those things.  But perhaps there is something in common between our two scenarios.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The multicast event implementation has some behaviour to capture the exceptions from the registered handlers, calling <code class=\"\" data-line=\"\">AcquireExceptionObject<\/code> to ensure that the exceptions &#8220;live&#8221; beyond the lifetime of the encapsulating handler (and are then freed when the containing <code class=\"\" data-line=\"\">EHandlerExceptions<\/code> exception is itself destroyed).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">But this is not especially exotic behaviour, nor does it cause any problems in any other version of Delphi or the 64-bit compiler of 10.4.  For completeness, here&#8217;s the test run output for that from the same build pipeline:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"640\" height=\"128\" src=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/image-1.png?resize=640%2C128&#038;ssl=1\" alt=\"\" class=\"wp-image-3039\" srcset=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/image-1.png?resize=1024%2C205&amp;ssl=1 1024w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/image-1.png?resize=300%2C60&amp;ssl=1 300w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/image-1.png?resize=768%2C154&amp;ssl=1 768w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/image-1.png?resize=380%2C76&amp;ssl=1 380w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/image-1.png?w=1280&amp;ssl=1 1280w\" sizes=\"auto, (max-width: 640px) 100vw, 640px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">And a summary of the results across the entire pipeline:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"alignleft is-resized\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/image-2.png?resize=289%2C682&#038;ssl=1\" alt=\"\" class=\"wp-image-3042\" width=\"289\" height=\"682\" srcset=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/image-2.png?resize=434%2C1024&amp;ssl=1 434w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/image-2.png?resize=127%2C300&amp;ssl=1 127w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/image-2.png?resize=110%2C260&amp;ssl=1 110w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/image-2.png?w=708&amp;ssl=1 708w\" sizes=\"auto, (max-width: 289px) 100vw, 289px\" \/><\/figure><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">The warnings in the PreXE2 job come from Delphi 7, caused by the mere existence of &#8220;deprecated&#8221; methods in the Smoketest framework (not triggered in any other compiler version), which I still need to resolve to remove this &#8216;noise&#8217; from my pipelines.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The multiple jobs for PreXE2, XEx86, XEx64 etc are due to the way I have &#8220;batched&#8221; my builds to take advantage of multiple build agents to get parallel builds and speed things up.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I would love to hear from anyone who has an explanation for why this problem only manifests in some circumstances and why simply passing the captured exception as a param to local procedure does enough to keep the compiler on the straight and narrow.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p><span class=\"span-reading-time rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\"> 4<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span>I just ran into a very frustrating issue in Delphi 10.4.1 with long-standing, basic functionality that is now broken in certain circumstances. Fortunately there is a work-around in those certain circumstances, but it&#8217;s not pretty.<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"A very puzzling re-raise Exception bug in the Delphi 10.4.1 x86 Compiler (doesn't affect x64).","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[4,348,48],"tags":[292],"class_list":["post-3037","post","type-post","status-publish","format-standard","hentry","category-delphi","category-smoketest","category-testing","tag-delphi"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p1TKYv-MZ","jetpack_sharing_enabled":true,"jetpack-related-posts":[],"_links":{"self":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/3037","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/comments?post=3037"}],"version-history":[{"count":2,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/3037\/revisions"}],"predecessor-version":[{"id":3043,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/3037\/revisions\/3043"}],"wp:attachment":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/media?parent=3037"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/categories?post=3037"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/tags?post=3037"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}