001/*
002 * BridJ - Dynamic and blazing-fast native interop for Java.
003 * http://bridj.googlecode.com/
004 *
005 * Copyright (c) 2010-2013, Olivier Chafik (http://ochafik.com/)
006 * All rights reserved.
007 *
008 * Redistribution and use in source and binary forms, with or without
009 * modification, are permitted provided that the following conditions are met:
010 * 
011 *     * Redistributions of source code must retain the above copyright
012 *       notice, this list of conditions and the following disclaimer.
013 *     * Redistributions in binary form must reproduce the above copyright
014 *       notice, this list of conditions and the following disclaimer in the
015 *       documentation and/or other materials provided with the distribution.
016 *     * Neither the name of Olivier Chafik nor the
017 *       names of its contributors may be used to endorse or promote products
018 *       derived from this software without specific prior written permission.
019 * 
020 * THIS SOFTWARE IS PROVIDED BY OLIVIER CHAFIK AND CONTRIBUTORS ``AS IS'' AND ANY
021 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
022 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
023 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
024 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
025 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
026 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
027 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
028 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
029 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
030 */
031package org.bridj.demangling;
032
033import org.bridj.ann.Convention.Style;
034import java.lang.reflect.*;
035import java.util.ArrayList;
036import java.util.Collections;
037import java.util.List;
038
039import org.bridj.NativeLibrary;
040import org.bridj.demangling.Demangler.ClassRef;
041import org.bridj.demangling.Demangler.DemanglingException;
042import org.bridj.demangling.Demangler.MemberRef;
043import org.bridj.demangling.Demangler.NamespaceRef;
044import org.bridj.demangling.Demangler.Ident;
045import org.bridj.demangling.Demangler.IdentLike;
046import org.bridj.demangling.Demangler.TypeRef;
047import org.bridj.demangling.Demangler.SpecialName;
048import org.bridj.CLong;
049import org.bridj.ann.Convention;
050import java.math.BigInteger;
051import java.util.Collection;
052
053public class VC9Demangler extends Demangler {
054
055    public VC9Demangler(NativeLibrary library, String str) {
056        super(library, str);
057    }
058
059    private AccessLevelAndStorageClass parseAccessLevelAndStorageClass() throws DemanglingException {
060        AccessLevelAndStorageClass ac = new AccessLevelAndStorageClass();
061        switch (consumeChar()) {
062            case 'A':
063            case 'B':
064                ac.modifiers = Modifier.PRIVATE;
065                break;
066            case 'C':
067            case 'D':
068                ac.modifiers = Modifier.PRIVATE | Modifier.STATIC;
069                break;
070            case 'E':
071            case 'F':
072                ac.modifiers = Modifier.PRIVATE;
073                ac.isVirtual = true;
074                break;
075            case 'G':
076            case 'H':
077                ac.modifiers = Modifier.PRIVATE;
078                ac.isThunk = true;
079                break;
080            case 'I':
081            case 'J':
082                ac.modifiers = Modifier.PROTECTED;
083                break;
084            case 'K':
085            case 'L':
086                ac.modifiers = Modifier.PROTECTED | Modifier.STATIC;
087                break;
088            case 'M':
089            case 'N':
090                ac.modifiers = Modifier.PROTECTED;
091                ac.isVirtual = true;
092                break;
093            case 'O':
094            case 'P':
095                ac.modifiers = Modifier.PROTECTED;
096                ac.isThunk = true;
097                break;
098            case 'Q':
099            case 'R':
100                ac.modifiers = Modifier.PUBLIC;
101                break;
102            case 'S':
103            case 'T':
104                ac.modifiers = Modifier.PUBLIC | Modifier.STATIC;
105                break;
106            case 'U':
107            case 'V':
108                ac.modifiers = Modifier.PUBLIC;
109                ac.isVirtual = true;
110                break;
111            case 'W':
112            case 'X':
113                ac.modifiers = Modifier.PUBLIC;
114                ac.isThunk = true;
115                break;
116            case 'Y':
117            case 'Z':
118                // No modifier, no storage class
119                ac.modifiers = 0;
120                break;
121            default:
122                throw error("Unknown access level + storage class");
123        }
124        return ac;
125    }
126
127    private ClassRef parseTemplateType() throws DemanglingException {
128        //System.out.println("# START OF parseTemplateParams()");
129        String name = parseNameFragment();
130        //return withEmptyQualifiedNames(new DemanglingOp<ClassRef>() { public ClassRef run() throws DemanglingException {
131        List<TemplateArg> args = parseTemplateParams();
132
133        List<Object> names = parseNameQualifications();
134
135        //String ns = parseNameFragment();
136
137        //System.out.println("parseTemplateParams() = " + args + ", ns = " + names);
138        ClassRef tr = new ClassRef(new Ident(name, args.toArray(new TemplateArg[args.size()])));
139        tr.setEnclosingType(reverseNamespace(names));
140
141        addBackRef(tr);
142
143        return tr;
144        //}});
145    }
146
147    private void parseFunctionProperty(MemberRef mr) throws DemanglingException {
148        mr.callingConvention = parseCallingConvention();
149        TypeRef returnType = consumeCharIf('@') ? classType(void.class) : parseType(true);
150//        allQualifiedNames.clear();
151        //withEmptyQualifiedNames(new DemanglingRunnable() { public void run() throws DemanglingException {
152        List<TypeRef> paramTypes = parseParams();
153        mr.paramTypes = paramTypes.toArray(new TypeRef[paramTypes.size()]);
154        if (!consumeCharIf('Z')) {
155            List<TypeRef> throwTypes = parseParams();
156            mr.throwTypes = throwTypes.toArray(new TypeRef[throwTypes.size()]);
157        }
158
159        mr.setValueType(returnType);
160        //}});
161    }
162
163    static class AnonymousTemplateArg implements TemplateArg {
164
165        public AnonymousTemplateArg(String v) {
166            this.v = v;
167        }
168        String v;
169
170        //@Override
171        public boolean matchesParam(Object param, Annotations annotations) {
172            return true; // TODO wtf ?
173        }
174
175        @Override
176        public String toString() {
177            return v;
178        }
179    }
180
181    private TemplateArg parseTemplateParameter() throws DemanglingException {
182        switch (peekChar()) {
183            case '?':
184                consumeChar();
185                return new AnonymousTemplateArg("'anonymous template param " + parseNumber(false) + "'");
186            case '$':
187                consumeChar();
188                switch (consumeChar()) {
189                    case '0':
190                        return new Constant(parseNumber(true));
191                    case '2':
192                        int a = parseNumber(true);
193                        int b = parseNumber(true);
194                        return new Constant(a * Math.exp(10 * (int) Math.log(b - Math.log10(a) + 1)));
195                    case 'D':
196                        return new AnonymousTemplateArg("'anonymous template param " + parseNumber(false) + "'");
197                    case 'F':
198                        return new AnonymousTemplateArg("'tuple (" + parseNumber(true) + ", " + parseNumber(true) + ")'");
199                    case 'G':
200                        return new AnonymousTemplateArg("'tuple (" + parseNumber(true) + ", " + parseNumber(true) + ", " + parseNumber(true) + ")'");
201                    case 'Q':
202                        return new AnonymousTemplateArg("'anonymous non-type template param " + parseNumber(false) + "'");
203                }
204                break;
205            default:
206            //error("Unexpected template param value");
207        }
208        return parseType(true);
209    }
210
211    static class AccessLevelAndStorageClass {
212
213        int modifiers;
214        boolean isVirtual = false, isThunk = false;
215    }
216
217    public MemberRef parseSymbol() throws DemanglingException {
218        MemberRef mr = new MemberRef();
219
220        int iAt = str.indexOf('@');
221        if (iAt >= 0 && consumeCharIf('_')) {
222            if (iAt > 0) {
223                mr.setMemberName(new Ident(str.substring(1, iAt)));
224                mr.setArgumentsStackSize(Integer.parseInt(str.substring(iAt + 1)));
225                return mr;
226            }
227        }
228        
229        if (!consumeCharIf('@', '?'))
230            return null;
231
232        IdentLike memberName = parseFirstQualifiedTypeNameComponent();
233        if (memberName instanceof SpecialName) {
234            SpecialName specialName = (SpecialName) memberName;
235            if (!specialName.isFunction()) {
236                return null;
237            }
238        }
239        mr.setMemberName(memberName);
240        List<Object> qNames = parseNameQualifications();
241
242        //TypeRef qualifiedName = parseQualifiedTypeName();
243
244        AccessLevelAndStorageClass ac = parseAccessLevelAndStorageClass();
245        CVClassModifier cvMod = null;
246        if (ac.modifiers != 0 && !Modifier.isStatic(ac.modifiers)) {
247            cvMod = parseCVClassModifier();
248        }
249
250        // Function property :
251        //allQualifiedNames.clear(); // TODO fix this !!
252        TypeRef encl;
253        if (cvMod != null && (cvMod.isMember || (memberName instanceof SpecialName) || Modifier.isPublic(ac.modifiers))) {
254            Object r = qNames.get(0);
255            ClassRef tr = r instanceof ClassRef ? (ClassRef) r : new ClassRef(new Ident((String) r));
256            //tr.setSimpleName(qNames.get(0));
257            qNames.remove(0);
258            tr.setEnclosingType(reverseNamespace(qNames));
259            encl = tr;
260        } else {
261            encl = reverseNamespace(qNames);
262        }
263
264        addBackRef(encl);
265        mr.setEnclosingType(encl);
266
267        parseFunctionProperty(mr);
268
269        if (position != length) {
270            error("Failed to demangle the whole symbol");
271        }
272        return mr;
273    }
274
275    TypeRef parseReturnType() throws DemanglingException {
276        TypeRef tr = parseType(true);
277        return tr;
278    }
279
280    int parseNumber(boolean allowSign) throws DemanglingException {
281        int sign = allowSign && consumeCharIf('?') ? -1 : 1;
282        if (Character.isDigit(peekChar())) {
283            char c = consumeChar();
284            return sign * (int) (c - '0');
285        }
286        if (peekChar() == '@') {
287            return 0;
288        }
289
290        char c;
291        StringBuilder b = new StringBuilder();
292        long n = 0;
293        while (((c = consumeChar()) >= 'A' && c <= 'P') && c != '@') {
294            n += 16 * (c - 'A');
295        }
296
297        String s = b.toString().trim();
298        if (c != '@' || s.length() == 0) {
299            throw error("Expected a number here", -b.length());
300        }
301        return sign * Integer.parseInt(s, 16);
302    }
303
304    TypeRef consumeIfBackRef() throws DemanglingException {
305        char c = peekChar();
306        if (Character.isDigit(c)) {
307            consumeChar();
308            int iBack = (int) (c - '0');
309            return getBackRef(iBack);
310        }
311        return null;
312    }
313
314    TypeRef parseType(boolean allowVoid) throws DemanglingException {
315        TypeRef backRef = consumeIfBackRef();
316        if (backRef != null) {
317            return backRef;
318        }
319
320        char c = consumeChar();
321        switch (c) {
322            case '_':
323                TypeRef tr;
324                switch (consumeChar()) {
325                    case 'D': // __int8
326                    case 'E': // unsigned __int8
327                        tr = classType(byte.class);
328                        break;
329                    case 'F': // __int16
330                    case 'G': // unsigned __int16
331                        tr = classType(short.class);
332                        break;
333                    case 'H': // __int32
334                    case 'I': // unsigned __int32
335                        tr = classType(int.class);
336                        break;
337                    case 'J': // __int64
338                    case 'K': // unsigned __int64
339                        tr = classType(long.class);
340                        break;
341                    case 'L': // __int128
342                        tr = classType(BigInteger.class);
343                        break;
344                    case 'N': // bool
345                        tr = classType(boolean.class);
346                        break;
347                    case '0': // array ??
348                        parseCVClassModifier();
349                        parseType(false);
350                        tr = classType(Object[].class);
351                        break;
352                    case 'W':
353                        tr = classType(char.class);//, Wide.class);
354                        break;
355                    default:
356                        throw error(-1);
357                }
358                addBackRef(tr);
359                return tr;
360            case 'Z':
361                return classType(Object[].class);
362            case 'O':
363                throw error("'long double' type cannot be mapped !", -1);
364            case 'C': // signed char
365            case 'D': // char
366            case 'E': // unsigned char
367                return classType(byte.class);
368            case 'F': // short
369            case 'G': // unsigned short
370                return classType(short.class);
371            case 'H': // int
372            case 'I': // unsigned int
373                return classType(int.class);
374            case 'J': // long
375            case 'K': // unsigned long
376                return classType(CLong.class);
377            case 'M': // float
378                return classType(float.class);
379            case 'N': // double
380                return classType(double.class);
381            case 'Y':
382                throw error("TODO handle cointerfaces", -1);
383            case 'X':
384                // TODO handle coclass case
385                if (!allowVoid) {
386                    return null;
387                }
388                return classType(void.class);
389            case '?':
390                parseCVClassModifier(); // TODO do something with this !
391                return parseType(allowVoid);
392            case 'A': // reference
393            case 'B': // volatile reference
394            case 'P': // pointer
395            case 'Q': // const pointer
396            case 'R': // volatile pointer
397            case 'S': // const volatile pointer
398                boolean isReference = c == 'A' || c == 'B';
399                boolean isConst = c == 'Q' || c == 'S';
400                if (!consumeCharsIf('$', 'A')) // __gc
401                {
402                    consumeCharsIf('$', 'B');  // __pin
403                }
404                CVClassModifier cvMods = parseCVClassModifier();
405                if (cvMods.isVariable) {
406                    if (consumeCharIf('Y')) {
407                        int dimensions = parseNumber(false);
408                        int[] indices = new int[dimensions];
409                        for (int i = 0; i < dimensions; i++) {
410                            indices[i] = parseNumber(false);
411                        }
412                    }
413                    tr = pointerType(parseType(true), isConst, isReference);
414                } else {
415                    MemberRef mr = new MemberRef();
416                    parseFunctionProperty(mr);
417                    tr = pointerType(new FunctionTypeRef(mr), isConst, isReference);
418                }
419                addBackRef(tr);
420                return tr;
421            case 'V': // class
422            case 'U': // struct
423            case 'T': // union
424                //System.out.println("Found struct, class or union");
425                return parseQualifiedTypeName();
426            case 'W': // enum
427                Class<?> cl;
428                switch (consumeChar()) {
429                    case '0':
430                    case '1':
431                        cl = byte.class;
432                        break;
433                    case '2':
434                    case '3':
435                        cl = short.class;
436                        break;
437                    case '4':
438                    case '5':
439                        cl = int.class;
440                        break;
441                    case '6':
442                    case '7': // CLong : int on win32 and win64 !
443                        cl = int.class;
444                        break;
445                    default:
446                        throw error("Unfinished enum", -1);
447                }
448                TypeRef qn = parseQualifiedTypeName();
449                addBackRef(qn);
450                return classType(cl);
451            default:
452                throw error(-1);
453        }
454    }
455
456    static NamespaceRef reverseNamespace(List<Object> names) {
457        if (names == null || names.isEmpty()) {
458            return null;
459        }
460        Collections.reverse(names);
461        return new NamespaceRef(names.toArray());
462    }
463    List<TypeRef> allQualifiedNames = new ArrayList<TypeRef>();
464
465    interface DemanglingOp<T> {
466
467        T run() throws DemanglingException;
468    }
469
470    <T> T withEmptyQualifiedNames(DemanglingOp<T> action) throws DemanglingException {
471        List<TypeRef> list = allQualifiedNames;
472        try {
473            allQualifiedNames = new ArrayList<TypeRef>();
474            return action.run();
475        } finally {
476            allQualifiedNames = list;
477        }
478    }
479
480    IdentLike parseFirstQualifiedTypeNameComponent() throws DemanglingException {
481        if (consumeCharIf('?')) {
482            if (consumeCharIf('$')) {
483                return parseTemplateType().getIdent();
484            } else {
485                return parseSpecialName();
486            }
487        } else {
488            return new Ident(parseNameFragment());
489        }
490    }
491
492    TypeRef parseQualifiedTypeName() throws DemanglingException {
493        TypeRef backRef = consumeIfBackRef();
494        if (backRef != null) {
495            return backRef;
496        }
497
498        char c = peekChar();
499        List<Object> names = parseNameQualifications();
500
501        // TODO fix this :
502        //names.add(0, parseFirstQualifiedTypeNameComponent());
503        Object first = names.get(0);
504        names.set(0, first instanceof String ? new Ident((String) first) : ((ClassRef) first).getIdent());
505
506        if (names.size() == 1 && (names.get(0) instanceof TypeRef)) {
507            return (TypeRef) names.get(0);
508        }
509
510        /*
511         if (Character.isDigit(c)) {
512         consumeChar();
513         int i = (int)(c - '0');
514         if (i < 0 || i >= allQualifiedNames.size())
515         throw error("Invalid back reference " + i + " (knows only " + allQualifiedNames + ")", -1);
516         names = new ArrayList<String>(allQualifiedNames.get(i));
517         } else {
518         names = parseNames();
519         }*/
520
521        ClassRef tr = new ClassRef((Ident) names.get(0));
522        names.remove(0);
523        tr.setEnclosingType(reverseNamespace(names));
524        return tr;
525    }
526
527    public IdentLike parseSpecialName() throws DemanglingException {
528        switch (consumeChar()) {
529            case '0':
530                return SpecialName.Constructor;
531            case '1':
532                return SpecialName.Destructor;
533            case '2':
534                return SpecialName.New;
535            case '3':
536                return SpecialName.Delete;
537            case '4':
538                return SpecialName.OperatorAssign;
539            case '5':
540                return SpecialName.OperatorRShift;
541            case '6':
542                return SpecialName.OperatorLShift;
543            case '7':
544                return SpecialName.OperatorLogicNot;
545            case '8':
546                return SpecialName.OperatorEquals;
547            case '9':
548                return SpecialName.OperatorDifferent;
549            case 'A':
550                return SpecialName.OperatorSquareBrackets;
551            case 'B':
552                return SpecialName.OperatorCast;
553            case 'C':
554                return SpecialName.OperatorArrow;
555            case 'D':
556                return SpecialName.OperatorMultiply;
557            case 'E':
558                return SpecialName.OperatorIncrement;
559            case 'F':
560                return SpecialName.OperatorDecrement;
561            case 'G':
562                return SpecialName.OperatorSubstract;
563            case 'H':
564                return SpecialName.OperatorAdd;
565            case 'I':
566                return SpecialName.OperatorBitAnd;
567            case 'J':
568                return SpecialName.OperatorArrowStar;
569            case 'K':
570                return SpecialName.OperatorDivide;
571            case 'L':
572                return SpecialName.OperatorModulo;
573            case 'M':
574                return SpecialName.OperatorLower;
575            case 'N':
576                return SpecialName.OperatorLowerEquals;
577            case 'O':
578                return SpecialName.OperatorGreater;
579            case 'P':
580                return SpecialName.OperatorGreaterEquals;
581            case 'Q':
582                return SpecialName.OperatorComma;
583            case 'R':
584                return SpecialName.OperatorParenthesis;
585            case 'S':
586                return SpecialName.OperatorBitNot;
587            case 'T':
588                return SpecialName.OperatorXOR;
589            case 'U':
590                return SpecialName.OperatorBitOr;
591            case 'V':
592                return SpecialName.OperatorLogicAnd;
593            case 'W':
594                return SpecialName.OperatorLogicOr;
595            case 'X':
596                return SpecialName.OperatorMultiplyAssign;
597            case 'Y':
598                return SpecialName.OperatorAddAssign;
599            case 'Z':
600                return SpecialName.OperatorSubstractAssign;
601            case '_':
602                switch (consumeChar()) {
603                    case '0':
604                        return SpecialName.OperatorDivideAssign;
605                    case '1':
606                        return SpecialName.OperatorModuloAssign;
607                    case '2':
608                        return SpecialName.OperatorLShiftAssign;
609                    case '3':
610                        return SpecialName.OperatorRShiftAssign;
611                    case '4':
612                        return SpecialName.OperatorBitAndAssign;
613                    case '5':
614                        return SpecialName.OperatorBitOrAssign;
615                    case '6':
616                        return SpecialName.OperatorXORAssign;
617                    case '7':
618                        return SpecialName.VFTable;
619                    case '8':
620                        return SpecialName.VBTable;
621                    case '9':
622                        return SpecialName.VCall;
623                    case 'E':
624                        return SpecialName.VectorDeletingDestructor;
625                    case 'G':
626                        return SpecialName.ScalarDeletingDestructor;
627                    default:
628                        throw error("unhandled extended special name");
629                }
630
631            default:
632                throw error("Invalid special name");
633        }
634    }
635
636    private List<TypeRef> parseParams() throws DemanglingException {
637        List<TypeRef> paramTypes = new ArrayList<TypeRef>();
638        if (!consumeCharIf('X')) {
639            char c;
640            while ((c = peekChar()) != '@' && c != 0 && (c != 'Z' || peekChar(2) == 'Z')) {
641                TypeRef tr = parseType(false);
642                if (tr == null) {
643                    continue;
644                }
645                paramTypes.add(tr);
646            }
647            if (c == 'Z') {
648                consumeChar();
649            }
650            //break;
651            if (c == '@') {
652                consumeChar();
653            }
654        }
655        return paramTypes;
656    }
657
658    private List<TemplateArg> parseTemplateParams() throws DemanglingException {
659        return withEmptyQualifiedNames(new DemanglingOp<List<TemplateArg>>() {
660            public List<TemplateArg> run() throws DemanglingException {
661                List<TemplateArg> paramTypes = new ArrayList<TemplateArg>();
662                if (!consumeCharIf('X')) {
663                    char c;
664                    while ((c = peekChar()) != '@' && c != 0) {
665                        TemplateArg tr = parseTemplateParameter();
666                        if (tr == null) {
667                            continue;
668                        }
669                        paramTypes.add(tr);
670                    }
671                }
672                return paramTypes;
673            }
674        });
675    }
676
677    String parseNameFragment() throws DemanglingException {
678        StringBuilder b = new StringBuilder();
679        char c;
680
681        while ((c = consumeChar()) != '@') {
682            b.append(c);
683        }
684
685        if (b.length() == 0) {
686            throw new DemanglingException("Unexpected empty name fragment");
687        }
688
689        String name = b.toString();
690//              allQualifiedNames.add(Collections.singletonList(name));
691        return name;
692    }
693
694    void addBackRef(TypeRef tr) {
695        if (tr == null || allQualifiedNames.contains(tr)) {
696            return;
697        }
698
699        allQualifiedNames.add(tr);
700    }
701
702    TypeRef getBackRef(int i) throws DemanglingException {
703        if (i == allQualifiedNames.size()) {
704            i--; // TODO fix this !!!
705        }
706        if (i < 0 || i >= allQualifiedNames.size()) {
707            throw error("Invalid back references in name qualifications", -1);
708        }
709        return allQualifiedNames.get(i);
710    }
711
712    private List<Object> parseNameQualifications() throws DemanglingException {
713        List<Object> names = new ArrayList<Object>();
714
715        if (Character.isDigit(peekChar())) {
716            try {
717                int i = consumeChar() - '0';
718                names.add(getBackRef(i));
719                expectChars('@');
720                return names;
721            } catch (Exception ex) {
722                throw error("Invalid back references in name qualifications", -1);
723            }
724        }
725
726        while (peekChar() != '@') {
727            names.add(parseNameQualification());
728        }
729
730        expectChars('@');
731        return names;
732    }
733
734    Object parseNameQualification() throws DemanglingException {
735        if (consumeCharIf('?')) {
736            if (consumeCharIf('$')) {
737                return parseTemplateType();
738            } else {
739                if (peekChar() == 'A') {
740                    throw error("Anonymous numbered namespaces not handled yet");
741                }
742                int namespaceNumber = parseNumber(false);
743                return String.valueOf(namespaceNumber);
744            }
745        } else {
746            return parseNameFragment();
747        }
748    }
749
750    Style parseCallingConvention() throws DemanglingException {
751        Convention.Style cc;
752        boolean exported = true;
753        switch (consumeChar()) {
754            case 'A':
755                exported = false;
756            case 'B':
757                cc = Convention.Style.CDecl;
758                break;
759            case 'C':
760                exported = false;
761            case 'D':
762                cc = Convention.Style.Pascal;
763                break;
764            case 'E':
765                exported = false;
766            case 'F':
767                cc = Convention.Style.ThisCall;
768                break;
769            case 'G':
770                exported = false;
771            case 'H':
772                cc = Convention.Style.StdCall;
773                break;
774            case 'I':
775                exported = false;
776            case 'J':
777                cc = Convention.Style.FastCall;
778                break;
779            case 'K':
780                exported = false;
781            case 'L':
782                cc = null;
783                break;
784            case 'N':
785                cc = Convention.Style.CLRCall;
786                break;
787            default:
788                throw error("Unknown calling convention");
789        }
790        return cc;
791    }
792
793    static class CVClassModifier {
794
795        boolean isVariable;
796        boolean isMember;
797        boolean isBased;
798    }
799
800    CVClassModifier parseCVClassModifier() throws DemanglingException {
801        CVClassModifier mod = new CVClassModifier();
802        switch (peekChar()) {
803            case 'E': // __ptr64
804            case 'F': // __unaligned 
805            case 'I': // __restrict
806                consumeChar();
807                break;
808        }
809        boolean based = false;
810        switch (consumeChar()) {
811            case 'M': // __based
812            case 'N': // __based
813            case 'O': // __based
814            case 'P': // __based
815                mod.isBased = true;
816            case 'A':
817            case 'B':
818            case 'J':
819            case 'C':
820            case 'G':
821            case 'K':
822            case 'D':
823            case 'H':
824            case 'L':
825                mod.isVariable = true;
826                mod.isMember = false;
827                break;
828            case '2': // __based
829            case '3': // __based
830            case '4': // __based
831            case '5': // __based
832                mod.isBased = true;
833            case 'Q':
834            case 'U':
835            case 'Y':
836            case 'R':
837            case 'V':
838            case 'Z':
839            case 'S':
840            case 'W':
841            case '0':
842            case 'T':
843            case 'X':
844            case '1':
845                mod.isVariable = true;
846                mod.isMember = true;
847                break;
848            case '_': // __based
849                mod.isBased = true;
850                switch (consumeChar()) {
851                    case 'A':
852                    case 'B':
853                        mod.isVariable = false;
854                        break;
855                    case 'C':
856                    case 'D':
857                        mod.isVariable = false;
858                        mod.isMember = true;
859                        break;
860                    default:
861                        throw error("Unknown extended __based class modifier", -1);
862                }
863                break;
864            case '6':
865            case '7':
866                mod.isVariable = false;
867                mod.isMember = false;
868                break;
869            case '8':
870            case '9':
871                mod.isVariable = false;
872                mod.isMember = true;
873                break;
874            default:
875                throw error("Unknown CV class modifier", -1);
876        }
877        if (mod.isBased) {
878            switch (consumeChar()) {
879                case '0': // __based(void)
880                    break;
881                case '2':
882                    parseNameQualifications();
883                    break;
884                case '5': // no __based() ??
885                    break;
886            }
887        }
888        return mod;
889    }
890}