Skip to content

Commit 1ed4f9d

Browse files
committed
create unified API for static and dynamic property accession
1 parent 919d579 commit 1ed4f9d

4 files changed

Lines changed: 99 additions & 60 deletions

File tree

src/DynamicObj/DynamicObj.fs

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,8 @@ type DynamicObj() =
2121
/// Gets property value
2222
member this.TryGetValue name =
2323
// first check the Properties collection for member
24-
match properties.TryGetValue name with
25-
| true,value -> Some value
26-
// Next check for Public properties via Reflection
27-
| _ -> ReflectionUtils.tryGetPropertyValue this name
24+
this.TryGetPropertyInfo(name)
25+
|> Option.map (fun pi -> pi.GetValue(this))
2826

2927

3028
member this.GetValue (name) =
@@ -39,12 +37,43 @@ type DynamicObj() =
3937
| :? 'a -> o :?> 'a |> Some
4038
| _ -> None
4139

40+
41+
member this.TryGetStaticPropertyInfo name : PropertyHelper option =
42+
ReflectionUtils.tryGetStaticPropertyInfo this name
43+
44+
member this.TryGetDynamicPropertyInfo name : PropertyHelper option =
45+
#if FABLE_COMPILER_JAVASCRIPT || FABLE_COMPILER_TYPESCRIPT
46+
FableJS.tryGetDynamicPropertyHelper this name
47+
#endif
48+
#if FABLE_COMPILER_PYTHON
49+
FablePy.tryGetDynamicPropertyHelper this name
50+
#endif
51+
#if !FABLE_COMPILER
52+
match properties.TryGetValue name with
53+
| true,_ ->
54+
Some {
55+
Name = name
56+
IsStatic = false
57+
IsDynamic = true
58+
IsMutable = true
59+
IsImmutable = false
60+
GetValue = fun o -> properties.[name]
61+
SetValue = fun o v -> properties.[name] <- v
62+
RemoveValue = fun o -> properties.Remove(name) |> ignore
63+
}
64+
| _ -> None
65+
#endif
66+
67+
member this.TryGetPropertyInfo name : PropertyHelper option =
68+
match this.TryGetStaticPropertyInfo name with
69+
| Some pi -> Some pi
70+
| None -> this.TryGetDynamicPropertyInfo name
4271

4372
/// Sets property value, creating a new property if none exists
4473
member this.SetValue (name,value) = // private
4574
// first check to see if there's a native property to set
4675

47-
match ReflectionUtils.tryGetPropertyInfo this name with
76+
match ReflectionUtils.tryGetStaticPropertyInfo this name with
4877
| Some pi ->
4978
if pi.IsMutable then
5079
pi.SetValue this value
@@ -65,10 +94,10 @@ type DynamicObj() =
6594
#endif
6695

6796
member this.Remove name =
68-
match ReflectionUtils.removeProperty this name with
69-
| true -> true
70-
// Maybe in map
71-
| false -> properties.Remove(name)
97+
match this.TryGetPropertyInfo name with
98+
| Some pi when pi.IsMutable -> pi.RemoveValue this
99+
| Some _ -> failwith $"Cannot remove value for static, immutable property \"{name}\""
100+
| None -> ()
72101

73102

74103
member this.GetPropertyHelpers (includeInstanceProperties) =

src/DynamicObj/FableJS.fs

Lines changed: 50 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -85,58 +85,68 @@ module FableJS =
8585
getPropertyValue o propName
8686

8787
[<Emit("Object.getOwnPropertyDescriptor($0, $1)")>]
88-
let getPropertyDescriptor (o:obj) (propName:string) =
88+
let tryGetPropertyDescriptor (o:obj) (propName:string) : obj option =
8989
jsNative
9090

