1+ using Akka . Util ;
12using Neo . Cryptography . ECC ;
2- using Neo . SmartContract . Iterators ;
3+ using Neo . SmartContract . Testing . Attributes ;
34using Neo . VM . Types ;
45using System ;
56using System . Collections . Generic ;
@@ -11,6 +12,9 @@ namespace Neo.SmartContract.Testing.Extensions
1112{
1213 public static class TestExtensions
1314 {
15+ private static readonly Dictionary < Type , Dictionary < int , PropertyInfo > > _propertyCache = new ( ) ;
16+ private static readonly Dictionary < Type , FieldInfo [ ] > _fieldCache = new ( ) ;
17+
1418 /// <summary>
1519 /// Convert Array stack item to dotnet array
1620 /// </summary>
@@ -75,18 +79,84 @@ public static class TestExtensions
7579 _ when type == typeof ( UInt160 ) => new UInt160 ( stackItem . GetSpan ( ) . ToArray ( ) ) ,
7680 _ when type == typeof ( UInt256 ) => new UInt256 ( stackItem . GetSpan ( ) . ToArray ( ) ) ,
7781 _ when type == typeof ( ECPoint ) => ECPoint . FromBytes ( stackItem . GetSpan ( ) . ToArray ( ) , ECCurve . Secp256r1 ) ,
78- _ when type == typeof ( IDictionary < object , object > ) && stackItem is Map mp => ToDictionary ( mp ) , // SubItems in StackItem type
79- _ when type == typeof ( Dictionary < object , object > ) && stackItem is Map mp => ToDictionary ( mp ) , // SubItems in StackItem type
80- _ when type == typeof ( IList < object > ) && stackItem is CompoundType cp => new List < object > ( cp . SubItems ) , // SubItems in StackItem type
81- _ when type == typeof ( List < object > ) && stackItem is CompoundType cp => new List < object > ( cp . SubItems ) , // SubItems in StackItem type
8282 _ when typeof ( IInteroperable ) . IsAssignableFrom ( type ) => CreateInteroperable ( stackItem , type ) ,
83- _ when type . IsArray && stackItem is CompoundType cp => CreateTypeArray ( cp . SubItems , type . GetElementType ( ) ! ) ,
8483 _ when stackItem is InteropInterface it && it . GetInterface ( ) . GetType ( ) == type => it . GetInterface ( ) ,
8584
85+ _ when stackItem is VM . Types . Array ar => type switch
86+ {
87+ _ when type == typeof ( IList < object > ) => new List < object > ( ar . SubItems ) , // SubItems in StackItem type
88+ _ when type == typeof ( List < object > ) => new List < object > ( ar . SubItems ) , // SubItems in StackItem type
89+ _ when type . IsArray => CreateTypeArray ( ar . SubItems , type . GetElementType ( ) ! ) ,
90+ _ when type . IsClass => CreateObject ( ar . SubItems , type ) ,
91+ _ when type . IsValueType => CreateValueType ( ar . SubItems , type ) ,
92+ _ => throw new FormatException ( $ "Impossible to convert { stackItem } to { type } ") ,
93+ } ,
94+ _ when stackItem is Map mp => type switch
95+ {
96+ _ when type == typeof ( IDictionary < object , object > ) => ToDictionary ( mp ) , // SubItems in StackItem type
97+ _ when type == typeof ( Dictionary < object , object > ) => ToDictionary ( mp ) , // SubItems in StackItem type
98+ _ => throw new FormatException ( $ "Impossible to convert { stackItem } to { type } ") ,
99+ } ,
100+
86101 _ => throw new FormatException ( $ "Impossible to convert { stackItem } to { type } ") ,
87102 } ;
88103 }
89104
105+ private static object CreateObject ( IEnumerable < StackItem > subItems , Type type )
106+ {
107+ var index = 0 ;
108+ var obj = Activator . CreateInstance ( type ) ?? throw new FormatException ( $ "Impossible create { type } ") ;
109+
110+ // Cache the object properties by offset
111+
112+ if ( ! _propertyCache . TryGetValue ( type , out var cache ) )
113+ {
114+ cache = new Dictionary < int , PropertyInfo > ( ) ;
115+
116+ foreach ( var property in type . GetProperties ( ) )
117+ {
118+ var fieldOffset = property . GetCustomAttribute < FieldOrderAttribute > ( ) ;
119+ if ( fieldOffset is null ) continue ;
120+ if ( ! property . CanWrite ) continue ;
121+
122+ cache . Add ( fieldOffset . Order , property ) ;
123+ }
124+
125+ if ( cache . Count == 0 )
126+ {
127+ // Without FieldOrderAttribute, by order
128+
129+ foreach ( var property in type . GetProperties ( ) )
130+ {
131+ if ( ! property . CanWrite ) continue ;
132+ cache . Add ( index , property ) ;
133+ index ++ ;
134+ }
135+ index = 0 ;
136+ }
137+
138+ _propertyCache [ type ] = cache ;
139+ }
140+
141+ // Fill the object
142+
143+ foreach ( var item in subItems )
144+ {
145+ if ( cache . TryGetValue ( index , out var property ) )
146+ {
147+ property . SetValue ( obj , ConvertTo ( item , property . PropertyType ) ) ;
148+ }
149+ else
150+ {
151+ throw new FormatException ( $ "Error converting { type } , the property with the offset { index } was not found.") ;
152+ }
153+
154+ index ++ ;
155+ }
156+
157+ return obj ;
158+ }
159+
90160 private static IDictionary < object , object > ToDictionary ( Map map )
91161 {
92162 Dictionary < object , object > dictionary = new ( ) ;
@@ -99,6 +169,32 @@ private static IDictionary<object, object> ToDictionary(Map map)
99169 return dictionary ;
100170 }
101171
172+ private static object CreateValueType ( IEnumerable < StackItem > objects , Type valueType )
173+ {
174+ var arr = objects . ToArray ( ) ;
175+ var value = Activator . CreateInstance ( valueType ) ;
176+
177+ // Cache the object properties by offset
178+
179+ if ( ! _fieldCache . TryGetValue ( valueType , out var cache ) )
180+ {
181+ cache = valueType . GetFields ( ) . ToArray ( ) ;
182+ _fieldCache [ valueType ] = cache ;
183+ }
184+
185+ if ( cache . Length != arr . Length )
186+ {
187+ throw new FormatException ( $ "Error converting { valueType } , field count doesn't match.") ;
188+ }
189+
190+ for ( int x = 0 ; x < arr . Length ; x ++ )
191+ {
192+ cache [ x ] . SetValue ( value , ConvertTo ( arr [ x ] , cache [ x ] . FieldType ) ) ;
193+ }
194+
195+ return value ;
196+ }
197+
102198 private static object CreateTypeArray ( IEnumerable < StackItem > objects , Type elementType )
103199 {
104200 var obj = objects . ToArray ( ) ;
0 commit comments