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.cpp;
032
033import org.bridj.SizeT;
034import java.util.Set;
035import org.bridj.ann.Template;
036import org.bridj.DynamicFunction;
037import org.bridj.demangling.Demangler.IdentLike;
038import org.bridj.demangling.Demangler.MemberRef;
039import org.bridj.util.Pair;
040import java.lang.reflect.Constructor;
041import org.bridj.DynamicFunctionFactory;
042import org.bridj.ann.Convention;
043import org.bridj.Callback;
044import org.bridj.Platform;
045import static org.bridj.util.Utils.*;
046import java.io.FileNotFoundException;
047import java.lang.reflect.Method;
048import java.lang.reflect.Modifier;
049import java.lang.reflect.GenericDeclaration;
050import java.lang.reflect.AnnotatedElement;
051import java.lang.reflect.InvocationTargetException;
052import java.lang.reflect.ParameterizedType;
053import java.lang.reflect.TypeVariable;
054import java.util.HashMap;
055import java.util.Map;
056
057import org.bridj.BridJ;
058import static org.bridj.BridJ.*;
059import org.bridj.JNI;
060import org.bridj.MethodCallInfo;
061import org.bridj.NativeLibrary;
062import org.bridj.NativeObject;
063import org.bridj.Pointer;
064import org.bridj.PointerIO;
065import static org.bridj.demangling.Demangler.getClassName;
066import static org.bridj.demangling.Demangler.getMethodName;
067
068import static org.bridj.dyncall.DyncallLibrary.*;
069
070import org.bridj.demangling.Demangler.Symbol;
071import org.bridj.NativeEntities.Builder;
072import org.bridj.ann.Virtual;
073import org.bridj.CRuntime;
074import org.bridj.NativeLibrary.SymbolAccepter;
075import org.bridj.util.Utils;
076import java.lang.reflect.Type;
077import java.util.ArrayList;
078import java.util.List;
079import java.util.Arrays;
080import java.util.Collections;
081import java.util.HashSet;
082import java.util.TreeMap;
083import org.bridj.ann.Convention.Style;
084import org.bridj.demangling.Demangler.SpecialName;
085import static org.bridj.Pointer.*;
086import org.bridj.StructObject;
087import org.bridj.ann.Name;
088import org.bridj.demangling.Demangler;
089
090/**
091 * C++ runtime (derives from the C runtime).<br>
092 * Deals with registration and lifecycle of C++ classes and methods (virtual or
093 * not).
094 *
095 * @author ochafik
096 */
097public class CPPRuntime extends CRuntime {
098
099    public static final int DEFAULT_CONSTRUCTOR = -1, SKIP_CONSTRUCTOR = -2;
100
101    public static CPPRuntime getInstance() {
102        return BridJ.getRuntimeByRuntimeClass(CPPRuntime.class);
103    }
104
105    @Override
106    public Type getType(NativeObject instance) {
107        if (!(instance instanceof CPPObject)) {
108            return super.getType(instance);
109        }
110
111        Class<?> cls = instance.getClass();
112        return new CPPType(cls, getTemplateParameters((CPPObject) instance, cls));
113    }
114
115    public static Object[] getTemplateParameters(CPPObject object, Class<?> typeClass) {
116        synchronized (object) {
117            Object[] params = null;
118            if (object.templateParameters != null) {
119                params = object.templateParameters.get(typeClass);
120            }
121            return params;// == null ? new Object[0] : params;
122        }
123    }
124
125    public static Type[] getTemplateTypeParameters(CPPObject object, Type type) {
126        if (!(type instanceof ParameterizedType)) {
127            return new Type[0];
128        }
129        Class<?> typeClass = Utils.getClass(type);
130        ParameterizedType pt = (ParameterizedType) type;
131        Object[] params = getTemplateParameters(object, typeClass);
132        Template t = typeClass.getAnnotation(Template.class);
133        if (t == null) {
134            throw new RuntimeException("No " + Template.class.getName() + " annotation on class " + typeClass.getName());
135        }
136        if (t.paramNames().length != params.length) {
137            throw new RuntimeException(Template.class.getName() + " annotation's paramNames on class " + typeClass.getName() + " (" + Arrays.asList(t.paramNames()) + " does not match count of actual template params " + Arrays.asList(params));
138        }
139        if (t.paramNames().length != t.value().length) {
140            throw new RuntimeException(Template.class.getName() + " annotation's paramNames and value lengths on class " + typeClass.getName() + " don't match");
141        }
142        int typeParamCount = pt.getActualTypeArguments().length;
143        Type[] ret = new Type[typeParamCount];
144        int typeParamIndex = 0;
145        for (int i = 0, n = params.length; i < typeParamCount; i++) {
146            Object value = t.value()[i];
147            if (Type.class.isInstance(value)) {
148                ret[typeParamIndex++] = (Type) value;
149            }
150        }
151        assert typeParamIndex == typeParamCount;
152        return ret; // TODO cache some of this method
153    }
154
155    public void setTemplateParameters(CPPObject object, Class<?> typeClass, Object[] params) {
156        synchronized (object) {
157            if (object.templateParameters == null) {
158                object.templateParameters = (Map) Collections.singletonMap(typeClass, params);
159            } else {
160                try {
161                    // Singleton map might not be mutable.
162                    object.templateParameters.put(typeClass, params);
163                } catch (Throwable th) {
164                    object.templateParameters = new HashMap<Class<?>, Object[]>(object.templateParameters);
165                    object.templateParameters.put(typeClass, params);
166                }
167            }
168        }
169    }
170
171    protected interface ClassTypeVariableExtractor {
172
173        Type extract(CPPObject instance);
174    }
175
176    protected interface MethodTypeVariableExtractor {
177
178        Type extract(CPPObject instance, Object[] methodTemplateParameters);
179    }
180
181    protected static int getAnnotatedTemplateTypeVariableIndexInArguments(TypeVariable<?> var) {
182        GenericDeclaration d = var.getGenericDeclaration();
183        AnnotatedElement e = (AnnotatedElement) d;
184
185        Template t = e.getAnnotation(Template.class);
186        if (t == null) {
187            throw new RuntimeException(e + " is not a C++ class template (misses the @" + Template.class.getName() + " annotation)");
188        }
189
190        int iTypeVar = Arrays.asList(d.getTypeParameters()).indexOf(var);
191        int nTypes = 0, iParam = -1;
192        Class<?>[] values = t.value();
193        for (int i = 0, n = values.length; i < n; i++) {
194            Class<?> c = values[i];
195            if (c == Class.class || c == Type.class) {
196                nTypes++;
197            }
198
199            if (nTypes == iTypeVar) {
200                iParam = i;
201                break;
202            }
203        }
204        if (iParam < 0) {
205            throw new RuntimeException("Couldn't find the type variable " + var + " (offset " + iTypeVar + ") in the @" + Template.class.getName() + " annotation : " + Arrays.asList(values));
206        }
207
208        return iParam;
209    }
210
211    protected ClassTypeVariableExtractor createClassTypeVariableExtractor(TypeVariable<Class<?>> var) {
212        final Class<?> typeClass = var.getGenericDeclaration();
213        final int iTypeInParams = getAnnotatedTemplateTypeVariableIndexInArguments(var);
214        return new ClassTypeVariableExtractor() {
215            public Type extract(CPPObject instance) {
216                typeClass.cast(instance);
217                Object[] params = getTemplateParameters(instance, typeClass);
218                if (params == null) {
219                    throw new RuntimeException("No type parameters found in this instance : " + instance);
220                }
221
222                return (Type) params[iTypeInParams];
223            }
224        };
225    }
226
227    protected MethodTypeVariableExtractor createMethodTypeVariableExtractor(TypeVariable<?> var) {
228        GenericDeclaration d = var.getGenericDeclaration();
229        if (d instanceof Class) {
230            final Class<?> typeClass = (Class<?>) d;
231            final ClassTypeVariableExtractor ce = createClassTypeVariableExtractor((TypeVariable) var);
232            return new MethodTypeVariableExtractor() {
233                public Type extract(CPPObject instance, Object[] methodTemplateParameters) {
234                    return ce.extract(instance);
235                }
236            };
237        } else {
238            Method method = (Method) d;
239            final Class<?> typeClass = method.getDeclaringClass();
240
241            final int iTypeInParams = getAnnotatedTemplateTypeVariableIndexInArguments(var);
242            return new MethodTypeVariableExtractor() {
243                public Type extract(CPPObject instance, Object[] methodTemplateParameters) {
244                    typeClass.cast(instance);
245                    return (Type) methodTemplateParameters[iTypeInParams];
246                }
247            };
248        }
249    }
250
251    @Override
252    public <T extends NativeObject> Class<? extends T> getActualInstanceClass(Pointer<T> pInstance, Type officialType) {
253        //String className = null;
254        // For C++ classes in general, take type info at offset -1 of vtable (if first field matches the address of a known static or dynamic virtual table) and use it to create the correct instance.
255//              Pointer<?> vptr = pInstance.getPointer(0);
256//              Symbol symbol = BridJ.getSymbolByAddress(vptr.getPeer());
257//              if (symbol != null && symbol.isVirtualTable()) {
258//                      if (symbol.enclosingType.matches(officialType))
259//                              return officialType;
260//                      
261//                      try {
262//                              Class<?> type = BridJ.getCPPRuntime().getCPPClass(symbol.enclosingType);
263//                              if (officialType == null || officialType.isAssignableFrom(type))
264//                                      return type;
265//                      } catch (ClassNotFoundException ex) {}
266//                      return officialType;
267//                      
268//                      /*long tinf = JNI.get_pointer(ptr - Pointer.SIZE);
269//                      symbol = BridJ.getSymbolByAddress(tinf);
270//                      if (symbol != null && symbol.isTypeInfo()) {
271//                              
272//                      }*/
273//              }
274        // For Objective-C classes, use "const char* className = class_getName([yourObject class]);" and match to registered classes or more
275        // Bundle auto-generated type mappings files : bridj::CPPTest=org.bridj.test.cpp.CPPTest
276        // 
277        return Utils.getClass(officialType);
278    }
279    Map<Class<?>, Integer> virtualMethodsCounts = new HashMap<Class<?>, Integer>();
280
281    public int getVirtualMethodsCount(Class<?> type) {
282        Integer count = virtualMethodsCounts.get(type);
283        if (count == null) {
284            List<VirtMeth> mets = new ArrayList<VirtMeth>();
285            listVirtualMethods(type, mets);
286
287            // TODO unify this !
288            virtualMethodsCounts.put(type, count = mets.size());
289        }
290        return count;
291    }
292
293    protected static class VirtMeth {
294
295        Method implementation, definition;
296    }
297
298    protected void listVirtualMethods(Class<?> type, List<VirtMeth> out) {
299        if (!CPPObject.class.isAssignableFrom(type)) {
300            return;
301        }
302
303        Class<?> sup = type.getSuperclass();
304        if (sup != CPPObject.class) {
305            listVirtualMethods(sup, out);
306        }
307
308        int nParentMethods = out.size();
309
310        Map<Integer, VirtMeth> newVirtuals = new TreeMap<Integer, VirtMeth>();
311
312        methods:
313        for (Method method : type.getDeclaredMethods()) {
314            String methodName = getMethodName(method);
315            Type[] methodParameterTypes = method.getGenericParameterTypes();
316            for (int iParentMethod = 0; iParentMethod < nParentMethods; iParentMethod++) {
317                VirtMeth pvm = out.get(iParentMethod);
318                Method parentMethod = pvm.definition;
319                if (parentMethod.getDeclaringClass() == type) {
320                    continue; // was just added in the same listVirtualMethods call !
321                }
322                //if (parentMethod.getAnnotation(Virtual.class) == null)
323                //    continue; // not a virtual method, too bad
324                if (getMethodName(parentMethod).equals(methodName) && isOverridenSignature(parentMethod.getGenericParameterTypes(), methodParameterTypes, 0)) {
325                    VirtMeth vm = new VirtMeth();
326                    vm.definition = pvm.definition;
327                    vm.implementation = method;
328                    out.set(iParentMethod, vm);
329                    continue methods;
330                }
331            }
332
333            Virtual virtual = method.getAnnotation(Virtual.class);
334            if (virtual != null) {
335                VirtMeth vm = new VirtMeth();
336                vm.definition = vm.implementation = method;
337                newVirtuals.put(virtual.value(), vm);
338            }
339        }
340        out.addAll(newVirtuals.values());
341    }
342
343    @Override
344    protected void registerNativeMethod(Class<?> type, NativeLibrary typeLibrary, Method method, NativeLibrary methodLibrary, Builder builder, MethodCallInfoBuilder methodCallInfoBuilder) throws FileNotFoundException {
345
346        int modifiers = method.getModifiers();
347        boolean isCPPClass = CPPObject.class.isAssignableFrom(method.getDeclaringClass());
348
349//              Annotation[][] anns = method.getParameterAnnotations();
350        if (!isCPPClass) {
351            super.registerNativeMethod(type, typeLibrary, method, methodLibrary, builder, methodCallInfoBuilder);
352            return;
353        }
354
355        MethodCallInfo mci = methodCallInfoBuilder.apply(method);
356
357        Virtual va = method.getAnnotation(Virtual.class);
358        if (va == null) {
359            Symbol symbol = methodLibrary.getSymbol(method);
360            mci.setForwardedPointer(symbol == null ? 0 : symbol.getAddress());
361            if (mci.getForwardedPointer() == 0) {
362                assert error("Method " + method.toGenericString() + " is not virtual but its address could not be resolved in the library.");
363                return;
364            }
365            if (Modifier.isStatic(modifiers)) {
366                builder.addFunction(mci);
367                if (debug) {
368                    info("Registering " + method + " as function or static C++ method " + symbol.getName());
369                }
370            } else {
371                builder.addFunction(mci);
372                if (debug) {
373                    info("Registering " + method + " as C++ method " + symbol.getName());
374                }
375            }
376        } else {
377            if (Modifier.isStatic(modifiers)) {
378                warning("Method " + method.toGenericString() + " is native and maps to a function, but is not static.");
379            }
380
381            int theoreticalVirtualIndex = va.value();
382            int theoreticalAbsoluteVirtualIndex = theoreticalVirtualIndex < 0 ? - 1 : getAbsoluteVirtualIndex(method, theoreticalVirtualIndex, type);
383
384            int absoluteVirtualIndex;
385
386            Pointer<Pointer<?>> pVirtualTable = isCPPClass && typeLibrary != null ? (Pointer) pointerToAddress(getVirtualTable(type, typeLibrary), Pointer.class) : null;
387            if (pVirtualTable == null) {
388                if (theoreticalAbsoluteVirtualIndex < 0) {
389                    error("Method " + method.toGenericString() + " is virtual but the virtual table of class " + type.getName() + " was not found and the virtual method index is not provided in its @Virtual annotation.");
390                    return;
391                }
392                absoluteVirtualIndex = theoreticalAbsoluteVirtualIndex;
393            } else {
394                int guessedAbsoluteVirtualIndex = getPositionInVirtualTable(pVirtualTable, method, typeLibrary);
395                if (guessedAbsoluteVirtualIndex < 0) {
396                    if (theoreticalAbsoluteVirtualIndex < 0) {
397                        error("Method " + method.toGenericString() + " is virtual but its position could not be found in the virtual table.");
398                        return;
399                    } else {
400                        absoluteVirtualIndex = theoreticalAbsoluteVirtualIndex;
401                    }
402                } else {
403                    if (theoreticalAbsoluteVirtualIndex >= 0 && guessedAbsoluteVirtualIndex != theoreticalAbsoluteVirtualIndex) {
404                        warning("Method " + method.toGenericString() + " has @Virtual annotation indicating virtual index " + theoreticalAbsoluteVirtualIndex + ", but analysis of the actual virtual table rather indicates it has index " + guessedAbsoluteVirtualIndex + " (using the guess)");
405                    }
406                    absoluteVirtualIndex = guessedAbsoluteVirtualIndex;
407                }
408            }
409            mci.setVirtualIndex(absoluteVirtualIndex);
410            if (debug) {
411                info("Registering " + method.toGenericString() + " as virtual C++ method with absolute virtual table index = " + absoluteVirtualIndex);
412            }
413            builder.addVirtualMethod(mci);
414        }
415    }
416
417    int getAbsoluteVirtualIndex(Method method, int virtualIndex, Class<?> type) {
418        Class<?> superclass = type.getSuperclass();
419        int virtualOffset = getVirtualMethodsCount(superclass);
420        boolean isNewVirtual = true;
421        if (superclass != null) {
422            try {
423                // TODO handle polymorphism in overloads :
424                superclass.getMethod(getMethodName(method), method.getParameterTypes());
425                isNewVirtual = false;
426            } catch (NoSuchMethodException ex) {
427            }
428        }
429        int absoluteVirtualIndex = isNewVirtual ? virtualOffset + virtualIndex : virtualIndex;
430        return absoluteVirtualIndex;
431    }
432
433    public static class MemoryOperators {
434
435        protected DynamicFunction<Pointer<?>> newFct;
436        protected DynamicFunction<Pointer<?>> newArrayFct;
437        protected DynamicFunction<Void> deleteFct;
438        protected DynamicFunction<Void> deleteArrayFct;
439
440        protected MemoryOperators() {
441        }
442
443        public MemoryOperators(NativeLibrary library) {
444            for (Symbol sym : library.getSymbols()) {
445                try {
446                    MemberRef parsedRef = sym.getParsedRef();
447                    IdentLike n = parsedRef.getMemberName();
448
449                    if (SpecialName.New.equals(n)) {
450                        newFct = pointerToAddress(sym.getAddress()).asDynamicFunction(null, Pointer.class, SizeT.class);
451                    } else if (SpecialName.NewArray.equals(n)) {
452                        newFct = pointerToAddress(sym.getAddress()).asDynamicFunction(null, Pointer.class, SizeT.class);
453                    } else if (SpecialName.Delete.equals(n)) {
454                        newFct = pointerToAddress(sym.getAddress()).asDynamicFunction(null, Void.class, Pointer.class);
455                    } else if (SpecialName.DeleteArray.equals(n)) {
456                        newFct = pointerToAddress(sym.getAddress()).asDynamicFunction(null, Void.class, Pointer.class);
457                    }
458
459                } catch (Exception ex) {
460                }
461            }
462        }
463
464        public Pointer<?> cppNew(long size) {
465            return newFct.apply(new SizeT(size));
466        }
467
468        public Pointer<?> cppNewArray(long size) {
469            return newArrayFct.apply(new SizeT(size));
470        }
471
472        public void cppDelete(Pointer<?> ptr) {
473            deleteFct.apply(ptr);
474        }
475
476        public void cppDeleteArray(Pointer<?> ptr) {
477            deleteArrayFct.apply(ptr);
478        }
479    }
480    volatile MemoryOperators memoryOperators;
481
482    public synchronized MemoryOperators getMemoryOperators() {
483        if (memoryOperators == null) {
484            try {
485                NativeLibrary libStdCpp = BridJ.getNativeLibrary("stdc++");
486                memoryOperators = new MemoryOperators(libStdCpp);
487            } catch (Exception ex) {
488                BridJ.error(null, ex);
489            }
490        }
491        return memoryOperators;
492    }
493
494    int getPositionInVirtualTable(Method method, NativeLibrary library) {
495        Class<?> type = method.getDeclaringClass();
496        Pointer<Pointer<?>> pVirtualTable = (Pointer) pointerToAddress(getVirtualTable(type, library), Pointer.class);
497        return getPositionInVirtualTable(pVirtualTable, method, library);
498    }
499
500    public int getPositionInVirtualTable(Pointer<Pointer<?>> pVirtualTable, Method method, NativeLibrary library) {
501        //Pointer<?> typeInfo = pVirtualTable.get(1);
502        int methodsOffset = 0;//library.isMSVC() ? 0 : -2;///2;
503        String className = getClassName(method.getDeclaringClass());
504        for (int iVirtual = 0;; iVirtual++) {
505            Pointer<?> pMethod = pVirtualTable.get(methodsOffset + iVirtual);
506            String virtualMethodName = pMethod == null ? null : library.getSymbolName(pMethod.getPeer());
507            //System.out.println("#\n# At index " + methodsOffset + " + " + iVirtual + " of vptr for class " + className + ", found symbol " + Long.toHexString(pMethod.getPeer()) + " = '" + virtualMethodName + "'\n#");
508            if (virtualMethodName == null) {
509                if (debug) {
510                    info("\tVtable(" + className + ")[" + iVirtual + "] = null");
511                }
512                return -1;
513            }
514            try {
515                MemberRef mr = library.parseSymbol(virtualMethodName);
516                if (debug) {
517                    info("\tVtable(" + className + ")[" + iVirtual + "] = " + virtualMethodName + " = " + mr);
518                }
519                if (mr != null && mr.matchesSignature(method)) {
520                    return iVirtual;
521                } else if (library.isMSVC() && !mr.matchesEnclosingType(method)) {
522                    break; // no NULL terminator in MSVC++ vtables, so we have to guess when we've reached the end
523                }
524            } catch (Demangler.DemanglingException ex) {
525                BridJ.warning("Failed to demangle '" + virtualMethodName + "' during inspection of virtual table for '" + method.toGenericString() + "' : " + ex);
526            }
527
528        }
529        return -1;
530    }
531
532    static int getDefaultDyncallCppConvention() {
533        int convention = DC_CALL_C_DEFAULT;
534        if (!Platform.is64Bits() && Platform.isWindows()) {
535            convention = DC_CALL_C_X86_WIN32_THIS_MS;
536        }
537        return convention;
538    }
539
540    private String ptrToString(Pointer<?> ptr, NativeLibrary library) {
541        return ptr == null ? "null" : Long.toHexString(ptr.getPeer()) + " (" + library.getSymbolName(ptr.getPeer()) + ")";
542    }
543
544    @Convention(Style.ThisCall)
545    public abstract static class CPPDestructor extends Callback {
546
547        public abstract void destroy(long peer);
548    }
549    Set<Type> typesThatDontNeedASyntheticVirtualTable = new HashSet<Type>();
550    Map<Type, VTable> syntheticVirtualTables = new HashMap<Type, VTable>();
551
552    protected boolean installRegularVTablePtr(Type type, NativeLibrary library, Pointer<?> peer) {
553        long vtablePtr = getVirtualTable(type, library);
554        if (vtablePtr != 0) {
555            if (BridJ.debug) {
556                BridJ.info("Installing regular vtable pointer " + Pointer.pointerToAddress(vtablePtr) + " to instance at " + peer + " (type = " + Utils.toString(type) + ")");
557            }
558            peer.setSizeT(vtablePtr);
559            return true;
560        }
561        return false;
562    }
563
564    protected boolean installSyntheticVTablePtr(Type type, NativeLibrary library, Pointer<?> peer) {
565        synchronized (syntheticVirtualTables) {
566            VTable vtable = syntheticVirtualTables.get(type);
567            if (vtable == null) {
568                if (!typesThatDontNeedASyntheticVirtualTable.contains(type)) {
569                    List<VirtMeth> methods = new ArrayList<VirtMeth>();
570                    listVirtualMethods(Utils.getClass(type), methods);
571                    boolean needsASyntheticVirtualTable = false;
572                    for (VirtMeth method : methods) {
573                        if (!Modifier.isNative(method.implementation.getModifiers())) {
574                            needsASyntheticVirtualTable = true;
575                            break;
576                        }
577                    }
578                    if (needsASyntheticVirtualTable) {
579                        Type parentType = Utils.getParent(type);
580                        Pointer<Pointer> parentVTablePtr = null;
581                        if (CPPObject.class.isAssignableFrom(Utils.getClass(parentType))) {
582                            parentVTablePtr = peer.getPointer(Pointer.class);
583                            if (BridJ.debug) {
584                                BridJ.info("Found parent virtual table pointer = " + ptrToString(parentVTablePtr, library));
585                                /*Pointer<Pointer> expectedParentVTablePtr = pointerToAddress(getVirtualTable(parentType, library), Pointer.class);
586                                 if (expectedParentVTablePtr != null && !Utils.eq(parentVTablePtr, expectedParentVTablePtr))
587                                 BridJ.warning("Weird parent virtual table pointer : expected " + ptrToString(expectedParentVTablePtr, library) + ", got " + ptrToString(parentVTablePtr, library));
588                                 */
589
590                            }
591                            //parentVTablePtr = pointerToAddress(getVirtualTable(parentType, library), Pointer.class);
592                        }
593                        syntheticVirtualTables.put(type, vtable = synthetizeVirtualTable(type, parentVTablePtr, methods, library));
594                    } else {
595                        typesThatDontNeedASyntheticVirtualTable.add(type);
596                    }
597                }
598            }
599            if (vtable != null) {
600                if (BridJ.debug) {
601                    BridJ.info("Installing synthetic vtable pointer " + vtable.ptr + " to instance at " + peer + " (type = " + Utils.toString(type) + ", " + vtable.callbacks.size() + " callbacks)");
602                }
603                peer.setPointer(vtable.ptr);
604                return vtable.ptr != null;
605            } else {
606                return false;
607            }
608        }
609    }
610
611    static class VTable {
612
613        Pointer<Pointer<?>> ptr;
614        Map<Method, Pointer<?>> callbacks = new HashMap<Method, Pointer<?>>();
615    }
616
617    protected VTable synthetizeVirtualTable(Type type, Pointer<Pointer> parentVTablePtr, List<VirtMeth> methods, NativeLibrary library) {
618        int nMethods = methods.size();
619        //Pointer<Pointer> parentVTablePtr = pointerToAddress(getVirtualTable(Utils.getParent(type), library), Pointer.class);
620        VTable vtable = new VTable();
621        vtable.ptr = allocatePointers(nMethods + 2).next(2); // leave two null pointers at index -2 and -1, to say there's no runtime type information available.
622
623        Class<?> c = Utils.getClass(type);
624        for (int iMethod = 0; iMethod < nMethods; iMethod++) {
625            VirtMeth vm = methods.get(iMethod);
626            Pointer<?> pMethod;
627            if (Modifier.isNative(vm.implementation.getModifiers())) {
628                pMethod = parentVTablePtr == null ? null : parentVTablePtr.get(iMethod);
629            } else {
630                try {
631                    MethodCallInfo mci = new MethodCallInfo(vm.implementation, vm.definition);
632                    mci.setDeclaringClass(vm.implementation.getDeclaringClass());
633                    pMethod = createCToJavaCallback(mci, c);
634                    vtable.callbacks.put(vm.implementation, pMethod);
635                } catch (Throwable th) {
636                    BridJ.error("Failed to register overridden method " + vm.implementation + " for type " + type + " (original method = " + vm.definition + ")", th);
637                    pMethod = null;
638                }
639            }
640            vtable.ptr.set(iMethod, (Pointer) pMethod);
641        }
642        return vtable;
643    }
644
645    static int getTemplateParametersCount(Class<?> typeClass) {
646        Template t = typeClass.getAnnotation(Template.class);
647        // TODO do something with these args !
648        int templateParametersCount = t == null ? 0 : t.value().length;
649        return templateParametersCount;
650    }
651    Map<Pair<Type, Integer>, DynamicFunction> constructors = new HashMap<Pair<Type, Integer>, DynamicFunction>();
652
653    DynamicFunction getConstructor(final Class<?> typeClass, final Type type, NativeLibrary lib, int constructorId) {
654        Pair<Type, Integer> key = new Pair<Type, Integer>(type, constructorId);
655        DynamicFunction constructor = constructors.get(key);
656        if (constructor == null) {
657            try {
658                final Constructor<?> constr;
659                try {
660                    constr = findConstructor(typeClass, constructorId, true);
661
662                    if (debug) {
663                        BridJ.info("Found constructor for " + Utils.toString(type) + " : " + constr);
664                    }
665                } catch (NoSuchMethodException ex) {
666                    if (debug) {
667                        BridJ.info("No constructor for " + Utils.toString(type));
668                    }
669                    return null;
670                }
671                Symbol symbol = lib == null ? null : lib.getFirstMatchingSymbol(new SymbolAccepter() {
672                    public boolean accept(Symbol symbol) {
673                        return symbol.matchesConstructor(constr.getDeclaringClass() == Utils.getClass(type) ? type : constr.getDeclaringClass() /* TODO */, constr);
674                    }
675                });
676                if (symbol == null) {
677                    if (debug) {
678                        BridJ.info("No matching constructor for " + Utils.toString(type) + " (" + constr + ")");
679                    }
680                    return null;
681                }
682
683                if (debug) {
684                    info("Registering constructor " + constr + " as " + symbol.getName());
685                }
686
687                // TODO do something with these args !
688                int templateParametersCount = getTemplateParametersCount(typeClass);
689
690                Class<?>[] consParamTypes = constr.getParameterTypes();
691                Class<?>[] consThisParamTypes = new Class[consParamTypes.length + 1 - templateParametersCount];
692                consThisParamTypes[0] = Pointer.class;
693                System.arraycopy(consParamTypes, templateParametersCount, consThisParamTypes, 1, consParamTypes.length - templateParametersCount);
694
695                DynamicFunctionFactory constructorFactory = getDynamicFunctionFactory(lib, Style.ThisCall, void.class, consThisParamTypes);
696
697                constructor = constructorFactory.newInstance(pointerToAddress(symbol.getAddress()));
698                constructors.put(key, constructor);
699            } catch (Throwable th) {
700                th.printStackTrace();
701                throw new RuntimeException("Unable to create constructor " + constructorId + " for " + type + " : " + th, th);
702            }
703        }
704        return constructor;
705    }
706    Map<Type, CPPDestructor> destructors = new HashMap<Type, CPPDestructor>();
707
708    CPPDestructor getDestructor(final Class<?> typeClass, Type type, NativeLibrary lib) {
709        CPPDestructor destructor = destructors.get(type);
710        if (destructor == null) {
711            Symbol symbol = lib.getFirstMatchingSymbol(new SymbolAccepter() {
712                public boolean accept(Symbol symbol) {
713                    return symbol.matchesDestructor(typeClass);
714                }
715            });
716            if (BridJ.debug && symbol != null) {
717                info("Registering destructor of " + Utils.toString(type) + " as " + symbol.getName());
718            }
719
720            if (symbol != null) {
721                destructors.put(type, destructor = pointerToAddress(symbol.getAddress(), CPPDestructor.class).get());
722            }
723        }
724        return destructor;
725    }
726
727    Pointer.Releaser newCPPReleaser(final Type type) {
728        try {
729            final Class<?> typeClass = Utils.getClass(type);
730            NativeLibrary lib = BridJ.getNativeLibrary(typeClass);
731            return newCPPReleaser(type, typeClass, lib);
732        } catch (Throwable th) {
733            throw new RuntimeException("Failed to create a C++ destructor for type " + Utils.toString(type) + " : " + th, th);
734        }
735    }
736
737    Pointer.Releaser newCPPReleaser(final Type type, final Class<?> typeClass, NativeLibrary lib) throws FileNotFoundException {
738        Pointer.Releaser releaser = null;
739        //final Class<?> typeClass = Utils.getClass(type);
740        //NativeLibrary lib = BridJ.getNativeLibrary(typeClass);
741        if (lib != null && BridJ.enableDestructors) {
742            final CPPDestructor destructor = getDestructor(typeClass, type, lib);
743            if (destructor != null) {
744                releaser = new Pointer.Releaser() { //@Override 
745                    public void release(Pointer<?> p) {
746                        if (BridJ.debug) {
747                            BridJ.info("Destructing instance of C++ type " + Utils.toString(type) + " (address = " + p + ", destructor = " + getPointer(destructor) + ")");
748                        }
749
750                        //System.out.println("Destructing instance of C++ type " + type + "...");
751                        long peer = p.getPeer();
752                        destructor.destroy(peer);
753                        BridJ.setJavaObjectFromNativePeer(peer, null);
754                    }
755                };
756            }
757        }
758        return releaser;
759    }
760
761    protected <T extends CPPObject> Pointer<T> newCPPInstance(T instance, final Type type, int constructorId, Object... args) {
762        Pointer<T> peer = null;
763        try {
764            final Class<T> typeClass = Utils.getClass(type);
765            NativeLibrary lib = BridJ.getNativeLibrary(typeClass);
766
767            if (BridJ.debug) {
768                info("Creating C++ instance of type " + type + " with args " + Arrays.asList(args));
769            }
770            Pointer.Releaser releaser = newCPPReleaser(type, typeClass, lib);
771
772            long size = sizeOf(type, null);
773            peer = (Pointer) Pointer.allocateBytes(PointerIO.getInstance(type), size, releaser).as(type);
774
775            DynamicFunction constructor = constructorId == SKIP_CONSTRUCTOR ? null : getConstructor(typeClass, type, lib, constructorId);
776
777            if (lib != null && CPPObject.class.isAssignableFrom(typeClass)) {
778                installRegularVTablePtr(type, lib, peer);
779            } else {
780                // TODO ObjCObject : call alloc on class type !!
781            }
782
783            // Calling the constructor with the non-template parameters :
784            if (constructor != null) {
785                Object[] consThisArgs = new Object[1 + args.length];
786                consThisArgs[0] = peer;
787                System.arraycopy(args, 0, consThisArgs, 1, args.length);
788
789                constructor.apply(consThisArgs);
790            }
791
792            // Install synthetic virtual table and associate the Java instance to the corresponding native pointer : 
793            if (CPPObject.class.isAssignableFrom(typeClass)) {
794                if (installSyntheticVTablePtr(type, lib, peer)) {
795                    BridJ.setJavaObjectFromNativePeer(peer.getPeer(), instance);
796                }
797            } else {
798                // TODO ObjCObject : call alloc on class type !!
799            }
800            return peer;
801        } catch (Exception ex) {
802            ex.printStackTrace();
803            if (peer != null) {
804                peer.release();
805            }
806            throw new RuntimeException("Failed to allocate new instance of type " + type, ex);
807        }
808    }
809    /*
810     Map<Type, Pointer<Pointer<?>>> vtablePtrs = new HashMap<Type, Pointer<Pointer<?>>>();
811     @SuppressWarnings("unchecked")
812     public
813     //Pointer<Pointer<?>>
814     long getVirtualTable(Type type, NativeLibrary library) {
815     Pointer<Pointer<?>> p = vtablePtrs.get(type);
816     if (p == null) {
817     Class<?> typeClass = Utils.getClass(type);
818     // TODO ask for real template name
819     String className = typeClass.getSimpleName();
820     String vtableSymbol;
821     if (Platform.isWindows())
822     vtableSymbol = "??_7" + className + "@@6B@";
823     else
824     vtableSymbol = "_ZTV" + className.length() + className;
825
826     long addr = library.getSymbolAddress(vtableSymbol);
827     //long addr = JNI.findSymbolInLibrary(getHandle(), vtableSymbolName);
828     //                 System.out.println(TestCPP.hex(addr));
829     //                 TestCPP.print(type.getName() + " vtable", addr, 5, 2);
830                
831     p = (Pointer)Pointer.pointerToAddress(addr, Pointer.class);
832     vtablePtrs.put(type, p);
833     }
834     return p.getPeer();
835     }*/
836    Map<Type, Long> vtables = new HashMap<Type, Long>();
837
838    long getVirtualTable(Type type, NativeLibrary library) {
839        Long vtable = vtables.get(type);
840        if (vtable == null) {
841            final Class<?> typeClass = Utils.getClass(type);
842            if (false) {
843                String className = getClassName(typeClass);
844                String vtableSymbol;
845                if (Platform.isWindows()) {
846                    vtableSymbol = "??_7" + className + "@@6B@";
847                } else {
848                    vtableSymbol = "_ZTV" + className.length() + className;
849                }
850
851                vtables.put(type, vtable = library.getSymbolAddress(vtableSymbol));
852            } else {
853                Symbol symbol = library.getFirstMatchingSymbol(new SymbolAccepter() {
854                    public boolean accept(Symbol symbol) {
855                        return symbol.matchesVirtualTable(typeClass);
856                    }
857                });
858                if (symbol != null) {
859                    if (BridJ.debug) {
860                        info("Registering vtable of " + Utils.toString(type) + " as " + symbol.getName());
861                    }
862//                    Pointer<Pointer> pp = pointerToAddress(symbol.getAddress(), Pointer.class);
863//                    
864//                    for (int i = 0; i < 6; i++) {
865//                        Pointer p = pp.get(i);
866////                        if (p == null)
867////                            break;
868//                        String n = p == null ? null : library.getSymbolName(p.getPeer());
869//                        info("\tVtable entry " + i + " = " + p + " (" + n + ")");
870////                        if (n == null)
871////                            break;
872//                    }
873                } else if (getVirtualMethodsCount(typeClass) > 0 && warnAboutMissingVTables()) {
874                    error("Failed to find a vtable for type " + Utils.toString(type));
875                }
876
877                if (symbol != null) {
878                    long address = symbol.getAddress();
879                    vtable = library.isMSVC() ? address : address + 2 * Pointer.SIZE;
880                } else {
881                    vtable = 0L;
882                }
883                vtables.put(type, vtable);//*/
884            }
885        }
886        return vtable;
887    }
888
889    protected boolean warnAboutMissingVTables() {
890        return true;
891    }
892
893    @Override
894    protected boolean shouldWarnIfNoFieldsInStruct() {
895        return false;
896    }
897
898    public class CPPTypeInfo<T extends CPPObject> extends CTypeInfo<T> {
899
900        protected final int typeParamCount;
901        protected Object[] templateParameters;
902
903        public CPPTypeInfo(Type type) {
904            super(type);
905            Class<?> typeClass = Utils.getClass(type);
906            typeParamCount = typeClass.getTypeParameters().length;
907            if (typeParamCount > 0 && !(type instanceof ParameterizedType)) {
908                throw new RuntimeException("Class " + typeClass.getName() + " takes type parameters");
909            }
910            templateParameters = getTemplateParameters(type);
911        }
912        Map<TypeVariable<Class<?>>, ClassTypeVariableExtractor> classTypeVariableExtractors;
913        Map<TypeVariable<?>, MethodTypeVariableExtractor> methodTypeVariableExtractors;
914
915        @Override
916        protected T newCastInstance() throws NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
917            if (templateParameters.length == 0) {
918                return super.newCastInstance();
919            }
920
921            Class<?> cc = getCastClass();
922            for (Constructor c : cc.getConstructors()) {
923                if (Utils.parametersComplyToSignature(templateParameters, c.getParameterTypes())) {
924                    c.setAccessible(true);
925                    T instance = (T) c.newInstance(templateParameters);
926//                    setTemplateParameters(instance, typeClass, templateParameters);
927                    return instance;
928                }
929            }
930            throw new RuntimeException("Failed to find template constructor in class " + cc.getName());
931        }
932
933        public Type resolveClassType(CPPObject instance, TypeVariable<?> var) {
934            return getClassTypeVariableExtractor((TypeVariable) var).extract(instance);
935        }
936
937        public Type resolveMethodType(CPPObject instance, Object[] methodTemplateParameters, TypeVariable<?> var) {
938            return getMethodTypeVariableExtractor(var).extract(instance, methodTemplateParameters);
939        }
940
941        protected synchronized ClassTypeVariableExtractor getClassTypeVariableExtractor(TypeVariable<Class<?>> var) {
942            if (classTypeVariableExtractors == null) {
943                classTypeVariableExtractors = new HashMap<TypeVariable<Class<?>>, ClassTypeVariableExtractor>();
944            }
945            ClassTypeVariableExtractor e = classTypeVariableExtractors.get(var);
946            if (e == null) {
947                classTypeVariableExtractors.put(var, e = createClassTypeVariableExtractor(var));
948            }
949            return e;
950        }
951
952        protected synchronized MethodTypeVariableExtractor getMethodTypeVariableExtractor(TypeVariable<?> var) {
953            if (methodTypeVariableExtractors == null) {
954                methodTypeVariableExtractors = new HashMap<TypeVariable<?>, MethodTypeVariableExtractor>();
955            }
956            MethodTypeVariableExtractor e = methodTypeVariableExtractors.get(var);
957            if (e == null) {
958                methodTypeVariableExtractors.put(var, e = createMethodTypeVariableExtractor(var));
959            }
960            return e;
961        }
962
963        @Override
964        public long sizeOf() {
965            // TODO handle template size here ? (depends on template args)
966            return super.sizeOf();
967        }
968
969        @Override
970        public T createReturnInstance() {
971            try {
972                T instance = newCastInstance();
973                initialize(instance, SKIP_CONSTRUCTOR, templateParameters);
974                //setTemplateParameters(instance, typeClass, getTemplateParameters(type));
975                return instance;
976            } catch (Throwable th) {
977                throw new RuntimeException("Failed to create a return instance for type " + Utils.toString(type) + " : " + th, th);
978            }
979        }
980
981        @Override
982        public T cast(Pointer peer) {
983            if (BridJ.isCastingNativeObjectReturnTypeInCurrentThread()) {
984                peer = peer.withReleaser(newCPPReleaser(type));
985            }
986            T instance = super.cast(peer);
987            setTemplateParameters(instance, (Class) typeClass, templateParameters);
988            return instance;
989        }
990
991        @Override
992        public void initialize(T instance, Pointer peer) {
993            setTemplateParameters(instance, typeClass, templateParameters);
994            super.initialize(instance, peer);
995        }
996
997        @SuppressWarnings("unchecked")
998        @Override
999        public void initialize(T instance, int constructorId, Object... targsAndArgs) {
1000            if (instance instanceof CPPObject) {
1001                CPPObject cppInstance = (CPPObject) instance;
1002                //instance.peer = allocate(instance.getClass(), constructorId, args);
1003                int[] position = new int[]{0};
1004
1005                //TODO
1006                //Type cppType = CPPType.parseCPPType(CPPType.cons((Class<? extends CPPObject>)typeClass, targsAndArgs), position);
1007//                assert Arrays.asList(templateParameters).equals(Arrays.asList(takeLeft(targsAndArgs, typeParamCount)));
1008//                Object[] args = takeRight(targsAndArgs, targsAndArgs.length - typeParamCount);
1009                Type cppType = type;
1010                setTemplateParameters(instance, (Class) typeClass, templateParameters);
1011                //int actualArgsOffset = position[0] - 1, nActualArgs = args.length - actualArgsOffset;
1012                //System.out.println("actualArgsOffset = " + actualArgsOffset);
1013                //Object[] actualArgs = new Object[nActualArgs];
1014                //System.arraycopy(args, actualArgsOffset, actualArgs, 0, nActualArgs);
1015
1016                setNativeObjectPeer(instance, newCPPInstance((CPPObject) instance, cppType, constructorId, targsAndArgs));
1017                super.initialize(instance, DEFAULT_CONSTRUCTOR);
1018            } else {
1019                super.initialize(instance, constructorId, targsAndArgs);
1020            }
1021        }
1022
1023        @Override
1024        public T clone(T instance) throws CloneNotSupportedException {
1025            if (instance instanceof CPPObject) {
1026                // TODO use copy constructor !!!
1027            }
1028            return super.clone(instance);
1029        }
1030
1031        @Override
1032        public void destroy(T instance) {
1033            //TODO call destructor here ? (and call here from finalizer manually created by autogenerated classes
1034        }
1035
1036        private Object[] getTemplateParameters(Type type) {
1037            if (!(type instanceof CPPType)) {
1038                return new Object[0];
1039            }
1040            return ((CPPType) type).getTemplateParameters();
1041        }
1042    }
1043    /// Needs not be fast : TypeInfo will be cached in BridJ anyway !
1044
1045    @Override
1046    public <T extends NativeObject> TypeInfo<T> getTypeInfo(final Type type) {
1047        return new CPPTypeInfo(type);
1048    }
1049
1050    @Override
1051    public Type getType(Class<?> cls, Object[] targsAndArgs, int[] typeParamCount) {
1052        Template tpl = cls.getAnnotation(Template.class);
1053        int targsCount = tpl != null ? tpl.value().length : cls.getTypeParameters().length;
1054        if (typeParamCount != null) {
1055            assert typeParamCount.length == 1;
1056            typeParamCount[0] = targsCount;
1057        }
1058        return new CPPType(cls, takeLeft(targsAndArgs, targsCount));
1059    }
1060
1061    public <T extends CPPObject> CPPTypeInfo<T> getCPPTypeInfo(final Type type) {
1062        return (CPPTypeInfo) getTypeInfo(type);
1063    }
1064}