Newtonsoft Converter FromJson - unexpected token - f#

I've been trying to write a JSON deserialiser for a while now, but haven't been able to find my error. Why is Newtonsoft telling me, Unexpected token when deserializing object: StartObject, after deserialising this?
type ThisFails =
{ a : string * string
m : Map<string, string> }
type ThisWorks =
{ y : Map<string, string>
z : string * string }
testCase "failing test - array before object" <| fun _ ->
let res = deserialise<ThisFails> Serialisation.converters
"""{"a":["xyz","zyx"],"m":{}}"""
Assert.Equal("should be eq to res", { a = "xyz", "zyx"; m = Map.empty }, res)
testCase "passing test - array after object" <| fun _ ->
let res = deserialise<ThisWorks> Serialisation.converters
"""{"y":{},"z":["xyz","zyx"]}"""
Assert.Equal("should be eq to res", { y = Map.empty; z = "xyz", "zyx" }, res)
The subject is the TupleArrayConverter.
The trace of that converter is:
reading json [Newtonsoft.Json.FSharp.TupleArrayConverter]
type => System.Tuple`2[System.String,System.String]
value token, pre-deserialise [Newtonsoft.Json.FSharp.TupleArrayConverter]
path => "a[0]"
token_type => String
value token, post-deserialise [Newtonsoft.Json.FSharp.TupleArrayConverter]
path => "a[1]"
token_type => String
value token, pre-deserialise [Newtonsoft.Json.FSharp.TupleArrayConverter]
path => "a[1]"
token_type => String
value token, post-deserialise [Newtonsoft.Json.FSharp.TupleArrayConverter]
path => "a"
token_type => EndArray
after EndArray token, returning [Newtonsoft.Json.FSharp.TupleArrayConverter]
path => "m"
token_type => PropertyName
In the converter, I'm consuming the last token, the end array, as you can see in the terminating case:
match reader.TokenType with
| JsonToken.EndArray ->
read JsonToken.EndArray |> req |> ignore
acc
And I'm consuming the StartArray token in the beginning...
So: why isn't this code working? (Newtonsoft.Json 6.0.8)
This is the error:
map tests/failing test - array before object: Exception: Newtonsoft.Json.JsonSerializationException: Unexpected token when deserializing object: StartObject. Path 'm', line 1, position 24.
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ResolvePropertyAndCreatorValues (Newtonsoft.Json.Serialization.JsonObjectContract contract, Newtonsoft.Json.Serialization.JsonProperty containerProperty, Newtonsoft.Json.JsonReader reader, System.Type objectType, IDictionary`2& extensionData) [0x00000] in <filename unknown>:0
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObjectUsingCreatorWithParameters (Newtonsoft.Json.JsonReader reader, Newtonsoft.Json.Serialization.JsonObjectContract contract, Newtonsoft.Json.Serialization.JsonProperty containerProperty, Newtonsoft.Json.Serialization.ObjectConstructor`1 creator, System.String id) [0x00000] in <filename unknown>:0
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateNewObject (Newtonsoft.Json.JsonReader reader, Newtonsoft.Json.Serialization.JsonObjectContract objectContract, Newtonsoft.Json.Serialization.JsonProperty containerMember, Newtonsoft.Json.Serialization.JsonProperty containerProperty, System.String id, System.Boolean& createdFromNonDefaultCreator) [0x00000] in <filename unknown>:0
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject (Newtonsoft.Json.JsonReader reader, System.Type objectType, Newtonsoft.Json.Serialization.JsonContract contract, Newtonsoft.Json.Serialization.JsonProperty member, Newtonsoft.Json.Serialization.JsonContainerContract containerContract, Newtonsoft.Json.Serialization.JsonProperty containerMember, System.Object existingValue) [0x00000] in <filename unknown>:0
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal (Newtonsoft.Json.JsonReader reader, System.Type objectType, Newtonsoft.Json.Serialization.JsonContract contract, Newtonsoft.Json.Serialization.JsonProperty member, Newtonsoft.Json.Serialization.JsonContainerContract containerContract, Newtonsoft.Json.Serialization.JsonProperty containerMember, System.Object existingValue) [0x00000] in <filename unknown>:0
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize (Newtonsoft.Json.JsonReader reader, System.Type objectType, Boolean checkAdditionalContent) [0x00000] in <filename unknown>:0 (00:00:00.1500996)
1 tests run: 0 passed, 0 ignored, 0 failed, 1 errored (00:00:00.2482623)

Been debugging your code together with JSON.Net.
It turns out that after you consumed the EndArray token in your failing case the reader then points to a PropertyName token, that is all good.
Then after your converter has completed execution JSON.Net executes this.
} while (!exit && reader.Read());
Read() then moves the reader to the next token which in your failing case is StartObject causing the deserializer to fail.
So, I am not an expert at JSON.Net but thinking about providing a provider for a string value in JSON.Net I probably wouldn't advance the reader after conversion meaning the reader still points to the string value. Along the same line of thinking it makes sense when consuming an array to leave the reader at last token of the array value ie the EndArray token.
So my suggestion is merely this:
match reader.TokenType with
| JsonToken.EndArray ->
// read JsonToken.EndArray |> req |> ignore
Logger.debug logger <| fun _ ->
LogLine.sprintf
[ "path", reader.Path |> box
"token_type", reader.TokenType |> box ]
"after EndArray token, returning"
acc
This makes my test program:
[<EntryPoint>]
let main argv =
let works = deserialize<ThisWorks> """{"y":{},"z":["xyz","zyx"]}"""
printfn "%A" works
let fails = deserialize<ThisFails> """{"a":["xyz","zyx"],"m":{}}"""
printfn "%A" fails
0
Print
{y = map [];
z = ("xyz", "zyx");}
{a = ("xyz", "zyx");
m = map [];}
Hopefully this helps you resolve this error (you might already have done so)

Related

Dapper F# - A Parameterless default A parameterless default constructor or one matching signature

I'm making, for demo purposes, an Insert function that I would like to return a variable of the type that is passed in.
I'm user Dapper.fsharp for the initial query, then I'd like to run a raw SQL query to get the last inserted value.
So I have something like this for demo purposes
let Insert<'a> asyncQuery =
asyncQuery
|> connection.InsertAsync<'a>
|> RunSynchronously
|> ignore
(*This is a Dapper.fsharp query. Running this returns the number of rows inserted (int) *)
let table = asyncQuery.Table (*This is a string*)
let result =
connection.Query<'a>($"""Select * From {table} Where id = (select last_insert_id())""") (*Should return an IENumerable of type 'a*)
|> EnumerableToArray
result |> first
And then that's called like
let newSession =
insert {
table "sessions"
value newSession
} |> Insert
where newSession is of type session
module Session
type session = {id: int; session_id: string; clerk_json: string; clerk_id: int; expires: int}
(*This is also the structure of the SQL table exactly*)
The error I get is
"A parameterless default constructor or one matching signature (System.Int32 id, System.String session_id, System.Int32 clerk_id, System.String clerk_json, System.Int32 expires) is required for Session+session materialization"
Which indicates to me that it's not getting the right type signature from the database, but the column names and type match, and nothing in the table is null.
Maybe I'm overlooking something simple or misunderstanding how the library should be used?
The type has to be [<CLIMutable>]...

Using FsCheck with NUnit: receiving exception on using Arbitrary types (or: how to use Arbitrary types with attributes)

In my previous question Kurt pointed me to this code of FsCheck about setting the Arbitrary type.
I have the following Arbitrary (disclaimer: I have no idea what I am doing..., still finding FsCheck notoriously hard to understand but I'm dead set on getting it to work), which in itself is a simplified version of something I created earlier:
type MyArb() =
inherit Arbitrary<DoNotSize<int64>>()
override x.Generator = Arb.Default.DoNotSizeInt64().Generator
And I use it as instructed:
[<Property(Verbose = true, Arbitrary= [| typeof<MyArb> |])>]
static member MultiplyIdentity (x: int64) = x * 1L = x
This gives me a (somewhat hopeful) error message that I'm missing something:
System.Reflection.TargetInvocationException : Exception has been thrown by the target of an invocation.
----> System.Exception : No instances found on type Tests.Arithmetic.MyArb. Check that the type is public and has public static members with the right signature.
at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at FsCheck.Runner.checkMethod(Config config, MethodInfo m, FSharpOption`1 target) in C:\Users\Kurt\Projects\FsCheck\FsCheck\src\FsCheck\Runner.fs:line 318
at FsCheck.NUnit.Addin.FsCheckTestMethod.runTestMethod(TestResult testResult) in C:\Users\Kurt\Projects\FsCheck\FsCheck\src\FsCheck.NUnit.Addin\FsCheckTestMethod.fs:line 100
Looking back at that Github code I see two Atrbitrary classes but neither with any inheritance and they both have different static members.
How can I create a random-number generator and assign it as an Arbitrary statically to my NUnit tests?
The type you provide in the Property.Arbitrary parameter should have static members (possibly several) of type Arb. As in the code you linked:
type TestArbitrary2 =
static member NegativeDouble() =
Arb.Default.Float()
|> Arb.mapFilter (abs >> ((-) 0.0)) (fun t -> t <= 0.0)
Applying this to your code, it should look like this:
type MyArb() =
static member m() = Arb.Default.DoNotSizeInt64()
The meaning of the Property.Arbitrary parameter is not "an implementation of Arbitrary", but rather "a bucket of typeclass implementations".
You see, the original Haskell implementation of QuickCheck relies on typeclasses to provide values of different types. In order for a particular type to be "quick-checkable", there needs to be an instance of the 'Arbitrary' class defined for that type (for example, here are instances for all basic types).
Since F# doesn't support type classes as such, FsCheck has to fake it, and this is the scheme used there: each type class instance is represented by a static member that returns the function table. For example, if we wanted to simulate the Eq typeclass, we'd define it something like this:
type Eq<'a> = { eq: 'a -> 'a -> bool; neq: 'a -> 'a -> bool }
type EqInstances() =
static member ForInt() : Eq<int> =
{ eq = (=); neq = (<>) }
static member ForMyCustomType() : Eq<MyCustomType> =
{ eq = fun a b -> a.CompareTo(b) = 0
neq = fun a b -> a.CompareTo(b) <> 0 }
But because you can't just scan all static member in all loaded assemblies (that would be prohibitively expensive), there is this little inconvenience of providing the type explicitly (as a bonus, it allows to control the visibility of "instances").
This question demonstrates clearly, IMO, why the Reflection-based API for FsCheck is less than ideal. I tend to avoid that API completely, so I'd instead write the OP property like this:
open FsCheck
open FsCheck.Xunit
[<Property>]
let MultiplyIdentity () =
Arb.Default.DoNotSizeInt64 () |> Prop.forAll <| fun (DoNotSize x) -> x * 1L = x
As the open directives suggest, this uses FsCheck.Xunit instead of FsCheck.NUnit, but AFAIK, there's no difference in the way the API works.
The advantage of this approach is that it's type-safe and more lightweight, because you don't have to implement static classes every time you need to tweak FsCheck.
If you prefer the approach described by Mark Seemann, then you may also consider using plain-FsCheck and get rid of FsCheck.Xunit entirely:
module Tests
open FsCheck
let [<Xunit.Fact>] ``Multiply Identity (passing)`` () =
Arb.Default.DoNotSizeInt64 ()
|> Prop.forAll
<| fun (DoNotSize x) ->
x * 1L = x
|> Check.QuickThrowOnFailure
let [<Xunit.Fact>] ``Multiply Identity (failing)`` () =
Arb.Default.DoNotSizeInt64 ()
|> Prop.forAll
<| fun (DoNotSize x) ->
x * 1L = -1L |# sprintf "(%A should equal %A)" (x * 1L) x
|> Check.QuickThrowOnFailure
xUnit.net testrunner output:
------ Test started: Assembly: Library1.dll ------
Test 'Tests.Multiply Identity (failing)' failed: System.Exception:
Falsifiable, after 1 test (2 shrinks) (StdGen (2100552947,296238694)):
Label of failing property: (0L should equal 0L)
Original:
DoNotSize -23143L
Shrunk:
DoNotSize 0L
at <StartupCode$FsCheck>.$Runner.get_throwingRunner#365-1.Invoke(String me..
at <StartupCode$FsCheck>.$Runner.get_throwingRunner#355.FsCheck-IRunner-On..
at FsCheck.Runner.check[a](Config config, a p)
at FsCheck.Check.QuickThrowOnFailure[Testable](Testable property)
C:\Users\Nikos\Desktop\Library1\Library1\Library1.fs(15,0): at Tests.Multi..
1 passed, 1 failed, 0 skipped, took 0.82 seconds (xUnit.net 2.1.0 build 3179).

