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        public boolean isReference;
564        public boolean isConst;
565
566        public PointerTypeRef(TypeRef pointedType, boolean isConst, boolean isReference) {
567            assert pointedType != null;
568            this.pointedType = pointedType;
569            this.isReference = isReference;
570            this.isConst = isConst;
571        }
572
573        @Override
574        public StringBuilder getQualifiedName(StringBuilder b, boolean generic) {
575            return b.append("org.bridj.Pointer");
576        }
577
578        @Override
579        public String toString() {
580            return (isConst ? "const " : "") + pointedType + (isReference ? "&" : "*");
581        }
582
583        @Override
584        public boolean matches(Type type, Annotations annotations) {
585            if (super.matches(type, annotations))
586                return true;
587
588            if (type == Long.TYPE && annotations.isAnnotationPresent(Ptr.class))
589                return true;
590            
591            Class typeClass = getTypeClass(type);
592            if (!Pointer.class.isAssignableFrom(typeClass))
593                return false;
594//            return true;
595            Type pointedType = normalize(Utils.getUniqueParameterizedTypeParameter(type));
596            if (this.pointedType == null || this.pointedType.toString().equals("void"))
597                return pointedType == null;
598            if (pointedType == null) {
599                return true;
600            }
601            return this.pointedType.matches(pointedType, annotations);
602        }
603        
604        
605    }
606
607    protected static TypeRef pointerType(TypeRef tr, boolean isConst, boolean isReference) {
608        return new PointerTypeRef(tr, isConst, isReference);
609    }
610
611    protected static TypeRef classType(final Class<?> c, Class<? extends Annotation>... annotations) {
612        return classType(c, null, annotations);
613    }
614
615    protected static TypeRef classType(final Class<?> c, final java.lang.reflect.Type[] genericTypes, Class<? extends Annotation>... annotations) {
616        JavaTypeRef tr = new JavaTypeRef();
617        if (genericTypes == null) {
618            tr.type = c;
619        } else {
620            tr.type = new DefaultParameterizedType(c, genericTypes);
621        }
622
623        tr.annotations = annotations;
624        return tr;
625    }
626
627    protected static TypeRef simpleType(String name, TemplateArg... args) {
628        return new ClassRef(new Ident(name, args));
629    }
630
631    protected static TypeRef simpleType(Ident ident) {
632        return new ClassRef(ident);
633    }
634
635    static Class<?> getTypeClass(Type type) {
636
637        if (type instanceof Class<?>) {
638            return (Class<?>) type;
639        }
640        if (type instanceof ParameterizedType) {
641            ParameterizedType pt = (ParameterizedType) type;
642            Class<?> c = (Class<?>) pt.getRawType();
643            if (ValuedEnum.class.isAssignableFrom(c)) {
644                Type[] types = pt.getActualTypeArguments();
645                if (types == null || types.length != 1) {
646                    c = int.class;
647                } else {
648                    c = getTypeClass(pt.getActualTypeArguments()[0]);
649                }
650            }
651            return c;
652        }
653        if (type instanceof GenericArrayType) {
654            if (Object.class.isAssignableFrom(getTypeClass(((GenericArrayType) type).getGenericComponentType()))) {
655                return Object[].class;
656            }
657        }
658        return null;
659//        throw new UnsupportedOperationException("Unknown type type : " + (type == null ? null : type.getClass().getName()));
660    }
661
662    private static Type normalize(Type t) {
663        if (t instanceof WildcardType) {
664            WildcardType wt = (WildcardType) t;
665            if (wt.getLowerBounds().length == 1)
666                return normalize(wt.getLowerBounds()[0]);
667            return null;
668        }
669        Class<?> c = Utils.getClass(t);
670        if (c != null && c.isPrimitive()) {
671            if (t == float.class) return Float.class;
672            if (t == double.class) return Double.class;
673            if (t == byte.class) return Byte.class;
674            if (t == char.class) return Character.class;
675            if (t == short.class) return Short.class;
676            if (t == int.class) return Integer.class;
677            if (t == long.class) return Long.class;
678            if (t == boolean.class) return Boolean.class;
679            if (t == void.class) return Void.class;
680        }
681        return t;
682    }
683    static boolean equivalentTypes(Type a, Class ac, Annotations aAnnotations, Type b, Class bc, Annotations bAnnotations) {
684        if (normalize(a).equals(normalize(b))) {
685            return true;
686        }
687
688        if (aAnnotations != null && bAnnotations != null) {
689            if (xor(isPointerLike(a, ac, aAnnotations), isPointerLike(b, bc, bAnnotations))) {
690                return false;
691            }
692            if (xor(isCLong(a, ac, aAnnotations), isCLong(b, bc, bAnnotations))) {
693                return false;
694            }
695        }
696        int as = getIntegralSize(a, ac, aAnnotations);
697        int bs = getIntegralSize(b, bc, bAnnotations);
698        return as != -1 && as == bs;
699    }
700    
701    static boolean xor(boolean a, boolean b) {
702        return a && !b || !a && b;
703    }
704    static boolean isPointerLike(Type type, Class typec, Annotations annotations) {
705        if (type == long.class || type == Long.class) {
706            return annotations == null && Pointer.SIZE == 8 ||
707                annotations != null && annotations.isAnnotationPresent(Ptr.class) &&
708                    !annotations.isAnnotationPresent(org.bridj.ann.CLong.class);
709        }
710        return type == SizeT.class || Pointer.class.isAssignableFrom(typec);
711    }
712
713    static boolean isCLong(Type type, Class typec, Annotations annotations) {
714        if (type == long.class || type == Long.class) {
715            return annotations == null && CLong.SIZE == 8 ||
716                annotations != null && annotations.isAnnotationPresent(org.bridj.ann.CLong.class);
717        }
718        return type == CLong.class;
719    }
720
721    static int getIntegralSize(Type type, Class typec, Annotations annotations) {
722        if (type == int.class || type == Integer.class) {
723            return 4;
724        }
725        if (type == long.class || type == Long.class) {
726            if (annotations != null) {
727                if (annotations.isAnnotationPresent(Ptr.class)) {
728                    return Pointer.SIZE;
729                }
730                if (annotations.isAnnotationPresent(org.bridj.ann.CLong.class)) {
731                    return CLong.SIZE;
732                }
733            }
734            return 8;
735        }
736        if (type == CLong.class) {
737            return CLong.SIZE;
738        }
739        if (type == SizeT.class) {
740            return SizeT.SIZE;
741        }
742        if (type == TimeT.class) {
743            return TimeT.SIZE;
744        }
745        if (type == byte.class || type == Byte.class) {
746            return 1;
747        }
748        if (type == char.class || type == Character.class || type == short.class || type == Short.class) {
749            return 2;
750        }
751        if (ValuedEnum.class.isAssignableFrom(typec)) {
752            return 4;
753        }
754        if (Pointer.class.isAssignableFrom(typec)) {
755            return SizeT.SIZE;
756        }
757        return -1;
758    }
759
760    public static boolean equivalentTypes(Type a, Annotations aAnnotations, Type b, Annotations bAnnotations) {
761        return equivalentTypes(a, getTypeClass(a), aAnnotations, b, getTypeClass(b), bAnnotations);
762    }
763
764    public static class JavaTypeRef extends TypeRef {
765
766        java.lang.reflect.Type type;
767        Class<? extends Annotation>[] annotations;
768
769        @Override
770        public StringBuilder getQualifiedName(StringBuilder b, boolean generic) {
771            return b.append(getFullClassName(this.type));
772        }
773
774        @Override
775        public boolean matches(Type type, Annotations annotations) {
776            Class<?> tc = getTypeClass(this.type);
777            Class<?> typec = getTypeClass(type);
778            if (typec == null)
779                return true; // TODO check
780            if (tc == typec || tc.equals(typec)) {
781                return true;
782            }
783
784            if ((type == Long.TYPE && Pointer.class.isAssignableFrom(tc))
785                    || (Pointer.class.isAssignableFrom(typec) && tc == Long.TYPE)) {
786                return true;
787            }
788
789            return equivalentTypes(type, typec, annotations, this.type, tc, null); // TODO isAssignableFrom or the opposite, depending on context
790        }
791
792        @Override
793        public String toString() {
794            StringBuilder b = new StringBuilder();
795            for (Class<?> ann : annotations) {
796                b.append(ann.getSimpleName()).append(' ');
797            }
798            b.append((type instanceof Class<?>) ? ((Class<?>) type).getSimpleName() : type.toString());
799            return b.toString();
800        }
801    }
802
803    public interface IdentLike {
804    }
805
806    public static class Ident implements IdentLike {
807
808        Object simpleName;
809        TemplateArg[] templateArguments;
810
811        public Ident(String simpleName, TemplateArg... templateArguments) {
812            this.simpleName = simpleName;
813            this.templateArguments = templateArguments;
814        }
815
816        @Override
817        public boolean equals(Object o) {
818            if (o == null || !(o instanceof Ident)) {
819                return false;
820            }
821
822            Ident ident = (Ident) o;
823            if (!simpleName.equals(ident.simpleName)) {
824                return false;
825            }
826
827            int n = templateArguments.length;
828            if (ident.templateArguments.length != n) {
829                return false;
830            }
831
832            for (int i = 0; i < n; i++) {
833                if (!templateArguments[i].equals(ident.templateArguments[i])) {
834                    return false;
835                }
836            }
837
838            return true;
839        }
840
841        @Override
842        public String toString() {
843            StringBuilder b = new StringBuilder();
844
845            b.append(simpleName);
846            appendTemplateArgs(b, templateArguments);
847            return b.toString();
848        }
849    }
850
851    public static class ClassRef extends TypeRef {
852
853        private TypeRef enclosingType;
854        //private Object simpleName;
855        //TemplateArg[] templateArguments;
856        final Ident ident;
857
858        public ClassRef(Ident ident) {
859            this.ident = ident;
860        }
861
862        public StringBuilder getQualifiedName(StringBuilder b, boolean generic) {
863            if (getEnclosingType() instanceof ClassRef) {
864                getEnclosingType().getQualifiedName(b, generic).append('$');
865            } else if (getEnclosingType() instanceof NamespaceRef) {
866                getEnclosingType().getQualifiedName(b, generic).append('.');
867            }
868            b.append(ident.simpleName);
869            if (generic && ident.templateArguments != null) {
870                int args = 0;
871                for (int i = 0, n = ident.templateArguments.length; i < n; i++) {
872                    TemplateArg arg = ident.templateArguments[i];
873                    if (!(arg instanceof TypeRef)) {
874                        continue;
875                    }
876
877                    if (args == 0) {
878                        b.append('<');
879                    } else {
880                        b.append(", ");
881                    }
882                    ((TypeRef) arg).getQualifiedName(b, generic);
883                    args++;
884                }
885                if (args > 0) {
886                    b.append('>');
887                }
888            }
889            return b;
890        }
891
892        public Ident getIdent() {
893            return ident;
894        }
895
896        public void setEnclosingType(TypeRef enclosingType) {
897            this.enclosingType = enclosingType;
898        }
899
900        public TypeRef getEnclosingType() {
901            return enclosingType;
902        }
903
904        @Override
905        public boolean matches(Type type, Annotations annotations) {
906            Class<?> typeClass = getTypeClass(type);
907            if (typeClass == null) {
908                return false;
909            }
910            String fullName = getFullClassName(
911                ValuedEnum.class.isAssignableFrom(typeClass) ?
912                    normalize(Utils.getUniqueParameterizedTypeParameter(type)) :
913                    typeClass);
914            String qname = getQualifiedName(false);
915            if (fullName != null && fullName.equals(qname)) {
916                return true;
917            }
918
919            return false;
920        }
921
922        @Override
923        public String toString() {
924            StringBuilder b = new StringBuilder();
925
926            if (enclosingType != null) {
927                b.append(enclosingType).append('.');
928            }
929
930            b.append(ident);
931            return b.toString();
932        }
933    }
934
935    static void appendTemplateArgs(StringBuilder b, Object[] params) {
936        if (params == null || params.length == 0) {
937            return;
938        }
939        appendArgs(b, '<', '>', params);
940    }
941
942    static void appendArgs(StringBuilder b, char pre, char post, Object[] params) {
943        b.append(pre);
944        if (params != null) {
945            for (int i = 0; i < params.length; i++) {
946                if (i != 0) {
947                    b.append(", ");
948                }
949                b.append(params[i]);
950            }
951        }
952        b.append(post);
953    }
954
955    public static class FunctionTypeRef extends TypeRef {
956
957        MemberRef function;
958
959        public FunctionTypeRef(MemberRef function) {
960            this.function = function;
961        }
962
963        @Override
964        public StringBuilder getQualifiedName(StringBuilder b, boolean generic) {
965            // TODO Auto-generated method stub
966            return null;
967        }
968
969        @Override
970        public boolean matches(Type type, Annotations annotations) {
971            Class<?> typeClass = getTypeClass(type);
972            if (!Callback.class.isAssignableFrom(typeClass))
973                return false;
974            Method method = CRuntime.getInstance().getFastestCallbackMethod(typeClass);
975            if (method == null)
976                return false;
977            return function.matches(method);
978        }
979
980        @Override
981        public String toString() {
982            return function.toString();
983        }
984    }
985
986    public enum SpecialName implements IdentLike {
987
988        Constructor("", true, true),
989        SpecialConstructor("", true, true),
990        Destructor("", true, true),
991        SelfishDestructor("", true, true),
992        DeletingDestructor("", true, true),
993        New("new", true, true),
994        Delete("delete", true, true),
995        NewArray("new[]", true, true),
996        DeleteArray("delete[]", true, true),
997        VFTable("vftable", false, true),
998        VBTable("vbtable", false, true),
999        VCall("vcall", false, false), // What is that ???
1000        TypeOf("typeof", false, false),
1001        ScalarDeletingDestructor("'scalar deleting destructor'", true, true),
1002        VectorDeletingDestructor("'vector deleting destructor'", true, true),
1003        OperatorAssign("operator=", true, true),
1004        OperatorRShift("operator>>", true, true),
1005        OperatorDivideAssign("operator/=", true, true),
1006        OperatorModuloAssign("operator%=", true, true),
1007        OperatorRShiftAssign("operator>>=", true, true),
1008        OperatorLShiftAssign("operator<<=", true, true),
1009        OperatorBitAndAssign("operator&=", true, true),
1010        OperatorBitOrAssign("operator|=", true, true),
1011        OperatorXORAssign("operator^=", true, true),
1012        OperatorLShift("operator<<", true, true),
1013        OperatorLogicNot("operator!", true, true),
1014        OperatorEquals("operator==", true, true),
1015        OperatorDifferent("operator!=", true, true),
1016        OperatorSquareBrackets("operator[]", true, true),
1017        OperatorCast("'some cast operator'", true, true),
1018        OperatorArrow("operator->", true, true),
1019        OperatorMultiply("operator*", true, true),
1020        OperatorIncrement("operator++", true, true),
1021        OperatorDecrement("operator--", true, true),
1022        OperatorSubstract("operator-", true, true),
1023        OperatorAdd("operator+", true, true),
1024        OperatorBitAnd("operator&=", true, true),
1025        /// Member pointer selector
1026        OperatorArrowStar("operator->*", true, true),
1027        OperatorDivide("operator/", true, true),
1028        OperatorModulo("operator%", true, true),
1029        OperatorLower("operator<", true, true),
1030        OperatorLowerEquals("operator<=", true, true),
1031        OperatorGreater("operator>", true, true),
1032        OperatorGreaterEquals("operator>=", true, true),
1033        OperatorComma("operator,", true, true),
1034        OperatorParenthesis("operator()", true, true),
1035        OperatorBitNot("operator~", true, true),
1036        OperatorXOR("operator^", true, true),
1037        OperatorBitOr("operator|", true, true),
1038        OperatorLogicAnd("operator&&", true, true),
1039        OperatorLogicOr("operator||", true, true),
1040        OperatorMultiplyAssign("operator*=", true, true),
1041        OperatorAddAssign("operator+=", true, true),
1042        OperatorSubstractAssign("operator-=", true, true);
1043
1044        private SpecialName(String name, boolean isFunction, boolean isMember) {
1045            this.name = name;
1046            this.isFunction = isFunction;
1047            this.isMember = isMember;
1048        }
1049        final String name;
1050        final boolean isFunction;
1051        final boolean isMember;
1052
1053        @Override
1054        public String toString() {
1055            return name;
1056        }
1057
1058        public boolean isFunction() {
1059            return isFunction;
1060        }
1061
1062        public boolean isMember() {
1063            return isMember;
1064        }
1065    }
1066
1067    public static class MemberRef {
1068
1069        private Integer argumentsStackSize;
1070        private TypeRef enclosingType;
1071        private TypeRef valueType;
1072        private IdentLike memberName;
1073        Boolean isStatic, isProtected, isPrivate;
1074        public int modifiers;
1075        public TypeRef[] paramTypes, throwTypes;
1076        TemplateArg[] templateArguments;
1077        public Style callingConvention;
1078
1079        public void setTemplateArguments(TemplateArg[] templateArguments) {
1080            this.templateArguments = templateArguments;
1081        }
1082
1083        public Integer getArgumentsStackSize() {
1084            return argumentsStackSize;
1085        }
1086
1087        public void setArgumentsStackSize(Integer argumentsStackSize) {
1088            this.argumentsStackSize = argumentsStackSize;
1089        }
1090
1091        protected boolean matchesEnclosingType(Type type) {
1092            return getEnclosingType() != null && getEnclosingType().matches(type, annotations(type));
1093        }
1094
1095        protected boolean matchesVirtualTable(Type type) {
1096            return memberName == SpecialName.VFTable && matchesEnclosingType(type);
1097        }
1098
1099        protected boolean matchesConstructor(Type type, java.lang.reflect.Constructor<?> constr) {
1100            if (memberName != SpecialName.Constructor) {
1101                return false;
1102            }
1103
1104            if (!matchesEnclosingType(type)) {
1105                return false;
1106            }
1107
1108            Template temp = Utils.getClass(type).getAnnotation(Template.class);
1109            Annotation[][] anns = constr.getParameterAnnotations();
1110            Type[] parameterTypes = constr.getGenericParameterTypes();
1111
1112            int overrideOffset = Utils.getEnclosedConstructorParametersOffset(constr);
1113            if (!matchesArgs(parameterTypes, anns, overrideOffset + (temp == null ? 0 : temp.value().length))) {
1114                return false;
1115            }
1116
1117            return true;
1118        }
1119
1120        protected boolean matchesDestructor(Type type) {
1121            return memberName == SpecialName.Destructor && matchesEnclosingType(type);
1122        }
1123
1124        static boolean hasInstance(Object[] array, Class<? extends Annotation>... cs) {
1125            for (Object o : array) {
1126                for (Class<?> c : cs) {
1127                    if (c.isInstance(o)) {
1128                        return true;
1129                    }
1130                }
1131            }
1132            return false;
1133        }
1134
1135        static int getArgumentsStackSize(Method method) {
1136            int total = 0;
1137            Type[] paramTypes = method.getGenericParameterTypes();
1138            Annotation[][] anns = method.getParameterAnnotations();
1139            for (int iArg = 0, nArgs = paramTypes.length; iArg < nArgs; iArg++) {
1140                Class<?> paramType = getTypeClass(paramTypes[iArg]);
1141                if (paramType == int.class) {
1142                    total += 4;
1143                } else if (paramType == long.class) {
1144                    Annotation[] as = anns[iArg];
1145                    if (isAnnotationPresent(Ptr.class, as) || isAnnotationPresent(org.bridj.ann.CLong.class, as)) //if (hasInstance(anns[iArg], Ptr.class, CLong.class))
1146                    {
1147                        total += Pointer.SIZE;
1148                    } else {
1149                        total += 8;
1150                    }
1151                } else if (paramType == float.class) {
1152                    total += 4;
1153                } else if (paramType == double.class) {
1154                    total += 8;
1155                } else if (paramType == byte.class) {
1156                    total += 1;
1157                } else if (paramType == char.class) {
1158                    total += Platform.WCHAR_T_SIZE;
1159                } else if (paramType == CLong.class) {
1160                    total += Platform.CLONG_SIZE;
1161                } else if (paramType == SizeT.class) {
1162                    total += Platform.SIZE_T_SIZE;
1163                } else if (paramType == TimeT.class) {
1164                    total += Platform.TIME_T_SIZE;
1165                } else if (paramType == short.class) {
1166                    total += 2;
1167                } else if (paramType == boolean.class) {
1168                    total += 1;
1169                } else if (Pointer.class.isAssignableFrom(paramType)) {
1170                    total += Pointer.SIZE;
1171                } else if (NativeObject.class.isAssignableFrom(paramType)) {
1172                    total += ((CRuntime) BridJ.getRuntime(paramType)).sizeOf(paramTypes[iArg], null);
1173                } else if (FlagSet.class.isAssignableFrom(paramType)) {
1174                    total += 4; // TODO
1175                } else {
1176                    throw new RuntimeException("Type not handled : " + paramType.getName());
1177                }
1178            }
1179            return total;
1180        }
1181
1182        protected boolean matches(Method method) {
1183
1184            if (memberName instanceof SpecialName) {
1185                return false; // use matchesConstructor... 
1186            }
1187            if (!matchesEnclosingType(method)) {
1188                return false;
1189            }
1190
1191            return matchesSignature(method);
1192        }
1193
1194        public boolean matchesEnclosingType(Method method) {
1195            TypeRef et = getEnclosingType();
1196            if (et == null) {
1197                return true;
1198            }
1199
1200            Annotations annotations = annotations(method);
1201            Class dc = method.getDeclaringClass();
1202            do {
1203                if (et.matches(dc, annotations)) {
1204                    return true;
1205                }
1206
1207                dc = dc.getSuperclass();
1208            } while (dc != null && dc != Object.class);
1209
1210            return false;
1211        }
1212
1213        public boolean matchesSignature(Method method) {
1214
1215            if (getArgumentsStackSize() != null && getArgumentsStackSize().intValue() != getArgumentsStackSize(method)) {
1216                return false;
1217            }
1218
1219            if (getMemberName() != null && !getMemberName().toString().equals(getMethodName(method))) {
1220                return false;
1221            }
1222
1223            if (getValueType() != null && !getValueType().matches(method.getReturnType(), annotations(method))) {
1224                return false;
1225            }
1226
1227            Template temp = method.getAnnotation(Template.class);
1228            Annotation[][] anns = method.getParameterAnnotations();
1229//            Class<?>[] methodArgTypes = method.getParameterTypes();
1230            Type[] parameterTypes = method.getGenericParameterTypes();
1231            //boolean hasThisAsFirstArgument = BridJ.hasThisAsFirstArgument(method);//methodArgTypes, anns, true);
1232
1233            if (paramTypes != null && !matchesArgs(parameterTypes, anns, temp == null ? 0 : temp.value().length))///*, hasThisAsFirstArgument*/))
1234            {
1235                return false;
1236            }
1237
1238            //int thisDirac = hasThisAsFirstArgument ? 1 : 0;
1239            /*
1240             switch (type) {
1241             case Constructor:
1242             case Destructor:
1243             Annotation ann = method.getAnnotation(type == SpecialName.Constructor ? Constructor.class : Destructor.class);
1244             if (ann == null)
1245             return false;
1246             if (!hasThisAsFirstArgument)
1247             return false;
1248             if (methodArgTypes.length - thisDirac != 0 )
1249             return false;
1250             break;
1251             case InstanceMethod:
1252             if (!hasThisAsFirstArgument)
1253             return false;
1254             break;
1255             case StaticMethod:
1256             if (hasThisAsFirstArgument)
1257             return false;
1258             break;
1259             }*/
1260
1261            return true;
1262        }
1263
1264        private boolean matchesArgs(Type[] parameterTypes, Annotation[][] anns, int offset) {
1265            int totalArgs = offset;
1266            for (int i = 0, n = templateArguments == null ? 0 : templateArguments.length; i < n; i++) {
1267                if (totalArgs >= parameterTypes.length) {
1268                    return false;
1269                }
1270
1271                Type paramType = parameterTypes[offset + i];
1272
1273                TemplateArg arg = templateArguments[i];
1274                if (arg instanceof TypeRef) {
1275                    if (!paramType.equals(Class.class)) {
1276                        return false;
1277                    }
1278                } else if (arg instanceof Constant) {
1279                    try {
1280                        getTypeClass(paramType).cast(((Constant) arg).value);
1281                    } catch (ClassCastException ex) {
1282                        return false;
1283                    }
1284                }
1285                totalArgs++;
1286            }
1287
1288            for (int i = 0, n = paramTypes == null ? 0 : paramTypes.length; i < n; i++) {
1289                if (totalArgs >= parameterTypes.length) {
1290                    return false;
1291                }
1292
1293                TypeRef paramType = paramTypes[i];
1294                Type parameterType = parameterTypes[totalArgs];
1295                if (!paramType.matches(parameterType, annotations(anns == null ? null : anns[i]))) {
1296                    return false;
1297                }
1298
1299                totalArgs++;
1300            }
1301
1302            if (totalArgs != parameterTypes.length) {
1303                BridJ.error("Not enough args for " + this);
1304                return false;
1305            }
1306
1307            return true;
1308        }
1309
1310        @Override
1311        public String toString() {
1312            StringBuilder b = new StringBuilder();
1313
1314            b.append(valueType).append(' ');
1315            boolean nameWritten = false;
1316            if (enclosingType != null) {
1317                b.append(enclosingType);
1318                b.append('.');
1319                if (memberName instanceof SpecialName) {
1320                    switch ((SpecialName) memberName) {
1321                        case Destructor:
1322                            b.append('~');
1323                        case Constructor:
1324                            b.append(((ClassRef) enclosingType).ident.simpleName);
1325                            nameWritten = true;
1326                            break;
1327                    }
1328                }
1329            }
1330            if (!nameWritten) {
1331                b.append(memberName);
1332            }
1333
1334            appendTemplateArgs(b, templateArguments);
1335            appendArgs(b, '(', ')', paramTypes);
1336            return b.toString();
1337        }
1338
1339        public void setMemberName(IdentLike memberName) {
1340            this.memberName = memberName;
1341        }
1342
1343        public IdentLike getMemberName() {
1344            return memberName;
1345        }
1346
1347        public void setValueType(TypeRef valueType) {
1348            this.valueType = valueType;
1349        }
1350
1351        public TypeRef getValueType() {
1352            return valueType;
1353        }
1354
1355        public void setEnclosingType(TypeRef enclosingType) {
1356            this.enclosingType = enclosingType;
1357        }
1358
1359        public TypeRef getEnclosingType() {
1360            return enclosingType;
1361        }
1362    }
1363}