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