long getVirtualTable(Type type, NativeLibrary library) {
    Long vtable = vtables.get(type);
    if (vtable == null) {
      final Class<?> typeClass = Utils.getClass(type);
      if (false) {
        String className = typeClass.getSimpleName();
        String vtableSymbol;
        if (Platform.isWindows()) vtableSymbol = "??_7" + className + "@@6B@";
        else vtableSymbol = "_ZTV" + className.length() + className;

        vtables.put(type, vtable = library.getSymbolAddress(vtableSymbol));
      } else {
        Symbol symbol =
            library.getFirstMatchingSymbol(
                new SymbolAccepter() {
                  public boolean accept(Symbol symbol) {
                    return symbol.matchesVirtualTable(typeClass);
                  }
                });
        if (symbol != null) {
          if (BridJ.debug)
            info("Registering vtable of " + Utils.toString(type) + " as " + symbol.getName());
          //                    Pointer<Pointer> pp = pointerToAddress(symbol.getAddress(),
          // Pointer.class);
          //
          //                    for (int i = 0; i < 6; i++) {
          //                        Pointer p = pp.get(i);
          ////                        if (p == null)
          ////                            break;
          //                        String n = p == null ? null :
          // library.getSymbolName(p.getPeer());
          //                        info("\tVtable entry " + i + " = " + p + " (" + n + ")");
          ////                        if (n == null)
          ////                            break;
          //                    }
        } else if (getVirtualMethodsCount(typeClass) > 0)
          error("Failed to find a vtable for type " + Utils.toString(type));

        if (symbol != null) {
          long address = symbol.getAddress();
          vtable = library.isMSVC() ? address : address + 2 * Pointer.SIZE;
        } else {
          vtable = 0L;
        }
        vtables.put(type, vtable); // */
      }
    }
    return vtable;
  }
 Pointer.Releaser newCPPReleaser(final Type type) {
   try {
     final Class<?> typeClass = Utils.getClass(type);
     NativeLibrary lib = BridJ.getNativeLibrary(typeClass);
     return newCPPReleaser(type, typeClass, lib);
   } catch (Throwable th) {
     throw new RuntimeException(
         "Failed to create a C++ destructor for type " + Utils.toString(type) + " : " + th, th);
   }
 }
  protected boolean installSyntheticVTablePtr(Type type, NativeLibrary library, Pointer<?> peer) {
    synchronized (syntheticVirtualTables) {
      VTable vtable = syntheticVirtualTables.get(type);
      if (vtable == null) {
        if (!typesThatDontNeedASyntheticVirtualTable.contains(type)) {
          List<VirtMeth> methods = new ArrayList<VirtMeth>();
          listVirtualMethods(Utils.getClass(type), methods);
          boolean needsASyntheticVirtualTable = false;
          for (VirtMeth method : methods)
            if (!Modifier.isNative(method.implementation.getModifiers())) {
              needsASyntheticVirtualTable = true;
              break;
            }
          if (needsASyntheticVirtualTable) {
            Type parentType = Utils.getParent(type);
            Pointer<Pointer> parentVTablePtr = null;
            if (CPPObject.class.isAssignableFrom(Utils.getClass(parentType))) {
              parentVTablePtr = peer.getPointer(Pointer.class);
              if (BridJ.debug) {
                BridJ.info(
                    "Found parent virtual table pointer = "
                        + ptrToString(parentVTablePtr, library));
                /*Pointer<Pointer> expectedParentVTablePtr = pointerToAddress(getVirtualTable(parentType, library), Pointer.class);
                if (expectedParentVTablePtr != null && !Utils.eq(parentVTablePtr, expectedParentVTablePtr))
                    BridJ.warning("Weird parent virtual table pointer : expected " + ptrToString(expectedParentVTablePtr, library) + ", got " + ptrToString(parentVTablePtr, library));
                */

              }
              // parentVTablePtr = pointerToAddress(getVirtualTable(parentType, library),
              // Pointer.class);
            }
            syntheticVirtualTables.put(
                type, vtable = synthetizeVirtualTable(type, parentVTablePtr, methods, library));
          } else {
            typesThatDontNeedASyntheticVirtualTable.add(type);
          }
        }
      }
      if (vtable != null) {
        if (BridJ.debug)
          BridJ.info(
              "Installing synthetic vtable pointer "
                  + vtable.ptr
                  + " to instance at "
                  + peer
                  + " (type = "
                  + Utils.toString(type)
                  + ", "
                  + vtable.callbacks.size()
                  + " callbacks)");
        peer.setPointer(vtable.ptr);
        return vtable.ptr != null;
      } else return false;
    }
  }
 @Override
 public T createReturnInstance() {
   try {
     Object[] templateParameters = getTemplateParameters(type);
     T instance = (T) getCastClass().newInstance();
     initialize(instance, SKIP_CONSTRUCTOR, templateParameters);
     // setTemplateParameters(instance, typeClass, getTemplateParameters(type));
     return instance;
   } catch (Throwable th) {
     throw new RuntimeException(
         "Failed to create a return instance for type " + Utils.toString(type) + " : " + th, th);
   }
 }
 protected boolean installRegularVTablePtr(Type type, NativeLibrary library, Pointer<?> peer) {
   long vtablePtr = getVirtualTable(type, library);
   if (vtablePtr != 0) {
     if (BridJ.debug)
       BridJ.info(
           "Installing regular vtable pointer "
               + Pointer.pointerToAddress(vtablePtr)
               + " to instance at "
               + peer
               + " (type = "
               + Utils.toString(type)
               + ")");
     peer.setSizeT(vtablePtr);
     return true;
   }
   return false;
 }
  CPPDestructor getDestructor(final Class<?> typeClass, Type type, NativeLibrary lib) {
    CPPDestructor destructor = destructors.get(type);
    if (destructor == null) {
      Symbol symbol =
          lib.getFirstMatchingSymbol(
              new SymbolAccepter() {
                public boolean accept(Symbol symbol) {
                  return symbol.matchesDestructor(typeClass);
                }
              });
      if (BridJ.debug && symbol != null)
        info("Registering destructor of " + Utils.toString(type) + " as " + symbol.getName());

      if (symbol != null)
        destructors.put(
            type, destructor = pointerToAddress(symbol.getAddress(), CPPDestructor.class).get());
    }
    return destructor;
  }
  public ValueType getValueType(
      int iParam,
      int nParams,
      Class<?> c,
      Type t,
      AnnotatedElement element,
      Annotation... directAnnotations) {
    boolean isPtr = isAnnotationPresent(Ptr.class, element, directAnnotations);
    boolean isCLong = isAnnotationPresent(org.bridj.ann.CLong.class, element, directAnnotations);
    Constructor cons = getAnnotation(Constructor.class, element, directAnnotations);

    if (isPtr || cons != null || isCLong) {
      if (!(c == Long.class || c == Long.TYPE))
        throw new RuntimeException(
            "Annotation should only be used on a long parameter, not on a " + c.getName());

      if (isPtr) {
        if (!Platform.is64Bits()) direct = false;
      } else if (isCLong) {
        if (Platform.CLONG_SIZE != 8) direct = false;
      } else if (cons != null) {
        isCPlusPlus = true;
        startsWithThis = true;
        if (iParam != 0)
          throw new RuntimeException(
              "Annotation "
                  + Constructor.class.getName()
                  + " cannot have more than one (long) argument");
      }
      return ValueType.eSizeTValue;
    }
    if (c == null || c.equals(Void.TYPE)) return ValueType.eVoidValue;
    if (c == Integer.class || c == Integer.TYPE) return ValueType.eIntValue;
    if (c == Long.class || c == Long.TYPE) {
      return !isPtr || Platform.is64Bits() ? ValueType.eLongValue : ValueType.eIntValue;
    }
    if (c == Short.class || c == Short.TYPE) return ValueType.eShortValue;
    if (c == Byte.class || c == Byte.TYPE) return ValueType.eByteValue;
    if (c == Boolean.class || c == Boolean.TYPE) return ValueType.eBooleanValue;
    if (c == Float.class || c == Float.TYPE) {
      usesFloats();
      return ValueType.eFloatValue;
    }
    if (c == char.class || c == Character.TYPE) {
      if (Platform.WCHAR_T_SIZE != 2) direct = false;
      return ValueType.eWCharValue;
    }
    if (c == Double.class || c == Double.TYPE) {
      usesFloats();
      return ValueType.eDoubleValue;
    }
    if (c == CLong.class) {
      direct = false;
      return ValueType.eCLongObjectValue;
    }
    if (c == SizeT.class) {
      direct = false;
      return ValueType.eSizeTObjectValue;
    }
    if (c == TimeT.class) {
      direct = false;
      return ValueType.eTimeTObjectValue;
    }
    if (Pointer.class.isAssignableFrom(c)) {
      direct = false;
      CallIO cio = CallIO.Utils.createPointerCallIO(c, t);
      if (BridJ.veryVerbose) BridJ.info("CallIO : " + cio);
      addCallIO(cio);
      return ValueType.ePointerValue;
    }
    if (c.isArray() && iParam == nParams - 1) {
      direct = false;
      return ValueType.eEllipsis;
    }
    if (ValuedEnum.class.isAssignableFrom(c)) {
      direct = false;
      CallIO cio =
          CallIO.Utils.createValuedEnumCallIO(
              (Class) Utils.getClass(Utils.getUniqueParameterizedTypeParameter(t)));
      if (BridJ.veryVerbose) BridJ.info("CallIO : " + cio);
      addCallIO(cio);

      return ValueType.eIntFlagSet;
    }
    if (NativeObject.class.isAssignableFrom(c)) {
      Pointer<DCstruct> pStruct = null;
      if (StructObject.class.isAssignableFrom(c)) {
        StructIO io = StructIO.getInstance(c, t);
        try {
          pStruct = DyncallStructs.buildDCstruct(io.desc);
        } catch (Throwable th) {
          BridJ.error(
              "Unable to create low-level struct metadata for "
                  + Utils.toString(t)
                  + " : won't be able to use it as a by-value function argument.",
              th);
        }
      }
      addCallIO(new CallIO.NativeObjectHandler((Class<? extends NativeObject>) c, t, pStruct));
      direct = false;
      return ValueType.eNativeObjectValue;
    }

    throw new NoSuchElementException(
        "No " + ValueType.class.getSimpleName() + " for class " + c.getName());
  }
  DynamicFunction getConstructor(
      final Class<?> typeClass, final Type type, NativeLibrary lib, int constructorId) {
    Pair<Type, Integer> key = new Pair<Type, Integer>(type, constructorId);
    DynamicFunction constructor = constructors.get(key);
    if (constructor == null) {
      try {
        final Constructor<?> constr;
        try {
          constr = findConstructor(typeClass, constructorId, true);

          if (debug) BridJ.info("Found constructor for " + Utils.toString(type) + " : " + constr);
        } catch (NoSuchMethodException ex) {
          if (debug) BridJ.info("No constructor for " + Utils.toString(type));
          return null;
        }
        Symbol symbol =
            lib == null
                ? null
                : lib.getFirstMatchingSymbol(
                    new SymbolAccepter() {
                      public boolean accept(Symbol symbol) {
                        return symbol.matchesConstructor(
                            constr.getDeclaringClass() == Utils.getClass(type)
                                ? type
                                : constr.getDeclaringClass() /* TODO */,
                            constr);
                      }
                    });
        if (symbol == null) {
          if (debug)
            BridJ.info("No matching constructor for " + Utils.toString(type) + " (" + constr + ")");
          return null;
        }

        if (debug) info("Registering constructor " + constr + " as " + symbol.getName());

        // TODO do something with these args !
        int templateParametersCount = getTemplateParametersCount(typeClass);

        Class<?>[] consParamTypes = constr.getParameterTypes();
        Class<?>[] consThisParamTypes =
            new Class[consParamTypes.length + 1 - templateParametersCount];
        consThisParamTypes[0] = Pointer.class;
        System.arraycopy(
            consParamTypes,
            templateParametersCount,
            consThisParamTypes,
            1,
            consParamTypes.length - templateParametersCount);

        DynamicFunctionFactory constructorFactory =
            getDynamicFunctionFactory(lib, Style.ThisCall, void.class, consThisParamTypes);

        constructor = constructorFactory.newInstance(pointerToAddress(symbol.getAddress()));
        constructors.put(key, constructor);
      } catch (Throwable th) {
        th.printStackTrace();
        throw new RuntimeException(
            "Unable to create constructor " + constructorId + " for " + type + " : " + th, th);
      }
    }
    return constructor;
  }