Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion src/fsharp/FSharp.Core/reflect.fs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,35 @@ module internal Impl =
param)
expr.Compile ()

let compileTupleConstructor tupleEncField getTupleConstructorMethod typ =
let rec constituentTuple (typ: Type) elements startIndex =
Expression.New (
getTupleConstructorMethod typ,
[
let genericArgs = typ.GetGenericArguments ()

for paramIndex in 0 .. genericArgs.Length - 1 do
let genericArg = genericArgs.[paramIndex]

if paramIndex = tupleEncField then
constituentTuple genericArg elements (startIndex + paramIndex) :> Expression
else
Expression.Convert (Expression.ArrayAccess (elements, Expression.Constant (startIndex + paramIndex)), genericArg)
])

let elements = Expression.Parameter (typeof<obj[]>, "elements")

let expr =
Expression.Lambda<Func<obj[], obj>> (
Expression.Convert (
constituentTuple typ elements 0,
typeof<obj>
),
elements
)

expr.Compile ()

//-----------------------------------------------------------------
// ATTRIBUTE DECOMPILATION

Expand Down Expand Up @@ -988,7 +1017,7 @@ type FSharpValue =

static member PreComputeTupleConstructor(tupleType: Type) =
checkTupleType("tupleType", tupleType)
getTupleConstructor tupleType
(compileTupleConstructor tupleEncField getTupleConstructorMethod tupleType).Invoke

static member PreComputeTupleConstructorInfo(tupleType: Type) =
checkTupleType("tupleType", tupleType)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,12 @@ type FSharpValueTests() =
let tuple1 = ( 1, "tuple1")
let tuple2 = ( 2, "tuple2", (fun x -> x + 1))
let tuple3 = ( 1, ( 2, "tuple"))
let longTuple = (("yup", 1s), 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, Some 12, 13, "nope", struct (15, 16), 17, 18, ValueSome 19)

let structTuple1 = struct ( 1, "tuple1")
let structTuple2 = struct ( 2, "tuple2", (fun x -> x + 1))
let structTuple3 = struct ( 1, struct ( 2, "tuple"))
let longStructTuple = struct (("yup", 1s), 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, Some 12, 13, "nope", struct (15, 16), 17, 18, ValueSome 19)

let func1 param = param + 1
let func2 param = param + ""
Expand Down Expand Up @@ -550,6 +552,9 @@ type FSharpValueTests() =
// Tuple
let tupleCtor = FSharpValue.PreComputeTupleConstructor(tuple1.GetType())
Assert.AreEqual( tupleCtor([| box 1; box "tuple1" |]) , box(tuple1))

let tupleCtor = FSharpValue.PreComputeTupleConstructor(longTuple.GetType())
Assert.AreEqual( tupleCtor([| box ("yup", 1s); box 2; box 3; box 4; box 5; box 6; box 7; box 8; box 9; box 10; box 11; box (Some 12); box 13; box "nope"; box (struct (15, 16)); box 17; box 18; box (ValueSome 19) |]) , box(longTuple))

// Tuple with function member
let tuplewithFuncCtor = FSharpValue.PreComputeTupleConstructor(tuple2.GetType())
Expand All @@ -573,6 +578,9 @@ type FSharpValueTests() =
let tupleCtor = FSharpValue.PreComputeTupleConstructor(structTuple1.GetType())
Assert.AreEqual( tupleCtor([| box 1; box "tuple1" |]) , box(structTuple1))

let tupleCtor = FSharpValue.PreComputeTupleConstructor(longStructTuple.GetType())
Assert.AreEqual( tupleCtor([| box ("yup", 1s); box 2; box 3; box 4; box 5; box 6; box 7; box 8; box 9; box 10; box 11; box (Some 12); box 13; box "nope"; box (struct (15, 16)); box 17; box 18; box (ValueSome 19) |]) , box(longStructTuple))

// Tuple with function member
let tuplewithFuncCtor = FSharpValue.PreComputeTupleConstructor(structTuple2.GetType())
let resultTuplewithFunc = tuplewithFuncCtor([| box 2; box "tuple2"; box (fun x -> x + 1) |])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@ module ``PreComputedTupleConstructor Tests`` =
let testDelegate = TestDelegate (fun () ->
Reflection.FSharpValue.PreComputeTupleConstructor(typeof<int * string>) [| box "text"; box 12; |] |> ignore)

Assert.Throws<System.ArgumentException> testDelegate |> ignore
Assert.Throws<System.InvalidCastException> testDelegate |> ignore