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