91-
let getStaticPropertyDescriptor (o:obj) (propName:string) =
92-
getPropertyDescriptor (getPrototype o) propName
93-
91+
let tryGetStaticPropertyDescriptor (o:obj) (propName:string) : obj option=
92+
tryGetPropertyDescriptor (getPrototype o) propName
93+
94+
let tryStaticPropertyHelperFromDescriptor (pd:obj) (name:string) : PropertyHelper option =
95+
let isWritable = PropertyDescriptor.isWritable pd
96+
if PropertyDescriptor.isFunction pd then
97+
None
98+
else
99+
{
100+
Name = name
101+
IsStatic = true
102+
IsDynamic = false
103+
IsMutable = isWritable
104+
IsImmutable = not isWritable
105+
GetValue = createGetter name
106+
SetValue = createSetter name
107+
RemoveValue = createRemover name true
108+
}
109+
|> Some
110+
111+
let tryGetStaticPropertyHelper (o:obj) (propName:string) : PropertyHelper option =
112+
tryGetStaticPropertyDescriptor o propName
113+
|> Option.bind (fun pd -> tryStaticPropertyHelperFromDescriptor pd propName)
114+
94115
let getStaticPropertyHelpers (o:obj) : PropertyHelper [] =
95116
getStaticPropertyNames o
96-
|> Array.choose (fun n ->
97-
let pd = getStaticPropertyDescriptor o n
98-
if PropertyDescriptor.isFunction pd then
99-
None
100-
else
101-
let isWritable = PropertyDescriptor.isWritable pd
102-
{
103-
Name = n
104-
IsStatic = true
105-
IsDynamic = false
106-
IsMutable = isWritable
107-
IsImmutable = not isWritable
108-
GetValue = createGetter n
109-
SetValue = createSetter n
110-
RemoveValue = createRemover n true
111-
}
112-
|> Some
113-
)
117+
|> Array.choose (tryGetStaticPropertyHelper o)
118+
119+
let tryGetDynamicPropertyDescriptor (o:obj) (propName:string) : obj option =
120+
tryGetPropertyDescriptor o propName
114121

115122
let transpiledPropertyRegex = "^[a-zA-Z]+@[0-9]+$"
116123

117124
let isTranspiledPropertyHelper (propertyName : string) =
118125
System.Text.RegularExpressions.Regex.IsMatch(propertyName, transpiledPropertyRegex)
119126

127+
let tryDynamicPropertyHelperFromDescriptor (pd:obj) (name:string) : PropertyHelper option =
128+
if PropertyDescriptor.isFunction pd || isTranspiledPropertyHelper name then
129+
None
130+
else
131+
{
132+
Name = name
133+
IsStatic = false
134+
IsDynamic = true
135+
IsMutable = true
136+
IsImmutable = false
137+
GetValue = createGetter name
138+
SetValue = createSetter name
139+
RemoveValue = createRemover name false
140+
}
141+
|> Some
142+
143+
let tryGetDynamicPropertyHelper (o:obj) (propName:string) : PropertyHelper option =
144+
tryGetDynamicPropertyDescriptor o propName
145+
|> Option.bind (fun pd -> tryDynamicPropertyHelperFromDescriptor pd propName)
146+
120147
let getDynamicPropertyHelpers (o:obj) : PropertyHelper [] =
121148
getOwnPropertyNames o
122-
|> Array.choose (fun n ->
123-
let pd = getPropertyDescriptor o n
124-
if PropertyDescriptor.isFunction pd || isTranspiledPropertyHelper n then
125-
None
126-
else
127-
let isWritable = PropertyDescriptor.isWritable pd
128-
{
129-
Name = n
130-
IsStatic = false
131-
IsDynamic = true
132-
IsMutable = isWritable
133-
IsImmutable = not isWritable
134-
GetValue = createGetter n
135-
SetValue = createSetter n
136-
RemoveValue = createRemover n false
137-
}
138-
|> Some
139-
)
149+
|> Array.choose (tryGetDynamicPropertyHelper o)
140150

