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