InteractiveMode | Newtonsoft.Json.JsonConvert.SerializeObject | Method has zero rva

Env: OS-X / Xamarin Studio 6.1 / F# Interactive for F# 4.0
Compiled: The following works fine as compiled:
type Movie = {
Name : string
Year: int
}
[<EntryPoint>]
let main argv =
let movies = [
{ Name = "Bad Boys"; Year = 1995 }
]
let json = Newtonsoft.Json.JsonConvert.SerializeObject(movies)
System.Console.WriteLine json
System.Console.ReadKey() |> ignore
0 // return an integer exit code
Interactive: In interactive mode Newtonsoft.Json' JsonConvert.SerializeObject fails
type Movie = {
Name : string
Year: int
}
let movies = [
{ Name = "Bad Boys"; Year = 1995 }
]
let json = Newtonsoft.Json.JsonConvert.SerializeObject(movies)
Output:
val movies : Movie list = [{Name = "Bad Boys";
  Year = 1995;}; {Name = "Bad Boys 2";
  Year = 2003;}]
 System.BadImageFormatException: Method has zero rva
 File name: 'System.Net.Http.Formatting'
  at <StartupCode$FSI_0015>.$FSI_0015.main# () [0x00006] in <filename unknown>:0
  at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (System.Reflection.MonoMethod,object,object[],System.Exception&)
  at System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00038] in <filename unknown>:0
 [MVID] 7747cf446af449e194e4b4e70d85e773 2
 [MVID] eddc6e27796e462ba5a0f4fbcf15e179 0
