1 /**
2 Structure to describe D classes with properties dynamically, to avoid generating Wren code ahead of time. Also saves some D code for properties.
3 
4 Copyright: Guillaume Piolat 2021.
5 License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
6 */
7 module dplug.wren.describe;
8 
9 import core.stdc.stdlib: malloc, free;
10 import dplug.core.vec;
11 
12 nothrow @nogc:
13 
14 // For the usage of WrenSupport, describe D classes in a way that can be generated at compile-time. 
15 
16 /// Note: WrenSupport must be able to generate the module, foreign classes, and foreign methods from this description.
17 class ScriptExportClass
18 {
19 nothrow @nogc:
20 public:
21 
22     this(TypeInfo_Class classInfo)
23     {
24         _concreteClassInfo = classInfo;
25 
26         // build wren identifier
27         string fullName = concreteClassInfo.name;
28         int LEN = cast(int)fullName.length;
29 
30         // try to find rightmost '.'
31         string strippedName = fullName;
32         for(int n = LEN - 1; n >= 0; --n)
33         {
34             if (fullName[n] == '.')
35             {
36                 strippedName = fullName[n+1..$];
37                 break;
38             }
39         }
40 
41         _wrenClassName = convertDStringToCString(strippedName);
42     }
43 
44     ~this()
45     {
46         free(_wrenClassName);
47     }
48 
49     /// The full D identifier of the class, with module identifiers.
50     /// eg: dplug.pbkwidgets.knob.UIKnob
51     string fullClassName()
52     {
53         return concreteClassInfo.name;
54     }
55 
56     /// Its .classinfo
57     TypeInfo_Class concreteClassInfo()
58     {
59         return _concreteClassInfo;
60     }
61 
62     ScriptPropertyDesc[] properties()
63     {
64         return _properties[];
65     }
66 
67     /// The identifier of the class in Wren code. It is stripped from module identifiers.
68     /// Zero-terminated.
69     /// eg: "UIKnob"
70     const(char)* wrenClassNameZ()
71     {
72         return _wrenClassName;
73     }
74 
75     void addProperty(ScriptPropertyDesc prop)
76     {
77         prop.nth = cast(int)_properties.length;
78         _properties ~= prop;
79     }
80 
81 private:
82 
83     char* _wrenClassName;
84 
85     /// Its .classinfo
86     TypeInfo_Class _concreteClassInfo; // the D ClassInfo of the D class named by fullClassName()
87 
88 
89     Vec!ScriptPropertyDesc _properties;
90 }
91 
92 /// All possible types of properties.
93 enum ScriptPropertyType
94 {
95     bool_,
96 
97     byte_,
98     ubyte_,
99     short_,
100     ushort_,
101     int_,
102     uint_,
103 
104     float_,
105     double_,
106 
107     // Color types from dplug:graphics
108     RGBA,
109 }
110 
111 struct ScriptPropertyDesc
112 {
113 nothrow @nogc:
114 public:
115     // type enumeration
116     ScriptPropertyType type;
117 
118     /// Byte offset in the class.
119     int offset;
120 
121     /// Number of the property in the _properties array.
122     int nth;
123 
124     /// The D identifier of the property.
125     /// Not owned, has to be compile-time.
126     string identifier;
127     
128     size_t sizeInInstance()
129     {
130         return SCRIPT_PROPERTY_SIZES[type];
131     }
132 
133 private:
134 }
135 
136 
137 private:
138 
139 static immutable size_t[ScriptPropertyType.max+1] SCRIPT_PROPERTY_SIZES =
140 [ 1, 1, 1, 2, 2, 4, 4, 4, 8, 4];
141 
142 
143 /// Allocated a zero-terminated C string from a D string. A copy is always made, and has to be released with free.
144 char* convertDStringToCString(const(char)[] cstr) nothrow @nogc
145 {
146     assert(cstr !is null);
147     size_t len = cstr.length;
148     char* copy = cast(char*) malloc(len + 1);
149     copy[0..len] = cstr[0..len];
150     copy[len] = '\0';
151     return copy;
152 }