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            }
513            if (pMethod == null) {
514                return -1;
515            } else if (virtualMethodName == null) {
516                continue;
517            }
518            try {
519                MemberRef mr = library.parseSymbol(virtualMethodName);
520                if (debug) {
521                    info("\tVtable(" + className + ")[" + iVirtual + "] = " + virtualMethodName + " = " + mr);
522                }
523                if (mr != null && mr.matchesSignature(method)) {
524                    return iVirtual;
525                } else if (library.isMSVC() && !mr.matchesEnclosingType(method)) {
526                    break; // no NULL terminator in MSVC++ vtables, so we have to guess when we've reached the end
527                }
528            } catch (Demangler.DemanglingException ex) {
529                BridJ.warning("Failed to demangle '" + virtualMethodName + "' during inspection of virtual table for '" + method.toGenericString() + "' : " + ex);
530            }
531
532        }
533        return -1;
534    }
535
536    static int getDefaultDyncallCppConvention() {
537        int convention = DC_CALL_C_DEFAULT;
538        if (!Platform.is64Bits() && Platform.isWindows()) {
539            convention = DC_CALL_C_X86_WIN32_THIS_MS;
540        }
541        return convention;
542    }
543
544    private String ptrToString(Pointer<?> ptr, NativeLibrary library) {
545        return ptr == null ? "null" : Long.toHexString(ptr.getPeer()) + " (" + library.getSymbolName(ptr.getPeer()) + ")";
546    }
547
548    @Convention(Style.ThisCall)
549    public abstract static class CPPDestructor extends Callback {
550
551        public abstract void destroy(long peer);
552    }
553    Set<Type> typesThatDontNeedASyntheticVirtualTable = new HashSet<Type>();
554    Map<Type, VTable> syntheticVirtualTables = new HashMap<Type, VTable>();
555
556    protected boolean installRegularVTablePtr(Type type, NativeLibrary library, Pointer<?> peer) {
557        long vtablePtr = getVirtualTable(type, library);
558        if (vtablePtr != 0) {
559            if (BridJ.debug) {
560                BridJ.info("Installing regular vtable pointer " + Pointer.pointerToAddress(vtablePtr) + " to instance at " + peer + " (type = " + Utils.toString(type) + ")");
561            }
562            peer.setSizeT(vtablePtr);
563            return true;
564        }
565        return false;
566    }
567
568    protected boolean installSyntheticVTablePtr(Type type, NativeLibrary library, Pointer<?> peer) {
569        synchronized (syntheticVirtualTables) {
570            VTable vtable = syntheticVirtualTables.get(type);
571            if (vtable == null) {
572                if (!typesThatDontNeedASyntheticVirtualTable.contains(type)) {
573                    List<VirtMeth> methods = new ArrayList<VirtMeth>();
574                    listVirtualMethods(Utils.getClass(type), methods);
575                    boolean needsASyntheticVirtualTable = false;
576                    for (VirtMeth method : methods) {
577                        if (!Modifier.isNative(method.implementation.getModifiers())) {
578                            needsASyntheticVirtualTable = true;
579                            break;
580                        }
581                    }
582                    if (needsASyntheticVirtualTable) {
583                        Type parentType = Utils.getParent(type);
584                        Pointer<Pointer> parentVTablePtr = null;
585                        if (CPPObject.class.isAssignableFrom(Utils.getClass(parentType))) {
586                            parentVTablePtr = peer.getPointer(Pointer.class);
587                            if (BridJ.debug) {
588                                BridJ.info("Found parent virtual table pointer = " + ptrToString(parentVTablePtr, library));
589                                /*Pointer<Pointer> expectedParentVTablePtr = pointerToAddress(getVirtualTable(parentType, library), Pointer.class);
590                                 if (expectedParentVTablePtr != null && !Utils.eq(parentVTablePtr, expectedParentVTablePtr))
591                                 BridJ.warning("Weird parent virtual table pointer : expected " + ptrToString(expectedParentVTablePtr, library) + ", got " + ptrToString(parentVTablePtr, library));
592                                 */
593
594                            }
595                            //parentVTablePtr = pointerToAddress(getVirtualTable(parentType, library), Pointer.class);
596                        }
597                        syntheticVirtualTables.put(type, vtable = synthetizeVirtualTable(type, parentVTablePtr, methods, library));
598                    } else {
599                        typesThatDontNeedASyntheticVirtualTable.add(type);
600                    }
601                }
602            }
603            if (vtable != null) {
604                if (BridJ.debug) {
605                    BridJ.info("Installing synthetic vtable pointer " + vtable.ptr + " to instance at " + peer + " (type = " + Utils.toString(type) + ", " + vtable.callbacks.size() + " callbacks)");
606                }
607                peer.setPointer(vtable.ptr);
608                return vtable.ptr != null;
609            } else {
610                return false;
611            }
612        }
613    }
614
615    static class VTable {
616
617        Pointer<Pointer<?>> ptr;
618        Map<Method, Pointer<?>> callbacks = new HashMap<Method, Pointer<?>>();
619    }
620
621    protected VTable synthetizeVirtualTable(Type type, Pointer<Pointer> parentVTablePtr, List<VirtMeth> methods, NativeLibrary library) {
622        int nMethods = methods.size();
623        //Pointer<Pointer> parentVTablePtr = pointerToAddress(getVirtualTable(Utils.getParent(type), library), Pointer.class);
624        VTable vtable = new VTable();
625        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.
626
627        Class<?> c = Utils.getClass(type);
628        for (int iMethod = 0; iMethod < nMethods; iMethod++) {
629            VirtMeth vm = methods.get(iMethod);
630            Pointer<?> pMethod;
631            if (Modifier.isNative(vm.implementation.getModifiers())) {
632                pMethod = parentVTablePtr == null ? null : parentVTablePtr.get(iMethod);
633            } else {
634                try {
635                    MethodCallInfo mci = new MethodCallInfo(vm.implementation, vm.definition);
636                    mci.setDeclaringClass(vm.implementation.getDeclaringClass());
637                    pMethod = createCToJavaCallback(mci, c);
638                    vtable.callbacks.put(vm.implementation, pMethod);
639                } catch (Throwable th) {
640                    BridJ.error("Failed to register overridden method " + vm.implementation + " for type " + type + " (original method = " + vm.definition + ")", th);
641                    pMethod = null;
642                }
643            }
644            vtable.ptr.set(iMethod, (Pointer) pMethod);
645        }
646        return vtable;
647    }
648
649    static int getTemplateParametersCount(Class<?> typeClass) {
650        Template t = typeClass.getAnnotation(Template.class);
651        // TODO do something with these args !
652        int templateParametersCount = t == null ? 0 : t.value().length;
653        return templateParametersCount;
654    }
655    Map<Pair<Type, Integer>, DynamicFunction> constructors = new HashMap<Pair<Type, Integer>, DynamicFunction>();
656
657    DynamicFunction getConstructor(final Class<?> typeClass, final Type type, NativeLibrary lib, int constructorId) {
658        Pair<Type, Integer> key = new Pair<Type, Integer>(type, constructorId);
659        DynamicFunction constructor = constructors.get(key);
660        if (constructor == null) {
661            try {
662                final Constructor<?> constr;
663                try {
664                    constr = findConstructor(typeClass, constructorId, true);
665
666                    if (debug) {
667                        BridJ.info("Found constructor for " + Utils.toString(type) + " : " + constr);
668                    }
669                } catch (NoSuchMethodException ex) {
670                    if (debug) {
671                        BridJ.info("No constructor for " + Utils.toString(type));
672                    }
673                    return null;
674                }
675                Symbol symbol = lib == null ? null : lib.getFirstMatchingSymbol(new SymbolAccepter() {
676                    public boolean accept(Symbol symbol) {
677                        return symbol.matchesConstructor(constr.getDeclaringClass() == Utils.getClass(type) ? type : constr.getDeclaringClass() /* TODO */, constr);
678                    }
679                });
680                if (symbol == null) {
681                    if (constructorId >= 0) {
682                        throw new RuntimeException("No matching constructor for " + Utils.toString(type) + " (" + constr + ")");
683                    } else if (debug) {
684                        BridJ.info("No matching constructor for " + Utils.toString(type) + " (" + constr + ")");
685                    }
686                    return null;
687                }
688
689                if (debug) {
690                    info("Registering constructor " + constr + " as " + symbol.getName());
691                }
692
693                // TODO do something with these args !
694                int templateParametersCount = getTemplateParametersCount(typeClass);
695
696                Class<?>[] consParamTypes = constr.getParameterTypes();
697                Class<?>[] consThisParamTypes = new Class[consParamTypes.length + 1 - templateParametersCount];
698                consThisParamTypes[0] = Pointer.class;
699                System.arraycopy(consParamTypes, templateParametersCount, consThisParamTypes, 1, consParamTypes.length - templateParametersCount);
700
701                DynamicFunctionFactory constructorFactory = getDynamicFunctionFactory(lib, Style.ThisCall, void.class, consThisParamTypes);
702
703                constructor = constructorFactory.newInstance(pointerToAddress(symbol.getAddress()));
704                constructors.put(key, constructor);
705            } catch (Throwable th) {
706                th.printStackTrace();
707                throw new RuntimeException("Unable to create constructor " + constructorId + " for " + type + " : " + th, th);
708            }
709        }
710        return constructor;
711    }
712    Map<Type, CPPDestructor> destructors = new HashMap<Type, CPPDestructor>();
713
714    CPPDestructor getDestructor(final Class<?> typeClass, Type type, NativeLibrary lib) {
715        CPPDestructor destructor = destructors.get(type);
716        if (destructor == null) {
717            Symbol symbol = lib.getFirstMatchingSymbol(new SymbolAccepter() {
718                public boolean accept(Symbol symbol) {
719                    return symbol.matchesDestructor(typeClass);
720                }
721            });
722            if (BridJ.debug && symbol != null) {
723                info("Registering destructor of " + Utils.toString(type) + " as " + symbol.getName());
724            }
725
726            if (symbol != null) {
727                destructors.put(type, destructor = pointerToAddress(symbol.getAddress(), CPPDestructor.class).get());
728            }
729        }
730        return destructor;
731    }
732
733    Pointer.Releaser newCPPReleaser(final Type type) {
734        try {
735            final Class<?> typeClass = Utils.getClass(type);
736            NativeLibrary lib = BridJ.getNativeLibrary(typeClass);
737            return newCPPReleaser(type, typeClass, lib);
738        } catch (Throwable th) {
739            throw new RuntimeException("Failed to create a C++ destructor for type " + Utils.toString(type) + " : " + th, th);
740        }
741    }
742
743    Pointer.Releaser newCPPReleaser(final Type type, final Class<?> typeClass, NativeLibrary lib) throws FileNotFoundException {
744        Pointer.Releaser releaser = null;
745        //final Class<?> typeClass = Utils.getClass(type);
746        //NativeLibrary lib = BridJ.getNativeLibrary(typeClass);
747        if (lib != null && BridJ.enableDestructors) {
748            final CPPDestructor destructor = getDestructor(typeClass, type, lib);
749            if (destructor != null) {
750                releaser = new Pointer.Releaser() { //@Override 
751                    public void release(Pointer<?> p) {
752                        if (BridJ.debug) {
753                            BridJ.info("Destructing instance of C++ type " + Utils.toString(type) + " (address = " + p + ", destructor = " + getPointer(destructor) + ")");
754                        }
755
756                        //System.out.println("Destructing instance of C++ type " + type + "...");
757                        long peer = p.getPeer();
758                        destructor.destroy(peer);
759                        BridJ.setJavaObjectFromNativePeer(peer, null);
760                    }
761                };
762            }
763        }
764        return releaser;
765    }
766
767    protected <T extends CPPObject> Pointer<T> newCPPInstance(T instance, final Type type, int constructorId, Object... args) {
768        Pointer<T> peer = null;
769        try {
770            final Class<T> typeClass = Utils.getClass(type);
771            NativeLibrary lib = BridJ.getNativeLibrary(typeClass);
772
773            if (BridJ.debug) {
774                info("Creating C++ instance of type " + type + " with args " + Arrays.asList(args));
775            }
776            Pointer.Releaser releaser = newCPPReleaser(type, typeClass, lib);
777
778            long size = sizeOf(type, null);
779            peer = (Pointer) Pointer.allocateBytes(PointerIO.getInstance(type), size, releaser).as(type);
780
781            DynamicFunction constructor = constructorId == SKIP_CONSTRUCTOR ? null : getConstructor(typeClass, type, lib, constructorId);
782
783            if (lib != null && CPPObject.class.isAssignableFrom(typeClass)) {
784                installRegularVTablePtr(type, lib, peer);
785            } else {
786                // TODO ObjCObject : call alloc on class type !!
787            }
788
789            // Calling the constructor with the non-template parameters :
790            if (constructor != null) {
791                Object[] consThisArgs = new Object[1 + args.length];
792                consThisArgs[0] = peer;
793                System.arraycopy(args, 0, consThisArgs, 1, args.length);
794
795                constructor.apply(consThisArgs);
796            }
797
798            // Install synthetic virtual table and associate the Java instance to the corresponding native pointer : 
799            if (CPPObject.class.isAssignableFrom(typeClass)) {
800                if (installSyntheticVTablePtr(type, lib, peer)) {
801                    BridJ.setJavaObjectFromNativePeer(peer.getPeer(), instance);
802                }
803            } else {
804                // TODO ObjCObject : call alloc on class type !!
805            }
806            return peer;
807        } catch (Exception ex) {
808            ex.printStackTrace();
809            if (peer != null) {
810                peer.release();
811            }
812            throw new RuntimeException("Failed to allocate new instance of type " + type, ex);
813        }
814    }
815    /*
816     Map<Type, Pointer<Pointer<?>>> vtablePtrs = new HashMap<Type, Pointer<Pointer<?>>>();
817     @SuppressWarnings("unchecked")
818     public
819     //Pointer<Pointer<?>>
820     long getVirtualTable(Type type, NativeLibrary library) {
821     Pointer<Pointer<?>> p = vtablePtrs.get(type);
822     if (p == null) {
823     Class<?> typeClass = Utils.getClass(type);
824     // TODO ask for real template name
825     String className = typeClass.getSimpleName();
826     String vtableSymbol;
827     if (Platform.isWindows())
828     vtableSymbol = "??_7" + className + "@@6B@";
829     else
830     vtableSymbol = "_ZTV" + className.length() + className;
831
832     long addr = library.getSymbolAddress(vtableSymbol);
833     //long addr = JNI.findSymbolInLibrary(getHandle(), vtableSymbolName);
834     //                 System.out.println(TestCPP.hex(addr));
835     //                 TestCPP.print(type.getName() + " vtable", addr, 5, 2);
836                
837     p = (Pointer)Pointer.pointerToAddress(addr, Pointer.class);
838     vtablePtrs.put(type, p);
839     }
840     return p.getPeer();
841     }*/
842    Map<Type, Long> vtables = new HashMap<Type, Long>();
843
844    long getVirtualTable(Type type, NativeLibrary library) {
845        Long vtable = vtables.get(type);
846        if (vtable == null) {
847            final Class<?> typeClass = Utils.getClass(type);
848            if (false) {
849                String className = getClassName(typeClass);
850                String vtableSymbol;
851                if (Platform.isWindows()) {
852                    vtableSymbol = "??_7" + className + "@@6B@";
853                } else {
854                    vtableSymbol = "_ZTV" + className.length() + className;
855                }
856
857                vtables.put(type, vtable = library.getSymbolAddress(vtableSymbol));
858            } else {
859                Symbol symbol = library.getFirstMatchingSymbol(new SymbolAccepter() {
860                    public boolean accept(Symbol symbol) {
861                        return symbol.matchesVirtualTable(typeClass);
862                    }
863                });
864                if (symbol != null) {
865                    if (BridJ.debug) {
866                        info("Registering vtable of " + Utils.toString(type) + " as " + symbol.getName());
867                    }
868//                    Pointer<Pointer> pp = pointerToAddress(symbol.getAddress(), Pointer.class);
869//                    
870//                    for (int i = 0; i < 6; i++) {
871//                        Pointer p = pp.get(i);
872////                        if (p == null)
873////                            break;
874//                        String n = p == null ? null : library.getSymbolName(p.getPeer());
875//                        info("\tVtable entry " + i + " = " + p + " (" + n + ")");
876////                        if (n == null)
877////                            break;
878//                    }
879                } else if (getVirtualMethodsCount(typeClass) > 0 && warnAboutMissingVTables()) {
880                    error("Failed to find a vtable for type " + Utils.toString(type));
881                }
882
883                if (symbol != null) {
884                    long address = symbol.getAddress();
885                    vtable = library.isMSVC() ? address : address + 2 * Pointer.SIZE;
886                } else {
887                    vtable = 0L;
888                }
889                vtables.put(type, vtable);//*/
890            }
891        }
892        return vtable;
893    }
894
895    protected boolean warnAboutMissingVTables() {
896        return true;
897    }
898
899    @Override
900    protected boolean shouldWarnIfNoFieldsInStruct() {
901        return false;
902    }
903
904    public class CPPTypeInfo<T extends CPPObject> extends CTypeInfo<T> {
905
906        protected final int typeParamCount;
907        protected Object[] templateParameters;
908
909        public CPPTypeInfo(Type type) {
910            super(type);
911            Class<?> typeClass = Utils.getClass(type);
912            typeParamCount = typeClass.getTypeParameters().length;
913            if (typeParamCount > 0 && !(type instanceof ParameterizedType)) {
914                throw new RuntimeException("Class " + typeClass.getName() + " takes type parameters");
915            }
916            templateParameters = getTemplateParameters(type);
917        }
918        Map<TypeVariable<Class<?>>, ClassTypeVariableExtractor> classTypeVariableExtractors;
919        Map<TypeVariable<?>, MethodTypeVariableExtractor> methodTypeVariableExtractors;
920
921        @Override
922        protected T newCastInstance() throws NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
923            if (templateParameters.length == 0) {
924                return super.newCastInstance();
925            }
926
927            Class<?> cc = getCastClass();
928            for (Constructor c : cc.getConstructors()) {
929                if (Utils.parametersComplyToSignature(templateParameters, c.getParameterTypes())) {
930                    c.setAccessible(true);
931                    T instance = (T) c.newInstance(templateParameters);
932//                    setTemplateParameters(instance, typeClass, templateParameters);
933                    return instance;
934                }
935            }
936            throw new RuntimeException("Failed to find template constructor in class " + cc.getName());
937        }
938
939        public Type resolveClassType(CPPObject instance, TypeVariable<?> var) {
940            return getClassTypeVariableExtractor((TypeVariable) var).extract(instance);
941        }
942
943        public Type resolveMethodType(CPPObject instance, Object[] methodTemplateParameters, TypeVariable<?> var) {
944            return getMethodTypeVariableExtractor(var).extract(instance, methodTemplateParameters);
945        }
946
947        protected synchronized ClassTypeVariableExtractor getClassTypeVariableExtractor(TypeVariable<Class<?>> var) {
948            if (classTypeVariableExtractors == null) {
949                classTypeVariableExtractors = new HashMap<TypeVariable<Class<?>>, ClassTypeVariableExtractor>();
950            }
951            ClassTypeVariableExtractor e = classTypeVariableExtractors.get(var);
952            if (e == null) {
953                classTypeVariableExtractors.put(var, e = createClassTypeVariableExtractor(var));
954            }
955            return e;
956        }
957
958        protected synchronized MethodTypeVariableExtractor getMethodTypeVariableExtractor(TypeVariable<?> var) {
959            if (methodTypeVariableExtractors == null) {
960                methodTypeVariableExtractors = new HashMap<TypeVariable<?>, MethodTypeVariableExtractor>();
961            }
962            MethodTypeVariableExtractor e = methodTypeVariableExtractors.get(var);
963            if (e == null) {
964                methodTypeVariableExtractors.put(var, e = createMethodTypeVariableExtractor(var));
965            }
966            return e;
967        }
968
969        @Override
970        public long sizeOf() {
971            // TODO handle template size here ? (depends on template args)
972            return super.sizeOf();
973        }
974
975        @Override
976        public T createReturnInstance() {
977            try {
978                T instance = newCastInstance();
979                initialize(instance, SKIP_CONSTRUCTOR, templateParameters);
980                //setTemplateParameters(instance, typeClass, getTemplateParameters(type));
981                return instance;
982            } catch (Throwable th) {
983                throw new RuntimeException("Failed to create a return instance for type " + Utils.toString(type) + " : " + th, th);
984            }
985        }
986
987        @Override
988        public T cast(Pointer peer) {
989            if (BridJ.isCastingNativeObjectReturnTypeInCurrentThread()) {
990                peer = peer.withReleaser(newCPPReleaser(type));
991            }
992            T instance = super.cast(peer);
993            setTemplateParameters(instance, (Class) typeClass, templateParameters);
994            return instance;
995        }
996
997        @Override
998        public void initialize(T instance, Pointer peer) {
999            setTemplateParameters(instance, typeClass, templateParameters);
1000            super.initialize(instance, peer);
1001        }
1002
1003        @SuppressWarnings("unchecked")
1004        @Override
1005        public void initialize(T instance, int constructorId, Object... targsAndArgs) {
1006            if (instance instanceof CPPObject) {
1007                CPPObject cppInstance = (CPPObject) instance;
1008                //instance.peer = allocate(instance.getClass(), constructorId, args);
1009                int[] position = new int[]{0};
1010
1011                //TODO
1012                //Type cppType = CPPType.parseCPPType(CPPType.cons((Class<? extends CPPObject>)typeClass, targsAndArgs), position);
1013//                assert Arrays.asList(templateParameters).equals(Arrays.asList(takeLeft(targsAndArgs, typeParamCount)));
1014//                Object[] args = takeRight(targsAndArgs, targsAndArgs.length - typeParamCount);
1015                Type cppType = type;
1016                setTemplateParameters(instance, (Class) typeClass, templateParameters);
1017                //int actualArgsOffset = position[0] - 1, nActualArgs = args.length - actualArgsOffset;
1018                //System.out.println("actualArgsOffset = " + actualArgsOffset);
1019                //Object[] actualArgs = new Object[nActualArgs];
1020                //System.arraycopy(args, actualArgsOffset, actualArgs, 0, nActualArgs);
1021
1022                setNativeObjectPeer(instance, newCPPInstance((CPPObject) instance, cppType, constructorId, targsAndArgs));
1023                super.initialize(instance, DEFAULT_CONSTRUCTOR);
1024            } else {
1025                super.initialize(instance, constructorId, targsAndArgs);
1026            }
1027        }
1028
1029        @Override
1030        public T clone(T instance) throws CloneNotSupportedException {
1031            if (instance instanceof CPPObject) {
1032                // TODO use copy constructor !!!
1033            }
1034            return super.clone(instance);
1035        }
1036
1037        @Override
1038        public void destroy(T instance) {
1039            //TODO call destructor here ? (and call here from finalizer manually created by autogenerated classes
1040        }
1041
1042        private Object[] getTemplateParameters(Type type) {
1043            if (!(type instanceof CPPType)) {
1044                return new Object[0];
1045            }
1046            return ((CPPType) type).getTemplateParameters();
1047        }
1048    }
1049    /// Needs not be fast : TypeInfo will be cached in BridJ anyway !
1050
1051    @Override
1052    public <T extends NativeObject> TypeInfo<T> getTypeInfo(final Type type) {
1053        return new CPPTypeInfo(type);
1054    }
1055
1056    @Override
1057    public Type getType(Class<?> cls, Object[] targsAndArgs, int[] typeParamCount) {
1058        Template tpl = cls.getAnnotation(Template.class);
1059        int targsCount = tpl != null ? tpl.value().length : cls.getTypeParameters().length;
1060        if (typeParamCount != null) {
1061            assert typeParamCount.length == 1;
1062            typeParamCount[0] = targsCount;
1063        }
1064        return new CPPType(cls, takeLeft(targsAndArgs, targsCount));
1065    }
1066
1067    public <T extends CPPObject> CPPTypeInfo<T> getCPPTypeInfo(final Type type) {
1068        return (CPPTypeInfo) getTypeInfo(type);
1069    }
1070}