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 java.io.EOFException;
034import org.bridj.ann.Convention.Style;
035import java.lang.reflect.*;
036import java.lang.annotation.*;
037import java.util.Arrays;
038import java.util.regex.Pattern;
039import org.bridj.AbstractBridJRuntime;
040import org.bridj.BridJ;
041import org.bridj.CRuntime;
042import org.bridj.FlagSet;
043import org.bridj.JNI;
044import org.bridj.NativeLibrary;
045import org.bridj.NativeObject;
046import org.bridj.Platform;
047
048import org.bridj.Pointer;
049import org.bridj.TimeT;
050import org.bridj.SizeT;
051import org.bridj.CLong;
052import org.bridj.Callback;
053import org.bridj.ValuedEnum;
054import org.bridj.ann.Constructor;
055import org.bridj.ann.Ptr;
056import org.bridj.ann.Convention;
057import org.bridj.ann.Name;
058import org.bridj.ann.Namespace;
059import org.bridj.ann.Template;
060import org.bridj.util.AnnotationUtils;
061import static org.bridj.util.AnnotationUtils.*;
062import org.bridj.util.DefaultParameterizedType;
063import org.bridj.util.Utils;
064
065/*
066 mvn compile exec:java -Dexec.mainClass=org.bridj.demangling.Demangler -e "-Dexec.args=?method_name@class_name@@QAEPAPADPAD0@Z"
067
068 ??4Class1@TestLib@@QAEAAV01@ABV01@@Z
069 ?f@Class1@TestLib@@QAEPADPAD0@Z
070
071 class TestLib::Class1 & TestLib::Class1::operator=(class TestLib::Class1 const &)
072 char * TestLib::Class1::f(char *,char *)
073 */
074/**
075 * Base class and core structures for symbol demanglers (typically, for C++
076 * symbols).
077 */
078public abstract class Demangler {
079
080    public static void main(String[] args) {
081//        try {
082////            String s = "?f@@YA?AW4E@@W41@@Z";
083//            String s = "?f@@YAPADPADPAF1@Z"; // "byte* f(byte*, short*, short*)"
084//            //String s = "?m@C@@SAPAV1@XZ";
085//            MemberRef mr = new VC9Demangler(null, s).parseSymbol();
086//            System.out.println(mr);
087//        } catch (DemanglingException ex) {
088//            Logger.getLogger(Demangler.class.getName()).error(null, ex);
089//        }
090        for (String arg : args) {
091            try {
092                System.out.println("VC9: " + new VC9Demangler(null, arg).parseSymbol());
093            } catch (Exception ex) {
094                ex.printStackTrace();
095            }
096            try {
097                System.out.println("GCC4: " + new GCC4Demangler(null, arg).parseSymbol());
098            } catch (Exception ex) {
099                ex.printStackTrace();
100            }
101        }
102    }
103
104    public interface Annotations {
105
106        <A extends Annotation> A getAnnotation(Class<A> c);
107
108        boolean isAnnotationPresent(Class<? extends Annotation> c);
109    }
110
111    public static Annotations annotations(final Annotation[] aa) {
112        return new Annotations() {
113            //@Override
114            public <A extends Annotation> A getAnnotation(Class<A> c) {
115                if (aa == null) {
116                    return null;
117                }
118
119                for (Annotation a : aa) {
120                    if (c.isInstance(a)) {
121                        return (A) a;
122                    }
123                }
124                return null;
125            }
126
127            public boolean isAnnotationPresent(Class<? extends Annotation> c) {
128                return AnnotationUtils.isAnnotationPresent(c, aa);
129            }
130        };
131    }
132
133    public static Annotations annotations(final Type e) {
134        return annotations((AnnotatedElement) Utils.getClass(e));
135    }
136
137    public static Annotations annotations(final AnnotatedElement e) {
138        return new Annotations() {
139            //@Override
140            public <A extends Annotation> A getAnnotation(Class<A> c) {
141                return e.getAnnotation(c);
142            }
143
144            public boolean isAnnotationPresent(Class<? extends Annotation> c) {
145                return AnnotationUtils.isAnnotationPresent(c, e);
146            }
147        };
148    }
149
150    public class DemanglingException extends Exception {
151
152        public DemanglingException(String mess) {
153            this(mess, null);
154        }
155
156        public DemanglingException(String mess, Throwable cause) {
157            super(mess + " (in symbol '" + str + "')", cause);
158        }
159    }
160
161    public abstract MemberRef parseSymbol() throws DemanglingException;
162    protected final String str;
163    protected final int length;
164    protected int position = 0;
165    protected final NativeLibrary library;
166
167    public Demangler(NativeLibrary library, String str) {
168        this.str = str;
169        this.length = str.length();
170        this.library = library;
171    }
172
173    public String getString() {
174        return str;
175    }
176
177    protected void expectChars(char... cs) throws DemanglingException {
178        for (char c : cs) {
179            char cc = consumeChar();
180            if (cc != c) {
181                throw error("Expected char '" + c + "', found '" + cc + "'");
182            }
183        }
184    }
185
186    protected void expectAnyChar(char... cs) throws DemanglingException {
187        char cc = consumeChar();
188        for (char c : cs) {
189            if (cc == c) {
190                return;
191            }
192        }
193        throw error("Expected any of " + Arrays.toString(cs) + ", found '" + cc + "'");
194    }
195
196    public static StringBuilder implode(StringBuilder b, Object[] items, String sep) {
197        return implode(b, Arrays.asList(items), sep);
198    }
199
200    public static StringBuilder implode(StringBuilder b, Iterable<?> items, String sep) {
201        boolean first = true;
202        for (Object item : items) {
203            if (first) {
204                first = false;
205            } else {
206                b.append(sep);
207            }
208            b.append(item);
209        }
210        return b;
211    }
212
213    protected char peekChar() {
214        return peekChar(1);
215    }
216
217    protected char peekChar(int dist) {
218        int p = position + dist - 1;
219        return p >= length ? 0 : str.charAt(p);
220    }
221
222    protected char lastChar() {
223        return position == 0 ? 0 : str.charAt(position - 1);
224    }
225
226    protected char consumeChar() throws DemanglingException {
227        char c = peekChar();
228        if (c != 0) {
229            position++;
230        } else {
231            throw new DemanglingException("Reached end of string '" + str + "'");
232        }
233        return c;
234    }
235
236    protected boolean consumeCharsIf(char... nextChars) {
237        int initialPosition = position;
238        try {
239            for (char c : nextChars) {
240                char cc = consumeChar();
241                if (cc != c) {
242                    position = initialPosition;
243                    return false;
244                }
245            }
246            return true;
247        } catch (DemanglingException ex) {
248            position = initialPosition;
249            return false;
250        }
251    }
252
253    protected boolean consumeCharIf(char... allowedChars) {
254        char c = peekChar();
255        for (char allowedChar : allowedChars) {
256            if (allowedChar == c) {
257                position++;
258                return true;
259            }
260        }
261        return false;
262    }
263
264    protected DemanglingException error(int deltaPosition) {
265        return error(null, deltaPosition);
266    }
267
268    protected DemanglingException error(String mess) {
269        return error(mess, -1);
270    }
271
272    protected DemanglingException error(String mess, int deltaPosition) {
273        StringBuilder err = new StringBuilder(position + 1);
274        int position = this.position + deltaPosition;
275        for (int i = 0; i < position; i++) {
276            err.append(' ');
277        }
278        err.append('^');
279        return new DemanglingException("Parsing error at position " + position + (mess == null ? "" : ": " + mess) + " \n\t" + str + "\n\t" + err);
280    }
281
282    public interface TemplateArg {
283
284        public boolean matchesParam(Object param, Annotations annotations);
285    }
286
287    public static String getMethodName(Method method) {
288        Name name = method.getAnnotation(Name.class);
289        return name == null ? method.getName() : name.value();
290    }
291
292    public static String getClassName(Type type) {
293        assert type != null;
294        Class<?> typeClass = Utils.getClass(type);//getTypeClass(type);
295        Name name = typeClass.getAnnotation(Name.class);
296        return name == null ? typeClass.getSimpleName() : name.value();
297    }
298
299    public static String getFullClassName(Type type) {
300        Class<?> typeClass = Utils.getClass(type);//getTypeClass(type);
301        String simpleName = getClassName(typeClass);
302        Namespace namespace = typeClass.getAnnotation(Namespace.class);
303        return namespace != null ? namespace.value().replaceAll("::", ".") + "." + simpleName : simpleName;
304    }
305
306    public static class Symbol {
307
308        final String symbol;
309        long address;
310        MemberRef ref;
311        boolean refParsed;
312        final NativeLibrary library;
313
314        public Symbol(String symbol, NativeLibrary library) {
315            this.symbol = symbol;
316            this.library = library;
317
318        }
319
320        public NativeLibrary getLibrary() {
321            return library;
322        }
323
324        public MemberRef getRef() {
325            return ref;
326        }
327
328        public Style getStyle() {
329            return style;
330        }
331
332        public String getSymbol() {
333            return symbol;
334        }
335
336        @Override
337        public String toString() {
338            return symbol + (ref == null ? "" : " (" + ref + ")");
339        }
340
341        public long getAddress() {
342            if (address == 0) {
343                address = library.getSymbolAddress(symbol);
344            }
345            return address;
346        }
347
348        public void setAddress(long address) {
349            this.address = address;
350        }
351        private Convention.Style style;
352
353        public Convention.Style getInferredCallingConvention() {
354            if (style == null) {
355                //System.out.println("Symbol " + symbol + " stdcall = " + symbol.matches("_.*?@\\d+"));
356                if (symbol.matches("_.*?@\\d+")) {
357                    style = Convention.Style.StdCall;
358                } else if (symbol.matches("@.*?@\\d+")) {
359                    style = Convention.Style.FastCall;
360                } else if (Platform.isWindows() && symbol.contains("@")) {
361                    try {
362                        MemberRef mr = getParsedRef();
363                        if (mr != null) {
364                            style = mr.callingConvention;
365                        }
366                    } catch (Throwable th) {
367                    }
368                }
369            }
370            return style;
371        }
372
373        public boolean matches(Method method) {
374            if (!symbol.contains(getMethodName(method))) {
375                return false;
376            }
377
378            //if (!Modifier.isStatic(method.getModifiers()) && !symbol.contains(method.getDeclaringClass().getSimpleName()))
379            //  return false;
380
381            parse();
382
383            try {
384                if (ref != null) {
385                    boolean res = ref.matches(method);
386                    if (!res && BridJ.debug) {
387                        BridJ.debug("Symbol " + symbol + " was a good candidate but expected demangled signature " + ref + " did not match the method " + method);
388                    }
389                    return res;
390                }
391            } catch (Exception ex) {
392                ex.printStackTrace();
393            }
394            return false;
395        }
396
397        public MemberRef getParsedRef() {
398            parse();
399            return ref;
400        }
401
402        synchronized void parse() {
403            if (!refParsed) {
404                try {
405                    ref = library.parseSymbol(symbol);
406                } catch (DemanglingException ex) {
407                    if (BridJ.debug) {
408                        ex.printStackTrace();
409                    }
410                    if (BridJ.verbose) {
411                        BridJ.warning("Symbol parsing failed : " + ex.getMessage());
412                    }
413                }
414                refParsed = true;
415            }
416        }
417
418        public String getName() {
419            return symbol;
420        }
421
422        public boolean matchesVirtualTable(Class<?> type) {
423            if (!symbol.contains(type.getSimpleName())) {
424                return false;
425            }
426
427            parse();
428
429            try {
430                if (ref != null) {
431                    return ref.matchesVirtualTable(type);
432                }
433            } catch (Exception ex) {
434                ex.printStackTrace();
435            }
436            return false;
437        }
438
439        public boolean matchesConstructor(Type type, java.lang.reflect.Constructor<?> constr) {
440            if (!symbol.contains(getClassName(type))) {
441                return false;
442            }
443
444            parse();
445
446            try {
447                if (ref != null) {
448                    return ref.matchesConstructor(type, constr);
449                }
450            } catch (Exception ex) {
451                ex.printStackTrace();
452            }
453            return false;
454        }
455
456        public boolean matchesDestructor(Class<?> type) {
457            if (!symbol.contains(type.getSimpleName())) {
458                return false;
459            }
460
461            parse();
462
463            try {
464                if (ref != null) {
465                    return ref.matchesDestructor(type);
466                }
467            } catch (Exception ex) {
468                ex.printStackTrace();
469            }
470            return false;
471        }
472
473        public boolean isVirtualTable() {
474            // TODO Auto-generated method stub
475            return false;
476        }
477    }
478
479    public static abstract class TypeRef implements TemplateArg {
480
481        public abstract StringBuilder getQualifiedName(StringBuilder b, boolean generic);
482
483        public String getQualifiedName(boolean generic) {
484            StringBuilder sb = getQualifiedName(new StringBuilder(), generic);
485            return sb == null ? null : sb.toString();
486        }
487
488        public boolean matchesParam(Object param, Annotations annotations) {
489            return (param instanceof Type) && matches((Type) param, annotations);
490        }
491
492        public boolean matches(Type type, Annotations annotations) {
493            String thisName = getQualifiedName(false);
494            if (thisName == null)
495                return false;
496            Class<?> typeClass = getTypeClass(type);
497            String name = getClassName(typeClass);
498            if (thisName.equals(name))
499                return true;
500            Namespace ns = typeClass.getAnnotation(Namespace.class);
501            String pack;
502            if (ns == null) {
503                if (typeClass.getPackage() == null)
504                    pack = "";
505                else
506                    pack = typeClass.getPackage().getName();
507            } else {
508                pack = ns.value().replaceAll("\b::\b", ".").trim();
509            }
510            if (pack.length() == 0)
511                return false;
512            String qname = pack + "." + name;
513            if (thisName.matches("\b" + Pattern.quote(qname)))
514                return true;
515            return false;
516        }
517
518        @Override
519        public boolean equals(Object obj) {
520            return toString().equals(obj.toString());
521        }
522    }
523
524    public static class Constant implements TemplateArg {
525
526        Object value;
527
528        public Constant(Object value) {
529            this.value = value;
530        }
531
532        public boolean matchesParam(Object param, Annotations annotations) {
533            return value.equals(param);
534        }
535
536        @Override
537        public String toString() {
538            return value.toString();
539        }
540    }
541
542    public static class NamespaceRef extends TypeRef {
543
544        Object[] namespace;
545
546        public NamespaceRef(Object... namespace) {
547            this.namespace = namespace;
548        }
549
550        public StringBuilder getQualifiedName(StringBuilder b, boolean generic) {
551            return implode(b, namespace, ".");
552        }
553
554        @Override
555        public String toString() {
556            return getQualifiedName(new StringBuilder(), true).toString();
557        }
558    }
559
560    public static class PointerTypeRef extends TypeRef {
561
562        public TypeRef pointedType;
563
564        public PointerTypeRef(TypeRef pointedType) {
565            assert pointedType != null;
566            this.pointedType = pointedType;
567        }
568
569        @Override
570        public StringBuilder getQualifiedName(StringBuilder b, boolean generic) {
571            return b.append("org.bridj.Pointer");
572        }
573
574        @Override
575        public String toString() {
576            return pointedType + "*";
577        }
578
579        @Override
580        public boolean matches(Type type, Annotations annotations) {
581            if (super.matches(type, annotations))
582                return true;
583
584            if (type == Long.TYPE && annotations.isAnnotationPresent(Ptr.class))
585                return true;
586            
587            Class typeClass = getTypeClass(type);
588            if (!Pointer.class.isAssignableFrom(typeClass))
589                return false;
590//            return true;
591            Type pointedType = normalize(Utils.getUniqueParameterizedTypeParameter(type));
592            if (this.pointedType == null || this.pointedType.toString().equals("void"))
593                return pointedType == null;
594            if (pointedType == null) {
595                return true;
596            }
597            return this.pointedType.matches(pointedType, annotations);
598        }
599        
600        
601    }
602
603    protected static TypeRef pointerType(TypeRef tr) {
604        return new PointerTypeRef(tr);
605    }
606
607    protected static TypeRef classType(final Class<?> c, Class<? extends Annotation>... annotations) {
608        return classType(c, null, annotations);
609    }
610
611    protected static TypeRef classType(final Class<?> c, final java.lang.reflect.Type[] genericTypes, Class<? extends Annotation>... annotations) {
612        JavaTypeRef tr = new JavaTypeRef();
613        if (genericTypes == null) {
614            tr.type = c;
615        } else {
616            tr.type = new DefaultParameterizedType(c, genericTypes);
617        }
618
619        tr.annotations = annotations;
620        return tr;
621    }
622
623    protected static TypeRef simpleType(String name, TemplateArg... args) {
624        return new ClassRef(new Ident(name, args));
625    }
626
627    protected static TypeRef simpleType(Ident ident) {
628        return new ClassRef(ident);
629    }
630
631    static Class<?> getTypeClass(Type type) {
632
633        if (type instanceof Class<?>) {
634            return (Class<?>) type;
635        }
636        if (type instanceof ParameterizedType) {
637            ParameterizedType pt = (ParameterizedType) type;
638            Class<?> c = (Class<?>) pt.getRawType();
639            if (ValuedEnum.class.isAssignableFrom(c)) {
640                Type[] types = pt.getActualTypeArguments();
641                if (types == null || types.length != 1) {
642                    c = int.class;
643                } else {
644                    c = getTypeClass(pt.getActualTypeArguments()[0]);
645                }
646            }
647            return c;
648        }
649        if (type instanceof GenericArrayType) {
650            if (Object.class.isAssignableFrom(getTypeClass(((GenericArrayType) type).getGenericComponentType()))) {
651                return Object[].class;
652            }
653        }
654        return null;
655//        throw new UnsupportedOperationException("Unknown type type : " + (type == null ? null : type.getClass().getName()));
656    }
657
658    private static Type normalize(Type t) {
659        if (t instanceof WildcardType) {
660            WildcardType wt = (WildcardType) t;
661            if (wt.getLowerBounds().length == 1)
662                return normalize(wt.getLowerBounds()[0]);
663            return null;
664        }
665        Class<?> c = Utils.getClass(t);
666        if (c != null && c.isPrimitive()) {
667            if (t == float.class) return Float.class;
668            if (t == double.class) return Double.class;
669            if (t == byte.class) return Byte.class;
670            if (t == char.class) return Character.class;
671            if (t == short.class) return Short.class;
672            if (t == int.class) return Integer.class;
673            if (t == long.class) return Long.class;
674            if (t == boolean.class) return Boolean.class;
675            if (t == void.class) return Void.class;
676        }
677        return t;
678    }
679    static boolean equivalentTypes(Type a, Class ac, Annotations aAnnotations, Type b, Class bc, Annotations bAnnotations) {
680        if (normalize(a).equals(normalize(b))) {
681            return true;
682        }
683
684        if (aAnnotations != null && bAnnotations != null) {
685            if (xor(isPointerLike(a, ac, aAnnotations), isPointerLike(b, bc, bAnnotations))) {
686                return false;
687            }
688            if (xor(isCLong(a, ac, aAnnotations), isCLong(b, bc, bAnnotations))) {
689                return false;
690            }
691        }
692        int as = getIntegralSize(a, ac, aAnnotations);
693        int bs = getIntegralSize(b, bc, bAnnotations);
694        return as != -1 && as == bs;
695    }
696    
697    static boolean xor(boolean a, boolean b) {
698        return a && !b || !a && b;
699    }
700    static boolean isPointerLike(Type type, Class typec, Annotations annotations) {
701        if (type == long.class || type == Long.class) {
702            return annotations == null && Pointer.SIZE == 8 ||
703                annotations != null && annotations.isAnnotationPresent(Ptr.class) &&
704                    !annotations.isAnnotationPresent(org.bridj.ann.CLong.class);
705        }
706        return type == SizeT.class || Pointer.class.isAssignableFrom(typec);
707    }
708
709    static boolean isCLong(Type type, Class typec, Annotations annotations) {
710        if (type == long.class || type == Long.class) {
711            return annotations == null && CLong.SIZE == 8 ||
712                annotations != null && annotations.isAnnotationPresent(org.bridj.ann.CLong.class);
713        }
714        return type == CLong.class;
715    }
716
717    static int getIntegralSize(Type type, Class typec, Annotations annotations) {
718        if (type == int.class || type == Integer.class) {
719            return 4;
720        }
721        if (type == long.class || type == Long.class) {
722            if (annotations != null) {
723                if (annotations.isAnnotationPresent(Ptr.class)) {
724                    return Pointer.SIZE;
725                }
726                if (annotations.isAnnotationPresent(org.bridj.ann.CLong.class)) {
727                    return CLong.SIZE;
728                }
729            }
730            return 8;
731        }
732        if (type == CLong.class) {
733            return CLong.SIZE;
734        }
735        if (type == SizeT.class) {
736            return SizeT.SIZE;
737        }
738        if (type == TimeT.class) {
739            return TimeT.SIZE;
740        }
741        if (type == byte.class || type == Byte.class) {
742            return 1;
743        }
744        if (type == char.class || type == Character.class || type == short.class || type == Short.class) {
745            return 2;
746        }
747        if (ValuedEnum.class.isAssignableFrom(typec)) {
748            return 4;
749        }
750        if (Pointer.class.isAssignableFrom(typec)) {
751            return SizeT.SIZE;
752        }
753        return -1;
754    }
755
756    public static boolean equivalentTypes(Type a, Annotations aAnnotations, Type b, Annotations bAnnotations) {
757        return equivalentTypes(a, getTypeClass(a), aAnnotations, b, getTypeClass(b), bAnnotations);
758    }
759
760    public static class JavaTypeRef extends TypeRef {
761
762        java.lang.reflect.Type type;
763        Class<? extends Annotation>[] annotations;
764
765        @Override
766        public StringBuilder getQualifiedName(StringBuilder b, boolean generic) {
767            return b.append(getFullClassName(this.type));
768        }
769
770        @Override
771        public boolean matches(Type type, Annotations annotations) {
772            Class<?> tc = getTypeClass(this.type);
773            Class<?> typec = getTypeClass(type);
774            if (typec == null)
775                return true; // TODO check
776            if (tc == typec || tc.equals(typec)) {
777                return true;
778            }
779
780            if ((type == Long.TYPE && Pointer.class.isAssignableFrom(tc))
781                    || (Pointer.class.isAssignableFrom(typec) && tc == Long.TYPE)) {
782                return true;
783            }
784
785            return equivalentTypes(type, typec, annotations, this.type, tc, null); // TODO isAssignableFrom or the opposite, depending on context
786        }
787
788        @Override
789        public String toString() {
790            StringBuilder b = new StringBuilder();
791            for (Class<?> ann : annotations) {
792                b.append(ann.getSimpleName()).append(' ');
793            }
794            b.append((type instanceof Class<?>) ? ((Class<?>) type).getSimpleName() : type.toString());
795            return b.toString();
796        }
797    }
798
799    public interface IdentLike {
800    }
801
802    public static class Ident implements IdentLike {
803
804        Object simpleName;
805        TemplateArg[] templateArguments;
806
807        public Ident(String simpleName, TemplateArg... templateArguments) {
808            this.simpleName = simpleName;
809            this.templateArguments = templateArguments;
810        }
811
812        @Override
813        public boolean equals(Object o) {
814            if (o == null || !(o instanceof Ident)) {
815                return false;
816            }
817
818            Ident ident = (Ident) o;
819            if (!simpleName.equals(ident.simpleName)) {
820                return false;
821            }
822
823            int n = templateArguments.length;
824            if (ident.templateArguments.length != n) {
825                return false;
826            }
827
828            for (int i = 0; i < n; i++) {
829                if (!templateArguments[i].equals(ident.templateArguments[i])) {
830                    return false;
831                }
832            }
833
834            return true;
835        }
836
837        @Override
838        public String toString() {
839            StringBuilder b = new StringBuilder();
840
841            b.append(simpleName);
842            appendTemplateArgs(b, templateArguments);
843            return b.toString();
844        }
845    }
846
847    public static class ClassRef extends TypeRef {
848
849        private TypeRef enclosingType;
850        //private Object simpleName;
851        //TemplateArg[] templateArguments;
852        final Ident ident;
853
854        public ClassRef(Ident ident) {
855            this.ident = ident;
856        }
857
858        public StringBuilder getQualifiedName(StringBuilder b, boolean generic) {
859            if (getEnclosingType() instanceof ClassRef) {
860                getEnclosingType().getQualifiedName(b, generic).append('$');
861            } else if (getEnclosingType() instanceof NamespaceRef) {
862                getEnclosingType().getQualifiedName(b, generic).append('.');
863            }
864            b.append(ident.simpleName);
865            if (generic && ident.templateArguments != null) {
866                int args = 0;
867                for (int i = 0, n = ident.templateArguments.length; i < n; i++) {
868                    TemplateArg arg = ident.templateArguments[i];
869                    if (!(arg instanceof TypeRef)) {
870                        continue;
871                    }
872
873                    if (args == 0) {
874                        b.append('<');
875                    } else {
876                        b.append(", ");
877                    }
878                    ((TypeRef) arg).getQualifiedName(b, generic);
879                    args++;
880                }
881                if (args > 0) {
882                    b.append('>');
883                }
884            }
885            return b;
886        }
887
888        public Ident getIdent() {
889            return ident;
890        }
891
892        public void setEnclosingType(TypeRef enclosingType) {
893            this.enclosingType = enclosingType;
894        }
895
896        public TypeRef getEnclosingType() {
897            return enclosingType;
898        }
899
900        @Override
901        public boolean matches(Type type, Annotations annotations) {
902            Class<?> typeClass = getTypeClass(type);
903            if (typeClass == null) {
904                return false;
905            }
906            String fullName = getFullClassName(
907                ValuedEnum.class.isAssignableFrom(typeClass) ?
908                    normalize(Utils.getUniqueParameterizedTypeParameter(type)) :
909                    typeClass);
910            String qname = getQualifiedName(false);
911            if (fullName != null && fullName.equals(qname)) {
912                return true;
913            }
914
915            return false;
916        }
917
918        @Override
919        public String toString() {
920            StringBuilder b = new StringBuilder();
921
922            if (enclosingType != null) {
923                b.append(enclosingType).append('.');
924            }
925
926            b.append(ident);
927            return b.toString();
928        }
929    }
930
931    static void appendTemplateArgs(StringBuilder b, Object[] params) {
932        if (params == null || params.length == 0) {
933            return;
934        }
935        appendArgs(b, '<', '>', params);
936    }
937
938    static void appendArgs(StringBuilder b, char pre, char post, Object[] params) {
939        b.append(pre);
940        if (params != null) {
941            for (int i = 0; i < params.length; i++) {
942                if (i != 0) {
943                    b.append(", ");
944                }
945                b.append(params[i]);
946            }
947        }
948        b.append(post);
949    }
950
951    public static class FunctionTypeRef extends TypeRef {
952
953        MemberRef function;
954
955        public FunctionTypeRef(MemberRef function) {
956            this.function = function;
957        }
958
959        @Override
960        public StringBuilder getQualifiedName(StringBuilder b, boolean generic) {
961            // TODO Auto-generated method stub
962            return null;
963        }
964
965        @Override
966        public boolean matches(Type type, Annotations annotations) {
967            Class<?> typeClass = getTypeClass(type);
968            if (!Callback.class.isAssignableFrom(typeClass))
969                return false;
970            Method method = CRuntime.getInstance().getFastestCallbackMethod(typeClass);
971            if (method == null)
972                return false;
973            return function.matches(method);
974        }
975
976        @Override
977        public String toString() {
978            return function.toString();
979        }
980    }
981
982    public enum SpecialName implements IdentLike {
983
984        Constructor("", true, true),
985        SpecialConstructor("", true, true),
986        Destructor("", true, true),
987        SelfishDestructor("", true, true),
988        DeletingDestructor("", true, true),
989        New("new", true, true),
990        Delete("delete", true, true),
991        NewArray("new[]", true, true),
992        DeleteArray("delete[]", true, true),
993        VFTable("vftable", false, true),
994        VBTable("vbtable", false, true),
995        VCall("vcall", false, false), // What is that ???
996        TypeOf("typeof", false, false),
997        ScalarDeletingDestructor("'scalar deleting destructor'", true, true),
998        VectorDeletingDestructor("'vector deleting destructor'", true, true),
999        OperatorAssign("operator=", true, true),
1000        OperatorRShift("operator>>", true, true),
1001        OperatorDivideAssign("operator/=", true, true),
1002        OperatorModuloAssign("operator%=", true, true),
1003        OperatorRShiftAssign("operator>>=", true, true),
1004        OperatorLShiftAssign("operator<<=", true, true),
1005        OperatorBitAndAssign("operator&=", true, true),
1006        OperatorBitOrAssign("operator|=", true, true),
1007        OperatorXORAssign("operator^=", true, true),
1008        OperatorLShift("operator<<", true, true),
1009        OperatorLogicNot("operator!", true, true),
1010        OperatorEquals("operator==", true, true),
1011        OperatorDifferent("operator!=", true, true),
1012        OperatorSquareBrackets("operator[]", true, true),
1013        OperatorCast("'some cast operator'", true, true),
1014        OperatorArrow("operator->", true, true),
1015        OperatorMultiply("operator*", true, true),
1016        OperatorIncrement("operator++", true, true),
1017        OperatorDecrement("operator--", true, true),
1018        OperatorSubstract("operator-", true, true),
1019        OperatorAdd("operator+", true, true),
1020        OperatorBitAnd("operator&=", true, true),
1021        /// Member pointer selector
1022        OperatorArrowStar("operator->*", true, true),
1023        OperatorDivide("operator/", true, true),
1024        OperatorModulo("operator%", true, true),
1025        OperatorLower("operator<", true, true),
1026        OperatorLowerEquals("operator<=", true, true),
1027        OperatorGreater("operator>", true, true),
1028        OperatorGreaterEquals("operator>=", true, true),
1029        OperatorComma("operator,", true, true),
1030        OperatorParenthesis("operator()", true, true),
1031        OperatorBitNot("operator~", true, true),
1032        OperatorXOR("operator^", true, true),
1033        OperatorBitOr("operator|", true, true),
1034        OperatorLogicAnd("operator&&", true, true),
1035        OperatorLogicOr("operator||", true, true),
1036        OperatorMultiplyAssign("operator*=", true, true),
1037        OperatorAddAssign("operator+=", true, true),
1038        OperatorSubstractAssign("operator-=", true, true);
1039
1040        private SpecialName(String name, boolean isFunction, boolean isMember) {
1041            this.name = name;
1042            this.isFunction = isFunction;
1043            this.isMember = isMember;
1044        }
1045        final String name;
1046        final boolean isFunction;
1047        final boolean isMember;
1048
1049        @Override
1050        public String toString() {
1051            return name;
1052        }
1053
1054        public boolean isFunction() {
1055            return isFunction;
1056        }
1057
1058        public boolean isMember() {
1059            return isMember;
1060        }
1061    }
1062
1063    public static class MemberRef {
1064
1065        private Integer argumentsStackSize;
1066        private TypeRef enclosingType;
1067        private TypeRef valueType;
1068        private IdentLike memberName;
1069        Boolean isStatic, isProtected, isPrivate;
1070        public int modifiers;
1071        public TypeRef[] paramTypes, throwTypes;
1072        TemplateArg[] templateArguments;
1073        public Style callingConvention;
1074
1075        public void setTemplateArguments(TemplateArg[] templateArguments) {
1076            this.templateArguments = templateArguments;
1077        }
1078
1079        public Integer getArgumentsStackSize() {
1080            return argumentsStackSize;
1081        }
1082
1083        public void setArgumentsStackSize(Integer argumentsStackSize) {
1084            this.argumentsStackSize = argumentsStackSize;
1085        }
1086
1087        protected boolean matchesEnclosingType(Type type) {
1088            return getEnclosingType() != null && getEnclosingType().matches(type, annotations(type));
1089        }
1090
1091        protected boolean matchesVirtualTable(Type type) {
1092            return memberName == SpecialName.VFTable && matchesEnclosingType(type);
1093        }
1094
1095        protected boolean matchesConstructor(Type type, java.lang.reflect.Constructor<?> constr) {
1096            if (memberName != SpecialName.Constructor) {
1097                return false;
1098            }
1099
1100            if (!matchesEnclosingType(type)) {
1101                return false;
1102            }
1103
1104            Template temp = Utils.getClass(type).getAnnotation(Template.class);
1105            Annotation[][] anns = constr.getParameterAnnotations();
1106            Type[] parameterTypes = constr.getGenericParameterTypes();
1107
1108            int overrideOffset = Utils.getEnclosedConstructorParametersOffset(constr);
1109            if (!matchesArgs(parameterTypes, anns, overrideOffset + (temp == null ? 0 : temp.value().length))) {
1110                return false;
1111            }
1112
1113            return true;
1114        }
1115
1116        protected boolean matchesDestructor(Type type) {
1117            return memberName == SpecialName.Destructor && matchesEnclosingType(type);
1118        }
1119
1120        static boolean hasInstance(Object[] array, Class<? extends Annotation>... cs) {
1121            for (Object o : array) {
1122                for (Class<?> c : cs) {
1123                    if (c.isInstance(o)) {
1124                        return true;
1125                    }
1126                }
1127            }
1128            return false;
1129        }
1130
1131        static int getArgumentsStackSize(Method method) {
1132            int total = 0;
1133            Type[] paramTypes = method.getGenericParameterTypes();
1134            Annotation[][] anns = method.getParameterAnnotations();
1135            for (int iArg = 0, nArgs = paramTypes.length; iArg < nArgs; iArg++) {
1136                Class<?> paramType = getTypeClass(paramTypes[iArg]);
1137                if (paramType == int.class) {
1138                    total += 4;
1139                } else if (paramType == long.class) {
1140                    Annotation[] as = anns[iArg];
1141                    if (isAnnotationPresent(Ptr.class, as) || isAnnotationPresent(org.bridj.ann.CLong.class, as)) //if (hasInstance(anns[iArg], Ptr.class, CLong.class))
1142                    {
1143                        total += Pointer.SIZE;
1144                    } else {
1145                        total += 8;
1146                    }
1147                } else if (paramType == float.class) {
1148                    total += 4;
1149                } else if (paramType == double.class) {
1150                    total += 8;
1151                } else if (paramType == byte.class) {
1152                    total += 1;
1153                } else if (paramType == char.class) {
1154                    total += Platform.WCHAR_T_SIZE;
1155                } else if (paramType == CLong.class) {
1156                    total += Platform.CLONG_SIZE;
1157                } else if (paramType == SizeT.class) {
1158                    total += Platform.SIZE_T_SIZE;
1159                } else if (paramType == TimeT.class) {
1160                    total += Platform.TIME_T_SIZE;
1161                } else if (paramType == short.class) {
1162                    total += 2;
1163                } else if (paramType == boolean.class) {
1164                    total += 1;
1165                } else if (Pointer.class.isAssignableFrom(paramType)) {
1166                    total += Pointer.SIZE;
1167                } else if (NativeObject.class.isAssignableFrom(paramType)) {
1168                    total += ((CRuntime) BridJ.getRuntime(paramType)).sizeOf(paramTypes[iArg], null);
1169                } else if (FlagSet.class.isAssignableFrom(paramType)) {
1170                    total += 4; // TODO
1171                } else {
1172                    throw new RuntimeException("Type not handled : " + paramType.getName());
1173                }
1174            }
1175            return total;
1176        }
1177
1178        protected boolean matches(Method method) {
1179
1180            if (memberName instanceof SpecialName) {
1181                return false; // use matchesConstructor... 
1182            }
1183            if (!matchesEnclosingType(method)) {
1184                return false;
1185            }
1186
1187            return matchesSignature(method);
1188        }
1189
1190        public boolean matchesEnclosingType(Method method) {
1191            TypeRef et = getEnclosingType();
1192            if (et == null) {
1193                return true;
1194            }
1195
1196            Annotations annotations = annotations(method);
1197            Class dc = method.getDeclaringClass();
1198            do {
1199                if (et.matches(dc, annotations)) {
1200                    return true;
1201                }
1202
1203                dc = dc.getSuperclass();
1204            } while (dc != null && dc != Object.class);
1205
1206            return false;
1207        }
1208
1209        public boolean matchesSignature(Method method) {
1210
1211            if (getArgumentsStackSize() != null && getArgumentsStackSize().intValue() != getArgumentsStackSize(method)) {
1212                return false;
1213            }
1214
1215            if (getMemberName() != null && !getMemberName().toString().equals(getMethodName(method))) {
1216                return false;
1217            }
1218
1219            if (getValueType() != null && !getValueType().matches(method.getReturnType(), annotations(method))) {
1220                return false;
1221            }
1222
1223            Template temp = method.getAnnotation(Template.class);
1224            Annotation[][] anns = method.getParameterAnnotations();
1225//            Class<?>[] methodArgTypes = method.getParameterTypes();
1226            Type[] parameterTypes = method.getGenericParameterTypes();
1227            //boolean hasThisAsFirstArgument = BridJ.hasThisAsFirstArgument(method);//methodArgTypes, anns, true);
1228
1229            if (paramTypes != null && !matchesArgs(parameterTypes, anns, temp == null ? 0 : temp.value().length))///*, hasThisAsFirstArgument*/))
1230            {
1231                return false;
1232            }
1233
1234            //int thisDirac = hasThisAsFirstArgument ? 1 : 0;
1235            /*
1236             switch (type) {
1237             case Constructor:
1238             case Destructor:
1239             Annotation ann = method.getAnnotation(type == SpecialName.Constructor ? Constructor.class : Destructor.class);
1240             if (ann == null)
1241             return false;
1242             if (!hasThisAsFirstArgument)
1243             return false;
1244             if (methodArgTypes.length - thisDirac != 0 )
1245             return false;
1246             break;
1247             case InstanceMethod:
1248             if (!hasThisAsFirstArgument)
1249             return false;
1250             break;
1251             case StaticMethod:
1252             if (hasThisAsFirstArgument)
1253             return false;
1254             break;
1255             }*/
1256
1257            return true;
1258        }
1259
1260        private boolean matchesArgs(Type[] parameterTypes, Annotation[][] anns, int offset) {
1261            int totalArgs = offset;
1262            for (int i = 0, n = templateArguments == null ? 0 : templateArguments.length; i < n; i++) {
1263                if (totalArgs >= parameterTypes.length) {
1264                    return false;
1265                }
1266
1267                Type paramType = parameterTypes[offset + i];
1268
1269                TemplateArg arg = templateArguments[i];
1270                if (arg instanceof TypeRef) {
1271                    if (!paramType.equals(Class.class)) {
1272                        return false;
1273                    }
1274                } else if (arg instanceof Constant) {
1275                    try {
1276                        getTypeClass(paramType).cast(((Constant) arg).value);
1277                    } catch (ClassCastException ex) {
1278                        return false;
1279                    }
1280                }
1281                totalArgs++;
1282            }
1283
1284            for (int i = 0, n = paramTypes == null ? 0 : paramTypes.length; i < n; i++) {
1285                if (totalArgs >= parameterTypes.length) {
1286                    return false;
1287                }
1288
1289                TypeRef paramType = paramTypes[i];
1290                Type parameterType = parameterTypes[totalArgs];
1291                if (!paramType.matches(parameterType, annotations(anns == null ? null : anns[i]))) {
1292                    return false;
1293                }
1294
1295                totalArgs++;
1296            }
1297
1298            if (totalArgs != parameterTypes.length) {
1299                BridJ.error("Not enough args for " + this);
1300                return false;
1301            }
1302
1303            return true;
1304        }
1305
1306        @Override
1307        public String toString() {
1308            StringBuilder b = new StringBuilder();
1309
1310            b.append(valueType).append(' ');
1311            boolean nameWritten = false;
1312            if (enclosingType != null) {
1313                b.append(enclosingType);
1314                b.append('.');
1315                if (memberName instanceof SpecialName) {
1316                    switch ((SpecialName) memberName) {
1317                        case Destructor:
1318                            b.append('~');
1319                        case Constructor:
1320                            b.append(((ClassRef) enclosingType).ident.simpleName);
1321                            nameWritten = true;
1322                            break;
1323                    }
1324                }
1325            }
1326            if (!nameWritten) {
1327                b.append(memberName);
1328            }
1329
1330            appendTemplateArgs(b, templateArguments);
1331            appendArgs(b, '(', ')', paramTypes);
1332            return b.toString();
1333        }
1334
1335        public void setMemberName(IdentLike memberName) {
1336            this.memberName = memberName;
1337        }
1338
1339        public IdentLike getMemberName() {
1340            return memberName;
1341        }
1342
1343        public void setValueType(TypeRef valueType) {
1344            this.valueType = valueType;
1345        }
1346
1347        public TypeRef getValueType() {
1348            return valueType;
1349        }
1350
1351        public void setEnclosingType(TypeRef enclosingType) {
1352            this.enclosingType = enclosingType;
1353        }
1354
1355        public TypeRef getEnclosingType() {
1356            return enclosingType;
1357        }
1358    }
1359}