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}