-
Notifications
You must be signed in to change notification settings - Fork 858
Expand file tree
/
Copy pathCodeGenRegressions_Exceptions.fs
More file actions
162 lines (141 loc) · 6.6 KB
/
CodeGenRegressions_Exceptions.fs
File metadata and controls
162 lines (141 loc) · 6.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
namespace EmittedIL
open Xunit
open FSharp.Test
open FSharp.Test.Compiler
open FSharp.Test.Utilities
module CodeGenRegressions_Exceptions =
let private getActualIL (result: CompilationResult) =
match result with
| CompilationResult.Success s ->
match s.OutputPath with
| Some p ->
let (_, _, actualIL) = ILChecker.verifyILAndReturnActual [] p [ "// dummy" ]
actualIL
| None -> failwith "No output path"
| _ -> failwith "Compilation failed"
// https://github.com/dotnet/fsharp/issues/878
[<Fact>]
let ``Issue_878_ExceptionSerialization`` () =
let source = """
module Test
exception Foo of x:string * y:int
"""
let result =
FSharp source
|> asLibrary
|> compile
|> shouldSucceed
result
|> verifyIL [
".method public strict virtual instance void GetObjectData(class [runtime]System.Runtime.Serialization.SerializationInfo info, valuetype [runtime]System.Runtime.Serialization.StreamingContext context) cil managed"
".custom instance void [runtime]System.Security.SecurityCriticalAttribute::.ctor() = ( 01 00 00 00 )"
"call instance void [runtime]System.Exception::GetObjectData(class [runtime]System.Runtime.Serialization.SerializationInfo,"
".method family specialname rtspecialname instance void .ctor(class [runtime]System.Runtime.Serialization.SerializationInfo info, valuetype [runtime]System.Runtime.Serialization.StreamingContext context) cil managed"
]
|> ignore
let actualIL = getActualIL result
Assert.Contains("AddValue", actualIL)
// https://github.com/dotnet/fsharp/issues/878
[<Fact>]
let ``Issue_878_ExceptionSerialization_Roundtrip`` () =
let source = """
module Test
open System
open System.Runtime.Serialization
#nowarn "44" // Serialization types are obsolete but needed for testing ISerializable
#nowarn "67"
exception Foo of x:string * y:int
let roundtrip (e: Exception) =
let info = SerializationInfo(e.GetType(), FormatterConverter())
let ctx = StreamingContext(StreamingContextStates.All)
e.GetObjectData(info, ctx)
let ctor =
e.GetType().GetConstructor(
System.Reflection.BindingFlags.Instance ||| System.Reflection.BindingFlags.NonPublic ||| System.Reflection.BindingFlags.Public,
null,
[| typeof<SerializationInfo>; typeof<StreamingContext> |],
null)
if ctor = null then failwith "Deserialization constructor not found"
ctor.Invoke([| info :> obj; ctx :> obj |]) :?> Exception
[<EntryPoint>]
let main _ =
let original = Foo("value", 42)
// Check GetObjectData actually writes our fields
let info = SerializationInfo(original.GetType(), FormatterConverter())
let ctx = StreamingContext(StreamingContextStates.All)
original.GetObjectData(info, ctx)
let xVal = info.GetString("x")
let yVal = info.GetInt32("y")
if xVal <> "value" then failwithf "GetObjectData: Expected x='value', got '%s'" xVal
if yVal <> 42 then failwithf "GetObjectData: Expected y=42, got %d" yVal
// Check full roundtrip
let cloned = roundtrip original
// Access fields via internal backing fields using reflection
let xField = cloned.GetType().GetField("x@", System.Reflection.BindingFlags.Instance ||| System.Reflection.BindingFlags.NonPublic)
let yField = cloned.GetType().GetField("y@", System.Reflection.BindingFlags.Instance ||| System.Reflection.BindingFlags.NonPublic)
if xField = null then failwith "Field x@ not found"
if yField = null then failwith "Field y@ not found"
let xCloned = xField.GetValue(cloned) :?> string
let yCloned = yField.GetValue(cloned) :?> int
if xCloned <> "value" then failwithf "Roundtrip: Expected x='value', got '%s'" xCloned
if yCloned <> 42 then failwithf "Roundtrip: Expected y=42, got %d" yCloned
printfn "SUCCESS: Foo(value, 42) roundtripped correctly"
0
"""
FSharp source
|> asExe
|> ignoreWarnings
|> compile
|> shouldSucceed
|> run
|> shouldSucceed
|> ignore
// FSharp.Core has [assembly: SecurityTransparent] which prevents overriding
// SecurityCritical methods like Exception.GetObjectData on .NET Framework.
// Verify that FSharp.Core exceptions (MatchFailureException) still load and work,
// have the deserialization ctor, but do NOT have a GetObjectData override.
[<Fact>]
let ``Issue_878_FSharpCoreExceptions_NoGetObjectDataOverride`` () =
let source = """
module Test
// Force MatchFailureException to be loaded by triggering an incomplete match
let triggerMatch x =
match x with
| 1 -> "one"
// Verify FSharp.Core exceptions can be created and used without TypeLoadException
let test () =
try
triggerMatch 999 |> ignore
failwith "Expected MatchFailureException"
with
| :? MatchFailureException as e ->
// Verify the exception loaded successfully (no TypeLoadException from GetObjectData)
printfn "MatchFailureException loaded OK: %s" e.Message
// Check that deserialization ctor exists (it should — base ctor is SecuritySafeCritical)
let ctorParams = [| typeof<System.Runtime.Serialization.SerializationInfo>; typeof<System.Runtime.Serialization.StreamingContext> |]
let ctor = typeof<MatchFailureException>.GetConstructor(
System.Reflection.BindingFlags.Instance ||| System.Reflection.BindingFlags.NonPublic,
null, ctorParams, null)
if ctor = null then failwith "Deserialization ctor missing on MatchFailureException"
printfn "Deserialization ctor present"
// GetObjectData should NOT be overridden on MatchFailureException
// (FSharp.Core is SecurityTransparent, can't override SecurityCritical base)
let godMethod = typeof<MatchFailureException>.GetMethod("GetObjectData",
System.Reflection.BindingFlags.Instance ||| System.Reflection.BindingFlags.Public,
null, ctorParams, null)
if godMethod <> null && godMethod.DeclaringType = typeof<MatchFailureException> then
failwith "GetObjectData should NOT be overridden on FSharp.Core exceptions"
printfn "GetObjectData correctly not overridden"
0
[<EntryPoint>]
let main _ = test ()
"""
FSharp source
|> asExe
|> ignoreWarnings
|> compile
|> shouldSucceed
|> run
|> shouldSucceed
|> ignore