Skip to content

Commit 6a84fe6

Browse files
authored
Merge pull request #259 from dsyme/bug5
Fix problem with System.Void, and bug with finally generation
2 parents 09bbea9 + 9d6b9fb commit 6a84fe6

File tree

5 files changed

+244
-18
lines changed

5 files changed

+244
-18
lines changed

examples/StressProvider.Tests/File1.fs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
module File1
22

33

4-
open MyNamespace
4+
open StressProvider
55

66
type Provided = Provider<"">
77
let providedTags = Provided.Tags
@@ -13,5 +13,11 @@ let providedTags2a = Provided2a.Tags
1313
type Provided2empty = Provider2<"">
1414
let providedTags2empty = Provided2empty.Tags
1515

16+
type Provided3 = Provider3<"three">
17+
1618
//type Provided2missing = Provider2< >
1719
//let providedTags2missing = Provided2missing.Tags
20+
21+
type Generated1 = GenerativeProvider<3>
22+
type Generated3 = GenerativeProvider3<"three">
23+

examples/StressProvider.Tests/StressProvider.Tests.fs

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,39 @@
44

55
module StressProvider.Tests
66

7+
open Xunit
78

8-
let x = File1.providedTags.Tag1
9-
let x2a = File1.providedTags2a.Tag1
10-
let x2empty = File1.providedTags2empty.Tag1
9+
[<Fact>]
10+
let ``StressProvider basic tests 1``() =
11+
12+
let x = File1.providedTags.Tag1
13+
let x2a = File1.providedTags2a.Tag1
14+
let x2empty = File1.providedTags2empty.Tag1
15+
16+
()
17+
18+
[<Fact>]
19+
let ``StressProvider basic tests 2``() =
20+
21+
let v1 = File1.Generated1("innerstate").PropertyWithTryCatch3
22+
let v2 = File1.Generated1("innerstate").PropertyWithTryFinally1
23+
()
24+
25+
26+
[<Fact>]
27+
let ``StressProvider erased try/finally``() =
28+
29+
let mutable disposed = false
30+
let disp = { new System.IDisposable with member __.Dispose() = disposed <- true }
31+
let v1 = File1.Provided3.Test(disp)
32+
Assert.Equal("[+] Yup, it worked totally.", v1)
33+
Assert.Equal(disposed, true)
34+
35+
[<Fact>]
36+
let ``StressProvider generative try/finally``() =
37+
38+
let mutable disposed = false
39+
let disp = { new System.IDisposable with member __.Dispose() = disposed <- true }
40+
let v1 = File1.Generated3.Test(disp)
41+
Assert.Equal("[+] Yup, it worked totally.", v1)
42+
Assert.Equal(disposed, true)

examples/StressProvider/StressProvider.fs

Lines changed: 128 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ open ProviderImplementation
55
open ProviderImplementation.ProvidedTypes
66
open FSharp.Quotations
77
open FSharp.Core.CompilerServices
8+
open System
89
open System.Reflection
910

1011

@@ -19,10 +20,10 @@ type Server (name : string) =
1920
member x.Name with get() : string = name
2021

2122
[<TypeProvider>]
22-
type ComboErasingProvider (config : TypeProviderConfig) as this =
23+
type StressErasingProvider (config : TypeProviderConfig) as this =
2324
inherit TypeProviderForNamespaces (config)
2425

25-
let ns = "MyNamespace"
26+
let ns = "StressProvider"
2627
let asm = Assembly.GetExecutingAssembly()
2728

2829
let newProperty t name getter isStatic = ProvidedProperty(name, t, getter, isStatic = isStatic)
@@ -55,7 +56,131 @@ type ComboErasingProvider (config : TypeProviderConfig) as this =
5556
provided
5657
)
5758

