CLR में बहिष्करण फिल्टर

हेलो हैबरा आज हम सीएलआर तंत्र में से एक को देखेंगे जो सीधे सी # डेवलपर्स के लिए सुलभ नहीं है - अपवाद फिल्टर।

मेरे दोस्तों C # प्रोग्रामर्स के बीच एक सर्वेक्षण से पता चला है कि उन्होंने (निश्चित रूप से) इस तंत्र का इस्तेमाल कभी नहीं किया और इसके अस्तित्व के बारे में भी नहीं जानते। इसलिए, मैं लेख के पाठ को पढ़ने के लिए सभी जिज्ञासुओं को आमंत्रित करता हूं।

इसलिए, अपवाद फ़िल्टर एक ऐसा तंत्र है जो एक catch पूर्व शर्त बताने की अनुमति देता है कि इस ब्लॉक द्वारा पकड़े जाने के लिए अपवाद को पूरा करना होगा। यह तंत्र ठीक वैसे ही काम नहीं करता है जैसे catch अंदर catch

कटौती के तहत VB.NET, F #, CIL और C # कोड है, साथ ही साथ फिल्टर तंत्र को संसाधित करने के लिए विभिन्न डिकंपाइलरों की जांच की जाती है।

जहां अपवाद फिल्टर हैं


अपवाद फ़िल्टर सीएलआर में निर्मित होते हैं और उन तंत्रों में से एक हैं जिनका उपयोग पर्यावरण प्रसंस्करण के अपवादों के दौरान करता है। अनुक्रम इस प्रकार है:



एक उपयुक्त catch लिए खोज चरण में catch सीएलआर अपवाद हैंडलर के अपने आंतरिक स्टैक को क्रॉल करता है और अपवाद फ़िल्टर भी करता है। ध्यान दें कि finally ब्लॉक में कोड निष्पादित होने से पहले ऐसा होता है। हम इस बिंदु पर बाद में चर्चा करेंगे।

यह VB.NET पर कैसे दिखता है

अपवाद फ़िल्टर मूल रूप से VB.NET भाषा द्वारा समर्थित है। यहाँ इस बात का उदाहरण है कि फ़िल्टर का उपयोग करने वाला कोड कैसा दिखता है:

 Sub FilterException() Try Dim exception As New Exception exception.Data.Add("foo", "bar1") Console.WriteLine("Throwing") Throw exception Catch ex As Exception When Filter(ex) '   Console.WriteLine("Caught") Finally Console.WriteLine("Finally") End Try End Sub Function Filter(exception As Exception) As Boolean Console.WriteLine("Filtering") Return exception.Data.Item("foo").Equals("bar") End Function 


जब इस कोड को निष्पादित किया जाता है, तो निम्न संदेश श्रृंखला जारी की जाएगी:

 Throwing Filtering Caught Finally 


यह F # में कैसा दिखता है

इस लेख को तैयार करने में, मैंने इंटरनेट पर पाया कि F # अपवाद फ़िल्टर का समर्थन करता है। खैर, इसकी जाँच करें। यहाँ एक नमूना कोड है:

F # पर कोड
 open System let filter (ex : Exception) = printfn "Filtering" ex.Data.["foo"] :?> string = "bar" let filterException() = try let ex = Exception() ex.Data.["foo"] <- "bar" printfn "Throwing" raise ex with //   | :? Exception as ex when filter(ex) -> printfn "Caught" [<EntryPoint>] let main argv = filterException() 0 



यह कोड बिना फिल्टर के संकलित होता है, सामान्य catch [mscorlib]System.Object । मैं अपवाद फ़िल्टर बनाने के लिए F # कंपाइलर प्राप्त करने में कभी कामयाब नहीं रहा। यदि आप ऐसा करने के वैकल्पिक तरीकों से अवगत हैं, तो टिप्पणी में आपका स्वागत है।

यह CIL में कैसे दिखता है

CIL (सामान्य मध्यवर्ती भाषा) एक .NET मशीन के लिए निम्न-स्तरीय असेंबली भाषा का एक एनालॉग है। संकलित असेंबलियों को इस भाषा में ilasm टूल का उपयोग करके अलग किया जा सकता है, और .NET के साथ शिप करने वाले ilasm का उपयोग करके वापस इकट्ठा किया जाता है।

यहाँ VB.NET पर कोड का एक टुकड़ा है जैसा कि मैंने इसे ildasm में देखा था:

बहुत सारे CIL कोड
 .method public static void FilterException() cil managed { // Code size 110 (0x6e) .maxstack 3 .locals init ([0] class [mscorlib]System.Exception exception, [1] class [mscorlib]System.Exception ex) IL_0000: nop IL_0001: nop .try { .try { IL_0002: newobj instance void [mscorlib]System.Exception::.ctor() IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: callvirt instance class [mscorlib]System.Collections.IDictionary [mscorlib]System.Exception::get_Data() IL_000e: ldstr "foo" IL_0013: ldstr "bar" IL_0018: callvirt instance void [mscorlib]System.Collections.IDictionary::Add(object, object) IL_001d: nop IL_001e: ldstr "Throwing" IL_0023: call void [mscorlib]System.Console::WriteLine(string) IL_0028: nop IL_0029: ldloc.0 IL_002a: throw IL_002b: leave.s IL_006b } // end .try filter { IL_002d: isinst [mscorlib]System.Exception IL_0032: dup IL_0033: brtrue.s IL_0039 IL_0035: pop IL_0036: ldc.i4.0 IL_0037: br.s IL_0049 IL_0039: dup IL_003a: stloc.1 IL_003b: call void [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.ProjectData::SetProjectError(class [mscorlib]System.Exception) IL_0040: ldloc.1 IL_0041: call bool FilterSamples.VbNetFilter::Filter(class [mscorlib]System.Exception) IL_0046: ldc.i4.0 IL_0047: cgt.un IL_0049: endfilter } // end filter { // handler IL_004b: pop IL_004c: ldstr "Caught" IL_0051: call void [mscorlib]System.Console::WriteLine(string) IL_0056: nop IL_0057: call void [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.ProjectData::ClearProjectError() IL_005c: leave.s IL_006b } // end handler } // end .try finally { IL_005e: nop IL_005f: ldstr "Finally" IL_0064: call void [mscorlib]System.Console::WriteLine(string) IL_0069: nop IL_006a: endfinally } // end handler IL_006b: nop IL_006c: nop IL_006d: ret } // end of method VbNetFilter::FilterException 



जैसा कि आप देख सकते हैं, VB.NET संकलक, निश्चित रूप से CIL के रूप में हमारे कोड को बहुत अधिक चित्रित करता है। filter ब्लॉक में हमारी सबसे अधिक रुचि क्या है:

 filter { // ,      System.Exception: IL_002d: isinst [mscorlib]System.Exception IL_0032: dup IL_0033: brtrue.s IL_0039 IL_0035: pop IL_0036: ldc.i4.0 //   -  : IL_0037: br.s IL_0049 IL_0039: dup //  -  : IL_003a: stloc.1 IL_003b: call void [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.ProjectData::SetProjectError(class [mscorlib]System.Exception) //  ,     : IL_0040: ldloc.1 IL_0041: call bool FilterSamples.VbNetFilter::Filter(class [mscorlib]System.Exception) IL_0046: ldc.i4.0 IL_0047: cgt.un IL_0049: endfilter } // end filter 


इसलिए, संकलक ने फ़िल्टर ब्लॉक में एक अपवाद प्रकार की जांच जारी की, साथ ही हमारे फ़ंक्शन को कॉल किया। यदि फ़िल्टर के निष्पादन के अंत में मूल्य 1 स्टैक पर है, तो इस फ़िल्टर के अनुरूप catch ब्लॉक निष्पादित किया जाएगा; अन्यथा, नहीं।

यह ध्यान देने योग्य है कि सी # कंपाइलर filter ब्लॉक में टाइप चेक नहीं लगाता है, लेकिन एक विशेष संकेत के साथ एक विशेष CIL निर्माण का उपयोग करता है। यही है, सी # कंपाइलर filter तंत्र का उपयोग बिल्कुल नहीं करता है।

वैसे, इस ब्लॉक को जेनरेट करने के लिए, आप ILGenerator.BeginExceptFilterBlock विधि (यदि आप अपना कंपाइलर लिख रहे हैं) का उपयोग कर सकते हैं।

डिकम्पॉइलर में यह कैसा दिखता है

इस खंड में, मैं कई प्रसिद्ध उपकरणों का उपयोग करके परिणामी कोड को विघटित करने की कोशिश करूंगा और देखूंगा कि क्या होता है।

नवीनतम JetBrains dotPeek 1.1, जब एक फिल्टर के साथ विधानसभा को विघटित करने की कोशिश हो रही है, तो खुशी से निम्नलिखित की सूचना दी:

 public static void FilterException() { // ISSUE: unable to decompile the method. } 


.NET रिफलेक्टर 8.2 ने अधिक पर्याप्त रूप से कार्य किया है और C # में कुछ विघटित करने में सक्षम है:

 public static void FilterException() { try { Exception exception = new Exception(); exception.Data.Add("foo", "bar"); Console.WriteLine("Throwing"); throw exception; } catch when (?) { Console.WriteLine("Caught"); ProjectData.ClearProjectError(); } finally { Console.WriteLine("Finally"); } } 


ठीक है, बुरा नहीं है - यद्यपि कोड संकलित नहीं है, आप इसके द्वारा फ़िल्टर भी देख सकते हैं। यह तथ्य कि फ़िल्टर को डिक्रिप्ट नहीं किया गया था, को C # अनुवादक की कमियों के लिए जिम्मेदार ठहराया जा सकता है। चलिए VB.NET में अनुवादक के साथ भी यही कोशिश करते हैं:

 Public Shared Sub FilterException() Try Dim exception As New Exception exception.Data.Add("foo", "bar") Console.WriteLine("Throwing") Throw exception Catch obj1 As Object When (?) Console.WriteLine("Caught") ProjectData.ClearProjectError Finally Console.WriteLine("Finally") End Try End Sub 


काश, उसी तरह से प्रयास विफल हो गया - किसी कारण से डिकंपाइलर फ़िल्टरिंग फ़ंक्शन का नाम निर्धारित नहीं कर सका (हालांकि, जैसा कि हमने ऊपर देखा, ildasm ने इसका एक बड़ा काम किया है)।

मैं केवल यह मान सकता हूं कि चर्चा किए गए उपकरण .NET4.5 फिल्टर कोड के साथ अच्छी तरह से काम नहीं करते हैं।

यह catch के शरीर में चेक से कैसे भिन्न होता है


उस कोड के टुकड़े पर विचार करें जो लगभग VB.NET कोड के समान है:

C # कोड
 static void FilterException() { try { var exception = new Exception(); exception.Data["foo"] = "bar"; Console.WriteLine("Throwing"); throw exception; } catch (Exception exception) { if (!Filter(exception)) { throw; } Console.WriteLine("Caught"); } } static bool Filter(Exception exception) { return exception.Data["foo"].Equals("bar"); } 



अब आइए C # और VB.NET में उदाहरणों के बीच व्यवहार में अंतर खोजने की कोशिश करते हैं। यह काफी सरल है: throw; सी में # स्टैक पर लाइन नंबर खो देता है। यदि आप फ़िल्टर को बदलते हैं ताकि यह false , तो एप्लिकेशन एक संदेश के साथ क्रैश हो जाएगा

 Unhandled Exception: System.Exception: Exception of type 'System.Exception' was thrown. at CSharpFilter.Program.FilterException() in CSharpFilter\Program.cs:line 25 at CSharpFilter.Program.Main(String[] args) in CSharpFilter\Program.cs:line 9 


स्टैक को देखते हुए, एक अपवाद पंक्ति 25 (लाइन throw; ) पर फेंका गया था, और लाइन 19 पर नहीं ( throw exception; )। समान शर्तों के तहत VB.NET कोड अपवाद का प्रारंभिक स्थान दिखाता है।

युपीडी। शुरू में, मैंने गलती से लिखा था कि throw; पूरे ढेर को खो देता है, लेकिन टिप्पणियों में उन्होंने सुझाव दिया कि यह वास्तव में बिल्कुल नहीं है। स्टैक पर लाइन संख्या के लिए केवल एक मामूली संशोधन है। इसके अलावा, यह मोनो पर नहीं खेलता है - अपवाद स्टैक throw; बाद वहां नहीं बदलता है throw; (इन विवरणों के लिए धन्यवाद kekekeks )।

सुरक्षा के बारे में


अपने ब्लॉग में एरिक लिपर्ट उस स्थिति पर विचार करते हैं, जहां अपवाद फ़िल्टर दुर्भावनापूर्ण पक्ष को कुछ मामलों में उन्नत विशेषाधिकार के साथ अपने कोड को निष्पादित करने की अनुमति देते हैं।

संक्षेप में: यदि आप कुछ बाहरी और संभावित विनाशकारी कोड के लिए एक अस्थायी विशेषाधिकार वृद्धि का प्रदर्शन कर रहे हैं, तो आप finally भरोसा नहीं कर सकते, क्योंकि finally ब्लॉक को निष्पादित करने से पहले, कॉल स्टैक के ऊपर स्थित अपवाद फिल्टर को बुलाया जा सकता है (और एक हमलावर इन फिल्टर के कोड में जो चाहे कर सकता है)। याद रखें - finally ब्लॉक निष्पादित होने से पहले हमेशा सही catch ढूंढना होता है।

निष्कर्ष


आज हमने C # में शायद ही कभी सीएलआर प्रोग्रामर में से एक को देखा। मैं खुद VB.NET पर नहीं लिखता, लेकिन मेरा मानना ​​है कि यह जानकारी .NET प्लेटफॉर्म के सभी डेवलपर्स के लिए उपयोगी हो सकती है। ठीक है, यदि आप इस प्लेटफ़ॉर्म के लिए भाषा, संकलक या डीकंपाइलर विकसित कर रहे हैं, तो यह जानकारी आपके लिए विशेष रूप से उपयोगी है।

पुनश्च। कोड, चित्र और लेख पाठ को github: github.com/ForNeVeR/ClrExceptionForters पर पोस्ट किया गया है।

Source: https://habr.com/ru/post/In192144/


All Articles