1 /// a D name mangler. You probably do not need this, use the language's built in `.mangleof` property instead. I don't even remember why I wrote it. 2 module arsd.mangle; 3 4 import std.conv; 5 6 static immutable string[23] primitives = [ 7 "char", // a 8 "bool", // b 9 "creal", // c 10 "double", // d 11 "real", // e 12 "float", // f 13 "byte", // g 14 "ubyte", // h 15 "int", // i 16 "ireal", // j 17 "uint", // k 18 "long", // l 19 "ulong", // m 20 null, // n 21 "ifloat", // o 22 "idouble", // p 23 "cfloat", // q 24 "cdouble", // r 25 "short", // s 26 "ushort", // t 27 "wchar", // u 28 "void", // v 29 "dchar", // w 30 ]; 31 32 // FIXME: using this will allocate at *runtime*! Unbelievable. 33 // it does that even if everything is enum 34 auto dTokensPain() { 35 immutable p = cast(immutable(string[])) primitives[]; 36 string[] ret; 37 foreach(i; (sort!"a.length > b.length"( 38 p~ 39 [ 40 "(", 41 ")", 42 ".", 43 ",", 44 "!", 45 "[", 46 "]", 47 "*", 48 "const", 49 "immutable", 50 "shared", 51 "extern", 52 ]))) { ret ~= i; } 53 54 return ret; 55 } 56 57 static immutable string[] dTokens = dTokensPain(); 58 59 60 char manglePrimitive(in char[] t) { 61 foreach(i, p; primitives) 62 if(p == t) 63 return cast(char) ('a' + i); 64 return 0; 65 } 66 67 import std.algorithm; 68 import std.array; 69 70 bool isIdentifierChar(char c) { 71 // FIXME: match the D spec 72 return c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'); 73 } 74 75 struct StackArray(Type, size_t capacity) { 76 Type[capacity] buffer; 77 size_t length; 78 Type[] slice() { return buffer[0 .. length]; } 79 void opOpAssign(string op : "~")(Type rhs, string file = __FILE__, size_t line = __LINE__) { 80 if(length >= capacity) { 81 throw new Error("no more room", file, line); 82 } 83 buffer[length] = rhs; 84 length++; 85 } 86 } 87 88 char[] mangle(const(char)[] decl, char[] buffer) { 89 90 91 StackArray!(const(char)[], 128) tokensBuffer; 92 main: while(decl.length) { 93 if(decl[0] == ' ' || decl[0] == '\t' || decl[0] == '\n') { 94 decl = decl[1 .. $]; 95 continue; 96 } 97 98 foreach(token; dTokens) { 99 if(token is null) continue; 100 if(decl.length >= token.length && decl[0 .. token.length] == token) { 101 // make sure this isn't an identifier that coincidentally starts with a keyword 102 if(decl.length == token.length || !token[$ - 1].isIdentifierChar() || !decl[token.length].isIdentifierChar()) { 103 tokensBuffer ~= token; 104 decl = decl[token.length .. $]; 105 continue main; 106 } 107 } 108 } 109 110 // could be an identifier or literal 111 112 int pos = 0; 113 while(pos < decl.length && decl[pos].isIdentifierChar) 114 pos++; 115 tokensBuffer ~= decl[0 .. pos]; 116 decl = decl[pos .. $]; 117 continue main; 118 119 // FIXME: literals should be handled too 120 } 121 122 assert(decl.length == 0); // we should have consumed all the input into tokens 123 124 auto tokens = tokensBuffer.slice(); 125 126 127 char[64] returnTypeBuffer; 128 auto returnType = parseAndMangleType(tokens, returnTypeBuffer); 129 char[256] nameBuffer; 130 auto name = parseName(tokens, nameBuffer[]); 131 StackArray!(const(char)[], 64) arguments; 132 // FIXME: templates and other types of thing should be handled 133 assert(tokens[0] == "(", "other stuff not implemented " ~ tokens[0]); 134 tokens = tokens[1 .. $]; 135 136 char[64][32] argTypeBuffers; 137 int i = 0; 138 139 while(tokens[0] != ")") { 140 arguments ~= parseAndMangleType(tokens, argTypeBuffers[i]); 141 i++; 142 if(tokens[0] == ",") 143 tokens = tokens[1 .. $]; 144 } 145 146 assert(tokens[0] == ")", "other stuff not implemented"); 147 148 return mangleFunction(name, returnType, arguments.slice(), buffer); 149 } 150 151 char[] parseName(ref const(char)[][] tokens, char[] nameBuffer) { 152 size_t where = 0; 153 more: 154 nameBuffer[where .. where + tokens[0].length] = tokens[0][]; 155 where += tokens[0].length; 156 tokens = tokens[1 .. $]; 157 if(tokens.length && tokens[0] == ".") { 158 tokens = tokens[1 .. $]; 159 nameBuffer[where++] = '.'; 160 goto more; 161 } 162 163 return nameBuffer[0 .. where]; 164 } 165 166 char[] intToString(int i, char[] buffer) { 167 int pos = cast(int) buffer.length - 1; 168 169 if(i == 0) { 170 buffer[pos] = '0'; 171 pos--; 172 } 173 174 while(pos > 0 && i) { 175 buffer[pos] = (i % 10) + '0'; 176 pos--; 177 i /= 10; 178 } 179 180 return buffer[pos + 1 .. $]; 181 } 182 183 184 185 char[] mangleName(in char[] name, char[] buffer) { 186 import std.algorithm; 187 import std.conv; 188 189 auto parts = name.splitter("."); 190 191 int bufferPos = 0; 192 foreach(part; parts) { 193 char[16] numberBuffer; 194 auto number = intToString(cast(int) part.length, numberBuffer); 195 196 buffer[bufferPos .. bufferPos + number.length] = number[]; 197 bufferPos += number.length; 198 199 buffer[bufferPos .. bufferPos + part.length] = part[]; 200 bufferPos += part.length; 201 } 202 203 return buffer[0 .. bufferPos]; 204 } 205 206 char[] mangleFunction(in char[] name, in char[] returnTypeMangled, in char[][] argumentsMangle, char[] buffer) { 207 int bufferPos = 0; 208 buffer[bufferPos++] = '_'; 209 buffer[bufferPos++] = 'D'; 210 211 char[256] nameBuffer; 212 auto mn = mangleName(name, nameBuffer); 213 buffer[bufferPos .. bufferPos + mn.length] = mn[]; 214 bufferPos += mn.length; 215 216 buffer[bufferPos++] = 'F'; 217 foreach(arg; argumentsMangle) { 218 buffer[bufferPos .. bufferPos + arg.length] = arg[]; 219 bufferPos += arg.length; 220 } 221 buffer[bufferPos++] = 'Z'; 222 buffer[bufferPos .. bufferPos + returnTypeMangled.length] = returnTypeMangled[]; 223 bufferPos += returnTypeMangled.length; 224 225 return buffer[0 .. bufferPos]; 226 } 227 228 char[] parseAndMangleType(ref const(char)[][] tokens, char[] buffer) { 229 assert(tokens.length); 230 231 int bufferPos = 0; 232 233 void prepend(char p) { 234 for(int i = bufferPos; i > 0; i--) { 235 buffer[i] = buffer[i - 1]; 236 } 237 buffer[0] = p; 238 bufferPos++; 239 } 240 241 // FIXME: handle all the random D type constructors 242 if(tokens[0] == "const" || tokens[0] == "immutable") { 243 if(tokens[0] == "const") 244 buffer[bufferPos++] = 'x'; 245 else if(tokens[0] == "immutable") 246 buffer[bufferPos++] = 'y'; 247 tokens = tokens[1 .. $]; 248 assert(tokens[0] == "("); 249 tokens = tokens[1 .. $]; 250 auto next = parseAndMangleType(tokens, buffer[bufferPos .. $]); 251 bufferPos += next.length; 252 assert(tokens[0] == ")"); 253 tokens = tokens[1 .. $]; 254 } else { 255 char primitive = manglePrimitive(tokens[0]); 256 if(primitive) { 257 buffer[bufferPos++] = primitive; 258 tokens = tokens[1 .. $]; 259 } else { 260 // probably a struct or something, parse it as an identifier 261 // FIXME 262 char[256] nameBuffer; 263 auto name = parseName(tokens, nameBuffer[]); 264 265 char[256] mangledNameBuffer; 266 auto mn = mangleName(name, mangledNameBuffer); 267 268 buffer[bufferPos++] = 'S'; 269 buffer[bufferPos .. bufferPos + mn.length] = mn[]; 270 bufferPos += mn.length; 271 } 272 } 273 274 while(tokens.length) { 275 if(tokens[0] == "[") { 276 tokens = tokens[1 .. $]; 277 prepend('A'); 278 assert(tokens[0] == "]", "other array not implemented"); 279 tokens = tokens[1 .. $]; 280 } else if(tokens[0] == "*") { 281 prepend('P'); 282 tokens = tokens[1 .. $]; 283 } else break; 284 } 285 286 return buffer[0 .. bufferPos]; 287 } 288 289 version(unittest) { 290 int foo(int, string, int); 291 string foo2(long, char[], int); 292 struct S { int a; string b; } 293 S foo3(S, S, string, long, int, S, int[], char[][]); 294 long testcomplex(int, const(const(char)[]*)[], long); 295 } 296 297 unittest { 298 import core.demangle; 299 char[512] buffer; 300 301 import std.stdio; 302 assert(mangle(demangle(foo.mangleof), buffer) == foo.mangleof); 303 assert(mangle(demangle(foo2.mangleof), buffer) == foo2.mangleof); 304 assert(mangle(demangle(foo3.mangleof), buffer) == foo3.mangleof); 305 306 assert(mangle(demangle(testcomplex.mangleof), buffer) == testcomplex.mangleof); 307 // FIXME: these all fail if the functions are defined inside the unittest{} block 308 // so still something wrong parsing those complex names or something 309 } 310 311 // _D6test303fooFiAyaZi 312 // _D6test303fooFiAyaZi 313 314 version(unittest) 315 void main(string[] args) { 316 317 char[512] buffer; 318 import std.stdio; 319 if(args.length > 1) 320 writeln(mangle(args[1], buffer)); 321 else 322 writeln(mangle("int test30.foo(int, immutable(char)[])", buffer)); 323 //mangle("int test30.foo(int, immutable(char)[])", buffer); 324 }