I am missing some setup or does Newtonsoft.Json not work in F#'s Interactive mode.
Note: I am using Xamarin Studio to add all references automatically to the interactive session (152 of them)
Update:
Using Xamarin Studio:
Edit / Send references to F# Interactive:
Which includes:
--> Referenced '/Users/sushi/code/sushi/GeneticGraphDatabase/packages/Newtonsoft.Json.9.0.1/lib/net45/Newtonsoft.Json.dll' (file may be locked by F# Interactive process)
JsonConvert.SerializeObject Produces:
System.BadImageFormatException: Method has zero rva
Referencing it 'manually' with the same assembly path:
#r "/Users/sushi/code/sushi/GeneticGraphDatabase/packages/Newtonsoft.Json.9.0.1/lib/net45/Newtonsoft.Json.dll";;
--> Referenced '/Users/sushi/code/sushi/GeneticGraphDatabase/packages/Newtonsoft.Json.9.0.1/lib/net45/Newtonsoft.Json.dll' (file may be locked by F# Interactive process)
Works as expected:
val json : string = "[{"Name":"Bad Boys","Year":1995}]"
Referencing Json.Net 'manually' using the same assembly path that Xamarin Studio is using when using the menu item: "Edit / Send references to F# Interactive"
#r "/Users/sushi/code/sushi/GeneticGraphDatabase/packages/Newtonsoft.Json.9.0.1/lib/net45/Newtonsoft.Json.dll";;
--> Referenced '/Users/sushi/code/sushi/GeneticGraphDatabase/packages/Newtonsoft.Json.9.0.1/lib/net45/Newtonsoft.Json.dll' (file may be locked by F# Interactive process)
Works as expected:
val json : string = "[{"Name":"Bad Boys","Year":1995}]"
Bugzilla: https://bugzilla.xamarin.com/show_bug.cgi?id=43307

using Protobuf-Net with f# class

I'm playing f# and making a server program for fun.
I think I can solve this question by using f# to access c# data class but I want to try the f# syntax.
I got f# record with [<CLIMutable>] works but f# class got error
my test code :
open System
open System.IO
open ProtoBuf
[<ProtoContract; Serializable>]
type Point (m_x : int, m_y : int) =
[<ProtoMember(1)>]
member this.x = m_x
[<ProtoMember(2)>]
member this.y = m_y
[<EntryPoint>]
let main argv =
let p : Point = new Point(10, 10)
let out = Console.OpenStandardOutput()
Serializer.Serialize<Point>(out, p)
printfn "finish"
0
and I got following output:
Unhandled Exception:
System.InvalidOperationException: Cannot apply changes to property Program+Point.x
at ProtoBuf.Serializers.PropertyDecorator.SanityCheck (ProtoBuf.Meta.TypeModel model, System.Reflection.PropertyInfo property, IProtoSerializer tail, System.Boolean& writeValue, Boolean nonPublic, Boolean allowInternal) [0x00000] in <filename unknown>:0
at ProtoBuf.Serializers.PropertyDecorator..ctor (ProtoBuf.Meta.TypeModel model, System.Type forType, System.Reflection.PropertyInfo property, IProtoSerializer tail) [0x00000] in <filename unknown>:0
...
Although I don't know much about Protobuf I suspect that its serialization cannot assign a new value to class Point fields because in F# they are immutable by default. You could change your class definition to use mutable fields/properties:
open System
open System.IO
open ProtoBuf
[<ProtoContract; Serializable>]
type Point (m_x : int, m_y : int) =
let mutable vx = 0
let mutable vy = 0
do
vy <- m_y
vx <- m_x
[<ProtoMember(1)>]
member this.x with get() = vx and set v = vx <- v
[<ProtoMember(1)>]
member this.y with get() = vy and set v = vy <- v
Maybe it'll help.
Also if F# record with [] works then maybe it is easier to use records? Records in F# support members as well as classes.

Using delegate/DLR Lambdas to override instance methods?

In an effort to learn F# and .Net, I've been playing around with the to-be-released DLR.
To that end, I've been playing around with reflection, in an effort to implement a basic type system that integrates nicely with the clr. While I'm able to instantiate a simple type that extends Object, I get an error when calling the method it defines.
Because at the end of the day DLR LambdaExpressions compile down to delegates, what I do is take generated MethodInfo off the generated delegate and call it, populating the stack with the args of the generated method. Then return it. It's at this point I get my error.
Here's my code:
open System
open System.Reflection
open System.Reflection.Emit
type ConstructorInformation=
{Types:System.Type array}
type MethodInformation=
{ParamTypes:System.Type array;
Name:string
Impl:System.Delegate}
let rec addConstructors (t:TypeBuilder) (baseType:System.Type) constructorInfos =
match constructorInfos with
|ci::rest ->
let cb = t.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard,ci.Types)
let ilGen = cb.GetILGenerator()
ilGen.Emit(OpCodes.Ldarg_0)
Array.iteri (fun (index:int) _-> ilGen.Emit(OpCodes.Ldarg, index+1)) ci.Types
ilGen.Emit( OpCodes.Call, baseType.GetConstructor(ci.Types) )
addConstructors t baseType rest
|[] -> ()
let rec addMethods (tb:TypeBuilder) baseType methodInfos =
match methodInfos with
|mi::rest ->
let mb = tb.DefineMethod(mi.Name, MethodAttributes.Public, typeof<obj>, mi.ParamTypes)
let ilGen = mb.GetILGenerator()
ilGen.Emit(OpCodes.Ldarg_0)
Array.iteri (fun index _ -> ilGen.Emit(OpCodes.Ldarg, index+1)) mi.ParamTypes
ilGen.EmitCall(OpCodes.Call, mi.Impl.Method, mi.ParamTypes)
ilGen.Emit(OpCodes.Ret)
addMethods tb baseType rest
|[] -> ()
let defineType (baseType:System.Type) constructorInfos methodInfos=
let ab = AppDomain.CurrentDomain.DefineDynamicAssembly( AssemblyName("test"), AssemblyBuilderAccess.Run)
let mb = ab.DefineDynamicModule("test")
let typeBuilder = mb.DefineType("testType", TypeAttributes.Public, baseType)// | TypeAttributes.Class
addConstructors typeBuilder baseType constructorInfos
addMethods typeBuilder baseType methodInfos
typeBuilder.CreateType()
type Delegate1 = delegate of obj -> obj
let echo y:#obj= (y :> obj)
let del1 : Delegate1 = new Delegate1(echo)
let mis:MethodInformation list=[{Impl=del1; Name="Echo"; ParamTypes=[|(typeof<obj>)|]}]
let cis:ConstructorInformation list=[]
let t= defineType (typeof<obj>) cis mis
let cinfo = t.GetConstructor([||])
let instance =cinfo.Invoke([||])
instance.GetType()
(t.GetMethod("Echo")).Invoke(instance, [| (1:>obj)|])
Here's my error, from fsi:
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.MethodAccessException: clo#51.Invoke(System.Object)
at testType.Echo(Object )
--- End of inner exception stack trace ---
at System.RuntimeMethodHandle._InvokeMethodFast(Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner)
at System.RuntimeMethodHandle.InvokeMethodFast(Object target, Object[] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at <StartupCode$FSI_0002>.$FSI_0002._main()
stopped due to error
Any help or suggestions would be appreciated-I'm a bit of a .Net newb so my mistake may be simple.
Mike Kohout
Well, I solved it. The generated cil was bad. Also, I had to dynamicinvoke on the delegate, not the function it was fronting.
#light
open System
open System.Reflection
open System.Reflection.Emit
type ConstructorInformation=
{Types:System.Type array}
type MethodInformation=
{ParamTypes:System.Type array;
Name:string;
Impl:System.Delegate;
mutable Field:FieldBuilder option}
let rec addConstructors (t:TypeBuilder) (baseType:System.Type) constructorInfos =
match constructorInfos with
|ci::rest ->
let cb = t.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard,ci.Types)
let ilGen = cb.GetILGenerator()
ilGen.Emit(OpCodes.Ldarg_0)
Array.iteri (fun (index:int) _-> ilGen.Emit(OpCodes.Ldarg, index+1)) ci.Types
ilGen.Emit( OpCodes.Call, baseType.GetConstructor(ci.Types) )
addConstructors t baseType rest
|[] -> ()
let rec addMethods (tb:TypeBuilder) baseType methodInfos =
match methodInfos with
|mi::rest ->
let fb = tb.DefineField(mi.Name+"_field", typeof<Delegate>, FieldAttributes.Public);
mi.Field <- Some(fb)
let mb = tb.DefineMethod(mi.Name, MethodAttributes.Public, typeof<obj>, mi.ParamTypes)
let ilGen = mb.GetILGenerator()
let arrayLocal = ilGen.DeclareLocal((typeof<obj[]>))
ilGen.Emit(OpCodes.Ldarg_0)
ilGen.Emit(OpCodes.Ldfld, fb)
ilGen.Emit(OpCodes.Ldc_I4, Array.length mi.ParamTypes)
ilGen.Emit(OpCodes.Newarr, typeof<obj>)
ilGen.Emit(OpCodes.Stloc, arrayLocal)
ilGen.Emit(OpCodes.Ldloc, arrayLocal)
Array.iteri (fun index _ -> ilGen.Emit(OpCodes.Ldc_I4, index)
ilGen.Emit(OpCodes.Ldarg, index+1)
ilGen.Emit(OpCodes.Stelem_Ref)
ilGen.Emit(OpCodes.Ldloc, arrayLocal)) mi.ParamTypes
ilGen.EmitCall(OpCodes.Callvirt, (mi.Impl.GetType()).GetMethod("DynamicInvoke", [|(typeof<obj[]>)|]), mi.ParamTypes)
ilGen.Emit(OpCodes.Ret)
addMethods tb baseType rest
|[] -> ()
let defineType (baseType:System.Type) constructorInfos methodInfos=
let ab = AppDomain.CurrentDomain.DefineDynamicAssembly( AssemblyName("test"), AssemblyBuilderAccess.Run)
let mb = ab.DefineDynamicModule("test")
let typeBuilder = mb.DefineType("testType", TypeAttributes.Public, baseType)// | TypeAttributes.Class
addConstructors typeBuilder baseType constructorInfos
addMethods typeBuilder baseType methodInfos
typeBuilder.CreateType()
type Delegate1 = delegate of obj -> obj
let echo y:#obj= (y :> obj)
let del1 : Delegate1 = new Delegate1(echo)
type Delegate2 = delegate of obj * obj -> obj
let echoFirst (x:#obj) (y:#obj)=(x:>obj)
let echoFirstDelegate:Delegate2 = new Delegate2(echoFirst)
echoFirstDelegate.DynamicInvoke( [|(1:>obj);(2:>obj)|])
//let mis:MethodInformation list=[{Impl=del1; Name="Echo"; ParamTypes=[|(typeof<obj>)|];Field=None}]
//let cis:ConstructorInformation list=[]
//let t= defineType (typeof<obj>) cis mis
//let cinfo = t.GetConstructor([||])
//let instance =cinfo.Invoke([||])
//instance.GetType()
//(t.GetField("Echo_field")).SetValue(instance, del1)
//let fieldDelegate = (t.GetField("Echo_field")).GetValue(instance) :?> Delegate
//(t.GetMethod("Echo")).Invoke(instance, [| (1:>obj)|])
//del1.DynamicInvoke( [|(1:>obj)|])
let mis:MethodInformation list=[{Impl=del1; Name="Echo"; ParamTypes=[|(typeof<obj>)|];Field=None};
{Impl=echoFirstDelegate; Name="EchoFirst"; ParamTypes=[| (typeof<obj>);(typeof<obj>)|]; Field=None}]
let cis:ConstructorInformation list=[]
let t= defineType (typeof<obj>) cis mis
let cinfo = t.GetConstructor([||])
let instance =cinfo.Invoke([||])
instance.GetType()
(t.GetField("Echo_field")).SetValue(instance, del1)
let fieldDelegate = (t.GetField("Echo_field")).GetValue(instance) :?> Delegate
(t.GetMethod("Echo")).Invoke(instance, [| (1:>obj)|])
(t.GetField("EchoFirst_field")).SetValue(instance, echoFirstDelegate)
(t.GetMethod("EchoFirst")).Invoke(instance, [| (1:>obj);(2:>obj)|])

Resources