58-
do this.AddNamespace(ns, [provider; provider2; tags])
59+
let provider3 = ProvidedTypeDefinition(asm, ns, "Provider3", Some typeof<obj>, hideObjectMethods = true)
60+
61+
do provider3.DefineStaticParameters([ProvidedStaticParameter("Host", typeof<string>)], fun name _ ->
62+
let provided = ProvidedTypeDefinition(asm, ns, name, Some typeof<obj>, hideObjectMethods = true)
63+
64+
let fn = ProvidedMethod("Test", [ ProvidedParameter("disp", typeof<IDisposable>) ], typeof<string>, fun [ arg ] ->
65+
<@@
66+
use __ = (%%arg : IDisposable)
67+
let mutable res = ""
68+
69+
try
70+
try
71+
System.Console.WriteLine() // test calling a method with void return type
72+
failwith "This will throw anyway, don't mind it."
73+
74+
res <- "[-] Should not get here."
75+
finally
76+
res <- "[+] Caught try-finally, nice."
77+
78+
try
79+
failwith "It failed again."
80+
81+
res <- "[-] Should not get here."
82+
with
83+
| _ ->
84+
res <- "[+] Caught try-with, nice."
85+
86+
try
87+
res <- "[?] Gonna go to finally without throwing..."
88+
finally
89+
res <- "[+] Yup, it worked totally."
90+
res
91+
with _ ->
92+
res
93+
@@>
94+
, isStatic = true)
95+
96+
provided.AddMember fn
97+
provided
98+
)
99+
100+
do this.AddNamespace(ns, [provider; provider2; provider3; tags])
101+
102+
[<TypeProvider>]
103+
type StressGenerativeProvider (config : TypeProviderConfig) as this =
104+
inherit TypeProviderForNamespaces (config)
105+
106+
let ns = "StressProvider"
107+
let asm = Assembly.GetExecutingAssembly()
108+
109+
// check we contain a copy of runtime files, and are not referencing the runtime DLL
110+
do assert (typeof<SomeRuntimeHelper>.Assembly.GetName().Name = asm.GetName().Name)
111+
112+
let createType typeName (count:int) =
113+
let asm = ProvidedAssembly()
114+
let myType = ProvidedTypeDefinition(asm, ns, typeName, Some typeof<obj>, isErased=false)
115+
116+
let ctor = ProvidedConstructor([], invokeCode = fun args -> <@@ "My internal state" :> obj @@>)
117+
myType.AddMember(ctor)
118+
119+
let ctor2 = ProvidedConstructor([ProvidedParameter("InnerState", typeof<string>)], invokeCode = fun args -> <@@ (%%(args.[1]):string) :> obj @@>)
120+
myType.AddMember(ctor2)
121+
122+
for i in 1 .. count do
123+
let prop = ProvidedProperty("PropertyWithTryCatch" + string i, typeof<int>, getterCode = fun args -> <@@ try i with _ -> i+1 @@>)
124+
myType.AddMember(prop)
125+
126+
for i in 1 .. count do
127+
let prop = ProvidedProperty("PropertyWithTryFinally" + string i, typeof<int>, getterCode = fun args -> <@@ try i finally ignore i @@>)
128+
myType.AddMember(prop)
129+
130+
let meth = ProvidedMethod("StaticMethod", [], typeof<SomeRuntimeHelper>, isStatic=true, invokeCode = (fun args -> Expr.Value(null, typeof<SomeRuntimeHelper>)))
131+
myType.AddMember(meth)
132+
asm.AddTypes [ myType ]
133+
134+
myType
135+
136+
let provider =
137+
let t = ProvidedTypeDefinition(asm, ns, "GenerativeProvider", Some typeof<obj>, isErased=false)
138+
t.DefineStaticParameters( [ProvidedStaticParameter("Count", typeof<int>)], fun typeName args -> createType typeName (unbox<int> args.[0]))
139+
t
140+
141+
let provider3 = ProvidedTypeDefinition(asm, ns, "GenerativeProvider3", Some typeof<obj>, hideObjectMethods = true)
142+
143+
do provider3.DefineStaticParameters([ProvidedStaticParameter("Host", typeof<string>)], fun name _ ->
144+
let provided = ProvidedTypeDefinition(asm, ns, name, Some typeof<obj>, hideObjectMethods = true)
145+
146+
let fn = ProvidedMethod("Test", [ ProvidedParameter("disp", typeof<IDisposable>) ], typeof<string>, fun [ arg ] ->
147+
<@@
148+
use __ = (%%arg : IDisposable)
149+
let mutable res = ""
150+
151+
try
152+
try
153+
System.Console.WriteLine() // test calling a method with void return type
154+
failwith "This will throw anyway, don't mind it."
155+
156+
res <- "[-] Should not get here."
157+
finally
158+
res <- "[+] Caught try-finally, nice."
159+
160+
try
161+
failwith "It failed again."
162+
163+
res <- "[-] Should not get here."
164+
with
165+
| _ ->
166+
res <- "[+] Caught try-with, nice."
167+
168+
try
169+
res <- "[?] Gonna go to finally without throwing..."
170+
finally
171+
res <- "[+] Yup, it worked totally."
172+
res
173+
with _ ->
174+
res
175+
@@>
176+
, isStatic = true)
177+
178+
provided.AddMember fn
179+
provided
180+
)
181+
182+
do
183+
this.AddNamespace(ns, [provider; provider3])
59184

60185

61186
[<assembly:CompilerServices.TypeProviderAssembly()>]

src/ProvidedTypes.fs

Lines changed: 70 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,19 @@ namespace ProviderImplementation.ProvidedTypes
275275
let canBindNestedType (bindingFlags: BindingFlags) (c: Type) =
276276
hasFlag bindingFlags BindingFlags.Public && c.IsNestedPublic || hasFlag bindingFlags BindingFlags.NonPublic && not c.IsNestedPublic
277277

