001 package org.bridj.demangling;
002
003 import java.util.ArrayList;
004 import java.util.Arrays;
005 import java.util.List;
006
007 import org.bridj.CLong;
008 import org.bridj.NativeLibrary;
009 import org.bridj.demangling.Demangler.ClassRef;
010 import org.bridj.demangling.Demangler.DemanglingException;
011 import org.bridj.demangling.Demangler.Ident;
012 import org.bridj.demangling.Demangler.MemberRef;
013 import org.bridj.demangling.Demangler.NamespaceRef;
014 import org.bridj.demangling.Demangler.TypeRef;
015 import org.bridj.demangling.Demangler.SpecialName;
016 import java.util.Collections;
017 import java.util.HashMap;
018 import java.util.HashSet;
019 import java.util.Map;
020 import java.util.Set;
021 import org.bridj.demangling.Demangler.IdentLike;
022
023 public class GCC4Demangler extends Demangler {
024
025 public GCC4Demangler(NativeLibrary library, String symbol) {
026 super(library, symbol);
027 }
028 private Map<String, List<IdentLike>> prefixShortcuts = new HashMap<String, List<IdentLike>>() {
029
030 {
031
032 // prefix shortcut: e.g. St is for std::
033 put("t", Arrays.asList((IdentLike) new Ident("std")));
034 put("a", Arrays.asList((IdentLike) new Ident("std"), new Ident("allocator")));
035 put("b", Arrays.asList((IdentLike) new Ident("std"), new Ident("basic_string")));
036 TypeRef chartype = classType(Byte.TYPE);
037 ClassRef charTraitsOfChar = enclosed("std", new ClassRef(new Ident("char_traits", new TemplateArg[]{chartype})));
038 ClassRef allocatorOfChar = enclosed("std", new ClassRef(new Ident("allocator", new TemplateArg[]{chartype})));
039 put("d", Arrays.asList((IdentLike) new Ident("std"), new Ident("basic_iostream", new TemplateArg[]{chartype, charTraitsOfChar})));
040 put("i", Arrays.asList((IdentLike) new Ident("std"), new Ident("basic_istream", new TemplateArg[]{chartype, charTraitsOfChar})));
041 put("o", Arrays.asList((IdentLike) new Ident("std"), new Ident("basic_ostream", new TemplateArg[]{chartype, charTraitsOfChar})));
042 // Ss == std::string == std::basic_string<char, std::char_traits<char>, std::allocator<char> >
043 put("s", Arrays.asList((IdentLike) new Ident("std"), new Ident("basic_string", new TemplateArg[]{classType(Byte.TYPE), charTraitsOfChar, allocatorOfChar})));
044
045 // used, as an helper: for i in a b c d e f g h i j k l m o p q r s t u v w x y z; do c++filt _Z1_S$i; done
046 }
047
048 private ClassRef enclosed(String ns, ClassRef classRef) {
049 classRef.setEnclosingType(new NamespaceRef(new Ident(ns)));
050 return classRef;
051 }
052 };
053 private Set<String> shouldContinueAfterPrefix = new HashSet<String>(Arrays.asList("t"));
054 private Map<String, TypeRef> typeShortcuts = new HashMap<String, TypeRef>();
055
056 private <T> T ensureOfType(Object o, Class<T> type) throws DemanglingException {
057 if (type.isInstance(o)) {
058 return type.cast(o);
059 } else {
060 throw new DemanglingException("Internal error in demangler: trying to cast to " + type.getCanonicalName() + " the object '" + o.toString() + "'");
061 }
062 }
063 int nextShortcutId = -1;
064
065 private String nextShortcutId() {
066 int n = nextShortcutId++;
067 return n == -1 ? "_" : Integer.toString(n, 36).toUpperCase() + "_";
068 }
069
070 private TypeRef parsePointerType() throws DemanglingException {
071 TypeRef pointed = parseType();
072 TypeRef res = pointerType(pointed);
073 String id = nextShortcutId();
074 typeShortcuts.put(id, res);
075 return res;
076 }
077
078 public TemplateArg parseTemplateArg() throws DemanglingException {
079 if (consumeCharIf('L')) {
080 TypeRef tr = parseType();
081 StringBuffer b = new StringBuffer();
082 char c;
083 while (Character.isDigit(c = peekChar())) {
084 consumeChar();
085 b.append(c);
086 }
087 expectChars('E');
088 // TODO switch on type !
089 return new Constant(Integer.parseInt(b.toString()));
090 } else {
091 return parseType();
092 }
093 }
094
095 public TypeRef parseType() throws DemanglingException {
096 if (Character.isDigit(peekChar())) {
097 Ident name = ensureOfType(parseNonCompoundIdent(), Ident.class);
098 String id = nextShortcutId(); // we get the id before parsing the part (might be template parameters and we need to get the ids in the right order)
099 TypeRef res = simpleType(name);
100 typeShortcuts.put(id, res);
101 return res;
102 }
103
104 char c = consumeChar();
105 switch (c) {
106 case 'S': { // here we first check if we have a type shorcut saved, if not we fallback to the (compound) identifier case
107 char cc = peekChar();
108 int delta = 0;
109 if (Character.isDigit(cc) || Character.isUpperCase(cc) || cc == '_') {
110 String id = "";
111 while ((c = peekChar()) != '_' && c != 0) {
112 id += consumeChar();
113 delta++;
114 }
115 if (peekChar() == 0) {
116 throw new DemanglingException("Encountered a unexpected end in gcc mangler shortcut '" + id + "' " + prefixShortcuts.keySet());
117 }
118 id += consumeChar(); // the '_'
119 delta++;
120 if (typeShortcuts.containsKey(id)) {
121 if (peekChar() != 'I') {
122 // just a shortcut
123 return typeShortcuts.get(id);
124 } else {
125 // shortcut but templated
126 List<IdentLike> nsPath = new ArrayList<IdentLike>(prefixShortcuts.get(id));
127 String templatedId = parsePossibleTemplateArguments(nsPath);
128 if (templatedId != null) {
129 return typeShortcuts.get(templatedId);
130 }
131 }
132 }
133 position -= delta;
134 }
135 }
136 // WARNING/INFO/NB: we intentionally continue to the N case
137 case 'N':
138 position--; // I actually would peek()
139 {
140 List<IdentLike> ns = new ArrayList<IdentLike>();
141 String newShortcutId = parseSimpleOrComplexIdentInto(ns, false);
142 ClassRef res = new ClassRef(ensureOfType(ns.remove(ns.size() - 1), Ident.class));
143 if (!ns.isEmpty()) {
144 res.setEnclosingType(new NamespaceRef(ns.toArray()));
145 }
146 if (newShortcutId != null) {
147 typeShortcuts.put(newShortcutId, res);
148 }
149 return res;
150 }
151 case 'P':
152 return parsePointerType();
153 case 'F':
154 // TODO parse function type correctly !!!
155 while (consumeChar() != 'E') {
156 }
157
158 return null;
159 case 'K':
160 return parseType();
161 case 'v': // char
162 return classType(Void.TYPE);
163 case 'c':
164 case 'a':
165 case 'h': // unsigned
166 return classType(Byte.TYPE);
167 case 'b': // bool
168 return classType(Boolean.TYPE);
169 case 'l':
170 case 'm': // unsigned
171 return classType(CLong.class);
172 //return classType(Platform.is64Bits() ? Long.TYPE : Integer.TYPE);
173 case 'x':
174 case 'y': // unsigned
175 return classType(Long.TYPE);
176 case 'i':
177 case 'j': // unsigned
178 return classType(Integer.TYPE);
179 case 's':
180 case 't': // unsigned
181 return classType(Short.TYPE);
182 case 'f':
183 return classType(Float.TYPE);
184 case 'd':
185 return classType(Double.TYPE);
186 case 'z': // varargs
187 return classType(Object[].class);
188 default:
189 throw error("Unexpected type char '" + c + "'", -1);
190 }
191 }
192
193 String parseName() throws DemanglingException { // parses a plain name, e.g. "4plop" (the 4 is the length)
194 char c;
195 StringBuilder b = new StringBuilder();
196 while (Character.isDigit(c = peekChar())) {
197 consumeChar();
198 b.append(c);
199 }
200 int len;
201 try {
202 len = Integer.parseInt(b.toString());
203 } catch (NumberFormatException ex) {
204 throw error("Expected a number", 0);
205 }
206 b.setLength(0);
207 for (int i = 0; i < len; i++) {
208 b.append(consumeChar());
209 }
210 return b.toString();
211 }
212
213 private String parseSimpleOrComplexIdentInto(List<IdentLike> res, boolean isParsingNonShortcutableElement) throws DemanglingException {
214 String newlyAddedShortcutForThisType = null;
215 boolean shouldContinue = false;
216 boolean expectEInTheEnd = false;
217 if (consumeCharIf('N')) { // complex (NB: they don't recursively nest (they actually can within a template parameter but not elsewhere))
218 if (consumeCharIf('S')) { // it uses some shortcut prefix or type
219 parseShortcutInto(res);
220 }
221 shouldContinue = true;
222 expectEInTheEnd = true;
223 } else { // simple
224 if (consumeCharIf('S')) { // it uses some shortcut prefix or type
225 shouldContinue = parseShortcutInto(res);
226 } else {
227 res.add(parseNonCompoundIdent());
228 }
229 }
230 if (shouldContinue) {
231 do {
232 String id = nextShortcutId(); // we get the id before parsing the part (might be template parameters and we need to get the ids in the right order)
233 newlyAddedShortcutForThisType = id;
234 IdentLike part = parseNonCompoundIdent();
235 res.add(part);
236 prefixShortcuts.put(id, new ArrayList<IdentLike>(res)); // the current compound name is saved by gcc as a shortcut (we do the same)
237 parsePossibleTemplateArguments(res);
238 } while (Character.isDigit(peekChar()) || peekChar() == 'C' || peekChar() == 'D');
239 if (isParsingNonShortcutableElement) {
240 //prefixShortcuts.remove(previousShortcutId()); // correct the fact that we parsed one too much
241 nextShortcutId--;
242 }
243 }
244 parsePossibleTemplateArguments(res);
245 if (expectEInTheEnd) {
246 expectAnyChar('E');
247 }
248 return newlyAddedShortcutForThisType;
249 }
250
251 /**
252 *
253 * @param res a list of identlikes with the namespace elements and finished with an Ident which will be replaced by a new one enriched with template info
254 * @return null if res was untouched, or the new id created because of the presence of template arguments
255 */
256 private String parsePossibleTemplateArguments(List<IdentLike> res) throws DemanglingException {
257 if (consumeCharIf('I')) {
258 List<TemplateArg> args = new ArrayList<TemplateArg>();
259 while (!consumeCharIf('E')) {
260 args.add(parseTemplateArg());
261 }
262 String id = nextShortcutId(); // we get the id after parsing the template parameters
263 // It is very important that we create a new Ident as the other one has most probably been added as a shortcut and should be immutable from then
264 Ident templatedIdent = new Ident(ensureOfType(res.remove(res.size() - 1), Ident.class).toString(), args.toArray(new TemplateArg[args.size()]));
265 res.add(templatedIdent);
266 prefixShortcuts.put(id, new ArrayList<IdentLike>(res));
267 {
268 List<IdentLike> ns = new ArrayList<IdentLike>(res);
269 ClassRef clss = new ClassRef(ensureOfType(ns.remove(ns.size() - 1), Ident.class));
270 if (!ns.isEmpty()) {
271 clss.setEnclosingType(new NamespaceRef(ns.toArray()));
272 }
273 typeShortcuts.put(id, clss);
274 }
275 return id;
276 }
277 return null;
278 }
279
280 /**
281 * @return whether we should expect more parsing after this shortcut (e.g. std::vector<...> is actually not NSt6vectorI...EE but St6vectorI...E (without trailing N)
282 */
283 private boolean parseShortcutInto(List<IdentLike> res) throws DemanglingException {
284 char c = peekChar();
285 // GCC builds shortcuts for each encountered type, they appear in the mangling as: S_, S0_, S1_, ..., SA_, SB_, ..., SZ_, S10_
286 if (c == '_') { // we encounter S_
287 List<IdentLike> toAdd = prefixShortcuts.get(Character.toString(consumeChar()));
288 if (toAdd == null) {
289 throw new DemanglingException("Encountered a yet undefined gcc mangler shortcut S_ (first one), i.e. '_' " + prefixShortcuts.keySet());
290 }
291 res.addAll(toAdd);
292 return false;
293 } else if (Character.isDigit(c) || Character.isUpperCase(c)) { // memory shorcut S[0-9A-Z]+_
294 String id = "";
295 while ((c = peekChar()) != '_' && c != 0) {
296 id += consumeChar();
297 }
298 if (peekChar() == 0) {
299 throw new DemanglingException("Encountered a unexpected end in gcc mangler shortcut '" + id + "' " + prefixShortcuts.keySet());
300 }
301 id += consumeChar(); // the '_'
302 List<IdentLike> toAdd = prefixShortcuts.get(id);
303 if (toAdd == null) {
304 throw new DemanglingException("Encountered a unexpected gcc mangler shortcut '" + id + "' " + prefixShortcuts.keySet());
305 }
306 res.addAll(toAdd);
307 return false;
308 } else if (Character.isLowerCase(c)) { // other, single character built-in shorcuts. We suppose for now that all shortcuts are lower case (e.g. Ss, St, ...)
309 String id = Character.toString(consumeChar());
310 List<IdentLike> toAdd = prefixShortcuts.get(id);
311 if (toAdd == null) {
312 throw new DemanglingException("Encountered a unexpected gcc mangler built-in shortcut '" + id + "' " + prefixShortcuts.keySet());
313 }
314 res.addAll(toAdd);
315 return shouldContinueAfterPrefix.contains(id);
316 } else {
317 throw new DemanglingException("Encountered a unexpected gcc unknown shortcut '" + c + "' " + prefixShortcuts.keySet());
318 }
319 }
320
321 IdentLike parseNonCompoundIdent() throws DemanglingException { // This is a plain name with possible template parameters (or special like constructor C1, C2, ...)
322 if (consumeCharIf('C')) {
323 if (consumeCharIf('1')) {
324 return SpecialName.Constructor;
325 } else if (consumeCharIf('2')) {
326 return SpecialName.SpecialConstructor;
327 } else {
328 throw error("Unknown constructor type 'C" + peekChar() + "'");
329 }
330 } else if (consumeCharIf('D')) {
331 // see http://zedcode.blogspot.com/2007/02/gcc-c-link-problems-on-small-embedded.html
332 if (consumeCharIf('0')) {
333 return SpecialName.DeletingDestructor;
334 } else if (consumeCharIf('1')) {
335 return SpecialName.Destructor;
336 } else if (consumeCharIf('2')) {
337 return SpecialName.SelfishDestructor;
338 } else {
339 throw error("Unknown destructor type 'D" + peekChar() + "'");
340 }
341 } else {
342 String n = parseName();
343 return new Ident(n);
344 }
345 }
346
347 @Override
348 public MemberRef parseSymbol() throws DemanglingException {
349 MemberRef mr = new MemberRef();
350 if (!consumeCharIf('_')) {
351 mr.setMemberName(new Ident(str));
352 return mr;
353 }
354 consumeCharIf('_');
355 expectChars('Z');
356
357 if (consumeCharIf('T')) {
358 if (consumeCharIf('V')) {
359 mr.setEnclosingType(ensureOfType(parseType(), ClassRef.class));
360 mr.setMemberName(SpecialName.VFTable);
361 return mr;
362 }
363 return null; // can be a type info, a virtual table or strange things like that
364 }
365 /*
366 Reverse engineering of C++ operators :
367 delete[] = __ZdaPv
368 delete = __ZdlPv
369 new[] = __Znam
370 new = __Znwm
371 */
372 if (consumeCharsIf('d', 'l', 'P', 'v')) {
373 mr.setMemberName(SpecialName.Delete);
374 return mr;
375 }
376 if (consumeCharsIf('d', 'a', 'P', 'v')) {
377 mr.setMemberName(SpecialName.DeleteArray);
378 return mr;
379 }
380 if (consumeCharsIf('n', 'w', 'm')) {
381 mr.setMemberName(SpecialName.New);
382 return mr;
383 }
384 if (consumeCharsIf('n', 'a', 'm')) {
385 mr.setMemberName(SpecialName.NewArray);
386 return mr;
387 }
388
389 {
390 List<IdentLike> ns = new ArrayList<IdentLike>();
391 parseSimpleOrComplexIdentInto(ns, true);
392 mr.setMemberName(ns.remove(ns.size() - 1));
393 if (!ns.isEmpty()) {
394 ClassRef parent = new ClassRef(ensureOfType(ns.remove(ns.size() - 1), Ident.class));
395 if (mr.getMemberName() == SpecialName.Constructor || mr.getMemberName() == SpecialName.SpecialConstructor)
396 typeShortcuts.put(nextShortcutId(), parent);
397 if (!ns.isEmpty()) {
398 parent.setEnclosingType(new NamespaceRef(ns.toArray()));
399 }
400 mr.setEnclosingType(parent);
401 }
402 }
403
404 //System.out.println("mr = " + mr + ", peekChar = " + peekChar());
405
406 //mr.isStatic =
407 //boolean isMethod = consumeCharIf('E');
408
409 if (consumeCharIf('v')) {
410 if (position < length) {
411 error("Expected end of symbol", 0);
412 }
413 mr.paramTypes = new TypeRef[0];
414 } else {
415 List<TypeRef> paramTypes = new ArrayList<TypeRef>();
416 while (position < length) {// && !consumeCharIf('E')) {
417 paramTypes.add(parseType());
418 }
419 mr.paramTypes = paramTypes.toArray(new TypeRef[paramTypes.size()]);
420 }
421 return mr;
422 }
423 }