Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deep copy improvements #46

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
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
541 changes: 532 additions & 9 deletions src/DynamicObj/DynamicObj.fs

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions src/DynamicObj/FableJS.fs
Original file line number Diff line number Diff line change
Expand Up @@ -168,4 +168,11 @@ module FableJS =
let cloneICloneable (o:obj) : obj =
jsNative

module Dictionaries =
[<Emit("""$0 instanceof Map""")>]
let isMap (o:obj) : bool =
jsNative
[<Emit("""$0 instanceof Dictionary""")>]
let isDict (o:obj) : bool =
jsNative
#endif
6 changes: 6 additions & 0 deletions src/DynamicObj/FablePy.fs
Original file line number Diff line number Diff line change
Expand Up @@ -220,4 +220,10 @@ module FablePy =
[<Emit("""$0.System_ICloneable_Clone()""")>]
let cloneICloneable (o:obj) : obj =
nativeOnly

module Dictionaries =
[<Emit("""isinstance($0, dict)""")>]
let isDict (o:obj) : bool =
nativeOnly

#endif
20 changes: 6 additions & 14 deletions src/DynamicObj/Playground.fsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,10 @@
open Fable.Pyxpecto
open DynamicObj

type T(dyn:string, stat:string) as this=
inherit DynamicObj()
let r1 = ResizeArray([1; 2])
let r2 = ResizeArray([1; 2])
let r3 = r1

do
this.SetProperty("Dyn", dyn)

member this.Stat = stat

let first = T("dyn1", "stat1")
let second = T("dyn2", "stat2")

let _ = second.ShallowCopyDynamicPropertiesTo(first)

first |> DynObj.print
second |> DynObj.print
printfn "%A" (LanguagePrimitives.PhysicalEquality r1 r2)
printfn "%A" (LanguagePrimitives.PhysicalEquality r2 r2)
printfn "%A" (LanguagePrimitives.PhysicalEquality r3 r1)
2 changes: 1 addition & 1 deletion tests/CSharpTests/CSharpTests.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>

Expand Down
382 changes: 382 additions & 0 deletions tests/DynamicObject.Tests/CopyUtils.tryDeepCopyObj/Dictionaries.fs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
module DeepCopyDynamicObj

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
module DeepCopyDynamicObjCollections

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
module DeepCopyICloneable

13 changes: 13 additions & 0 deletions tests/DynamicObject.Tests/CopyUtils.tryDeepCopyObj/Main.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module CopyUtils.Tests

open System
open Fable.Pyxpecto
open DynamicObj
open Fable.Core

let main = testList "CopyUtils.tryDeepCopyObj" [
DeepCopyPrimitives.tests_DeepCopyPrimitives
DeepCopyResizeArrays.tests_DeepCopyResizeArrays
DeepCopyDictionaries.tests_DeepCopyDictionaries
]

60 changes: 60 additions & 0 deletions tests/DynamicObject.Tests/CopyUtils.tryDeepCopyObj/Primitives.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
module DeepCopyPrimitives

open System
open Fable.Pyxpecto
open DynamicObj
open Fable.Core
open TestUtils

let tests_DeepCopyPrimitives = testList "Primitives" [
testCase "bool" <| fun _ ->
let original, copy = constructDeepCopiedObj true
Expect.equal copy original "Expected values of copy and original to be equal"
testCase "byte" <| fun _ ->
let original, copy = constructDeepCopiedObj 1uy
Expect.equal copy original "Expected values of copy and original to be equal"
testCase "sbyte" <| fun _ ->
let original, copy = constructDeepCopiedObj 1y
Expect.equal copy original "Expected values of copy and original to be equal"
testCase "int16" <| fun _ ->
let original, copy = constructDeepCopiedObj 1s
Expect.equal copy original "Expected values of copy and original to be equal"
testCase "uint16" <| fun _ ->
let original, copy = constructDeepCopiedObj 1us
Expect.equal copy original "Expected values of copy and original to be equal"
testCase "int" <| fun _ ->
let original, copy = constructDeepCopiedObj 1
Expect.equal copy original "Expected values of copy and original to be equal"
testCase "uint" <| fun _ ->
let original, copy = constructDeepCopiedObj 1u
Expect.equal copy original "Expected values of copy and original to be equal"
testCase "int64" <| fun _ ->
let original, copy = constructDeepCopiedObj 1L
Expect.equal copy original "Expected values of copy and original to be equal"
testCase "uint64" <| fun _ ->
let original, copy = constructDeepCopiedObj 1uL
Expect.equal copy original "Expected values of copy and original to be equal"
#if !FABLE_COMPILER
testCase "nativeint" <| fun _ ->
let original, copy = constructDeepCopiedObj (System.IntPtr(1))
Expect.equal copy original "Expected values of copy and original to be equal"
testCase "unativeint" <| fun _ ->
let original, copy = constructDeepCopiedObj (System.UIntPtr(1u))
Expect.equal copy original "Expected values of copy and original to be equal"
#endif
testCase "float" <| fun _ ->
let original, copy = constructDeepCopiedObj 1.0
Expect.equal copy original "Expected values of copy and original to be equal"
testCase "float32" <| fun _ ->
let original, copy = constructDeepCopiedObj 1.0f
Expect.equal copy original "Expected values of copy and original to be equal"
testCase "char" <| fun _ ->
let original, copy = constructDeepCopiedObj 'A'
Expect.equal copy original "Expected values of copy and original to be equal"
testCase "string" <| fun _ ->
let original, copy = constructDeepCopiedObj "Hi"
Expect.equal copy original "Expected values of copy and original to be equal"
testCase "unit" <| fun _ ->
let original, copy = constructDeepCopiedObj ()
Expect.equal copy original "Expected values of copy and original to be equal"
]
148 changes: 148 additions & 0 deletions tests/DynamicObject.Tests/CopyUtils.tryDeepCopyObj/ResizeArrays.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
module DeepCopyResizeArrays