278+
// We only want to return source types "typeof<Void>" values as _target_ types in one very specific location due to a limitation in the
279+
// F# compiler code for multi-targeting.
280+
let ImportProvidedMethodBaseAsILMethodRef_OnStack_HACK() =
281+
let rec loop i =
282+
if i > 9 then
283+
false
284+
else
285+
let frame = StackFrame(i, true)
286+
match frame.GetMethod() with
287+
| null -> loop (i+1)
288+
| m -> m.Name = "ImportProvidedMethodBaseAsILMethodRef" || loop (i+1)
289+
loop 1
290+
278291
//--------------------------------------------------------------------------------
279292
// UncheckedQuotations
280293

@@ -1039,17 +1052,36 @@ namespace ProviderImplementation.ProvidedTypes
10391052

10401053
// Implement overloads
10411054
override __.GetParameters() = parameterInfos
1055+
10421056
override __.Attributes = attrs
1057+
10431058
override __.Name = methodName
1059+
10441060
override __.DeclaringType = declaringType |> nonNone "DeclaringType" :> Type
1061+
10451062
override __.IsDefined(_attributeType, _inherit): bool = true
1063+
10461064
override __.MemberType = MemberTypes.Method
1065+
10471066
override x.CallingConvention =
10481067
let cc = CallingConventions.Standard
10491068
let cc = if not x.IsStatic then cc ||| CallingConventions.HasThis else cc
10501069
cc
1051-
override __.ReturnType = returnType
1070+
1071+
override __.ReturnType =
1072+
if isTgt then
1073+
match returnType.Namespace, returnType.Name with
1074+
| "System", "Void"->
1075+
if ImportProvidedMethodBaseAsILMethodRef_OnStack_HACK() then
1076+
typeof<Void>
1077+
else
1078+
returnType
1079+
| _ -> returnType
1080+
else
1081+
returnType
1082+
10521083
override __.ReturnParameter = null // REVIEW: Give it a name and type?
1084+
10531085
override __.ToString() = "Method " + methodName
10541086

