@@ -40,6 +40,36 @@ open System.Runtime.CompilerServices
4040type private ShallowEqDelegate < 'a > = delegate of 'a * 'a -> bool
4141type private ShallowHashDelegate < 'a > = delegate of 'a -> int
4242
43+ [<AutoOpen>]
44+ module ` `DynamicMethod Extensions`` =
45+ let private canEmit =
46+ try
47+ let meth =
48+ DynamicMethod(
49+ " testEmitMethod" ,
50+ MethodAttributes.Static ||| MethodAttributes.Public,
51+ CallingConventions.Standard,
52+ typeof< int>,
53+ [| typeof< int> |],
54+ typeof< obj>,
55+ true
56+ )
57+ let il = meth.GetILGenerator()
58+ il.Emit( OpCodes.Ldarg_ 0)
59+ il.Emit( OpCodes.Ldc_ I4, 10 )
60+ il.Emit( OpCodes.Mul)
61+ il.Emit( OpCodes.Ret)
62+
63+ let f = meth.CreateDelegate( typeof< System.Func< int, int>>) |> unbox< System.Func< int, int>>
64+
65+ f.Invoke( 1 ) = 10
66+ with _ ->
67+ false
68+
69+ type DynamicMethod with
70+ static member IsSupported = canEmit
71+
72+
4373[<AbstractClass; Sealed>]
4474type private HashCodeHelpers private () =
4575
@@ -79,103 +109,111 @@ type ShallowEqualityComparer<'a> private() =
79109 if isUnmanaged then
80110 Unchecked.hash
81111 elif typ.IsValueType then
82- let fields =
83- typ.GetFields( BindingFlags.Instance ||| BindingFlags.NonPublic ||| BindingFlags.Public)
112+ if DynamicMethod.IsSupported then
113+ let fields =
114+ typ.GetFields( BindingFlags.Instance ||| BindingFlags.NonPublic ||| BindingFlags.Public)
84115
85- let meth =
86- DynamicMethod(
87- " shallowHash" ,
88- MethodAttributes.Static ||| MethodAttributes.Public,
89- CallingConventions.Standard,
90- typeof< int>,
91- [| typeof< 'a> |],
92- typeof< 'a>,
93- true
94- )
116+ let meth =
117+ DynamicMethod(
118+ " shallowHash" ,
119+ MethodAttributes.Static ||| MethodAttributes.Public,
120+ CallingConventions.Standard,
121+ typeof< int>,
122+ [| typeof< 'a> |],
123+ typeof< 'a>,
124+ true
125+ )
95126
96127
97- let il = meth.GetILGenerator()
98- let l = il.DeclareLocal( typeof< int>)
99-
100- // l <- 0
101- il.Emit( OpCodes.Ldc_ I4_ 0)
102- il.Emit( OpCodes.Stloc, l)
103-
104- for f in fields do
105- let self = self.MakeGenericType [| f.FieldType |]
106- let hash =
107- self.GetMethod(
108- " ShallowHashCode" ,
109- BindingFlags.Static ||| BindingFlags.NonPublic ||| BindingFlags.Public,
110- System.Type.DefaultBinder,
111- [| f.FieldType |],
112- null
113- )
128+ let il = meth.GetILGenerator()
129+ let l = il.DeclareLocal( typeof< int>)
114130
115- il.Emit( OpCodes.Ldarg_ 0)
116- il.Emit( OpCodes.Ldfld, f)
117- il.EmitCall( OpCodes.Call, hash, null )
118- il.Emit( OpCodes.Ldloc, l)
119- il.EmitCall( OpCodes.Call, HashCodeHelpers.CombineMethod, null )
131+ // l <- 0
132+ il.Emit( OpCodes.Ldc_ I4_ 0)
120133 il.Emit( OpCodes.Stloc, l)
134+
135+ for f in fields do
136+ let self = self.MakeGenericType [| f.FieldType |]
137+ let hash =
138+ self.GetMethod(
139+ " ShallowHashCode" ,
140+ BindingFlags.Static ||| BindingFlags.NonPublic ||| BindingFlags.Public,
141+ System.Type.DefaultBinder,
142+ [| f.FieldType |],
143+ null
144+ )
145+
146+ il.Emit( OpCodes.Ldarg_ 0)
147+ il.Emit( OpCodes.Ldfld, f)
148+ il.EmitCall( OpCodes.Call, hash, null )
149+ il.Emit( OpCodes.Ldloc, l)
150+ il.EmitCall( OpCodes.Call, HashCodeHelpers.CombineMethod, null )
151+ il.Emit( OpCodes.Stloc, l)
121152
122- il.Emit( OpCodes.Ldloc, l)
123- il.Emit( OpCodes.Ret)
153+ il.Emit( OpCodes.Ldloc, l)
154+ il.Emit( OpCodes.Ret)
124155
125- let del = meth.CreateDelegate( typeof< ShallowHashDelegate< 'a>>) |> unbox< ShallowHashDelegate< 'a>>
126- del.Invoke
156+ let del = meth.CreateDelegate( typeof< ShallowHashDelegate< 'a>>) |> unbox< ShallowHashDelegate< 'a>>
157+ del.Invoke
158+ else
159+ // TODO: any better way??
160+ fun ( v : 'a ) -> 0
127161 else
128162 fun ( v : 'a ) -> RuntimeHelpers.GetHashCode( v :> obj)
129163
130164 static let equals =
131165 if isUnmanaged then
132166 Unchecked.equals
133167 elif typ.IsValueType then
134- let fields =
135- typ.GetFields( BindingFlags.Instance ||| BindingFlags.NonPublic ||| BindingFlags.Public)
136-
137- let meth =
138- DynamicMethod(
139- " shallowEquals" ,
140- MethodAttributes.Static ||| MethodAttributes.Public,
141- CallingConventions.Standard,
142- typeof< bool>,
143- [| typeof< 'a>; typeof< 'a> |],
144- typeof< 'a>,
145- true
146- )
147-
148- let il = meth.GetILGenerator()
149- let falseLabel = il.DefineLabel()
150-
151- for f in fields do
152- let self = self.MakeGenericType [| f.FieldType |]
153- let eq =
154- self.GetMethod(
155- " ShallowEquals" ,
156- BindingFlags.Static ||| BindingFlags.NonPublic ||| BindingFlags.Public,
157- System.Type.DefaultBinder,
158- [| f.FieldType; f.FieldType |],
159- null
168+ if DynamicMethod.IsSupported then
169+ let fields =
170+ typ.GetFields( BindingFlags.Instance ||| BindingFlags.NonPublic ||| BindingFlags.Public)
171+
172+ let meth =
173+ DynamicMethod(
174+ " shallowEquals" ,
175+ MethodAttributes.Static ||| MethodAttributes.Public,
176+ CallingConventions.Standard,
177+ typeof< bool>,
178+ [| typeof< 'a>; typeof< 'a> |],
179+ typeof< 'a>,
180+ true
160181 )
161182
162- il.Emit( OpCodes.Ldarg_ 0)
163- il.Emit( OpCodes.Ldfld, f)
183+ let il = meth.GetILGenerator()
184+ let falseLabel = il.DefineLabel()
185+
186+ for f in fields do
187+ let self = self.MakeGenericType [| f.FieldType |]
188+ let eq =
189+ self.GetMethod(
190+ " ShallowEquals" ,
191+ BindingFlags.Static ||| BindingFlags.NonPublic ||| BindingFlags.Public,
192+ System.Type.DefaultBinder,
193+ [| f.FieldType; f.FieldType |],
194+ null
195+ )
196+
197+ il.Emit( OpCodes.Ldarg_ 0)
198+ il.Emit( OpCodes.Ldfld, f)
164199
165- il.Emit( OpCodes.Ldarg_ 1)
166- il.Emit( OpCodes.Ldfld, f)
167-
168- il.EmitCall( OpCodes.Call, eq, null )
169- il.Emit( OpCodes.Brfalse, falseLabel)
170-
171- il.Emit( OpCodes.Ldc_ I4_ 1)
172- il.Emit( OpCodes.Ret)
173- il.MarkLabel( falseLabel)
174- il.Emit( OpCodes.Ldc_ I4_ 0)
175- il.Emit( OpCodes.Ret)
176-
177- let del = meth.CreateDelegate( typeof< ShallowEqDelegate< 'a>>) |> unbox< ShallowEqDelegate< 'a>>
178- fun ( a : 'a ) ( b : 'a ) -> del.Invoke( a, b)
200+ il.Emit( OpCodes.Ldarg_ 1)
201+ il.Emit( OpCodes.Ldfld, f)
202+
203+ il.EmitCall( OpCodes.Call, eq, null )
204+ il.Emit( OpCodes.Brfalse, falseLabel)
205+
206+ il.Emit( OpCodes.Ldc_ I4_ 1)
207+ il.Emit( OpCodes.Ret)
208+ il.MarkLabel( falseLabel)
209+ il.Emit( OpCodes.Ldc_ I4_ 0)
210+ il.Emit( OpCodes.Ret)
211+
212+ let del = meth.CreateDelegate( typeof< ShallowEqDelegate< 'a>>) |> unbox< ShallowEqDelegate< 'a>>
213+ fun ( a : 'a ) ( b : 'a ) -> del.Invoke( a, b)
214+ else
215+ /// TODO: better way?
216+ fun ( a : 'a ) ( b : 'a ) -> false
179217 else
180218 fun ( a : 'a ) ( b : 'a ) -> System.Object.ReferenceEquals( a :> obj, b :> obj)
181219
0 commit comments