open System
open Fable.Pyxpecto
open DynamicObj
open Fable.Core
open TestUtils

let tests_DeepCopyResizeArrays = testList "ResizeArrays" [
testCase "bool" <| fun _ ->
let arr = ResizeArray([true; false])
let original, copy = constructDeepCopiedObj arr
Expect.sequenceEqual copy original "Expected values of copy and original to be equal"
Expect.notReferenceEqual copy original "Expected values of copy and original to be not reference equal"
arr[0] <- false
Expect.sequenceEqual original (ResizeArray([false; false])) "Original schould have been mutated"
Expect.sequenceEqual copy (ResizeArray([true; false])) "Clone should not be affected by original mutation"
testCase "byte" <| fun _ ->
let arr = ResizeArray([1uy; 2uy])
let original, copy = constructDeepCopiedObj arr
Expect.sequenceEqual copy original "Expected values of copy and original to be equal"
Expect.notReferenceEqual copy original "Expected values of copy and original to be not reference equal"
arr[0] <- 2uy
Expect.sequenceEqual original (ResizeArray([2uy; 2uy])) "Original schould have been mutated"
Expect.sequenceEqual copy (ResizeArray([1uy; 2uy])) "Clone should not be affected by original mutation"
testCase "sbyte" <| fun _ ->
let arr = ResizeArray([1y; 2y])
let original, copy = constructDeepCopiedObj arr
Expect.sequenceEqual copy original "Expected values of copy and original to be equal"
Expect.notReferenceEqual copy original "Expected values of copy and original to be not reference equal"
arr[0] <- 2y
Expect.sequenceEqual original (ResizeArray([2y; 2y])) "Original schould have been mutated"
Expect.sequenceEqual copy (ResizeArray([1y; 2y])) "Clone should not be affected by original mutation"
testCase "int16" <| fun _ ->
let arr = ResizeArray([1s; 2s])
let original, copy = constructDeepCopiedObj arr
Expect.sequenceEqual copy original "Expected values of copy and original to be equal"
Expect.notReferenceEqual copy original "Expected values of copy and original to be not reference equal"
arr[0] <- 2s
Expect.sequenceEqual original (ResizeArray([2s; 2s])) "Original schould have been mutated"
Expect.sequenceEqual copy (ResizeArray([1s; 2s])) "Clone should not be affected by original mutation"
testCase "uint16" <| fun _ ->
let arr = ResizeArray([1us; 2us])
let original, copy = constructDeepCopiedObj arr
Expect.sequenceEqual copy original "Expected values of copy and original to be equal"
Expect.notReferenceEqual copy original "Expected values of copy and original to be not reference equal"
arr[0] <- 2us
Expect.sequenceEqual original (ResizeArray([2us; 2us])) "Original schould have been mutated"
Expect.sequenceEqual copy (ResizeArray([1us; 2us])) "Clone should not be affected by original mutation"
testCase "int" <| fun _ ->
let arr = ResizeArray([1; 2])
let original, copy = constructDeepCopiedObj arr
Expect.sequenceEqual copy original "Expected values of copy and original to be equal"
Expect.notReferenceEqual copy original "Expected values of copy and original to be not reference equal"
arr[0] <- 2
Expect.sequenceEqual original (ResizeArray([2; 2])) "Original schould have been mutated"
Expect.sequenceEqual copy (ResizeArray([1; 2])) "Clone should not be affected by original mutation"
testCase "uint" <| fun _ ->
let arr = ResizeArray([1u; 2u])
let original, copy = constructDeepCopiedObj arr
Expect.sequenceEqual copy original "Expected values of copy and original to be equal"
Expect.notReferenceEqual copy original "Expected values of copy and original to be not reference equal"
arr[0] <- 2u
Expect.sequenceEqual original (ResizeArray([2u; 2u])) "Original schould have been mutated"
Expect.sequenceEqual copy (ResizeArray([1u; 2u])) "Clone should not be affected by original mutation"
testCase "int64" <| fun _ ->
let arr = ResizeArray([1L; 2L])
let original, copy = constructDeepCopiedObj arr
Expect.sequenceEqual copy original "Expected values of copy and original to be equal"
Expect.notReferenceEqual copy original "Expected values of copy and original to be not reference equal"
arr[0] <- 2L
Expect.sequenceEqual original (ResizeArray([2L; 2L])) "Original schould have been mutated"
Expect.sequenceEqual copy (ResizeArray([1L; 2L])) "Clone should not be affected by original mutation"
testCase "uint64" <| fun _ ->
let arr = ResizeArray([1uL; 2uL])
let original, copy = constructDeepCopiedObj arr
Expect.sequenceEqual copy original "Expected values of copy and original to be equal"
Expect.notReferenceEqual copy original "Expected values of copy and original to be not reference equal"
arr[0] <- 2uL
Expect.sequenceEqual original (ResizeArray([2uL; 2uL])) "Original schould have been mutated"
Expect.sequenceEqual copy (ResizeArray([1uL; 2uL])) "Clone should not be affected by original mutation"
testCase "float" <| fun _ ->
let arr = ResizeArray([1.0; 2.0])
let original, copy = constructDeepCopiedObj arr
Expect.sequenceEqual copy original "Expected values of copy and original to be equal"
Expect.notReferenceEqual copy original "Expected values of copy and original to be not reference equal"
arr[0] <- 2.0
Expect.sequenceEqual original (ResizeArray([2.0; 2.0])) "Original schould have been mutated"
Expect.sequenceEqual copy (ResizeArray([1.0; 2.0])) "Clone should not be affected by original mutation"
testCase "float32" <| fun _ ->
let arr = ResizeArray([1.0f; 2.0f])
let original, copy = constructDeepCopiedObj arr
Expect.sequenceEqual copy original "Expected values of copy and original to be equal"
Expect.notReferenceEqual copy original "Expected values of copy and original to be not reference equal"
arr[0] <- 2.0f
Expect.sequenceEqual original (ResizeArray([2.0f; 2.0f])) "Original schould have been mutated"
Expect.sequenceEqual copy (ResizeArray([1.0f; 2.0f])) "Clone should not be affected by original mutation"
testCase "char" <| fun _ ->
let arr = ResizeArray(['A'; 'B'])
let original, copy = constructDeepCopiedObj arr
Expect.sequenceEqual copy original "Expected values of copy and original to be equal"
Expect.notReferenceEqual copy original "Expected values of copy and original to be not reference equal"
arr[0] <- 'B'
Expect.sequenceEqual original (ResizeArray(['B'; 'B'])) "Original schould have been mutated"
Expect.sequenceEqual copy (ResizeArray(['A'; 'B'])) "Clone should not be affected by original mutation"
testCase "string" <| fun _ ->
let arr = ResizeArray(["Hi"; "Bye"])
let original, copy = constructDeepCopiedObj arr
Expect.sequenceEqual copy original "Expected values of copy and original to be equal"
Expect.notReferenceEqual copy original "Expected values of copy and original to be not reference equal"
arr[0] <- "Bye"
Expect.sequenceEqual original (ResizeArray(["Bye"; "Bye"])) "Original schould have been mutated"
Expect.sequenceEqual copy (ResizeArray(["Hi"; "Bye"])) "Clone should not be affected by original mutation"
testCase "unit" <| fun _ ->
let arr = ResizeArray([(); ()])
let original, copy = constructDeepCopiedObj arr
Expect.sequenceEqual copy original "Expected values of copy and original to be equal"
Expect.notReferenceEqual copy original "Expected values of copy and original to be not reference equal"
// transpilation fun
let arr2 = ResizeArray([()])
arr.Add(arr2[0])
Expect.sequenceEqual original (ResizeArray([(); (); ()])) "Original schould have been mutated"
Expect.sequenceEqual copy (ResizeArray([(); ()])) "Clone should not be affected by original mutation"

// some cases are not transpilable

#if !FABLE_COMPILER_PYTHON
testCase "decimal" <| fun _ ->
let arr = ResizeArray([1.0M; 2.0M])
let original, copy = constructDeepCopiedObj arr
Expect.sequenceEqual copy original "Expected values of copy and original to be equal"
Expect.notReferenceEqual copy original "Expected values of copy and original to be not reference equal"
arr[0] <- 2.0M
Expect.sequenceEqual original (ResizeArray([2.0M; 2.0M])) "Original schould have been mutated"
Expect.sequenceEqual copy (ResizeArray([1.0M; 2.0M])) "Clone should not be affected by original mutation"
#endif

#if !FABLE_COMPILER
testCase "nativeint" <| fun _ ->
let original, copy = constructDeepCopiedObj (ResizeArray([System.IntPtr(1); System.IntPtr(2)]))
Expect.sequenceEqual copy original "Expected values of copy and original to be equal"
Expect.notReferenceEqual copy original "Expected values of copy and original to be not reference equal"
testCase "unativeint" <| fun _ ->
let original, copy = constructDeepCopiedObj (ResizeArray([System.UIntPtr(1u); System.UIntPtr(2u)]))
Expect.sequenceEqual copy original "Expected values of copy and original to be equal"
Expect.notReferenceEqual copy original "Expected values of copy and original to be not reference equal"
#endif
]
7 changes: 7 additions & 0 deletions tests/DynamicObject.Tests/DynamicObject.Tests.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@

