From 71078db75b9cc0b1cbdf6d5509a0a39e4f032b8d Mon Sep 17 00:00:00 2001 From: HLWeil Date: Tue, 20 Aug 2024 16:36:53 +0200 Subject: [PATCH] finish up first working version of js and py compatible DynamicObj --- src/DynamicObj/DynObj.fs | 2 +- src/DynamicObj/DynamicObj.fs | 19 ++- src/DynamicObj/FablePy.fs | 192 ++++++++++++++++++++---------- src/DynamicObj/ReflectionUtils.fs | 6 +- 4 files changed, 144 insertions(+), 75 deletions(-) diff --git a/src/DynamicObj/DynObj.fs b/src/DynamicObj/DynObj.fs index 78bcb0f..685e631 100644 --- a/src/DynamicObj/DynObj.fs +++ b/src/DynamicObj/DynObj.fs @@ -82,7 +82,7 @@ module DynObj = dyn.TryGetValue name let remove (dyn:DynamicObj) propName = - DynamicObj.Remove (dyn, propName) |> ignore + DynamicObj.remove (dyn, propName) |> ignore let format (d:DynamicObj) = diff --git a/src/DynamicObj/DynamicObj.fs b/src/DynamicObj/DynamicObj.fs index 74c3d6e..55b2d28 100644 --- a/src/DynamicObj/DynamicObj.fs +++ b/src/DynamicObj/DynamicObj.fs @@ -38,6 +38,9 @@ type DynamicObj() = // Next check for Public properties via Reflection | _ -> ReflectionUtils.tryGetPropertyValue this name + + member this.GetValue (name) = + this.TryGetValue(name).Value /// Gets property value member this.TryGetTypedValue<'a> name = @@ -65,7 +68,8 @@ type DynamicObj() = #endif #if FABLE_COMPILER_PYTHON FablePy.setPropertyValue this name value - #else + #endif + #if !FABLE_COMPILER // Next check the Properties collection for member match properties.TryGetValue name with | true,_ -> properties.[name] <- value @@ -91,7 +95,8 @@ type DynamicObj() = |> Seq.filter (fun pd -> includeInstanceProperties || pd.IsDynamic ) - #else + #endif + #if !FABLE_COMPILER seq [ if includeInstanceProperties then yield! ReflectionUtils.getStaticProperties (this) @@ -132,7 +137,8 @@ type DynamicObj() = else None ) - #else + #endif + #if !FABLE_COMPILER seq [ if includeInstanceProperties then for prop in ReflectionUtils.getStaticProperties (this) -> @@ -170,10 +176,11 @@ type DynamicObj() = // this.CopyDynamicPropertiesTo(target) // target - static member GetValue (lookup:DynamicObj,name) = - lookup.TryGetValue(name).Value - static member Remove (lookup:DynamicObj,name) = + static member getValue (lookup:DynamicObj,name) = + lookup.GetValue(name) + + static member remove (lookup:DynamicObj,name) = lookup.Remove(name) override this.Equals o = diff --git a/src/DynamicObj/FablePy.fs b/src/DynamicObj/FablePy.fs index 180ce25..0e43cca 100644 --- a/src/DynamicObj/FablePy.fs +++ b/src/DynamicObj/FablePy.fs @@ -6,6 +6,21 @@ open System.Collections.Generic module FablePy = + module Dictionary = + + let ofSeq (s:seq>) = + let d = new System.Collections.Generic.Dictionary<_,_>() + s |> Seq.iter (fun kv -> d.Add(kv.Key, kv.Value)) + d + + let choose (f: 'T -> 'U option) (d:System.Collections.Generic.Dictionary<_,'T>) = + let nd = new System.Collections.Generic.Dictionary<_,'U>() + for kv in d do + match f kv.Value with + | Some v -> nd.Add(kv.Key, v) + | None -> () + nd + type PropertyObject = abstract fget : obj abstract fset : obj @@ -13,60 +28,80 @@ module FablePy = module PropertyObject = [] - let tryGetGetter (o:PropertyObject) : obj option = + let tryGetGetter (o:PropertyObject) : (obj -> obj) option = nativeOnly [] - let tryGetSetter (o:PropertyObject) : obj option = + let tryGetSetter (o:PropertyObject) : (obj -> obj -> unit) option = nativeOnly - let containsGetter (o:obj) : bool = + let getGetter (o : PropertyObject) : obj -> obj = + match tryGetGetter o with + | Some f -> f + | None -> fun o -> failwith ("Property does not contain getter") + + let getSetter (o:PropertyObject) : obj -> obj -> unit = + match tryGetSetter o with + | Some f -> f + | None -> fun s o -> failwith ("Property does not contain setter") + + let containsGetter (o:PropertyObject) : bool = match tryGetGetter o with | Some _ -> true | None -> false - let containsSetter (o:obj) : bool = + let containsSetter (o:PropertyObject) : bool = match tryGetSetter o with | Some _ -> true | None -> false - let isWritable (o:obj) : bool = + let isWritable (o:PropertyObject) : bool = containsSetter o [] let isProperty (o:obj) : bool = nativeOnly - [] - let getOwnMemberObjects (o:obj) : Dictionary = - nativeOnly + let tryProperty (o:obj) : PropertyObject option = + if isProperty o then + Some (o :?> PropertyObject) + else + None - [] - let getClass (o:obj) : obj = + [] + let getPropertyValue (o:obj) (propName:string) = nativeOnly - let getOwnPropertyObjects (o:obj) : Dictionary = - getOwnMemberObjects o - - - - let getStaticPropertyObjects (o:obj) = - getClass o - |> getOwnPropertyNames - |> Array.filter (fun n -> n <> "constructor") + let createGetter (propName:string) = + fun (o:obj) -> + getPropertyValue o propName - [] - let setPropertyValue (o:obj) (propName:string) (value:obj) = + [] + let setPropertyValue (o:obj) (propName:string) (value:obj) : unit = nativeOnly let createSetter (propName:string) = fun (o:obj) (value:obj) -> setPropertyValue o propName value + + [] + let getOwnMemberObjects (o:obj) : Dictionary = + nativeOnly + + [] + let getClass (o:obj) : obj = + nativeOnly + + let getStaticPropertyObjects (o:obj) : Dictionary = + getClass o + |> getOwnMemberObjects + |> Dictionary.choose PropertyObject.tryProperty + let removeStaticPropertyValue (o:obj) (propName:string) = setPropertyValue o propName null - [] + [] let deleteDynamicPropertyValue (o:obj) (propName:string) = nativeOnly @@ -79,67 +114,92 @@ module FablePy = deleteDynamicPropertyValue o propName - [] - let getPropertyValue (o:obj) (propName:string) = - nativeOnly - let createGetter (propName:string) = - fun (o:obj) -> - getPropertyValue o propName - - [] - let getPropertyDescriptor (o:obj) (propName:string) = + [] + let getMemberObject (o:obj) (propName:string) = nativeOnly - let getStaticPropertyDescriptor (o:obj) (propName:string) = - getPropertyDescriptor (getPrototype o) propName - - let getStaticPropertyHelpers (o:obj) : PropertyHelper [] = - getStaticPropertyNames o - |> Array.choose (fun n -> - let pd = getStaticPropertyDescriptor o n - if PropertyDescriptor.isFunction pd then - None - else - let isWritable = PropertyDescriptor.isWritable pd - { - Name = n - IsStatic = true - IsDynamic = false - IsMutable = isWritable - IsImmutable = not isWritable - GetValue = createGetter n - SetValue = createSetter n - RemoveValue = createRemover n true - } - |> Some - ) - - let transpiledPropertyRegex = "^[a-zA-Z]+@[0-9]+$" + let tryGetPropertyObject (o:obj) (propName:string) : PropertyObject option = + match PropertyObject.tryProperty (getMemberObject o propName) with + | Some po -> Some po + | None -> None + + let tryGetDynamicPropertyHelper (o:obj) (propName:string) : PropertyHelper option = + match getMemberObject o propName with + | Some _ -> + Some { + Name = propName + IsStatic = false + IsDynamic = true + IsMutable = true + IsImmutable = false + GetValue = createGetter propName + SetValue = createSetter propName + RemoveValue = fun o -> deleteDynamicPropertyValue o propName + } + | None -> None + + let tryGetStaticPropertyHelper (o:obj) (propName:string) : PropertyHelper option = + match tryGetPropertyObject (getClass o) propName with + | Some po -> + let isWritable = PropertyObject.isWritable po + Some { + Name = propName + IsStatic = true + IsDynamic = false + IsMutable = isWritable + IsImmutable = not isWritable + GetValue = createGetter propName + SetValue = createSetter propName + RemoveValue = fun o -> removeStaticPropertyValue o propName + } + | None -> None + + let transpiledPropertyRegex = "^[a-zA-Z]+_[0-9]+$" let isTranspiledPropertyHelper (propertyName : string) = System.Text.RegularExpressions.Regex.IsMatch(propertyName, transpiledPropertyRegex) + let getDynamicPropertyHelpers (o:obj) : PropertyHelper [] = - getOwnPropertyNames o - |> Array.choose (fun n -> - let pd = getPropertyDescriptor o n - if PropertyDescriptor.isFunction pd || isTranspiledPropertyHelper n then + getOwnMemberObjects o + |> Seq.choose (fun kv -> + let n = kv.Key + if isTranspiledPropertyHelper n then None - else - let isWritable = PropertyDescriptor.isWritable pd + else { Name = n IsStatic = false IsDynamic = true - IsMutable = isWritable - IsImmutable = not isWritable + IsMutable = true + IsImmutable = false GetValue = createGetter n SetValue = createSetter n - RemoveValue = createRemover n false - } + RemoveValue = fun o -> deleteDynamicPropertyValue o n + } |> Some ) + |> Seq.toArray + + + let getStaticPropertyHelpers (o:obj) : PropertyHelper [] = + getStaticPropertyObjects o + |> Seq.map (fun kv -> + let n = kv.Key + let po = kv.Value + { + Name = n + IsStatic = true + IsDynamic = false + IsMutable = PropertyObject.isWritable po + IsImmutable = not (PropertyObject.isWritable po) + GetValue = createGetter n + SetValue = createSetter n + RemoveValue = fun o -> removeStaticPropertyValue o n + } + ) + |> Seq.toArray let getPropertyHelpers (o:obj) = getDynamicPropertyHelpers o diff --git a/src/DynamicObj/ReflectionUtils.fs b/src/DynamicObj/ReflectionUtils.fs index fea4084..75d6452 100644 --- a/src/DynamicObj/ReflectionUtils.fs +++ b/src/DynamicObj/ReflectionUtils.fs @@ -13,7 +13,8 @@ module ReflectionUtils = #endif #if FABLE_COMPILER_PYTHON FablePy.getStaticPropertyHelpers o - #else + #endif + #if !FABLE_COMPILER let t = o.GetType() [| for propInfo in t.GetProperties() -> propInfo @@ -29,7 +30,8 @@ module ReflectionUtils = #endif #if FABLE_COMPILER_PYTHON FablePy.getPropertyHelpers o - #else + #endif + #if !FABLE_COMPILER getStaticProperties (o) #endif |> Array.tryFind (fun n -> n.Name = propName)