10551087
// These don't have to return fully accurate results - they are used
@@ -3049,6 +3081,7 @@ namespace ProviderImplementation.ProvidedTypes.AssemblyReader
30493081
type ILGlobals =
30503082
{ typ_Object: ILType
30513083
typ_String: ILType
3084+
typ_Void: ILType
30523085
typ_Type: ILType
30533086
typ_TypedReference: ILType option
30543087
typ_SByte: ILType
@@ -4537,6 +4570,7 @@ namespace ProviderImplementation.ProvidedTypes.AssemblyReader
45374570
let mkILTyspec nsp nm = mkILNonGenericTySpec(ILTypeRef(ILTypeRefScope.Top(systemRuntimeScopeRef),USome nsp,nm))
45384571
{ typ_Object = ILType.Boxed (mkILTyspec "System" "Object")
45394572
typ_String = ILType.Boxed (mkILTyspec "System" "String")
4573+
typ_Void = ILType.Value (mkILTyspec "System" "Void")
45404574
typ_Type = ILType.Boxed (mkILTyspec "System" "Type")
45414575
typ_Int64 = ILType.Value (mkILTyspec "System" "Int64")
45424576
typ_UInt64 = ILType.Value (mkILTyspec "System" "UInt64")
@@ -7465,7 +7499,17 @@ namespace ProviderImplementation.ProvidedTypes
74657499
override __.Attributes = inp.Attributes
74667500
override __.GetParameters() = inp.Parameters |> Array.map (txILParameter (gps, gps2))
74677501
override __.CallingConvention = if inp.IsStatic then CallingConventions.Standard else CallingConventions.HasThis ||| CallingConventions.Standard
7468-
override __.ReturnType = inp.Return.Type |> txILType (gps, gps2)
7502+
7503+
override __.ReturnType =
7504+
let returnType = inp.Return.Type |> txILType (gps, gps2)
7505+
match returnType.Namespace, returnType.Name with
7506+
| "System", "Void"->
7507+
if ImportProvidedMethodBaseAsILMethodRef_OnStack_HACK() then
7508+
typeof<Void>
7509+
else
7510+
returnType
7511+
| t -> returnType
7512+
74697513
override __.GetCustomAttributesData() = inp.CustomAttrs |> txCustomAttributesData
74707514
override __.GetGenericArguments() = gps2
74717515
override __.IsGenericMethod = (gps2.Length <> 0)
@@ -7629,7 +7673,7 @@ namespace ProviderImplementation.ProvidedTypes
76297673
and txILType gps (ty: ILType) =
76307674

76317675
match ty with
7632-
| ILType.Void -> typeof<System.Void>
7676+
| ILType.Void -> txILType gps ilGlobals.typ_Void
76337677
| ILType.Value tspec
76347678
| ILType.Boxed tspec ->
76357679
let tdefR = txILTypeRef tspec.TypeRef
@@ -7855,7 +7899,7 @@ namespace ProviderImplementation.ProvidedTypes
78557899
// https://github.com/Microsoft/visualfsharp/blob/44fa027b308681a1b78a089e44fa1ab35ff77b41/src/fsharp/MethodCalls.fs#L842
78567900
// for the accepted types.
78577901
match inp.Namespace, inp.Name with
7858-
| USome "System", "Void"-> typeof<Void>
7902+
//| USome "System", "Void"-> typeof<Void>
78597903
(*
78607904
| USome "System", "Boolean" -> typeof<bool>
78617905
| USome "System", "String"-> typeof<string>
@@ -8835,6 +8879,12 @@ namespace ProviderImplementation.ProvidedTypes
88358879
Expr.NewObjectUnchecked (convConstructorRefToTgt c, exprsR)
88368880
| Coerce (expr, t) ->
88378881
Expr.Coerce (convExprToTgt expr, convTypeToTgt t)
8882+
| TypeTest (expr, t) ->
8883+
Expr.TypeTest (convExprToTgt expr, convTypeToTgt t)
8884+
| TryWith (body, filterVar, filterBody, catchVar, catchBody) ->
8885+
Expr.TryWith (convExprToTgt body, convVarToTgt filterVar, convExprToTgt filterBody, convVarToTgt catchVar, convExprToTgt catchBody)
8886+
| TryFinally (body, compensation) ->
8887+
Expr.TryFinally (convExprToTgt body, convExprToTgt compensation)
88388888
| NewArray (t, exprs) ->
88398889
Expr.NewArrayUnchecked (convTypeToTgt t, List.map convExprToTgt exprs)
88408890
| NewTuple (exprs) ->
@@ -13260,8 +13310,9 @@ namespace ProviderImplementation.ProvidedTypes
1326013310
| FilterCatch of ILCodeLabel * (ILCodeLabel * ILCodeLabel)
1326113311
| TypeCatch of ILCodeLabel * ILType
1326213312

13263-
type ILExceptionBlockBuilder(i: ILCodeLabel) =
13313+
type ILExceptionBlockBuilder(i: ILCodeLabel, leave: ILCodeLabel) =
1326413314
member __.StartIndex = i
13315+
member __.Leave = leave
1326513316
member val EndIndex : int = 0 with get, set
1326613317
member val Clause : ILExceptionClauseBuilder option = None with get, set
1326713318

@@ -13291,10 +13342,12 @@ namespace ProviderImplementation.ProvidedTypes
1329113342
ILLocalBuilder(idx)
1329213343

1329313344
member ilg.BeginExceptionBlock() =
13294-
exceptionBlocks.Push(ILExceptionBlockBuilder(ilg.DefineLabelHere()))
13345+
exceptionBlocks.Push(ILExceptionBlockBuilder(ilg.DefineLabelHere(), ilg.DefineLabel()))
1329513346

1329613347
member ilg.EndGuardedBlock() =
13297-
exceptionBlocks.Peek().EndIndex <- ilg.DefineLabelHere()
13348+
let block = exceptionBlocks.Peek()
13349+
ilg.Emit(I_leave block.Leave)
13350+
block.EndIndex <- ilg.DefineLabelHere()
1329813351

1329913352
member ilg.BeginCatchBlock(typ: ILType) =
1330013353
exceptionBlocks.Peek().Clause <- Some <|
@@ -13314,7 +13367,17 @@ namespace ProviderImplementation.ProvidedTypes
1331413367

1331513368
member ilg.EndExceptionBlock() =
1331613369
let exnBlock = exceptionBlocks.Pop()
13370+
match exnBlock.Clause.Value with
13371+
| ILExceptionClauseBuilder.Finally(start) ->
13372+
ilg.Emit(I_endfinally)
13373+
| ILExceptionClauseBuilder.Fault(start) ->
13374+
ilg.Emit(I_endfinally)
13375+
| ILExceptionClauseBuilder.FilterCatch _ ->
13376+
ilg.Emit(I_leave exnBlock.Leave)
13377+
| ILExceptionClauseBuilder.TypeCatch _ ->
13378+
ilg.Emit(I_leave exnBlock.Leave)
1331713379
let endIndex = ilg.DefineLabelHere()
13380+
ilg.MarkLabel(exnBlock.Leave)
1331813381
let clause =
1331913382
match exnBlock.Clause.Value with
1332013383
| ILExceptionClauseBuilder.Finally(start) ->

0 commit comments

Comments
 (0)