@@ -43,27 +43,19 @@ internal class SafeFlattenVisitor : ScmLibraryVisitor
43
43
var singleProperty = propertyTypeProvider . Properties . Single ( ) ;
44
44
45
45
// make the current property internal
46
- var internalSingleProperty = type ! . Properties . Single ( p => p . Type . AreNamesEqual ( propertyTypeProvider . Type ) ) ; // type equal not working here, so we use AreNamesEqual
47
- internalizedProperties . Add ( internalSingleProperty ) ;
46
+ var internalProperty = type ! . Properties . Single ( p => p . Type . AreNamesEqual ( propertyTypeProvider . Type ) ) ; // type equal not working here, so we use AreNamesEqual
47
+ internalizedProperties . Add ( internalProperty ) ;
48
48
49
49
// flatten the single property to public and associate it with the internal property
50
- var flattenPropertyName = $ "{ internalSingleProperty . Name } { singleProperty . Name } "; // TODO: handle name conflicts
51
- var checkNullExpression = This . Property ( internalSingleProperty . Name ) . Is ( Null ) ;
52
- MethodBodyStatement setter = new List < MethodBodyStatement >
53
- {
54
- new IfStatement ( checkNullExpression )
55
- {
56
- internalSingleProperty . Assign ( New . Instance ( propertyTypeProvider . Type ! ) ) . Terminate ( )
57
- } ,
58
- This . Property ( internalSingleProperty . Name ) . Property ( singleProperty . Name ) . Assign ( Value ) . Terminate ( )
59
- } ;
50
+ var ( isFlattenedPropertyReadOnly , includeGetterNullCheck , includeSetterNullCheck ) = GetFlags ( property . IsReadOnly , singleProperty , propertyTypeProvider , propertyTypeProvider ) ;
51
+ var flattenPropertyName = $ "{ internalProperty . Name } { singleProperty . Name } "; // TODO: handle name conflicts
60
52
var flattenPropertyBody = new MethodPropertyBody (
61
- Return ( new TernaryConditionalExpression ( checkNullExpression , Default , new MemberExpression ( internalSingleProperty , singleProperty . Name ) ) ) ,
62
- singleProperty . Body ? . HasSetter == true ? setter : null // only add setter for flattend property if the internal property has a setter
53
+ BuildGetter ( includeGetterNullCheck , internalProperty , propertyTypeProvider , singleProperty ) ,
54
+ isFlattenedPropertyReadOnly ? null : BuildSetter ( includeSetterNullCheck , propertyTypeProvider , internalProperty , singleProperty )
63
55
) ;
64
56
var flattenedProperty = new PropertyProvider ( singleProperty . Description , singleProperty . Modifiers , singleProperty . Type , flattenPropertyName , flattenPropertyBody , type , singleProperty . ExplicitInterface , singleProperty . WireInfo , singleProperty . Attributes ) ;
65
57
flattenedProperties . Add ( flattenedProperty ) ;
66
- flattenedPropertyMap . Add ( internalSingleProperty . Type , flattenedProperty ) ;
58
+ flattenedPropertyMap . Add ( internalProperty . Type , flattenedProperty ) ;
67
59
}
68
60
}
69
61
}
@@ -75,6 +67,114 @@ internal class SafeFlattenVisitor : ScmLibraryVisitor
75
67
return base . PreVisitModel ( model , type ) ;
76
68
}
77
69
70
+ private static ( bool IsReadOnly , bool ? IncludeGetterNullCheck , bool IncludeSetterNullCheck ) GetFlags ( bool isPropertyReadOnly , PropertyProvider singleProperty , ModelProvider innerModel , ModelProvider propertyModel )
71
+ {
72
+ var isInnerPropertyReadOnly = ! singleProperty . Body . HasSetter ;
73
+ if ( ! isPropertyReadOnly && isInnerPropertyReadOnly )
74
+ {
75
+ if ( HasDefaultPublicCtor ( innerModel ) )
76
+ {
77
+ if ( singleProperty . Type . Arguments . Count > 0 )
78
+ return ( true , true , false ) ;
79
+ else
80
+ return ( true , false , false ) ;
81
+ }
82
+ else
83
+ {
84
+ return ( false , false , false ) ;
85
+ }
86
+ }
87
+ else if ( ! isPropertyReadOnly && ! isInnerPropertyReadOnly )
88
+ {
89
+ if ( HasDefaultPublicCtor ( propertyModel ) )
90
+ return ( false , false , true ) ;
91
+ else
92
+ return ( false , false , false ) ;
93
+ }
94
+
95
+ return ( true , null , false ) ;
96
+ }
97
+
98
+ private static bool HasDefaultPublicCtor ( ModelProvider innerModel )
99
+ {
100
+ foreach ( var ctor in innerModel . Constructors )
101
+ {
102
+ if ( ctor . Signature . Modifiers . HasFlag ( MethodSignatureModifiers . Public ) && ! ctor . Signature . Parameters . Any ( ) )
103
+ return true ;
104
+ }
105
+
106
+ return false ;
107
+ }
108
+
109
+ private MethodBodyStatement BuildGetter ( bool ? includeGetterNullCheck , PropertyProvider internalProperty , ModelProvider innerModel , PropertyProvider singleProperty )
110
+ {
111
+ var checkNullExpression = This . Property ( internalProperty . Name ) . Is ( Null ) ;
112
+ if ( includeGetterNullCheck == true )
113
+ {
114
+ return new List < MethodBodyStatement > {
115
+ new IfStatement ( checkNullExpression )
116
+ {
117
+ internalProperty . Assign ( New . Instance ( innerModel . Type ) ) . Terminate ( )
118
+ } ,
119
+ Return ( new MemberExpression ( internalProperty , singleProperty . Name ) )
120
+ } ;
121
+ }
122
+ else if ( includeGetterNullCheck == false )
123
+ {
124
+ return Return ( new TernaryConditionalExpression ( checkNullExpression , Default , new MemberExpression ( internalProperty , singleProperty . Name ) ) ) ;
125
+ }
126
+ else
127
+ {
128
+ if ( innerModel . Type . IsNullable )
129
+ {
130
+ return Return ( new MemberExpression ( internalProperty . AsVariableExpression . NullConditional ( ) , singleProperty . Name ) ) ;
131
+ }
132
+ return Return ( new MemberExpression ( internalProperty , singleProperty . Name ) ) ;
133
+ }
134
+ }
135
+
136
+ private MethodBodyStatement BuildSetter ( bool includeSetterCheck , ModelProvider innerModel , PropertyProvider internalProperty , PropertyProvider singleProperty )
137
+ {
138
+ var isOverriddenValueType = innerModel . Type . IsValueType && ! innerModel . Type . IsNullable ;
139
+ var setter = new List < MethodBodyStatement > ( ) ;
140
+ var internalPropertyExpression = This . Property ( internalProperty . Name ) ;
141
+ if ( includeSetterCheck )
142
+ {
143
+ if ( isOverriddenValueType )
144
+ {
145
+ var ifStatement = new IfStatement ( Value . Property ( nameof ( Nullable < int > . HasValue ) ) )
146
+ {
147
+ new IfStatement ( internalPropertyExpression . Is ( Null ) )
148
+ {
149
+ internalPropertyExpression . Assign ( New . Instance ( innerModel . Type ! ) ) . Terminate ( ) ,
150
+ internalPropertyExpression . Property ( singleProperty . Name ) . Assign ( Value . Property ( nameof ( Nullable < int > . Value ) ) ) . Terminate ( )
151
+ }
152
+ } ;
153
+ setter . Add ( new IfElseStatement ( ifStatement , internalProperty . AsVariableExpression . Assign ( Null ) . Terminate ( ) ) ) ;
154
+ }
155
+ else
156
+ {
157
+ setter . Add ( new IfStatement ( internalPropertyExpression . Is ( Null ) )
158
+ {
159
+ internalPropertyExpression . Assign ( New . Instance ( innerModel . Type ! ) ) . Terminate ( )
160
+ } ) ;
161
+ setter . Add ( internalPropertyExpression . Property ( singleProperty . Name ) . Assign ( Value ) . Terminate ( ) ) ;
162
+ }
163
+ }
164
+ else
165
+ {
166
+ if ( isOverriddenValueType )
167
+ {
168
+ setter . Add ( internalPropertyExpression . Assign ( new TernaryConditionalExpression ( Value . Property ( nameof ( Nullable < int > . HasValue ) ) , new MemberExpression ( internalProperty , singleProperty . Name ) , Default ) ) . Terminate ( ) ) ;
169
+ }
170
+ else
171
+ {
172
+ setter . Add ( internalPropertyExpression . Assign ( New . Instance ( innerModel . Type , Value ) ) . Terminate ( ) ) ;
173
+ }
174
+ }
175
+ return setter ;
176
+ }
177
+
78
178
protected override TypeProvider ? VisitType ( TypeProvider type )
79
179
{
80
180
if ( type is ModelProvider model )
0 commit comments