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                if (!consumeCharsIf('$', 'A')) // __gc
399                {
400                    consumeCharsIf('$', 'B');  // __pin
401                }
402                CVClassModifier cvMods = parseCVClassModifier();
403                if (cvMods.isVariable) {
404                    if (consumeCharIf('Y')) {
405                        int dimensions = parseNumber(false);
406                        int[] indices = new int[dimensions];
407                        for (int i = 0; i < dimensions; i++) {
408                            indices[i] = parseNumber(false);
409                        }
410                    }
411                    tr = pointerType(parseType(true));
412                } else {
413                    MemberRef mr = new MemberRef();
414                    parseFunctionProperty(mr);
415                    tr = pointerType(new FunctionTypeRef(mr));
416                }
417                addBackRef(tr);
418                return tr;
419            case 'V': // class
420            case 'U': // struct
421            case 'T': // union
422                //System.out.println("Found struct, class or union");
423                return parseQualifiedTypeName();
424            case 'W': // enum
425                Class<?> cl;
426                switch (consumeChar()) {
427                    case '0':
428                    case '1':
429                        cl = byte.class;
430                        break;
431                    case '2':
432                    case '3':
433                        cl = short.class;
434                        break;
435                    case '4':
436                    case '5':
437                        cl = int.class;
438                        break;
439                    case '6':
440                    case '7': // CLong : int on win32 and win64 !
441                        cl = int.class;
442                        break;
443                    default:
444                        throw error("Unfinished enum", -1);
445                }
446                TypeRef qn = parseQualifiedTypeName();
447                addBackRef(qn);
448                return classType(cl);
449            default:
450                throw error(-1);
451        }
452    }
453
454    static NamespaceRef reverseNamespace(List<Object> names) {
455        if (names == null || names.isEmpty()) {
456            return null;
457        }
458        Collections.reverse(names);
459        return new NamespaceRef(names.toArray());
460    }
461    List<TypeRef> allQualifiedNames = new ArrayList<TypeRef>();
462
463    interface DemanglingOp<T> {
464
465        T run() throws DemanglingException;
466    }
467
468    <T> T withEmptyQualifiedNames(DemanglingOp<T> action) throws DemanglingException {
469        List<TypeRef> list = allQualifiedNames;
470        try {
471            allQualifiedNames = new ArrayList<TypeRef>();
472            return action.run();
473        } finally {
474            allQualifiedNames = list;
475        }
476    }
477
478    IdentLike parseFirstQualifiedTypeNameComponent() throws DemanglingException {
479        if (consumeCharIf('?')) {
480            if (consumeCharIf('$')) {
481                return parseTemplateType().getIdent();
482            } else {
483                return parseSpecialName();
484            }
485        } else {
486            return new Ident(parseNameFragment());
487        }
488    }
489
490    TypeRef parseQualifiedTypeName() throws DemanglingException {
491        TypeRef backRef = consumeIfBackRef();
492        if (backRef != null) {
493            return backRef;
494        }
495
496        char c = peekChar();
497        List<Object> names = parseNameQualifications();
498
499        // TODO fix this :
500        //names.add(0, parseFirstQualifiedTypeNameComponent());
501        Object first = names.get(0);
502        names.set(0, first instanceof String ? new Ident((String) first) : ((ClassRef) first).getIdent());
503
504        if (names.size() == 1 && (names.get(0) instanceof TypeRef)) {
505            return (TypeRef) names.get(0);
506        }
507
508        /*
509         if (Character.isDigit(c)) {
510         consumeChar();
511         int i = (int)(c - '0');
512         if (i < 0 || i >= allQualifiedNames.size())
513         throw error("Invalid back reference " + i + " (knows only " + allQualifiedNames + ")", -1);
514         names = new ArrayList<String>(allQualifiedNames.get(i));
515         } else {
516         names = parseNames();
517         }*/
518
519        ClassRef tr = new ClassRef((Ident) names.get(0));
520        names.remove(0);
521        tr.setEnclosingType(reverseNamespace(names));
522        return tr;
523    }
524
525    public IdentLike parseSpecialName() throws DemanglingException {
526        switch (consumeChar()) {
527            case '0':
528                return SpecialName.Constructor;
529            case '1':
530                return SpecialName.Destructor;
531            case '2':
532                return SpecialName.New;
533            case '3':
534                return SpecialName.Delete;
535            case '4':
536                return SpecialName.OperatorAssign;
537            case '5':
538                return SpecialName.OperatorRShift;
539            case '6':
540                return SpecialName.OperatorLShift;
541            case '7':
542                return SpecialName.OperatorLogicNot;
543            case '8':
544                return SpecialName.OperatorEquals;
545            case '9':
546                return SpecialName.OperatorDifferent;
547            case 'A':
548                return SpecialName.OperatorSquareBrackets;
549            case 'B':
550                return SpecialName.OperatorCast;
551            case 'C':
552                return SpecialName.OperatorArrow;
553            case 'D':
554                return SpecialName.OperatorMultiply;
555            case 'E':
556                return SpecialName.OperatorIncrement;
557            case 'F':
558                return SpecialName.OperatorDecrement;
559            case 'G':
560                return SpecialName.OperatorSubstract;
561            case 'H':
562                return SpecialName.OperatorAdd;
563            case 'I':
564                return SpecialName.OperatorBitAnd;
565            case 'J':
566                return SpecialName.OperatorArrowStar;
567            case 'K':
568                return SpecialName.OperatorDivide;
569            case 'L':
570                return SpecialName.OperatorModulo;
571            case 'M':
572                return SpecialName.OperatorLower;
573            case 'N':
574                return SpecialName.OperatorLowerEquals;
575            case 'O':
576                return SpecialName.OperatorGreater;
577            case 'P':
578                return SpecialName.OperatorGreaterEquals;
579            case 'Q':
580                return SpecialName.OperatorComma;
581            case 'R':
582                return SpecialName.OperatorParenthesis;
583            case 'S':
584                return SpecialName.OperatorBitNot;
585            case 'T':
586                return SpecialName.OperatorXOR;
587            case 'U':
588                return SpecialName.OperatorBitOr;
589            case 'V':
590                return SpecialName.OperatorLogicAnd;
591            case 'W':
592                return SpecialName.OperatorLogicOr;
593            case 'X':
594                return SpecialName.OperatorMultiplyAssign;
595            case 'Y':
596                return SpecialName.OperatorAddAssign;
597            case 'Z':
598                return SpecialName.OperatorSubstractAssign;
599            case '_':
600                switch (consumeChar()) {
601                    case '0':
602                        return SpecialName.OperatorDivideAssign;
603                    case '1':
604                        return SpecialName.OperatorModuloAssign;
605                    case '2':
606                        return SpecialName.OperatorLShiftAssign;
607                    case '3':
608                        return SpecialName.OperatorRShiftAssign;
609                    case '4':
610                        return SpecialName.OperatorBitAndAssign;
611                    case '5':
612                        return SpecialName.OperatorBitOrAssign;
613                    case '6':
614                        return SpecialName.OperatorXORAssign;
615                    case '7':
616                        return SpecialName.VFTable;
617                    case '8':
618                        return SpecialName.VBTable;
619                    case '9':
620                        return SpecialName.VCall;
621                    case 'E':
622                        return SpecialName.VectorDeletingDestructor;
623                    case 'G':
624                        return SpecialName.ScalarDeletingDestructor;
625                    default:
626                        throw error("unhandled extended special name");
627                }
628
629            default:
630                throw error("Invalid special name");
631        }
632    }
633
634    private List<TypeRef> parseParams() throws DemanglingException {
635        List<TypeRef> paramTypes = new ArrayList<TypeRef>();
636        if (!consumeCharIf('X')) {
637            char c;
638            while ((c = peekChar()) != '@' && c != 0 && (c != 'Z' || peekChar(2) == 'Z')) {
639                TypeRef tr = parseType(false);
640                if (tr == null) {
641                    continue;
642                }
643                paramTypes.add(tr);
644            }
645            if (c == 'Z') {
646                consumeChar();
647            }
648            //break;
649            if (c == '@') {
650                consumeChar();
651            }
652        }
653        return paramTypes;
654    }
655
656    private List<TemplateArg> parseTemplateParams() throws DemanglingException {
657        return withEmptyQualifiedNames(new DemanglingOp<List<TemplateArg>>() {
658            public List<TemplateArg> run() throws DemanglingException {
659                List<TemplateArg> paramTypes = new ArrayList<TemplateArg>();
660                if (!consumeCharIf('X')) {
661                    char c;
662                    while ((c = peekChar()) != '@' && c != 0) {
663                        TemplateArg tr = parseTemplateParameter();
664                        if (tr == null) {
665                            continue;
666                        }
667                        paramTypes.add(tr);
668                    }
669                }
670                return paramTypes;
671            }
672        });
673    }
674
675    String parseNameFragment() throws DemanglingException {
676        StringBuilder b = new StringBuilder();
677        char c;
678
679        while ((c = consumeChar()) != '@') {
680            b.append(c);
681        }
682
683        if (b.length() == 0) {
684            throw new DemanglingException("Unexpected empty name fragment");
685        }
686
687        String name = b.toString();
688//              allQualifiedNames.add(Collections.singletonList(name));
689        return name;
690    }
691
692    void addBackRef(TypeRef tr) {
693        if (tr == null || allQualifiedNames.contains(tr)) {
694            return;
695        }
696
697        allQualifiedNames.add(tr);
698    }
699
700    TypeRef getBackRef(int i) throws DemanglingException {
701        if (i == allQualifiedNames.size()) {
702            i--; // TODO fix this !!!
703        }
704        if (i < 0 || i >= allQualifiedNames.size()) {
705            throw error("Invalid back references in name qualifications", -1);
706        }
707        return allQualifiedNames.get(i);
708    }
709
710    private List<Object> parseNameQualifications() throws DemanglingException {
711        List<Object> names = new ArrayList<Object>();
712
713        if (Character.isDigit(peekChar())) {
714            try {
715                int i = consumeChar() - '0';
716                names.add(getBackRef(i));
717                expectChars('@');
718                return names;
719            } catch (Exception ex) {
720                throw error("Invalid back references in name qualifications", -1);
721            }
722        }
723
724        while (peekChar() != '@') {
725            names.add(parseNameQualification());
726        }
727
728        expectChars('@');
729        return names;
730    }
731
732    Object parseNameQualification() throws DemanglingException {
733        if (consumeCharIf('?')) {
734            if (consumeCharIf('$')) {
735                return parseTemplateType();
736            } else {
737                if (peekChar() == 'A') {
738                    throw error("Anonymous numbered namespaces not handled yet");
739                }
740                int namespaceNumber = parseNumber(false);
741                return String.valueOf(namespaceNumber);
742            }
743        } else {
744            return parseNameFragment();
745        }
746    }
747
748    Style parseCallingConvention() throws DemanglingException {
749        Convention.Style cc;
750        boolean exported = true;
751        switch (consumeChar()) {
752            case 'A':
753                exported = false;
754            case 'B':
755                cc = Convention.Style.CDecl;
756                break;
757            case 'C':
758                exported = false;
759            case 'D':
760                cc = Convention.Style.Pascal;
761                break;
762            case 'E':
763                exported = false;
764            case 'F':
765                cc = Convention.Style.ThisCall;
766                break;
767            case 'G':
768                exported = false;
769            case 'H':
770                cc = Convention.Style.StdCall;
771                break;
772            case 'I':
773                exported = false;
774            case 'J':
775                cc = Convention.Style.FastCall;
776                break;
777            case 'K':
778                exported = false;
779            case 'L':
780                cc = null;
781                break;
782            case 'N':
783                cc = Convention.Style.CLRCall;
784                break;
785            default:
786                throw error("Unknown calling convention");
787        }
788        return cc;
789    }
790
791    static class CVClassModifier {
792
793        boolean isVariable;
794        boolean isMember;
795        boolean isBased;
796    }
797
798    CVClassModifier parseCVClassModifier() throws DemanglingException {
799        CVClassModifier mod = new CVClassModifier();
800        switch (peekChar()) {
801            case 'E': // __ptr64
802            case 'F': // __unaligned 
803            case 'I': // __restrict
804                consumeChar();
805                break;
806        }
807        boolean based = false;
808        switch (consumeChar()) {
809            case 'M': // __based
810            case 'N': // __based
811            case 'O': // __based
812            case 'P': // __based
813                mod.isBased = true;
814            case 'A':
815            case 'B':
816            case 'J':
817            case 'C':
818            case 'G':
819            case 'K':
820            case 'D':
821            case 'H':
822            case 'L':
823                mod.isVariable = true;
824                mod.isMember = false;
825                break;
826            case '2': // __based
827            case '3': // __based
828            case '4': // __based
829            case '5': // __based
830                mod.isBased = true;
831            case 'Q':
832            case 'U':
833            case 'Y':
834            case 'R':
835            case 'V':
836            case 'Z':
837            case 'S':
838            case 'W':
839            case '0':
840            case 'T':
841            case 'X':
842            case '1':
843                mod.isVariable = true;
844                mod.isMember = true;
845                break;
846            case '_': // __based
847                mod.isBased = true;
848                switch (consumeChar()) {
849                    case 'A':
850                    case 'B':
851                        mod.isVariable = false;
852                        break;
853                    case 'C':
854                    case 'D':
855                        mod.isVariable = false;
856                        mod.isMember = true;
857                        break;
858                    default:
859                        throw error("Unknown extended __based class modifier", -1);
860                }
861                break;
862            case '6':
863            case '7':
864                mod.isVariable = false;
865                mod.isMember = false;
866                break;
867            case '8':
868            case '9':
869                mod.isVariable = false;
870                mod.isMember = true;
871                break;
872            default:
873                throw error("Unknown CV class modifier", -1);
874        }
875        if (mod.isBased) {
876            switch (consumeChar()) {
877                case '0': // __based(void)
878                    break;
879                case '2':
880                    parseNameQualifications();
881                    break;
882                case '5': // no __based() ??
883                    break;
884            }
885        }
886        return mod;
887    }
888}