141151
let getPropertyHelpers (o:obj) =
142152
getDynamicPropertyHelpers o

src/DynamicObj/ReflectionUtils.fs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,28 +24,28 @@ module ReflectionUtils =
2424
#endif
2525

2626
/// Try to get the PropertyInfo by name using reflection
27-
let tryGetPropertyInfo (o:obj) (propName:string) =
27+
let tryGetStaticPropertyInfo (o:obj) (propName:string) =
2828
#if FABLE_COMPILER_JAVASCRIPT || FABLE_COMPILER_TYPESCRIPT
29-
FableJS.getPropertyHelpers o
29+
FableJS.tryGetStaticPropertyHelper o propName
3030
#endif
3131
#if FABLE_COMPILER_PYTHON
32-
FablePy.getPropertyHelpers o
32+
FablePy.tryGetStaticPropertyHelper o propName
3333
#endif
3434
#if !FABLE_COMPILER
35-
getStaticProperties (o)
35+
getStaticProperties (o)
36+
|> Array.tryFind (fun n -> n.Name = propName)
3637
#endif
37-
|> Array.tryFind (fun n -> n.Name = propName)
3838

3939
let trySetPropertyValue (o:obj) (propName:string) (value:obj) =
40-
match tryGetPropertyInfo o propName with
40+
match tryGetStaticPropertyInfo o propName with
4141
| Some property when property.IsMutable ->
4242
property.SetValue o value
4343
true
4444
| _ -> false
4545

4646
let tryGetPropertyValue (o:obj) (propName:string) =
4747
try
48-
match tryGetPropertyInfo o propName with
48+
match tryGetStaticPropertyInfo o propName with
4949
| Some v -> Some (v.GetValue(o))
5050
| None -> None
5151
with
@@ -65,7 +65,7 @@ module ReflectionUtils =
6565

6666
let removeProperty (o:obj) (propName:string) =
6767

68-
match tryGetPropertyInfo o propName with
68+
match tryGetStaticPropertyInfo o propName with
6969
| Some property when property.IsMutable ->
7070
property.RemoveValue(o)
7171
true

tests/DynamicObject.Tests/ReflectionUtils.fs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,23 +39,23 @@ let tests_PropertyHelper = testList "PropertyHelper" [
3939

4040
testCase "TryGetPropertyInfo" <| fun _ ->
4141
let p = TestObject("1", "test")
42-
let idOption = ReflectionUtils.tryGetPropertyInfo p "Id"
42+
let idOption = ReflectionUtils.tryGetStaticPropertyInfo p "Id"
4343
let id = Expect.wantSome idOption "Should have immutable property"
4444
Expect.equal id.Name "Id" "Should have correct property"
4545
Expect.equal id.IsStatic true "Id should be static"
4646
Expect.equal id.IsDynamic false "Id should not be dynamic"
4747
Expect.equal id.IsMutable false "Id should not be mutable"
4848
Expect.equal id.IsImmutable true "Id should be immutable"
4949

50-
let nameOption = ReflectionUtils.tryGetPropertyInfo p "Name"
50+
let nameOption = ReflectionUtils.tryGetStaticPropertyInfo p "Name"
5151
let name = Expect.wantSome nameOption "Should have mutable property"
5252
Expect.equal name.Name "Name" "Should have correct property"
5353
Expect.equal name.IsStatic true "Name should be static"
5454
Expect.equal name.IsDynamic false "Name should not be dynamic"
5555
Expect.equal name.IsMutable true "Name should be mutable"
5656
Expect.equal name.IsImmutable false "Name should not be immutable"
5757

58-
let nonExistingOption = ReflectionUtils.tryGetPropertyInfo p "NonExisting"
58+
let nonExistingOption = ReflectionUtils.tryGetStaticPropertyInfo p "NonExisting"
5959
Expect.isNone nonExistingOption "Should not have property"
6060
]
6161

0 commit comments

Comments
 (0)