<ItemGroup>
<Compile Include="TestUtils.fs" />
<Compile Include="CopyUtils.tryDeepCopyObj\Primitives.fs" />
<Compile Include="CopyUtils.tryDeepCopyObj\ResizeArrays.fs" />
<Compile Include="CopyUtils.tryDeepCopyObj\Dictionaries.fs" />
<Compile Include="CopyUtils.tryDeepCopyObj\DynamicObjCollections.fs" />
<Compile Include="CopyUtils.tryDeepCopyObj\ICloneable.fs" />
<Compile Include="CopyUtils.tryDeepCopyObj\DynamicObj.fs" />
<Compile Include="CopyUtils.tryDeepCopyObj\Main.fs" />
<Compile Include="DynamicObj\RemoveProperty.fs" />
<Compile Include="DynamicObj\SetProperty.fs" />
<Compile Include="DynamicObj\GetHashcode.fs" />
Expand Down
1 change: 1 addition & 0 deletions tests/DynamicObject.Tests/Main.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ open Fable.Pyxpecto

let all = testSequenced <| testList "DynamicObj" [
ReflectionUtils.Tests.main
CopyUtils.Tests.main
DynamicObj.Tests.main
DynObj.Tests.main
Inheritance.Tests.main
Expand Down
32 changes: 29 additions & 3 deletions tests/DynamicObject.Tests/TestUtils.fs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,11 @@ let constructDeepCopiedClone<'T> (props: seq<string*obj>) =
let original = DynamicObj()
props
|> Seq.iter (fun (propertyName, propertyValue) -> original.SetProperty(propertyName, propertyValue))
let clone = original.DeepCopyProperties()
original, clone |> unbox<'T>
let clone : 'T = original.DeepCopyProperties() |> unbox<'T>
original, clone

let constructDeepCopiedObj<'T> (original: 'T) =
original, (CopyUtils.tryDeepCopyObj original |> unbox<'T>)

let bulkMutate (props: seq<string*obj>) (dyn: #DynamicObj) =
props |> Seq.iter (fun (propertyName, propertyValue) -> dyn.SetProperty(propertyName, propertyValue))
Expand All @@ -56,6 +59,13 @@ module DynObj =
| _ -> failwith "Empty property list"
getProp dyn props

#if FABLE_COMPILER_PYTHON
module Py =
[<Emit("$0 is $1")>]
let isReferenceEqual o1 o2 : bool =
nativeOnly
#endif

module Expect =
/// Expects the `actual` sequence to equal the `expected` one.
let sequenceEqual actual expected message =
Expand All @@ -71,6 +81,22 @@ module Expect =
failwithf "%s. Sequence actual longer than expected, at pos %i found item %O."
message i a



let referenceEqual actual expected message =
#if FABLE_COMPILER_PYTHON
if not (Py.isReferenceEqual actual expected) then
failwith message
#else
if not (LanguagePrimitives.PhysicalEquality actual expected) then
failwith message
failwith message
#endif

let notReferenceEqual actual expected message =
#if FABLE_COMPILER_PYTHON
if (Py.isReferenceEqual actual expected) then
failwith message
#else
if (LanguagePrimitives.PhysicalEquality actual expected) then
failwith message
#endif
Loading
Loading