1 module dvardumper.typevar;
2 
3 import std.meta : Alias;
4 import std.traits;
5 
6 template V(alias a)
7 {
8     enum string V = __traits(identifier, a);
9 }
10 
11 TypeVar toTypeVar(T)(T var, string varname = "")
12 {
13     import std.conv : to;
14 
15     TypeVar typeVar;
16 
17     static if (isAggregateType!T) {
18         typeVar = new AggregateTypeVar(typeid(var));
19         static foreach(member; __traits(allMembers, T))
20         {
21             /*
22             static if (!isAccessible!(T, member)) {
23                 pragma(msg, "Skipping non-accessible member " ~ T.stringof ~ "." ~ member);
24             } else */static if (isProperty!(T, member)) {
25                 pragma(msg, "Dumping " ~ T.stringof ~ "." ~ member);
26                 (cast(AggregateTypeVar)typeVar)
27                     .addField(__traits(getMember, var, member).toTypeVar(member));
28             }
29         }
30 
31     } else static if (isBasicType!T) {
32         typeVar = new BasicTypeVar(typeid(var))
33                        .value(var.to!string);
34     } else static if (isPointer!T) {
35         typeVar = new PointerTypeVar(typeid(var))
36                         .pointer(cast(void*)var);
37     } else static if (isArray!T) {
38         typeVar = new ArrayTypeVar(typeid(var))
39                         .elementCount(var.length)
40                         .elementSize(ArrayElementType!T.sizeof)
41                         .isPrintable(isSomeString!T)
42                         .array(cast(byte[])var);
43     } else {
44         typeVar = new UnknownTypeVar(typeid(var));
45     }
46 
47     typeVar.name(varname);
48 
49     return typeVar;
50 }
51 
52 @("Test basic type to TypeVar conversion")
53 unittest
54 {
55     import unit_threaded;
56 
57     int i = 42;
58     TypeVar typeVar = i.toTypeVar("ivar");
59 
60     auto basicTypeVar = cast(BasicTypeVar)typeVar;
61     basicTypeVar.shouldNotBeNull;
62 
63     basicTypeVar.name.should == "ivar";
64     basicTypeVar.typeName.should == "int";
65     basicTypeVar.size.should == int.sizeof;
66     basicTypeVar.value.should == "42";
67 }
68 
69 @("Test pointer type to TypeVar conversion")
70 unittest
71 {
72     import unit_threaded;
73 
74     int* i = new int(5);
75 
76     (*i).should == 5;
77 
78     TypeVar typeVar = i.toTypeVar("pointer");
79 
80     auto pointerTypeVar = cast(PointerTypeVar)typeVar;
81     pointerTypeVar.shouldNotBeNull;
82 
83     pointerTypeVar.name.should == "pointer";
84     pointerTypeVar.typeName.should == "int*";
85     pointerTypeVar.size.should == (int*).sizeof;
86 
87     int* pointer = cast(int*)pointerTypeVar.pointer;
88 
89     (cast(size_t)pointer).shouldBeGreaterThan(0);
90     (*pointer).should == 5;
91 }
92 
93 @("Test array type to TypeVar conversion")
94 unittest
95 {
96     import unit_threaded;
97 
98     int[] arr = [1,2,3];
99     TypeVar typeVar = arr.toTypeVar("arr_array");
100 
101     auto arrayTypeVar = cast(ArrayTypeVar)typeVar;
102     arrayTypeVar.shouldNotBeNull;
103 
104     arrayTypeVar.name.should == "arr_array";
105     arrayTypeVar.typeName.should == "int[]";
106     arrayTypeVar.size.should == size_t.sizeof + size_t.sizeof;
107     arrayTypeVar.elementCount.should == 3;
108     arrayTypeVar.elementSize.should == int.sizeof;
109     arrayTypeVar.isPrintable.should == false;
110 }
111 
112 @("Test unknown type to TypeVar conversion")
113 unittest
114 {
115     import unit_threaded;
116 
117     TypeVar typeVar = null.toTypeVar("unknown_type");
118 
119     auto unknownTypeVar = cast(UnknownTypeVar)typeVar;
120     unknownTypeVar.shouldNotBeNull;
121 
122     unknownTypeVar.name.should == "unknown_type";
123     unknownTypeVar.typeName.should == "typeof(null)";
124     unknownTypeVar.size.should == typeof(null).sizeof;
125 }
126 
127 struct S
128 {
129     int s1 = 4;
130     bool s2 = true;
131 }
132 
133 @("Test aggregate type to TypeVar conversion")
134 unittest
135 {
136     import unit_threaded;
137 
138     S s;
139     TypeVar typeVar = s.toTypeVar("s_struct");
140 
141     auto aggregateTypeVar = cast(AggregateTypeVar)typeVar;
142     aggregateTypeVar.shouldNotBeNull;
143 
144     aggregateTypeVar.name.should == "s_struct";
145     aggregateTypeVar.typeName.should == "dvardumper.typevar.S";
146     aggregateTypeVar.size.should == S.sizeof;
147     aggregateTypeVar.fields.length.should == 2;
148 }
149 
150 template isAccessible(T, string member)
151 {
152     enum isAccessible = __traits(getProtection, __traits(getMember, T, member)) == "public";
153 }
154 
155 template isProperty(T, string member)
156 {
157     alias fieldValue = Alias!(__traits(getMember, T, member));
158     enum isProperty = !isType!fieldValue && !isFunction!fieldValue && !__traits(isTemplate, fieldValue);
159 }
160 
161 template ArrayElementType(T : T[])
162 {
163   alias T ArrayElementType;
164 }
165 
166 abstract class TypeVar
167 {
168     protected:
169         TypeInfo _typeInfo;
170         string _name; // var name or field name
171         string _typeName;
172         size_t _size;
173 
174     public:
175         this(TypeInfo typeInfo)
176         {
177             import std.conv : to;
178 
179             this.typeName = typeInfo.to!string;
180             this.size = typeInfo.tsize;
181         }
182 
183         this(string typeName, size_t size)
184         {
185             this.typeName(typeName);
186             this.size(size);
187         }
188 
189         @property pure
190         string name()
191         {
192             return _name;
193         }
194 
195         @property pure
196         typeof(this) name(string name)
197         {
198             _name = name;
199 
200             return this;
201         }
202 
203         @property pure
204         string typeName()
205         {
206             return _typeName;
207         }
208 
209         @property pure
210         typeof(this) typeName(string typeName)
211         {
212             _typeName = typeName;
213 
214             return this;
215         }
216 
217         @property pure
218         size_t size()
219         {
220             return _size;
221         }
222 
223         @property pure
224         typeof(this) size(size_t size)
225         {
226             _size = size;
227 
228             return this;
229         }
230 }
231 
232 class BasicTypeVar : TypeVar
233 {
234     protected:
235         string _value;
236 
237     public:
238         this(TypeInfo typeInfo)
239         {
240             super(typeInfo);
241         }
242 
243         @property pure
244         string value()
245         {
246             return _value;
247         }
248 
249         @property pure
250         typeof(this) value(string value)
251         {
252             _value = value;
253 
254             return this;
255         }
256 }
257 
258 class PointerTypeVar : TypeVar
259 {
260     protected:
261         void* _pointer;
262 
263     public:
264         this(TypeInfo typeInfo)
265         {
266             super(typeInfo);
267         }
268 
269         @property pure
270         void* pointer()
271         {
272             return _pointer;
273         }
274 
275         @property pure
276         typeof(this) pointer(void* ptr)
277         {
278             _pointer = ptr;
279 
280             return this;
281         }
282 }
283 
284 class ArrayTypeVar : TypeVar
285 {
286     protected:
287         byte[] _array;
288         size_t _elementCount;
289         size_t _elementSize;
290         bool   _isPrintable;
291         size_t _maxPrintCount = 512;
292 
293     public:
294         this(TypeInfo typeInfo)
295         in
296         {
297             assert(cast(TypeInfo_Array)typeInfo !is null
298                 || cast(TypeInfo_StaticArray)typeInfo !is null);
299         }
300         body
301         {
302             super(typeInfo);
303         }
304 
305         @property pure
306         byte[] array()
307         {
308             return _array;
309         }
310 
311         @property pure
312         typeof(this) array(byte[] array)
313         {
314             _array = array;
315 
316             return this;
317         }
318 
319         size_t elementCount()
320         {
321             return _elementCount;
322         }
323 
324         typeof(this) elementCount(size_t count)
325         {
326             _elementCount = count;
327 
328             return this;
329         }
330 
331         size_t elementSize()
332         {
333             return _elementSize;
334         }
335 
336         typeof(this) elementSize(size_t size)
337         {
338             _elementSize = size;
339 
340             return this;
341         }
342 
343         bool isPrintable()
344         {
345             return _isPrintable;
346         }
347 
348         typeof(this) isPrintable(bool isPrintable)
349         {
350             _isPrintable = isPrintable;
351 
352             return this;
353         }
354 
355         size_t maxPrintCount()
356         {
357             return _maxPrintCount;
358         }
359 }
360 
361 class UnknownTypeVar : TypeVar
362 {
363     this(TypeInfo typeInfo)
364     {
365         super(typeInfo);
366     }
367 }
368 
369 class AggregateTypeVar : TypeVar
370 {
371     protected:
372         TypeVar[string] _fields;
373 
374     public:
375         this(TypeInfo typeInfo)
376         {
377             super(typeInfo);
378         }
379 
380         pure
381         void addField(TypeVar typeVar)
382         in
383         {
384             assert(typeVar.name != "");
385         }
386         body
387         {
388             _fields[typeVar.name] = typeVar;
389         }
390 
391         @property pure
392         auto fields()
393         {
394             return _fields;
395         }
396 }