diff --git a/src/DynamicObj/DynamicObj.fs b/src/DynamicObj/DynamicObj.fs index a0c7276..03b8153 100644 --- a/src/DynamicObj/DynamicObj.fs +++ b/src/DynamicObj/DynamicObj.fs @@ -267,7 +267,11 @@ type DynamicObj() = /// /// The following cases are handled (in this precedence): /// - /// - Basic F# types (int, float, bool, string, char, byte, sbyte, int16, uint16, int32, uint32, int64, uint64, single, decimal) + /// - Basic F# types (bool, byte, sbyte, int16, uint16, int, uint, int64, uint64, nativeint, unativeint, float, float32, char, string, unit, decimal) + /// + /// - ResizeArrays and Dictionaries containing any combination of basic F# types + /// + /// - Dictionaries containing DynamicObj as keys or values in any combination with DynamicObj or basic F# types as keys or values /// /// - array<DynamicObj>, list<DynamicObj>, ResizeArray<DynamicObj>: These collections of DynamicObj are copied as a new collection with recursively deep copied elements. /// @@ -307,7 +311,11 @@ type DynamicObj() = /// /// The following cases are handled (in this precedence): /// - /// - Basic F# types (int, float, bool, string, char, byte, sbyte, int16, uint16, int32, uint32, int64, uint64, single, decimal) + /// - Basic F# types (bool, byte, sbyte, int16, uint16, int, uint, int64, uint64, nativeint, unativeint, float, float32, char, string, unit, decimal) + /// + /// - ResizeArrays and Dictionaries containing any combination of basic F# types + /// + /// - Dictionaries containing DynamicObj as keys or values in any combination with DynamicObj or basic F# types as keys or values /// /// - array<DynamicObj>, list<DynamicObj>, ResizeArray<DynamicObj>: These collections of DynamicObj are copied as a new collection with recursively deep copied elements. /// @@ -395,11 +403,25 @@ and CopyUtils = // might be that we do not need this case, however if we remove it, some types will match the // ICloneable case in transpiled code, which we'd like to prevent, so well keep it for now. - | :? int | :? float | :? bool - | :? string | :? char | :? byte - | :? sbyte | :? int16 | :? uint16 - | :? int32 | :? uint32 | :? int64 - | :? uint64 | :? single + // https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/basic-types + | :? bool + | :? byte + | :? sbyte + | :? int16 + | :? uint16 + | :? int + | :? uint + | :? int64 + | :? uint64 + #if !FABLE_COMPILER + | :? nativeint + | :? unativeint + #endif + | :? float + | :? float32 + | :? char + | :? string + | :? unit -> o #if !FABLE_COMPILER_PYTHON @@ -407,12 +429,513 @@ and CopyUtils = | :? decimal -> o #endif + #if !FABLE_COMPILER + + // we can do some more type checking in F# land + + // ResizeArray typematches are all translated as `isArrayLike` in Fable JS/Py, so all these cases are the same in transpiled code. + // However this is fine, as we can transpile one single case (the `ResizeArray` case) that recursively applies `tryDeepCopyObj` on all items. + // That way, everything is fine in the untyped world, and we can keep the boxed types in F#. + + // ResizeArrays are mutable and we need to copy them. For primitives, we can do this easily. + | :? ResizeArray as r -> ResizeArray(r) |> box + | :? ResizeArray as r -> ResizeArray(r) |> box + | :? ResizeArray as r -> ResizeArray(r) |> box + | :? ResizeArray as r -> ResizeArray(r) |> box + | :? ResizeArray as r -> ResizeArray(r) |> box + | :? ResizeArray as r -> ResizeArray(r) |> box + | :? ResizeArray as r -> ResizeArray(r) |> box + | :? ResizeArray as r -> ResizeArray(r) |> box + | :? ResizeArray as r -> ResizeArray(r) |> box + | :? ResizeArray as r -> ResizeArray(r) |> box + | :? ResizeArray as r -> ResizeArray(r) |> box + | :? ResizeArray as r -> ResizeArray(r) |> box + | :? ResizeArray as r -> ResizeArray(r) |> box + | :? ResizeArray as r -> ResizeArray(r) |> box + | :? ResizeArray as r -> ResizeArray(r) |> box + | :? ResizeArray as r -> ResizeArray(r) |> box + | :? ResizeArray as r -> ResizeArray(r) |> box + + // Dictionaries are mutable and we need to copy them. For primitives, we can do this easily, it is just a lot of work to write man + // Fable does simply not compile these typechecks, i guess because everything is an object/dictionary in JS/PY. + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + | :? Dictionary as dict -> Dictionary(dict) |> box + + // same for dictionaries containing DynamicObj as value + | :? Dictionary as dict -> + let newDict = Dictionary() + for kv in dict do newDict.Add(kv.Key, tryDeepCopyObj kv.Value :?> DynamicObj) + newDict |> box + | :? Dictionary as dict -> + let newDict = Dictionary() + for kv in dict do newDict.Add(kv.Key, tryDeepCopyObj kv.Value :?> DynamicObj) + newDict |> box + | :? Dictionary as dict -> + let newDict = Dictionary() + for kv in dict do newDict.Add(kv.Key, tryDeepCopyObj kv.Value :?> DynamicObj) + newDict |> box + | :? Dictionary as dict -> + let newDict = Dictionary() + for kv in dict do newDict.Add(kv.Key, tryDeepCopyObj kv.Value :?> DynamicObj) + newDict |> box + | :? Dictionary as dict -> + let newDict = Dictionary() + for kv in dict do newDict.Add(kv.Key, tryDeepCopyObj kv.Value :?> DynamicObj) + newDict |> box + | :? Dictionary as dict -> + let newDict = Dictionary() + for kv in dict do newDict.Add(kv.Key, tryDeepCopyObj kv.Value :?> DynamicObj) + newDict |> box + | :? Dictionary as dict -> + let newDict = Dictionary() + for kv in dict do newDict.Add(kv.Key, tryDeepCopyObj kv.Value :?> DynamicObj) + newDict |> box + | :? Dictionary as dict -> + let newDict = Dictionary() + for kv in dict do newDict.Add(kv.Key, tryDeepCopyObj kv.Value :?> DynamicObj) + newDict |> box + | :? Dictionary as dict -> + let newDict = Dictionary() + for kv in dict do newDict.Add(kv.Key, tryDeepCopyObj kv.Value :?> DynamicObj) + newDict |> box + | :? Dictionary as dict -> + let newDict = Dictionary() + for kv in dict do newDict.Add(kv.Key, tryDeepCopyObj kv.Value :?> DynamicObj) + newDict |> box + | :? Dictionary as dict -> + let newDict = Dictionary() + for kv in dict do newDict.Add(kv.Key, tryDeepCopyObj kv.Value :?> DynamicObj) + newDict |> box + | :? Dictionary as dict -> + let newDict = Dictionary() + for kv in dict do newDict.Add(kv.Key, tryDeepCopyObj kv.Value :?> DynamicObj) + newDict |> box + | :? Dictionary as dict -> + let newDict = Dictionary() + for kv in dict do newDict.Add(kv.Key, tryDeepCopyObj kv.Value :?> DynamicObj) + newDict |> box + | :? Dictionary as dict -> + let newDict = Dictionary() + for kv in dict do newDict.Add(kv.Key, tryDeepCopyObj kv.Value :?> DynamicObj) + newDict |> box + | :? Dictionary as dict -> + let newDict = Dictionary() + for kv in dict do newDict.Add(kv.Key, tryDeepCopyObj kv.Value :?> DynamicObj) + newDict |> box + | :? Dictionary as dict -> + let newDict = Dictionary() + for kv in dict do newDict.Add(kv.Key, tryDeepCopyObj kv.Value :?> DynamicObj) + newDict |> box + + // And for dictionaries containing DynamicObj as key + | :? Dictionary as dict -> + let newDict = Dictionary() + for kv in dict do newDict.Add(tryDeepCopyObj kv.Key :?> DynamicObj, kv.Value) + newDict |> box + | :? Dictionary as dict -> + let newDict = Dictionary() + for kv in dict do newDict.Add(tryDeepCopyObj kv.Key :?> DynamicObj, kv.Value) + newDict |> box + | :? Dictionary as dict -> + let newDict = Dictionary() + for kv in dict do newDict.Add(tryDeepCopyObj kv.Key :?> DynamicObj, kv.Value) + newDict |> box + | :? Dictionary as dict -> + let newDict = Dictionary() + for kv in dict do newDict.Add(tryDeepCopyObj kv.Key :?> DynamicObj, kv.Value) + newDict |> box + | :? Dictionary as dict -> + let newDict = Dictionary() + for kv in dict do newDict.Add(tryDeepCopyObj kv.Key :?> DynamicObj, kv.Value) + newDict |> box + | :? Dictionary as dict -> + let newDict = Dictionary() + for kv in dict do newDict.Add(tryDeepCopyObj kv.Key :?> DynamicObj, kv.Value) + newDict |> box + | :? Dictionary as dict -> + let newDict = Dictionary() + for kv in dict do newDict.Add(tryDeepCopyObj kv.Key :?> DynamicObj, kv.Value) + newDict |> box + | :? Dictionary as dict -> + let newDict = Dictionary() + for kv in dict do newDict.Add(tryDeepCopyObj kv.Key :?> DynamicObj, kv.Value) + newDict |> box + | :? Dictionary as dict -> + let newDict = Dictionary() + for kv in dict do newDict.Add(tryDeepCopyObj kv.Key :?> DynamicObj, kv.Value) + newDict |> box + | :? Dictionary as dict -> + let newDict = Dictionary() + for kv in dict do newDict.Add(tryDeepCopyObj kv.Key :?> DynamicObj, kv.Value) + newDict |> box + | :? Dictionary as dict -> + let newDict = Dictionary() + for kv in dict do newDict.Add(tryDeepCopyObj kv.Key :?> DynamicObj, kv.Value) + newDict |> box + | :? Dictionary as dict -> + let newDict = Dictionary() + for kv in dict do newDict.Add(tryDeepCopyObj kv.Key :?> DynamicObj, kv.Value) + newDict |> box + | :? Dictionary as dict -> + let newDict = Dictionary() + for kv in dict do newDict.Add(tryDeepCopyObj kv.Key :?> DynamicObj, kv.Value) + newDict |> box + | :? Dictionary as dict -> + let newDict = Dictionary() + for kv in dict do newDict.Add(tryDeepCopyObj kv.Key :?> DynamicObj, kv.Value) + newDict |> box + | :? Dictionary as dict -> + let newDict = Dictionary() + for kv in dict do newDict.Add(tryDeepCopyObj kv.Key :?> DynamicObj, kv.Value) + newDict |> box + | :? Dictionary as dict -> + let newDict = Dictionary() + for kv in dict do newDict.Add(tryDeepCopyObj kv.Key :?> DynamicObj, kv.Value) + newDict |> box + | :? Dictionary as dict -> + let newDict = Dictionary() + for kv in dict do newDict.Add(tryDeepCopyObj kv.Key :?> DynamicObj, tryDeepCopyObj kv.Value :?> DynamicObj) + newDict |> box + #endif + + // native fallbacks for matching Map/dict, see https://github.com/CSBiology/DynamicObj/issues/47 + + #if FABLE_COMPILER_JAVASCRIPT || FABLE_COMPILER_TYPESCRIPT + | o when FableJS.Dictionaries.isMap o -> + let o = o |> unbox> + let newDict = Dictionary() + for kv in o do newDict.Add(tryDeepCopyObj kv.Key, tryDeepCopyObj kv.Value) + newDict |> box + | o when FableJS.Dictionaries.isMap o -> + let o = o |> unbox> + let newDict = Dictionary() + for kv in o do newDict.Add(tryDeepCopyObj kv.Key, tryDeepCopyObj kv.Value) + newDict |> box + #endif + #if FABLE_COMPILER_PYTHON + // https://github.com/fable-compiler/Fable/issues/3972 + | o when FablePy.Dictionaries.isDict o -> + let o = o |> unbox> + let newDict = Dictionary() + for kv in o do newDict.Add(tryDeepCopyObj kv.Key, tryDeepCopyObj kv.Value) + newDict |> box + #endif + + // These collections of DynamicObj can be cloned recursively + | :? ResizeArray as dyns -> + box (ResizeArray([for dyn in dyns -> tryDeepCopyObj dyn :?> DynamicObj])) + #if !FABLE_COMPILER + // this gets compiled to isArrayLike just the same as ResizeArray, but generates a slightly different result handling, so we only transpile the above. | :? array as dyns -> box [|for dyn in dyns -> tryDeepCopyObj dyn :?> DynamicObj|] + #endif | :? list as dyns -> box [for dyn in dyns -> tryDeepCopyObj dyn :?> DynamicObj] - | :? ResizeArray as dyns -> - box (ResizeArray([for dyn in dyns -> tryDeepCopyObj dyn :?> DynamicObj])) + + // Fable compilable version of typematching against implemented IClonable #if FABLE_COMPILER_JAVASCRIPT || FABLE_COMPILER_TYPESCRIPT | o when FableJS.Interfaces.implementsICloneable o -> FableJS.Interfaces.cloneICloneable o #endif diff --git a/src/DynamicObj/FableJS.fs b/src/DynamicObj/FableJS.fs index ff457ff..16c384e 100644 --- a/src/DynamicObj/FableJS.fs +++ b/src/DynamicObj/FableJS.fs @@ -168,4 +168,11 @@ module FableJS = let cloneICloneable (o:obj) : obj = jsNative + module Dictionaries = + [] + let isMap (o:obj) : bool = + jsNative + [] + let isDict (o:obj) : bool = + jsNative #endif \ No newline at end of file diff --git a/src/DynamicObj/FablePy.fs b/src/DynamicObj/FablePy.fs index 1bc575e..ceb22a7 100644 --- a/src/DynamicObj/FablePy.fs +++ b/src/DynamicObj/FablePy.fs @@ -220,4 +220,10 @@ module FablePy = [] let cloneICloneable (o:obj) : obj = nativeOnly + + module Dictionaries = + [] + let isDict (o:obj) : bool = + nativeOnly + #endif \ No newline at end of file diff --git a/src/DynamicObj/Playground.fsx b/src/DynamicObj/Playground.fsx index 1c5f40a..a98a44c 100644 --- a/src/DynamicObj/Playground.fsx +++ b/src/DynamicObj/Playground.fsx @@ -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 \ No newline at end of file +printfn "%A" (LanguagePrimitives.PhysicalEquality r1 r2) +printfn "%A" (LanguagePrimitives.PhysicalEquality r2 r2) +printfn "%A" (LanguagePrimitives.PhysicalEquality r3 r1) \ No newline at end of file diff --git a/tests/CSharpTests/CSharpTests.csproj b/tests/CSharpTests/CSharpTests.csproj index faf0826..ddf25ca 100644 --- a/tests/CSharpTests/CSharpTests.csproj +++ b/tests/CSharpTests/CSharpTests.csproj @@ -1,7 +1,7 @@ - net6.0 + net8.0 false diff --git a/tests/DynamicObject.Tests/CopyUtils.tryDeepCopyObj/Dictionaries.fs b/tests/DynamicObject.Tests/CopyUtils.tryDeepCopyObj/Dictionaries.fs new file mode 100644 index 0000000..e7c3f6b --- /dev/null +++ b/tests/DynamicObject.Tests/CopyUtils.tryDeepCopyObj/Dictionaries.fs @@ -0,0 +1,382 @@ +module DeepCopyDictionaries + +open System +open Fable.Pyxpecto +open DynamicObj +open Fable.Core +open TestUtils +open System.Collections.Generic + +let tests_DeepCopyDictionaries = testList "Dictionaries" [ +// there are hundreds of potential test cases here for each type combination + testList "bool keys" [ + testCase "bool values" <| fun _ -> + let d = new Dictionary() + d.Add(true, true) + let original, copy = constructDeepCopiedObj d + 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" + d.[true] <- false + Expect.sequenceEqual original (dict [true, false]) "Original schould have been mutated" + Expect.sequenceEqual copy (dict [true, true]) "Clone should not be affected by original mutation" + testCase "byte values" <| fun _ -> + let d = new Dictionary() + d.Add(true, 1uy) + let original, copy = constructDeepCopiedObj d + 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" + d.[true] <- 2uy + Expect.sequenceEqual original (dict [true, 2uy]) "Original schould have been mutated" + Expect.sequenceEqual copy (dict [true, 1uy]) "Clone should not be affected by original mutation" + testCase "sbyte values" <| fun _ -> + let d = new Dictionary() + d.Add(true, 1y) + let original, copy = constructDeepCopiedObj d + 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" + d.[true] <- 2y + Expect.sequenceEqual original (dict [true, 2y]) "Original schould have been mutated" + Expect.sequenceEqual copy (dict [true, 1y]) "Clone should not be affected by original mutation" + testCase "int16 values" <| fun _ -> + let d = new Dictionary() + d.Add(true, 1s) + let original, copy = constructDeepCopiedObj d + 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" + d.[true] <- 2s + Expect.sequenceEqual original (dict [true, 2s]) "Original schould have been mutated" + Expect.sequenceEqual copy (dict [true, 1s]) "Clone should not be affected by original mutation" + testCase "uint16 values" <| fun _ -> + let d = new Dictionary() + d.Add(true, 1us) + let original, copy = constructDeepCopiedObj d + 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" + d.[true] <- 2us + Expect.sequenceEqual original (dict [true, 2us]) "Original schould have been mutated" + Expect.sequenceEqual copy (dict [true, 1us]) "Clone should not be affected by original mutation" + testCase "int values" <| fun _ -> + let d = new Dictionary() + d.Add(true, 1) + let original, copy = constructDeepCopiedObj d + 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" + d.[true] <- 2 + Expect.sequenceEqual original (dict [true, 2]) "Original schould have been mutated" + Expect.sequenceEqual copy (dict [true, 1]) "Clone should not be affected by original mutation" + testCase "uint values" <| fun _ -> + let d = new Dictionary() + d.Add(true, 1u) + let original, copy = constructDeepCopiedObj d + 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" + d.[true] <- 2u + Expect.sequenceEqual original (dict [true, 2u]) "Original schould have been mutated" + Expect.sequenceEqual copy (dict [true, 1u]) "Clone should not be affected by original mutation" + testCase "int64 values" <| fun _ -> + let d = new Dictionary() + d.Add(true, 1L) + let original, copy = constructDeepCopiedObj d + 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" + d.[true] <- 2L + Expect.sequenceEqual original (dict [true, 2L]) "Original schould have been mutated" + Expect.sequenceEqual copy (dict [true, 1L]) "Clone should not be affected by original mutation" + testCase "uint64 values" <| fun _ -> + let d = new Dictionary() + d.Add(true, 1uL) + let original, copy = constructDeepCopiedObj d + 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" + d.[true] <- 2uL + Expect.sequenceEqual original (dict [true, 2uL]) "Original schould have been mutated" + Expect.sequenceEqual copy (dict [true, 1uL]) "Clone should not be affected by original mutation" + testCase "float values" <| fun _ -> + let d = new Dictionary() + d.Add(true, 1.0) + let original, copy = constructDeepCopiedObj d + 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" + d.[true] <- 2.0 + Expect.sequenceEqual original (dict [true, 2.0]) "Original schould have been mutated" + Expect.sequenceEqual copy (dict [true, 1.0]) "Clone should not be affected by original mutation" + testCase "float32 values" <| fun _ -> + let d = new Dictionary() + d.Add(true, 1.0f) + let original, copy = constructDeepCopiedObj d + 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" + d.[true] <- 2.0f + Expect.sequenceEqual original (dict [true, 2.0f]) "Original schould have been mutated" + Expect.sequenceEqual copy (dict [true, 1.0f]) "Clone should not be affected by original mutation" + testCase "char values" <| fun _ -> + let d = new Dictionary() + d.Add(true, 'A') + let original, copy = constructDeepCopiedObj d + 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" + d.[true] <- 'B' + Expect.sequenceEqual original (dict [true, 'B']) "Original schould have been mutated" + Expect.sequenceEqual copy (dict [true, 'A']) "Clone should not be affected by original mutation" + testCase "string values" <| fun _ -> + let d = new Dictionary() + d.Add(true, "k1") + let original, copy = constructDeepCopiedObj d + 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" + d.[true] <- "k2" + Expect.sequenceEqual original (dict [true, "k2"]) "Original schould have been mutated" + Expect.sequenceEqual copy (dict [true, "k1"]) "Clone should not be affected by original mutation" + testCase "unit values" <| fun _ -> + let d = new Dictionary() + d.Add(true, ()) + let original, copy = constructDeepCopiedObj d + 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" + d.Add(false,()) + Expect.sequenceEqual original (dict [true, (); false, ()]) "Original schould have been mutated" + Expect.sequenceEqual copy (dict [true, ()]) "Clone should not be affected by original mutation" + #if !FABLE_COMPILER_PYTHON + testCase "decimal values" <| fun _ -> + let d = new Dictionary() + d.Add(true, 1.0M) + let original, copy = constructDeepCopiedObj d + 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" + d.[true] <- 2.0M + Expect.sequenceEqual original (dict [true, 2.0M]) "Original schould have been mutated" + Expect.sequenceEqual copy (dict [true, 1.0M]) "Clone should not be affected by original mutation" + #endif + #if !FABLE_COMPILER + testCase "nativeint values" <| fun _ -> + let d = new Dictionary() + d.Add(true, System.IntPtr(1)) + let original, copy = constructDeepCopiedObj d + 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" + d.[true] <- System.IntPtr(2) + Expect.sequenceEqual original (dict [true, System.IntPtr(2)]) "Original schould have been mutated" + Expect.sequenceEqual copy (dict [true, System.IntPtr(1)]) "Clone should not be affected by original mutation" + testCase "unativeint values" <| fun _ -> + let d = new Dictionary() + d.Add(true, System.UIntPtr(1u)) + let original, copy = constructDeepCopiedObj d + 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" + d.[true] <- System.UIntPtr(2u) + Expect.sequenceEqual original (dict [true, System.UIntPtr(2u)]) "Original schould have been mutated" + Expect.sequenceEqual copy (dict [true, System.UIntPtr(1u)]) "Clone should not be affected by original mutation" + #endif + ] + testList "byte keys" [ + testCase "bool values" <| fun _ -> + let d = new Dictionary() + d.Add(1uy, true) + let original, copy = constructDeepCopiedObj d + 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" + d.[1uy] <- false + Expect.sequenceEqual original (dict [1uy, false]) "Original schould have been mutated" + Expect.sequenceEqual copy (dict [1uy, true]) "Clone should not be affected by original mutation" + testCase "byte values" <| fun _ -> + let d = new Dictionary() + d.Add(1uy, 1uy) + let original, copy = constructDeepCopiedObj d + 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" + d.[1uy] <- 2uy + Expect.sequenceEqual original (dict [1uy, 2uy]) "Original schould have been mutated" + Expect.sequenceEqual copy (dict [1uy, 1uy]) "Clone should not be affected by original mutation" + testCase "sbyte values" <| fun _ -> + let d = new Dictionary() + d.Add(1uy, 1y) + let original, copy = constructDeepCopiedObj d + 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" + d.[1uy] <- 2y + Expect.sequenceEqual original (dict [1uy, 2y]) "Original schould have been mutated" + Expect.sequenceEqual copy (dict [1uy, 1y]) "Clone should not be affected by original mutation" + testCase "int16 values" <| fun _ -> + let d = new Dictionary() + d.Add(1uy, 1s) + let original, copy = constructDeepCopiedObj d + 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" + d.[1uy] <- 2s + Expect.sequenceEqual original (dict [1uy, 2s]) "Original schould have been mutated" + Expect.sequenceEqual copy (dict [1uy, 1s]) "Clone should not be affected by original mutation" + testCase "uint16 values" <| fun _ -> + let d = new Dictionary() + d.Add(1uy, 1us) + let original, copy = constructDeepCopiedObj d + 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" + d.[1uy] <- 2us + Expect.sequenceEqual original (dict [1uy, 2us]) "Original schould have been mutated" + Expect.sequenceEqual copy (dict [1uy, 1us]) "Clone should not be affected by original mutation" + testCase "int values" <| fun _ -> + let d = new Dictionary() + d.Add(1uy, 1) + let original, copy = constructDeepCopiedObj d + 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" + d.[1uy] <- 2 + Expect.sequenceEqual original (dict [1uy, 2]) "Original schould have been mutated" + Expect.sequenceEqual copy (dict [1uy, 1]) "Clone should not be affected by original mutation" + testCase "uint values" <| fun _ -> + let d = new Dictionary() + d.Add(1uy, 1u) + let original, copy = constructDeepCopiedObj d + 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" + d.[1uy] <- 2u + Expect.sequenceEqual original (dict [1uy, 2u]) "Original schould have been mutated" + Expect.sequenceEqual copy (dict [1uy, 1u]) "Clone should not be affected by original mutation" + testCase "int64 values" <| fun _ -> + let d = new Dictionary() + d.Add(1uy, 1L) + let original, copy = constructDeepCopiedObj d + 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" + d.[1uy] <- 2L + Expect.sequenceEqual original (dict [1uy, 2L]) "Original schould have been mutated" + Expect.sequenceEqual copy (dict [1uy, 1L]) "Clone should not be affected by original mutation" + testCase "uint64 values" <| fun _ -> + let d = new Dictionary() + d.Add(1uy, 1uL) + let original, copy = constructDeepCopiedObj d + 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" + d.[1uy] <- 2uL + Expect.sequenceEqual original (dict [1uy, 2uL]) "Original schould have been mutated" + Expect.sequenceEqual copy (dict [1uy, 1uL]) "Clone should not be affected by original mutation" + testCase "float values" <| fun _ -> + let d = new Dictionary() + d.Add(1uy, 1.0) + let original, copy = constructDeepCopiedObj d + 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" + d.[1uy] <- 2.0 + Expect.sequenceEqual original (dict [1uy, 2.0]) "Original schould have been mutated" + Expect.sequenceEqual copy (dict [1uy, 1.0]) "Clone should not be affected by original mutation" + testCase "float32 values" <| fun _ -> + let d = new Dictionary() + d.Add(1uy, 1.0f) + let original, copy = constructDeepCopiedObj d + 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" + d.[1uy] <- 2.0f + Expect.sequenceEqual original (dict [1uy, 2.0f]) "Original schould have been mutated" + Expect.sequenceEqual copy (dict [1uy, 1.0f]) "Clone should not be affected by original mutation" + testCase "char values" <| fun _ -> + let d = new Dictionary() + d.Add(1uy, 'A') + let original, copy = constructDeepCopiedObj d + 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" + d.[1uy] <- 'B' + Expect.sequenceEqual original (dict [1uy, 'B']) "Original schould have been mutated" + Expect.sequenceEqual copy (dict [1uy, 'A']) "Clone should not be affected by original mutation" + testCase "string values" <| fun _ -> + let d = new Dictionary() + d.Add(1uy, "k1") + let original, copy = constructDeepCopiedObj d + 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" + d.[1uy] <- "k2" + Expect.sequenceEqual original (dict [1uy, "k2"]) "Original schould have been mutated" + Expect.sequenceEqual copy (dict [1uy, "k1"]) "Clone should not be affected by original mutation" + testCase "unit values" <| fun _ -> + let d = new Dictionary() + d.Add(1uy, ()) + let original, copy = constructDeepCopiedObj d + 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" + d.Add(2uy,()) + Expect.sequenceEqual original (dict [1uy, (); 2uy, ()]) "Original schould have been mutated" + Expect.sequenceEqual copy (dict [1uy, ()]) "Clone should not be affected by original mutation" + #if !FABLE_COMPILER_PYTHON + testCase "decimal values" <| fun _ -> + let d = new Dictionary() + d.Add(1uy, 1.0M) + let original, copy = constructDeepCopiedObj d + 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" + d.[1uy] <- 2.0M + Expect.sequenceEqual original (dict [1uy, 2.0M]) "Original schould have been mutated" + Expect.sequenceEqual copy (dict [1uy, 1.0M]) "Clone should not be affected by original mutation" + #endif + #if !FABLE_COMPILER + testCase "nativeint values" <| fun _ -> + let d = new Dictionary() + d.Add(1uy, System.IntPtr(1)) + let original, copy = constructDeepCopiedObj d + 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" + d.[1uy] <- System.IntPtr(2) + Expect.sequenceEqual original (dict [1uy, System.IntPtr(2)]) "Original schould have been mutated" + Expect.sequenceEqual copy (dict [1uy, System.IntPtr(1)]) "Clone should not be affected by original mutation" + testCase "unativeint values" <| fun _ -> + let d = new Dictionary() + d.Add(1uy, System.UIntPtr(1u)) + let original, copy = constructDeepCopiedObj d + 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" + d.[1uy] <- System.UIntPtr(2u) + Expect.sequenceEqual original (dict [1uy, System.UIntPtr(2u)]) "Original schould have been mutated" + Expect.sequenceEqual copy (dict [1uy, System.UIntPtr(1u)]) "Clone should not be affected by original mutation" + #endif + ] + //testList "byte keys" [ + + //] + //testList "sbyte keys" [ + + //] + //testList "int16 keys" [ + + //] + //testList "uint16 keys" [ + + //] + //testList "int keys" [ + + //] + //testList "uint keys" [ + + //] + //testList "int64 keys" [ + + //] + //testList "uint64 keys" [ + + //] + //testList "float keys" [ + + //] + //testList "float32 keys" [ + + //] + //testList "char keys" [ + + //] + //testList "string keys" [ + + //] + //testList "unit keys" [ + + //] + #if !FABLE_COMPILER_PYTHON + //testList "decimal keys" [ + + //] + #endif + #if !FABLE_COMPILER + //testList "nativeint keys" [ + + //] + //testList "unativeint keys" [ + + //] + #endif +] \ No newline at end of file diff --git a/tests/DynamicObject.Tests/CopyUtils.tryDeepCopyObj/DynamicObj.fs b/tests/DynamicObject.Tests/CopyUtils.tryDeepCopyObj/DynamicObj.fs new file mode 100644 index 0000000..16d4c78 --- /dev/null +++ b/tests/DynamicObject.Tests/CopyUtils.tryDeepCopyObj/DynamicObj.fs @@ -0,0 +1,2 @@ +module DeepCopyDynamicObj + diff --git a/tests/DynamicObject.Tests/CopyUtils.tryDeepCopyObj/DynamicObjCollections.fs b/tests/DynamicObject.Tests/CopyUtils.tryDeepCopyObj/DynamicObjCollections.fs new file mode 100644 index 0000000..82d75b1 --- /dev/null +++ b/tests/DynamicObject.Tests/CopyUtils.tryDeepCopyObj/DynamicObjCollections.fs @@ -0,0 +1,2 @@ +module DeepCopyDynamicObjCollections + diff --git a/tests/DynamicObject.Tests/CopyUtils.tryDeepCopyObj/ICloneable.fs b/tests/DynamicObject.Tests/CopyUtils.tryDeepCopyObj/ICloneable.fs new file mode 100644 index 0000000..41d05df --- /dev/null +++ b/tests/DynamicObject.Tests/CopyUtils.tryDeepCopyObj/ICloneable.fs @@ -0,0 +1,2 @@ +module DeepCopyICloneable + diff --git a/tests/DynamicObject.Tests/CopyUtils.tryDeepCopyObj/Main.fs b/tests/DynamicObject.Tests/CopyUtils.tryDeepCopyObj/Main.fs new file mode 100644 index 0000000..593626e --- /dev/null +++ b/tests/DynamicObject.Tests/CopyUtils.tryDeepCopyObj/Main.fs @@ -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 +] + diff --git a/tests/DynamicObject.Tests/CopyUtils.tryDeepCopyObj/Primitives.fs b/tests/DynamicObject.Tests/CopyUtils.tryDeepCopyObj/Primitives.fs new file mode 100644 index 0000000..f0db390 --- /dev/null +++ b/tests/DynamicObject.Tests/CopyUtils.tryDeepCopyObj/Primitives.fs @@ -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" +] \ No newline at end of file diff --git a/tests/DynamicObject.Tests/CopyUtils.tryDeepCopyObj/ResizeArrays.fs b/tests/DynamicObject.Tests/CopyUtils.tryDeepCopyObj/ResizeArrays.fs new file mode 100644 index 0000000..a892b2b --- /dev/null +++ b/tests/DynamicObject.Tests/CopyUtils.tryDeepCopyObj/ResizeArrays.fs @@ -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 +] \ No newline at end of file diff --git a/tests/DynamicObject.Tests/DynamicObject.Tests.fsproj b/tests/DynamicObject.Tests/DynamicObject.Tests.fsproj index 99440c8..b81ae5f 100644 --- a/tests/DynamicObject.Tests/DynamicObject.Tests.fsproj +++ b/tests/DynamicObject.Tests/DynamicObject.Tests.fsproj @@ -9,6 +9,13 @@ + + + + + + + diff --git a/tests/DynamicObject.Tests/Main.fs b/tests/DynamicObject.Tests/Main.fs index 8b79a73..0f2cf55 100644 --- a/tests/DynamicObject.Tests/Main.fs +++ b/tests/DynamicObject.Tests/Main.fs @@ -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 diff --git a/tests/DynamicObject.Tests/TestUtils.fs b/tests/DynamicObject.Tests/TestUtils.fs index 1086fc5..62b6d1f 100644 --- a/tests/DynamicObject.Tests/TestUtils.fs +++ b/tests/DynamicObject.Tests/TestUtils.fs @@ -33,8 +33,11 @@ let constructDeepCopiedClone<'T> (props: seq) = 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) (dyn: #DynamicObj) = props |> Seq.iter (fun (propertyName, propertyValue) -> dyn.SetProperty(propertyName, propertyValue)) @@ -56,6 +59,13 @@ module DynObj = | _ -> failwith "Empty property list" getProp dyn props +#if FABLE_COMPILER_PYTHON +module Py = + [] + let isReferenceEqual o1 o2 : bool = + nativeOnly +#endif + module Expect = /// Expects the `actual` sequence to equal the `expected` one. let sequenceEqual actual expected message = @@ -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 \ No newline at end of file + 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 \ No newline at end of file diff --git a/tests/FSharpConsole/Program.fs b/tests/FSharpConsole/Program.fs index 68e5933..cd3dcc9 100644 --- a/tests/FSharpConsole/Program.fs +++ b/tests/FSharpConsole/Program.fs @@ -1,30 +1,25 @@ -// For more information see https://aka.ms/fsharp-console-apps +open DynamicObj -open DynamicObj +let constructDeepCopiedClone<'T> (props: seq) = + let original = DynamicObj() + props + |> Seq.iter (fun (propertyName, propertyValue) -> original.SetProperty(propertyName, propertyValue)) + let clone : 'T = original.DeepCopyProperties() |> unbox<'T> + original, clone -type Inner() = - inherit DynamicObj() - static member init( - ?inner_value: string - ) = - Inner() - |> DynObj.withOptionalProperty "inner_value" inner_value +let item1 = DynamicObj() |> DynObj.withProperty "item" 1 +let item2 = DynamicObj() |> DynObj.withProperty "item" 2 +let item3 = DynamicObj() |> DynObj.withProperty "item" 3 +let arr = [|item1; item2; item3|] +let original, clone = constructDeepCopiedClone ["arr", box arr] +item1.SetProperty("item", -1) +item2.SetProperty("item", -1) +item3.SetProperty("item", -1) -type Outer() = - inherit DynamicObj() - static member init( - ?A: int, - ?B: string, - ?Inner: Inner - ) = - Outer() - |> DynObj.withOptionalProperty "A" A - |> DynObj.withOptionalProperty "B" B - |> DynObj.withOptionalProperty "Inner" Inner +original +|> DynObj.tryGetTypedPropertyValue "arr" +|> Option.iter (Seq.iter DynObj.print) - -let outer1 = Outer.init(A = 1, B = "first", Inner = Inner.init(inner_value = "inner_first")) -let outer2 = Outer.init(A = 2, B = "second", Inner = Inner.init(inner_value = "inner_second")) -let expected = Outer.init(A = 2, B = "second", Inner = Inner.init(inner_value = "inner_second")) - -printfn "%A" ((DynObj.combine outer1 outer2) = expected) \ No newline at end of file +clone +|> DynObj.tryGetTypedPropertyValue "arr" +|> Option.iter (Seq.iter DynObj.print) \ No newline at end of file