├── .gitignore ├── APIGenerator.java ├── APIHelper.cpp ├── APIHelper.h ├── JNIBridge.cpp ├── JNIBridge.h ├── JNIBridge.java ├── LICENSE ├── Makefile ├── Makefile.android ├── Makefile.api ├── Makefile.osx ├── PrepareAndroidSDK.pm ├── Proxy.cpp ├── Proxy.h ├── build-osx.pl ├── build.pl ├── templates ├── android.os.Bundle.cpp ├── android.os.Bundle.h ├── android.view.Display.cpp ├── android.view.Display.h ├── java.lang.CharSequence.cpp ├── java.lang.CharSequence.h ├── java.lang.Number.cpp ├── java.lang.Number.h ├── java.lang.String.cpp └── java.lang.String.h └── test ├── Makefile └── Test.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /APIGenerator.java: -------------------------------------------------------------------------------- 1 | 2 | import java.lang.reflect.*; 3 | import java.io.*; 4 | import java.net.*; 5 | import java.util.*; 6 | import java.util.jar.*; 7 | import java.util.regex.*; 8 | 9 | public class APIGenerator 10 | { 11 | final static Set KEYWORDS = new HashSet(Arrays.asList(new String[] { 12 | "Assert", "asm", "namespace" 13 | })); 14 | 15 | static final Comparator CLASSNAME_COMPARATOR = new Comparator() { 16 | public int compare(Class lhs, Class rhs) { return lhs.getName().compareTo(rhs.getName()); } 17 | }; 18 | 19 | final Set m_AllClasses = new TreeSet(CLASSNAME_COMPARATOR); 20 | final Set m_VisitedClasses = new TreeSet(CLASSNAME_COMPARATOR); 21 | final Set m_DependencyChain = new LinkedHashSet(); 22 | 23 | public static void main(String[] argsArray) throws Exception 24 | { 25 | LinkedList args = new LinkedList(Arrays.asList(argsArray)); 26 | if (args.size() < 2) 27 | { 28 | System.err.format("Usage: APIGenerator \n"); 29 | System.exit(1); 30 | } 31 | String dst = args.pollFirst(); 32 | if (!new File(dst).isDirectory()) 33 | { 34 | System.err.format("Usage: APIGenerator \n"); 35 | System.err.format("%s: is not a directory.\n", dst); 36 | System.exit(1); 37 | } 38 | 39 | String jarList = args.pollFirst(); 40 | LinkedList jars = new LinkedList(); 41 | if(jarList.contains(";")) 42 | { 43 | for(String jar : jarList.split(";")) 44 | { 45 | if (!new File(jar).isFile()) 46 | { 47 | System.err.format("Usage: APIGenerator \n"); 48 | System.err.format("%s: is not a jar file.\n", jar); 49 | System.exit(1); 50 | } 51 | else 52 | { 53 | jars.push(new JarFile(jar)); 54 | } 55 | } 56 | } 57 | else if (!new File(jarList).isFile()) 58 | { 59 | System.err.format("Usage: APIGenerator \n"); 60 | System.err.format("%s: is not a jar file.\n", jarList); 61 | System.exit(1); 62 | } 63 | else 64 | { 65 | jars.push(new JarFile(jarList)); 66 | } 67 | 68 | // We need this to box proxy values 69 | args.add("::java::lang::Byte"); 70 | args.add("::java::lang::Short"); 71 | args.add("::java::lang::Integer"); 72 | args.add("::java::lang::Long"); 73 | args.add("::java::lang::Float"); 74 | args.add("::java::lang::Double"); 75 | args.add("::java::lang::Character"); 76 | args.add("::java::lang::Boolean"); 77 | args.add("::java::lang::Class"); 78 | args.add("::java::lang::NoSuchMethodError"); 79 | args.add("::java::lang::System"); 80 | 81 | APIGenerator generator = new APIGenerator(); 82 | generator.collectDependencies(jars, args); 83 | generator.print(dst); 84 | } 85 | 86 | public void collectDependencies(LinkedList files, LinkedList args) throws Exception 87 | { 88 | LinkedList urls = new LinkedList(); 89 | for(JarFile file : files) 90 | { 91 | urls.push(new File(file.getName()).toURI().toURL()); 92 | } 93 | URLClassLoader customClasses = new URLClassLoader(urls.toArray(new URL[urls.size()]), null); 94 | 95 | for(JarFile file : files) 96 | { 97 | System.err.format("Loading classes from '%s'\n", file.getName()); 98 | 99 | Enumeration entries = file.entries(); 100 | while (entries.hasMoreElements()) 101 | { 102 | String name = entries.nextElement().getName(); 103 | if (name.endsWith(".class")) 104 | { 105 | try 106 | { 107 | m_AllClasses.add(customClasses.loadClass(name.substring(0, name.length() - 6).replace("/", "."))); 108 | } catch (Throwable ignore) {} 109 | } 110 | } 111 | } 112 | 113 | System.err.format("Searching for candidates\n"); 114 | for (String arg : args) 115 | { 116 | Pattern pattern = Pattern.compile(arg); 117 | for (Class clazz : m_AllClasses) 118 | { 119 | String cppClassName = getClassName(clazz); 120 | if (!pattern.matcher(cppClassName).matches()) 121 | continue; 122 | int nClasses = m_DependencyChain.size(); 123 | collectDependencies(clazz); 124 | System.err.format("[%d][%d]\t%s\n", m_DependencyChain.size(), m_DependencyChain.size() - nClasses, cppClassName); 125 | break; 126 | } 127 | } 128 | } 129 | 130 | public void collectDependencies(Class clazz) throws Exception 131 | { 132 | clazz = collectDirectDependencies(clazz); 133 | if (clazz == null) 134 | return; 135 | 136 | if (m_VisitedClasses.contains(clazz)) 137 | return; 138 | 139 | m_VisitedClasses.add(clazz); 140 | 141 | Class superClass = clazz.getSuperclass(); 142 | if (superClass != null) 143 | collectDependencies(superClass); 144 | 145 | for (Class interfaceClass : clazz.getInterfaces()) 146 | collectDependencies(interfaceClass); 147 | 148 | for (Field field : getDeclaredFieldsSorted(clazz)) 149 | { 150 | if (!isValid(field)) 151 | continue; 152 | collectDependencies(field.getType()); 153 | } 154 | 155 | for (Method method : getDeclaredMethodsSorted(clazz)) 156 | { 157 | if (!isValid(method)) 158 | continue; 159 | for (Class paramType : method.getParameterTypes()) 160 | collectDependencies(paramType); 161 | collectDependencies(method.getReturnType()); 162 | } 163 | 164 | for (Constructor constructor : getDeclaredConstructorsSorted(clazz)) 165 | { 166 | if (!isValid(constructor)) 167 | continue; 168 | for (Class paramType : constructor.getParameterTypes()) 169 | collectDependencies(paramType); 170 | } 171 | } 172 | 173 | private Class collectDirectDependencies(Class clazz) throws Exception 174 | { 175 | while (clazz.isArray()) 176 | clazz = clazz.getComponentType(); 177 | 178 | if (!isValid(clazz)) 179 | return null; 180 | 181 | if (m_DependencyChain.contains(clazz)) 182 | return clazz; 183 | 184 | Class superClass = clazz.getSuperclass(); 185 | if (superClass != null) 186 | collectDirectDependencies(superClass); 187 | 188 | m_DependencyChain.add(clazz); 189 | 190 | return clazz; 191 | } 192 | 193 | private String safe(String name, Class clazz) 194 | { 195 | if (clazz != null && name.equals(getSimpleName(clazz))) 196 | name = "x" + name; 197 | while (KEYWORDS.contains(name)) 198 | name = "x" + name; 199 | return name; 200 | } 201 | 202 | private String getMethodName(Method method) 203 | { 204 | String name = capitalize(method.getName().replace('$', '_')); 205 | return safe(name, method.getDeclaringClass()); 206 | } 207 | 208 | private String getFieldName(Field field) 209 | { 210 | String name = "f" + capitalize(field.getName().replace('$', '_')); 211 | return safe(name, field.getDeclaringClass()); 212 | } 213 | 214 | private String capitalize(String str) 215 | { 216 | char[] chars = str.toCharArray(); 217 | chars[0] = Character.toUpperCase(chars[0]); 218 | return new String(chars); 219 | } 220 | 221 | private String getSimpleName(Class clazz) 222 | { 223 | if (clazz.isArray()) 224 | return "Array< " + getClassName(clazz.getComponentType()) + " >"; 225 | if (clazz.isPrimitive()) 226 | return "j" + clazz.getSimpleName(); 227 | String fullClassName = clazz.getName(); 228 | return safe(fullClassName.substring(fullClassName.lastIndexOf('.') + 1), null).replace('$', '_'); 229 | } 230 | 231 | private String getPrimitiveType(Class clazz) 232 | { 233 | if (clazz.isArray()) 234 | { 235 | Class elementClazz = clazz.getComponentType(); 236 | if (elementClazz.isPrimitive()) 237 | return "j" + elementClazz.getSimpleName() + "Array"; 238 | return "jobjectArray"; 239 | } 240 | if (clazz.isPrimitive()) 241 | return "j" + clazz.getSimpleName(); 242 | return "jobject"; 243 | } 244 | 245 | private String getNameSpace(Class clazz) 246 | { 247 | if (clazz.isPrimitive()) 248 | return ""; 249 | if (clazz.isArray()) 250 | return "jni"; 251 | String fullName = clazz.getName(); 252 | String simpleName = getSimpleName(clazz); 253 | String packageName = fullName.substring(0, Math.max(fullName.length() - simpleName.length() - 1, 0)); 254 | StringBuilder namespace = new StringBuilder(""); 255 | String[] namespaceComponents = packageName.split("\\."); 256 | for (int i = 0; i < namespaceComponents.length; ++i) 257 | { 258 | namespace.append("::"); 259 | namespace.append(safe(namespaceComponents[i], null)); 260 | } 261 | return namespace.toString(); 262 | } 263 | 264 | private static String getSignature(Class clazz) throws Exception 265 | { 266 | if (clazz == null) 267 | return "V"; 268 | if (clazz.isArray()) 269 | return "[" + getSignature(clazz.getComponentType()); 270 | if (clazz.isPrimitive()) 271 | { 272 | if (clazz.equals(byte.class)) return "B"; 273 | if (clazz.equals(short.class)) return "S"; 274 | if (clazz.equals(int.class)) return "I"; 275 | if (clazz.equals(long.class)) return "J"; 276 | if (clazz.equals(float.class)) return "F"; 277 | if (clazz.equals(double.class)) return "D"; 278 | if (clazz.equals(char.class)) return "C"; 279 | if (clazz.equals(boolean.class))return "Z"; 280 | if (clazz.equals(void.class)) return "V"; 281 | throw new Exception("Unknown primitive: " + clazz); 282 | } 283 | return "L" + clazz.getName().replace('.', '/') + ";"; 284 | } 285 | 286 | private static String getSignature(Member member) throws Exception 287 | { 288 | if(member instanceof Field) 289 | return getSignature(((Field)member).getType()); 290 | if(member instanceof Method) 291 | return String.format("(%s)%s", (getSignature(((Method)member).getParameterTypes())), getSignature(((Method)member).getReturnType())); 292 | return String.format("(%s)V", getSignature(((Constructor)member).getParameterTypes())); 293 | } 294 | 295 | private static String getSignature(Class... clazzes) throws Exception 296 | { 297 | StringBuilder signature = new StringBuilder(); 298 | for (Class clazz : clazzes) 299 | signature.append(getSignature(clazz)); 300 | return signature.toString(); 301 | } 302 | 303 | private Class box(Class clazz) 304 | { 305 | if (clazz.isPrimitive()) 306 | { 307 | if (clazz.equals(byte.class)) return Byte.class; 308 | if (clazz.equals(short.class)) return Short.class; 309 | if (clazz.equals(int.class)) return Integer.class; 310 | if (clazz.equals(long.class)) return Long.class; 311 | if (clazz.equals(float.class)) return Float.class; 312 | if (clazz.equals(double.class)) return Double.class; 313 | if (clazz.equals(char.class)) return Character.class; 314 | if (clazz.equals(boolean.class))return Boolean.class; 315 | } 316 | return clazz; 317 | } 318 | 319 | private String runtimeUnbox(Class clazz) 320 | { 321 | if (clazz.isPrimitive()) 322 | { 323 | if (clazz.equals(byte.class)) return ".ByteValue()"; 324 | if (clazz.equals(short.class)) return ".ShortValue()"; 325 | if (clazz.equals(int.class)) return ".IntValue()"; 326 | if (clazz.equals(long.class)) return ".LongValue()"; 327 | if (clazz.equals(float.class)) return ".FloatValue()"; 328 | if (clazz.equals(double.class)) return ".DoubleValue()"; 329 | if (clazz.equals(char.class)) return ".CharValue()"; 330 | if (clazz.equals(boolean.class))return ".BooleanValue()"; 331 | } 332 | return ""; 333 | } 334 | 335 | private String getClassName(Class clazz) 336 | { 337 | StringBuilder buffer = new StringBuilder(); 338 | buffer.append(getNameSpace(clazz)); 339 | buffer.append("::"); 340 | buffer.append(getSimpleName(clazz)); 341 | return buffer.toString(); 342 | } 343 | 344 | private String getSuperClassName(Class clazz) 345 | { 346 | Class superClass = clazz.getSuperclass(); 347 | if (superClass == null) 348 | return clazz.isInterface() ? "java::lang::Object" : "jni::Object"; 349 | else 350 | return getClassName(superClass); 351 | } 352 | 353 | private boolean isValid(Class clazz) 354 | { 355 | return !( 356 | clazz == null || 357 | clazz.isPrimitive() || 358 | isAnonymous(clazz) || 359 | "package-info".equals(clazz.getSimpleName()) 360 | ); 361 | } 362 | 363 | private boolean isInner(Class clazz) { return clazz.getEnclosingClass() != null; } 364 | private boolean isAnonymous(Class clazz) { return isInner(clazz) && clazz.getDeclaringClass() == null; } 365 | private boolean isStatic(Member member) { return (Modifier.STATIC & member.getModifiers()) != 0; } 366 | private boolean isFinal(Member member) { return (Modifier.FINAL & member.getModifiers()) != 0; } 367 | private boolean isStaticFinal(Member member) { return isStatic(member) && isFinal(member); } 368 | private boolean isPublic(Member member) { return (Modifier.PUBLIC & member.getModifiers()) != 0; } 369 | private boolean isProtected(Member member) { return (Modifier.PROTECTED & member.getModifiers()) != 0; } 370 | private boolean isValid(Member member) { return (isPublic(member) || isProtected(member)) && !member.isSynthetic(); } 371 | private boolean isValid(Method method) { return isValid((Member) method) && !method.isBridge(); } 372 | private boolean isValid(Constructor ctor, Class clazz) 373 | { 374 | return !(ctor.getParameterTypes().length == 1 && ctor.getParameterTypes()[0] == clazz) 375 | && isValid((Member)ctor); 376 | } 377 | 378 | private String enterNameSpace(PrintStream out, String currentNameSpace, Class clazz) 379 | { 380 | String nameSpace = getNameSpace(clazz); 381 | if (nameSpace.equals(currentNameSpace)) 382 | return currentNameSpace; 383 | 384 | closeNameSpace(out, currentNameSpace); 385 | 386 | for (String part : nameSpace.split("::")) 387 | { 388 | if (!part.isEmpty()) // ignore anonymous namespaces 389 | { 390 | out.print("namespace "); 391 | out.print(part); 392 | out.print(" { "); 393 | } 394 | } 395 | out.print("\n"); 396 | return nameSpace; 397 | } 398 | 399 | private String closeNameSpace(PrintStream out, String currentNameSpace) 400 | { 401 | if (currentNameSpace != null) 402 | { 403 | for (String part : currentNameSpace.split("::")) 404 | if (!part.isEmpty()) // ignore anonymous namespaces 405 | out.print('}'); 406 | out.print("\n\n"); 407 | } 408 | return null; 409 | } 410 | 411 | private String getParameterSignature(Class[] parameterTypes) 412 | { 413 | StringBuilder buffer = new StringBuilder(); 414 | for (int i = 0; i < parameterTypes.length; ++i) 415 | { 416 | if (i > 0) buffer.append(", "); 417 | buffer.append("const "); 418 | buffer.append(getClassName(parameterTypes[i])); 419 | buffer.append("& arg"); 420 | buffer.append(i); 421 | } 422 | return buffer.toString(); 423 | } 424 | 425 | private String getParameterNames(int nParameters) 426 | { 427 | StringBuilder buffer = new StringBuilder(); 428 | for (int i = 0; i < nParameters; ++i) 429 | { 430 | if (i > 0) buffer.append(", "); 431 | buffer.append("arg"); 432 | buffer.append(i); 433 | } 434 | return buffer.toString(); 435 | } 436 | 437 | private String getParameterJNINames(Class[] parameterTypes) 438 | { 439 | StringBuilder buffer = new StringBuilder(); 440 | for (int i = 0; i < parameterTypes.length; ++i) 441 | { 442 | buffer.append(", "); 443 | if (parameterTypes[i] != null && !parameterTypes[i].isPrimitive()) 444 | buffer.append("(jobject)"); 445 | buffer.append("arg"); 446 | buffer.append(i); 447 | } 448 | return buffer.toString(); 449 | } 450 | 451 | private String getParametersFromJNIObjectArray(Class[] parameterTypes) 452 | { 453 | StringBuilder buffer = new StringBuilder(); 454 | for (int i = 0; i < parameterTypes.length; ++i) 455 | { 456 | if (i > 0) 457 | buffer.append(", "); 458 | buffer.append(getClassName(box(parameterTypes[i]))); 459 | buffer.append("(jni::GetObjectArrayElement(args, "); 460 | buffer.append(i); 461 | buffer.append("))"); 462 | buffer.append(runtimeUnbox(parameterTypes[i])); 463 | } 464 | return buffer.toString(); 465 | } 466 | 467 | private void print(String dst) throws Exception 468 | { 469 | System.err.println("Generating cpp code"); 470 | // Implement classes 471 | for (Class clazz : m_VisitedClasses) 472 | { 473 | PrintStream source = new PrintStream(new FileOutputStream(new File(dst, clazz.getCanonicalName() + ".cpp"))); 474 | source.format("#include \"API.h\"\n"); 475 | implementClass(source, clazz); 476 | source.close(); 477 | } 478 | 479 | System.err.println("Creating header file"); 480 | PrintStream header = new PrintStream(new FileOutputStream(new File(dst, "API.h"))); 481 | header.format("#pragma once\n"); 482 | header.format("#include \"APIHelper.h\"\n"); 483 | 484 | // Declare classes 485 | String currentNameSpace = null; 486 | for (Class clazz : m_VisitedClasses) 487 | { 488 | currentNameSpace = enterNameSpace(header, currentNameSpace, clazz); 489 | header.format("struct %s;\n", getSimpleName(clazz)); 490 | } 491 | 492 | // Define classes 493 | for (Class clazz : m_DependencyChain) 494 | { 495 | currentNameSpace = enterNameSpace(header, currentNameSpace, clazz); 496 | declareClass(header, clazz); 497 | } 498 | currentNameSpace = closeNameSpace(header, currentNameSpace); 499 | header.close(); 500 | } 501 | 502 | private void declareClass(PrintStream header, Class clazz) throws Exception 503 | { 504 | header.format("struct "); 505 | header.format("%s : %s", getSimpleName(clazz), getSuperClassName(clazz)); 506 | header.format("\n{\n"); 507 | header.format("\tstatic jni::Class __CLASS;\n\n"); 508 | 509 | // Use cast operators for interfaces to avoid deadly diamond of death 510 | for (Class interfaze : clazz.getInterfaces()) 511 | header.format("\toperator %s();\n", getClassName(interfaze)); 512 | 513 | declareClassMembers(header, clazz); 514 | 515 | if (clazz.isInterface()) 516 | declareProxy(header, clazz); 517 | 518 | header.format("};\n\n"); 519 | } 520 | 521 | private void declareProxy(PrintStream header, Class clazz) throws Exception 522 | { 523 | header.format("\tstruct __Proxy : public virtual jni::ProxyInvoker\n"); 524 | header.format("\t{\n"); 525 | header.format("\t\toperator %s();\n", getClassName(clazz)); 526 | // Use cast operators for interfaces to avoid deadly diamond of death 527 | for (Class interfaze : clazz.getInterfaces()) 528 | header.format("\t\toperator %s();\n", getClassName(interfaze)); 529 | declareProxyMembers(header, clazz); 530 | header.format("\t};\n"); 531 | } 532 | 533 | private void declareProxyMembers(PrintStream out, Class clazz) throws Exception 534 | { 535 | out.format("\tprotected:\n"); 536 | out.format("\t\tbool __TryInvoke(jclass, jmethodID, jobjectArray, bool*, jobject*);\n"); 537 | for (Method method : getDeclaredMethodsSorted(clazz)) 538 | { 539 | if (!isValid(method) || isStatic(method)) 540 | continue; 541 | out.format("\t\tvirtual %s %s(%s) = 0;\n", 542 | method.getReturnType() == void.class ? "void" : getClassName(method.getReturnType()), 543 | getMethodName(method), 544 | getParameterSignature(method.getParameterTypes())); 545 | } 546 | } 547 | 548 | private void declareClassMembers(PrintStream out, Class clazz) throws Exception 549 | { 550 | File templateFile = new File("templates", clazz.getName() + ".h"); 551 | boolean hasTemplate = templateFile.exists(); 552 | 553 | /* example ------------------ 554 | static ::java::util::Comparator& fCASE_INSENSITIVE_ORDER(); 555 | */ 556 | for (Field field : getDeclaredFieldsSorted(clazz)) 557 | { 558 | if (!isValid(field)) 559 | continue; 560 | out.format("\t%s%s%s %s()%s;\n", 561 | isStatic(field) ? "static " : "", 562 | getClassName(field.getType()), 563 | isStaticFinal(field) ? "&" : "", 564 | getFieldName(field), 565 | isStatic(field) ? "" : " const"); 566 | 567 | if (isFinal(field)) 568 | continue; 569 | out.format("\t%svoid %s(%s)%s;\n", 570 | isStatic(field) ? "static " : "", 571 | getFieldName(field), 572 | getParameterSignature(new Class[] {field.getType()}), 573 | isStatic(field) ? "" : " const"); 574 | } 575 | /* example ------------------ 576 | jni::Array< ::java::lang::String > Split(const ::java::lang::String& arg0, const ::jint& arg1) const; 577 | */ 578 | for (Method method : getDeclaredMethodsSorted(clazz)) 579 | { 580 | if (!isValid(method)) 581 | continue; 582 | out.format("\t%s%s %s(%s)%s;\n", 583 | isStatic(method) ? "static " : "", 584 | getClassName(method.getReturnType()), 585 | getMethodName(method), 586 | getParameterSignature(method.getParameterTypes()), 587 | isStatic(method) ? "" : " const"); 588 | } 589 | /* example ------------------ 590 | static jobject __Constructor(const jni::Array< ::jchar >& arg0, const ::jint& arg1, const ::jint& arg2); 591 | String(const jni::Array< ::jchar >& arg0, const ::jint& arg1, const ::jint& arg2) : ::java::lang::Object(__Constructor(arg0, arg1, arg2)) { __Initialize(); } 592 | */ 593 | for (Constructor constructor : getDeclaredConstructorsSorted(clazz)) 594 | { 595 | if (!isValid(constructor, clazz)) 596 | continue; 597 | Class[] params = constructor.getParameterTypes(); 598 | out.format("\tstatic jobject __Constructor(%s);\n", getParameterSignature(params)); 599 | out.format("\t%s(%s) : %s(__Constructor(%s)) {%s}\n", 600 | getSimpleName(clazz), 601 | getParameterSignature(params), 602 | getSuperClassName(clazz), 603 | getParameterNames(params.length), 604 | hasTemplate ? " __Initialize(); " : ""); 605 | } 606 | // Standard constructors 607 | out.format("\texplicit %s(jobject o) : %s(o) {%s}\n", 608 | getSimpleName(clazz), 609 | getSuperClassName(clazz), 610 | hasTemplate ? " __Initialize(); " : ""); 611 | out.format("\t%s(const %s& o) : %s(o) {%s}\n", 612 | getSimpleName(clazz), 613 | getSimpleName(clazz), 614 | getSuperClassName(clazz), 615 | hasTemplate ? " __Initialize(); " : ""); 616 | if (hasTemplate) 617 | { 618 | out.format("%s\n", new Scanner(templateFile).useDelimiter("\\Z").next()); 619 | out.format("private:\n"); 620 | out.format("\tvoid __Initialize();\n"); 621 | } 622 | out.format("\n"); 623 | } 624 | 625 | private void implementClass(PrintStream out, Class clazz) throws Exception 626 | { 627 | String namespace = enterNameSpace(out, null, clazz); 628 | out.format("jni::Class %s::__CLASS(\"%s\");\n\n", getSimpleName(clazz), clazz.getName().replace('.', '/')); 629 | 630 | for (Class interfaze : clazz.getInterfaces()) 631 | out.format("%s::operator %s() { return %s((jobject)*this); }\n", getSimpleName(clazz), getClassName(interfaze), getClassName(interfaze)); 632 | 633 | implementClassMembers(out, clazz); 634 | 635 | // Apply template 636 | File tempalteFile = new File("templates", clazz.getName() + ".cpp"); 637 | if (tempalteFile.exists()) 638 | out.format("%s\n", new Scanner(tempalteFile).useDelimiter("\\Z").next()); 639 | 640 | if (clazz.isInterface()) 641 | implementProxy(out, clazz); 642 | 643 | closeNameSpace(out, namespace); 644 | } 645 | 646 | private void implementProxy(PrintStream out, Class clazz) throws Exception 647 | { 648 | out.format("%s::__Proxy::operator %s() { return %s(static_cast(__ProxyObject())); }\n", getSimpleName(clazz), getSimpleName(clazz), getSimpleName(clazz)); 649 | for (Class interfaze : clazz.getInterfaces()) 650 | out.format("%s::__Proxy::operator %s() { return %s(static_cast(__ProxyObject())); }\n", getSimpleName(clazz), getClassName(interfaze), getClassName(interfaze)); 651 | out.format("bool %s::__Proxy::__TryInvoke(jclass clazz, jmethodID methodID, jobjectArray args, bool* success, jobject* result) {\n", getSimpleName(clazz)); 652 | 653 | int nMethods = 0; 654 | Method[] methods = getDeclaredMethodsSorted(clazz); 655 | for (Method method : methods) 656 | { 657 | if (!isValid(method) || isStatic(method)) 658 | continue; 659 | ++nMethods; 660 | } 661 | 662 | // early out if there are no methods to invoke 663 | if (nMethods == 0) { out.format("\treturn false;\n}"); return; } 664 | 665 | // return if success was already achieved 666 | out.format("\tif (*success)\n\t\treturn false;\n\n"); 667 | 668 | out.format("\tif (!jni::IsSameObject(clazz, %s::__CLASS))\n\t\treturn false;\n\n", getSimpleName(clazz)); 669 | 670 | out.format("\tstatic jmethodID methodIDs[] = {\n"); 671 | for (Method method : methods) 672 | { 673 | if (!isValid(method) || isStatic(method)) 674 | continue; 675 | out.format("\t\tjni::GetMethodID(__CLASS, \"%s\", \"%s\"),\n", method.getName(), getSignature(method)); 676 | } 677 | out.format("\t};\n"); 678 | int i = 0; 679 | for (Method method : methods) 680 | { 681 | if (!isValid(method) || isStatic(method)) 682 | continue; 683 | Class returnType = method.getReturnType(); 684 | Class[] params = method.getParameterTypes(); 685 | if (returnType != void.class) 686 | out.format("\tif (methodIDs[%d] == methodID) { *result = jni::NewLocalRef(static_cast< %s >(%s(%s))); *success = true; return true; }\n", 687 | i++, 688 | getClassName(box(returnType)), 689 | getMethodName(method), 690 | getParametersFromJNIObjectArray(params)); 691 | else 692 | out.format("\tif (methodIDs[%d] == methodID) { *result = NULL; %s(%s); *success = true; return true; }\n", 693 | i++, 694 | getMethodName(method), 695 | getParametersFromJNIObjectArray(params)); 696 | } 697 | out.format("\treturn false;\n}"); 698 | } 699 | 700 | private void implementClassMembers(PrintStream out, Class clazz) throws Exception 701 | { 702 | /* example ------------------ 703 | ::java::util::Comparator& String::fCASE_INSENSITIVE_ORDER() 704 | { 705 | static jfieldID fieldID = jni::GetStaticFieldID(__CLASS, "CASE_INSENSITIVE_ORDER", "Ljava/util/Comparator;"); 706 | static ::java::util::Comparator val = ::java::util::Comparator(jni::Op::GetStaticField(__CLASS, fieldID)); 707 | return val; 708 | } 709 | */ 710 | for (Field field : getDeclaredFieldsSorted(clazz)) 711 | { 712 | if (!isValid(field)) 713 | continue; 714 | out.format("%s%s %s::%s()%s\n", 715 | getClassName(field.getType()), 716 | isStaticFinal(field) ? "&" : "", 717 | getSimpleName(clazz), 718 | getFieldName(field), 719 | isStatic(field) ? "" : " const"); 720 | out.format("{\n"); 721 | out.format("\tstatic jfieldID fieldID = jni::Get%sFieldID(__CLASS, \"%s\", \"%s\");\n", 722 | isStatic(field) ? "Static" : "", 723 | field.getName(), 724 | getSignature(field)); 725 | out.format("\t%s%s val = %s(jni::Op<%s>::Get%sField(%s, fieldID));\n", 726 | isStaticFinal(field) ? "static " : "", 727 | getClassName(field.getType()), 728 | getClassName(field.getType()), 729 | getPrimitiveType(field.getType()), 730 | isStatic(field) ? "Static" : "", 731 | isStatic(field) ? "__CLASS" : "m_Object"); 732 | out.format("\treturn val;\n"); 733 | out.format("}\n"); 734 | 735 | if (isFinal(field)) 736 | continue; 737 | out.format("void %s::%s(%s)%s\n", 738 | getSimpleName(clazz), 739 | getFieldName(field), 740 | getParameterSignature(new Class[] {field.getType()}), 741 | isStatic(field) ? "" : " const"); 742 | out.format("{\n"); 743 | out.format("\tstatic jfieldID fieldID = jni::Get%sFieldID(__CLASS, \"%s\", \"%s\");\n", 744 | isStatic(field) ? "Static" : "", 745 | field.getName(), 746 | getSignature(field)); 747 | out.format("\tjni::Op<%s>::Set%sField(%s, fieldID%s);\n", 748 | getPrimitiveType(field.getType()), 749 | isStatic(field) ? "Static" : "", 750 | isStatic(field) ? "__CLASS" : "m_Object", 751 | getParameterJNINames(new Class[] {field.getType()})); 752 | out.format("}\n"); 753 | 754 | } 755 | 756 | /* example ------------------ 757 | jni::Array< ::java::lang::String > String::Split(const ::java::lang::String& arg0, const ::jint& arg1) const 758 | { 759 | static jmethodID methodID = jni::GetMethodID(__CLASS, "split", "(Ljava/lang/String;I)[Ljava/lang/String;"); 760 | return jni::Array< ::java::lang::String >(jni::Op::CallMethod(m_Object, methodID, (jobject)arg0, arg1)); 761 | } 762 | */ 763 | for (Method method : getDeclaredMethodsSorted(clazz)) 764 | { 765 | if (!isValid(method)) 766 | continue; 767 | Class[] params = method.getParameterTypes(); 768 | out.format("%s %s::%s(%s)%s\n", 769 | getClassName(method.getReturnType()), 770 | getSimpleName(clazz), 771 | getMethodName(method), 772 | getParameterSignature(params), 773 | isStatic(method) ? "" : " const"); 774 | out.format("{\n"); 775 | out.format("\tstatic jmethodID methodID = jni::Get%sMethodID(__CLASS, \"%s\", \"%s\");\n", 776 | isStatic(method) ? "Static" : "", 777 | method.getName(), 778 | getSignature(method)); 779 | out.format("\treturn %s(jni::Op<%s>::Call%sMethod(%s, methodID%s));\n", 780 | getClassName(method.getReturnType()), 781 | getPrimitiveType(method.getReturnType()), 782 | isStatic(method) ? "Static" : "", 783 | isStatic(method) ? "__CLASS" : "m_Object", 784 | getParameterJNINames(params)); 785 | out.format("}\n"); 786 | } 787 | 788 | /* example ------------------ 789 | jobject String::__Constructor(const jni::Array< ::jbyte >& arg0, const ::jint& arg1, const ::jint& arg2) 790 | { 791 | static jmethodID constructorID = jni::GetMethodID(__CLASS, "", "([BII)V"); 792 | return jni::NewObject(__CLASS, constructorID, (jobject)arg0, arg1, arg2); 793 | } 794 | */ 795 | for (Constructor constructor : getDeclaredConstructorsSorted(clazz)) 796 | { 797 | if (!isValid(constructor, clazz)) 798 | continue; 799 | Class[] params = constructor.getParameterTypes(); 800 | out.format("jobject %s::__Constructor(%s)\n", getSimpleName(clazz), getParameterSignature(params)); 801 | out.format("{\n"); 802 | out.format("\tstatic jmethodID constructorID = jni::GetMethodID(__CLASS, \"\", \"%s\");\n", 803 | getSignature(constructor)); 804 | out.format("\treturn jni::NewObject(__CLASS, constructorID%s);\n", 805 | getParameterJNINames(params)); 806 | out.format("}\n"); 807 | } 808 | } 809 | 810 | public static Field[] getDeclaredFieldsSorted(Class clazz) 811 | { 812 | Field[] fields = clazz.getDeclaredFields(); 813 | Arrays.sort(fields, new ByNameAndSignature()); 814 | return fields; 815 | } 816 | 817 | public static Constructor[] getDeclaredConstructorsSorted(Class clazz) 818 | { 819 | Constructor[] constructors = clazz.getDeclaredConstructors(); 820 | Arrays.sort(constructors, new ByNameAndSignature ()); 821 | return constructors; 822 | } 823 | 824 | public static Method[] getDeclaredMethodsSorted(Class clazz) 825 | { 826 | Method[] methods = clazz.getDeclaredMethods(); 827 | Arrays.sort(methods, new ByNameAndSignature()); 828 | return methods; 829 | } 830 | 831 | private static class ByNameAndSignature implements Comparator 832 | { 833 | @Override 834 | public int compare(T o1, T o2) 835 | { 836 | int res = o1.getName().compareTo(o2.getName()); 837 | if (res != 0) 838 | return res; 839 | try 840 | { 841 | return getSignature(o1).compareTo(getSignature(o2)); 842 | } catch (Throwable ignore){} 843 | return res; 844 | } 845 | } 846 | } 847 | -------------------------------------------------------------------------------- /APIHelper.cpp: -------------------------------------------------------------------------------- 1 | #include "APIHelper.h" 2 | 3 | #include 4 | #include 5 | 6 | namespace jni 7 | { 8 | 9 | Class::Class(const char* name, jclass clazz) : m_Class(clazz) 10 | { 11 | m_ClassName = static_cast(malloc(strlen(name) + 1)); 12 | strcpy(m_ClassName, name); 13 | } 14 | 15 | Class::~Class() 16 | { 17 | free(m_ClassName); 18 | } 19 | 20 | 21 | 22 | 23 | } 24 | -------------------------------------------------------------------------------- /APIHelper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "JNIBridge.h" 4 | 5 | namespace jni 6 | { 7 | 8 | class GlobalRefAllocator 9 | { 10 | public: 11 | static jobject Alloc(jobject o) { return jni::NewGlobalRef(o); } 12 | static void Free(jobject o) { return jni::DeleteGlobalRef(o); } 13 | }; 14 | 15 | class WeakGlobalRefAllocator 16 | { 17 | public: 18 | static jobject Alloc(jobject o) { return jni::NewWeakGlobalRef(o); } 19 | static void Free(jobject o) { return jni::DeleteWeakGlobalRef(o); } 20 | }; 21 | 22 | template 23 | class Ref 24 | { 25 | public: 26 | Ref(ObjType object) { m_Ref = new RefCounter(object); } 27 | Ref(const Ref& o) { Aquire(o.m_Ref); } 28 | ~Ref() { Release(); } 29 | 30 | inline operator ObjType() const { return *m_Ref; } 31 | Ref& operator = (const Ref& o) 32 | { 33 | if (m_Ref == o.m_Ref) 34 | return *this; 35 | 36 | Release(); 37 | Aquire(o.m_Ref); 38 | 39 | return *this; 40 | } 41 | 42 | private: 43 | class RefCounter 44 | { 45 | public: 46 | RefCounter(ObjType object) 47 | { 48 | m_Object = static_cast(object ? RefType::Alloc(object) : 0); 49 | m_Counter = 1; 50 | } 51 | ~RefCounter() 52 | { 53 | if (m_Object) 54 | RefType::Free(m_Object); 55 | m_Object = 0; 56 | } 57 | 58 | inline operator ObjType() const { return m_Object; } 59 | void Aquire() { __sync_add_and_fetch(&m_Counter, 1); } 60 | bool Release() { return __sync_sub_and_fetch(&m_Counter, 1); } 61 | 62 | private: 63 | ObjType m_Object; 64 | volatile int m_Counter; 65 | }; 66 | 67 | void Aquire(RefCounter* ref) 68 | { 69 | m_Ref = ref; 70 | m_Ref->Aquire(); 71 | } 72 | 73 | void Release() 74 | { 75 | if (!m_Ref->Release()) 76 | { 77 | delete m_Ref; 78 | m_Ref = NULL; 79 | } 80 | } 81 | 82 | private: 83 | class RefCounter* m_Ref; 84 | }; 85 | 86 | 87 | class Class 88 | { 89 | public: 90 | Class(const char* name, jclass clazz = 0); 91 | ~Class(); 92 | 93 | inline operator jclass() 94 | { 95 | jclass result = m_Class; 96 | if (result) 97 | return result; 98 | return m_Class = jni::FindClass(m_ClassName); 99 | } 100 | 101 | private: 102 | Class(const Class& clazz); 103 | Class& operator = (const Class& o); 104 | 105 | private: 106 | char* m_ClassName; 107 | Ref m_Class; 108 | }; 109 | 110 | class Object 111 | { 112 | public: 113 | explicit inline Object(jobject obj) : m_Object(obj) { } 114 | 115 | inline operator bool() const { return m_Object != 0; } 116 | inline operator jobject() const { return m_Object; } 117 | 118 | protected: 119 | Ref m_Object; 120 | }; 121 | 122 | 123 | // ------------------------------------------------ 124 | // Utillities 125 | // ------------------------------------------------ 126 | template inline bool InstanceOf(jobject o) { return jni::IsInstanceOf(o, T::__CLASS); } 127 | template inline T Cast(jobject o) { return T(InstanceOf(o) ? o : 0); } 128 | template inline bool Catch() { return jni::ExceptionThrown(T::__CLASS); } 129 | template inline bool ThrowNew(const char* message) { return jni::ThrowNew(T::__CLASS, message) == 0; } 130 | 131 | // ------------------------------------------------ 132 | // Array Support 133 | // ------------------------------------------------ 134 | template 135 | class ArrayBase 136 | { 137 | protected: 138 | explicit ArrayBase(T obj) : m_Array(obj) {} 139 | explicit ArrayBase(jobject obj) : m_Array(static_cast(obj)) {} 140 | 141 | public: 142 | inline size_t Length() const { return m_Array != 0 ? jni::GetArrayLength(m_Array) : 0; } 143 | 144 | inline operator bool() const { return m_Array != 0; } 145 | inline operator T() const { return m_Array; } 146 | 147 | protected: 148 | Ref m_Array; 149 | }; 150 | 151 | template 152 | class PrimitiveArrayBase : public ArrayBase 153 | { 154 | protected: 155 | explicit PrimitiveArrayBase(AT obj) : ArrayBase(obj) {}; 156 | explicit PrimitiveArrayBase(jobject obj) : ArrayBase(obj) {}; 157 | explicit PrimitiveArrayBase(size_t length) : ArrayBase(jni::Op::NewArray(length)) {}; 158 | template 159 | explicit PrimitiveArrayBase(size_t length, T2* elements) : ArrayBase(jni::Op::NewArray(length)) 160 | { 161 | T* array = jni::Op::GetArrayElements(*this); 162 | for (int i = 0; i < length; ++i) 163 | array[i] = static_cast(elements[i]); 164 | jni::Op::ReleaseArrayElements(*this, array, 0); 165 | }; 166 | 167 | public: 168 | inline T operator[] (const int i) const 169 | { 170 | T value = 0; 171 | if (*this) 172 | jni::Op::GetArrayRegion(*this, i, 1, &value); 173 | return value; 174 | } 175 | 176 | inline T* Lock() const 177 | { 178 | return *this ? jni::Op::GetArrayElements(*this) : 0; 179 | } 180 | inline void Release(T* elements, bool writeBackData = true) const 181 | { 182 | if (*this) 183 | jni::Op::ReleaseArrayElements(*this, elements, writeBackData ? 0 : JNI_ABORT); 184 | } 185 | 186 | inline T* LockCritical() const 187 | { 188 | return *this ? jni::GetPrimitiveArrayCritical(*this, NULL) : 0; 189 | } 190 | inline void ReleaseCritical(T* elements, bool writeBackData = true) const 191 | { 192 | if (*this) 193 | jni::ReleasePrimitiveArrayCritical(*this, elements, writeBackData ? 0 : JNI_ABORT); 194 | } 195 | }; 196 | 197 | template 198 | class ObjectArray : public ArrayBase 199 | { 200 | protected: 201 | explicit inline ObjectArray(jobject obj) : ArrayBase(obj) {}; 202 | explicit inline ObjectArray(jobjectArray obj) : ArrayBase(obj) {}; 203 | explicit inline ObjectArray(jclass type, size_t length, T initialElement) : ArrayBase(jni::NewObjectArray(length, type, initialElement)) {}; 204 | template 205 | explicit inline ObjectArray(jclass type, size_t length, T2* elements) : ArrayBase(jni::NewObjectArray(length, type, NULL)) 206 | { 207 | for (int i = 0; i < length; ++i) 208 | jni::SetObjectArrayElement(*this, i, static_cast(elements[i])); 209 | } 210 | 211 | public: 212 | inline T operator[] (const int i) { return T(*this ? jni::GetObjectArrayElement(*this, i) : 0); } 213 | 214 | inline T* Lock() 215 | { 216 | T* elements = reinterpret_cast(malloc(Length() * sizeof(T))); 217 | for (int i = 0; i < Length(); ++i) 218 | new (&(elements[i])) T(jni::GetObjectArrayElement(*this, i)); 219 | 220 | return elements; 221 | } 222 | inline void Release(T* elements) 223 | { 224 | for (int i = 0; i < Length(); ++i) 225 | { 226 | jni::SetObjectArrayElement(*this, i, elements[i]); 227 | elements[i].~T(); 228 | } 229 | 230 | free(elements); 231 | } 232 | }; 233 | 234 | template 235 | class Array : public ObjectArray 236 | { 237 | public: 238 | explicit inline Array(jobject obj) : ObjectArray(obj) {}; 239 | explicit inline Array(jobjectArray obj) : ObjectArray(obj) {}; 240 | explicit inline Array(size_t length, T initialElement = 0) : ObjectArray(T::__CLASS, length, initialElement) {}; 241 | template 242 | explicit inline Array(size_t length, T2* elements) : ObjectArray(T::__CLASS, length, elements) {}; 243 | }; 244 | 245 | template <> 246 | class Array : public ObjectArray 247 | { 248 | public: 249 | explicit inline Array(jobject obj) : ObjectArray(obj) {}; 250 | explicit inline Array(jobjectArray obj) : ObjectArray(obj) {}; 251 | template 252 | explicit inline Array(jclass type, size_t length, T initialElement = 0) : ObjectArray(type, length, initialElement) {}; 253 | template 254 | explicit inline Array(jclass type, size_t length, T2* elements) : ObjectArray(type, length, elements) {}; 255 | }; 256 | 257 | #define DEF_PRIMITIVE_ARRAY_TYPE(t) \ 258 | template <> \ 259 | class Array : public PrimitiveArrayBase \ 260 | { \ 261 | public: \ 262 | explicit inline Array(jobject obj) : PrimitiveArrayBase(obj) {}; \ 263 | explicit inline Array(t##Array obj) : PrimitiveArrayBase(obj) {}; \ 264 | explicit inline Array(size_t length) : PrimitiveArrayBase(length) {}; \ 265 | template \ 266 | explicit inline Array(size_t length, T2* elements) : PrimitiveArrayBase(length, elements) {}; \ 267 | }; 268 | 269 | DEF_PRIMITIVE_ARRAY_TYPE(jboolean) 270 | DEF_PRIMITIVE_ARRAY_TYPE(jint) 271 | DEF_PRIMITIVE_ARRAY_TYPE(jshort) 272 | DEF_PRIMITIVE_ARRAY_TYPE(jbyte) 273 | DEF_PRIMITIVE_ARRAY_TYPE(jlong) 274 | DEF_PRIMITIVE_ARRAY_TYPE(jfloat) 275 | DEF_PRIMITIVE_ARRAY_TYPE(jdouble) 276 | DEF_PRIMITIVE_ARRAY_TYPE(jchar) 277 | 278 | #undef DEF_PRIMITIVE_ARRAY_TYPE 279 | 280 | // ------------------------------------------------ 281 | // Proxy Support 282 | // ------------------------------------------------ 283 | class ProxyInvoker 284 | { 285 | public: 286 | ProxyInvoker() {} 287 | virtual ~ProxyInvoker() {}; 288 | virtual jobject __Invoke(jclass, jmethodID, jobjectArray) = 0; 289 | 290 | public: 291 | static bool __Register(); 292 | 293 | protected: 294 | virtual ::jobject __ProxyObject() const = 0; 295 | 296 | private: 297 | ProxyInvoker(const ProxyInvoker& proxy); 298 | ProxyInvoker& operator = (const ProxyInvoker& o); 299 | }; 300 | 301 | } 302 | -------------------------------------------------------------------------------- /JNIBridge.cpp: -------------------------------------------------------------------------------- 1 | #include "JNIBridge.h" 2 | #include 3 | #include 4 | #include 5 | 6 | namespace jni 7 | { 8 | 9 | // __thread is not supported on android (dynamic linker) :( 10 | template 11 | class TLS 12 | { 13 | public: 14 | TLS() { pthread_key_create(&m_Key, free); } 15 | ~TLS() { pthread_key_delete(m_Key); } 16 | inline operator T () const { return static_cast(pthread_getspecific(m_Key)); } 17 | inline T operator = (const T value) { pthread_setspecific(m_Key, value); return value; } 18 | private: 19 | TLS(const TLS& tls); 20 | TLS operator = (const TLS&); 21 | private: 22 | pthread_key_t m_Key; 23 | }; 24 | 25 | static JavaVM* g_JavaVM; 26 | 27 | jobject kNull(0); 28 | 29 | // -------------------------------------------------------------------------------------- 30 | // Oracle JNI functions (hidden) 31 | // http://docs.oracle.com/javase/6/docs/technotes/guides/jni/spec/functions.html#wp9502 32 | // -------------------------------------------------------------------------------------- 33 | static jint PushLocalFrame(jint capacity) 34 | { 35 | JNI_CALL_RETURN(jint, true, true, env->PushLocalFrame(capacity)); 36 | } 37 | 38 | static jobject PopLocalFrame(jobject result) 39 | { 40 | JNI_CALL_RETURN(jobject, true, false, env->PopLocalFrame(result)); 41 | } 42 | 43 | // -------------------------------------------------------------------------------------- 44 | // Initialization and error functions (hidden) 45 | // -------------------------------------------------------------------------------------- 46 | struct Error 47 | { 48 | Errno errno; 49 | char errstr[256]; 50 | }; 51 | static TLS g_Error; 52 | 53 | static inline Error& GetErrorInternal() 54 | { 55 | Error* error = g_Error; 56 | if (!error) 57 | { 58 | error = static_cast(malloc(sizeof(*error))); 59 | memset(error, 0, sizeof(*error)); 60 | g_Error = error; 61 | } 62 | return *error; 63 | } 64 | 65 | static inline void SetError(Errno errno, const char* errmsg) 66 | { 67 | Error& error = GetErrorInternal(); 68 | if (error.errno) 69 | return; 70 | 71 | error.errno = errno; 72 | strcpy(error.errstr, errmsg); 73 | } 74 | 75 | static void ClearErrors() 76 | { 77 | JNIEnv* env = AttachCurrentThread(); 78 | if (env) 79 | { 80 | GetErrorInternal().errno = kJNI_NO_ERROR; 81 | env->ExceptionClear(); 82 | } 83 | } 84 | 85 | // -------------------------------------------------------------------------------------- 86 | // Initialization and error functions (public) 87 | // -------------------------------------------------------------------------------------- 88 | void Initialize(JavaVM& vm) 89 | { 90 | g_JavaVM = &vm; 91 | } 92 | 93 | Errno PeekError() 94 | { 95 | return GetErrorInternal().errno; 96 | } 97 | 98 | const char* GetErrorMessage() 99 | { 100 | return &GetErrorInternal().errstr[0]; 101 | } 102 | 103 | Errno CheckError() 104 | { 105 | Errno errno = PeekError(); 106 | if (errno) 107 | ClearErrors(); 108 | return errno; 109 | } 110 | 111 | jthrowable ExceptionThrown(jclass clazz) 112 | { 113 | JNIEnv* env = AttachCurrentThread(); 114 | if (!env) 115 | return 0; 116 | 117 | jthrowable t = env->ExceptionOccurred(); 118 | if (!t) 119 | return 0; 120 | 121 | if (clazz) 122 | { 123 | env->ExceptionClear(); 124 | if (!env->IsInstanceOf(t, clazz)) 125 | { 126 | env->Throw(t); // re-throw 127 | return 0; 128 | } 129 | } 130 | 131 | ClearErrors(); 132 | return t; 133 | } 134 | 135 | bool CheckForParameterError(bool valid) 136 | { 137 | if (!valid) 138 | SetError(kJNI_INVALID_PARAMETERS, "java.lang.IllegalArgumentException: Null parameter detected"); 139 | return !valid; 140 | } 141 | 142 | bool CheckForExceptionError(JNIEnv* env) // Do we need to make this safer? 143 | { 144 | if (env->ExceptionCheck()) 145 | { 146 | Error& error = GetErrorInternal(); 147 | if (!error.errno) 148 | { 149 | SetError(kJNI_EXCEPTION_THROWN, "java.lang.IllegalThreadStateException: Unable to determine exception message"); 150 | 151 | LocalFrame frame; 152 | jthrowable t = env->ExceptionOccurred(); 153 | env->ExceptionClear(); 154 | { 155 | jclass jobject_class = env->FindClass("java/lang/Object"); 156 | jstring jmessage = Op::CallMethod(t, env->GetMethodID(jobject_class, "toString", "()Ljava/lang/String;")); 157 | 158 | const char* message = env->GetStringUTFChars(jmessage, NULL); 159 | strncpy(error.errstr, message, sizeof(error.errstr)); 160 | error.errstr[sizeof(error.errstr) - 1] = 0; 161 | 162 | env->ReleaseStringUTFChars(jmessage, message); 163 | } 164 | env->Throw(t); // re-throw exception 165 | if (!env->ExceptionOccurred()) 166 | { 167 | int* p = 0; *p = 4711; 168 | } 169 | } 170 | return true; 171 | } 172 | return false; 173 | } 174 | 175 | // -------------------------------------------------------------------------------------- 176 | // Oracle JNI functions (public) 177 | // http://docs.oracle.com/javase/6/docs/technotes/guides/jni/spec/functions.html#wp9502 178 | // -------------------------------------------------------------------------------------- 179 | 180 | JavaVM* GetJavaVM() 181 | { 182 | return g_JavaVM; 183 | } 184 | 185 | JNIEnv* GetEnv() 186 | { 187 | JavaVM* vm = g_JavaVM; 188 | if (!vm) 189 | return 0; 190 | 191 | JNIEnv* env(0); 192 | vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6); 193 | return env; 194 | } 195 | 196 | JNIEnv* AttachCurrentThread() 197 | { 198 | JavaVM* vm = g_JavaVM; 199 | if (!vm) 200 | return 0; 201 | 202 | JNIEnv* env = GetEnv(); 203 | if (!env) 204 | { 205 | JavaVMAttachArgs args; 206 | args.version = JNI_VERSION_1_6; 207 | args.name = NULL; 208 | args.group = NULL; 209 | #if !ANDROID 210 | vm->AttachCurrentThread(reinterpret_cast(&env), &args); 211 | #else 212 | vm->AttachCurrentThread(&env, &args); 213 | #endif 214 | } 215 | 216 | if (!env) 217 | SetError(kJNI_ATTACH_FAILED, "java.lang.IllegalThreadStateException: Unable to attach to VM"); 218 | 219 | return env; 220 | } 221 | 222 | void DetachCurrentThread() 223 | { 224 | JavaVM* vm = g_JavaVM; 225 | if (!vm) 226 | return; 227 | 228 | vm->DetachCurrentThread(); 229 | } 230 | 231 | jclass FindClass(const char* name) 232 | { 233 | JNI_CALL_RETURN(jclass, name, true, env->FindClass(name)); 234 | } 235 | 236 | jint Throw(jthrowable object) 237 | { 238 | JNI_CALL_RETURN(jint, object, true, env->Throw(object)); 239 | } 240 | 241 | jint ThrowNew(jclass clazz, const char* message) 242 | { 243 | JNI_CALL_RETURN(jint, clazz && message, true, env->ThrowNew(clazz, message)); 244 | } 245 | 246 | void FatalError(const char* str) 247 | { 248 | JNI_CALL(str, false, env->FatalError(str)); 249 | } 250 | 251 | jobject NewLocalRef(jobject object) 252 | { 253 | JNI_CALL_RETURN(jobject, object, true, env->NewLocalRef(object)); 254 | } 255 | 256 | void DeleteLocalRef(jobject object) 257 | { 258 | JNI_CALL(object, false, env->DeleteLocalRef(object)); 259 | } 260 | 261 | jobject NewGlobalRef(jobject object) 262 | { 263 | JNI_CALL_RETURN(jobject, object, true, env->NewGlobalRef(object)); 264 | } 265 | 266 | void DeleteGlobalRef(jobject object) 267 | { 268 | JNI_CALL(object, false, env->DeleteGlobalRef(object)); 269 | } 270 | 271 | jobject NewWeakGlobalRef(jobject object) 272 | { 273 | JNI_CALL_RETURN(jobject, object, true, env->NewWeakGlobalRef(object)); 274 | } 275 | 276 | void DeleteWeakGlobalRef(jobject object) 277 | { 278 | JNI_CALL(object, false, env->DeleteWeakGlobalRef(object)); 279 | } 280 | 281 | jclass GetObjectClass(jobject object) 282 | { 283 | JNI_CALL_RETURN(jclass, object, true, env->GetObjectClass(object)); 284 | } 285 | 286 | jboolean IsInstanceOf(jobject object, jclass clazz) 287 | { 288 | JNI_CALL_RETURN(jboolean, object && clazz, true, env->IsInstanceOf(object, clazz)); 289 | } 290 | 291 | jboolean IsSameObject(jobject object1, jobject object2) 292 | { 293 | JNI_CALL_RETURN(jboolean, object1 && object2, true, env->IsSameObject(object1, object2)); 294 | } 295 | 296 | jmethodID GetMethodID(jclass clazz, const char* name, const char* signature) 297 | { 298 | JNI_CALL_RETURN(jmethodID, clazz && name && signature, true, env->GetMethodID(clazz, name, signature)); 299 | } 300 | 301 | jfieldID GetFieldID(jclass clazz, const char* name, const char* signature) 302 | { 303 | JNI_CALL_RETURN(jfieldID, clazz && name && signature, true, env->GetFieldID(clazz, name, signature)); 304 | } 305 | 306 | jmethodID GetStaticMethodID(jclass clazz, const char* name, const char* signature) 307 | { 308 | JNI_CALL_RETURN(jmethodID, clazz && name && signature, true, env->GetStaticMethodID(clazz, name, signature)); 309 | } 310 | 311 | jfieldID GetStaticFieldID(jclass clazz, const char* name, const char* signature) 312 | { 313 | JNI_CALL_RETURN(jfieldID, clazz && name && signature, true, env->GetStaticFieldID(clazz, name, signature)); 314 | } 315 | 316 | jobject ToReflectedMethod(jclass clazz, jmethodID methodID, bool isStatic) 317 | { 318 | JNI_CALL_RETURN(jobject, clazz && methodID, true, env->ToReflectedMethod(clazz, methodID, isStatic)); 319 | } 320 | 321 | jobject NewObject(jclass clazz, jmethodID methodID, ...) 322 | { 323 | va_list args; 324 | va_start(args, methodID); 325 | JNI_CALL_DECLARE(jobject, result, clazz && methodID, true, env->NewObjectV(clazz, methodID, args)); 326 | va_end(args); 327 | return result; 328 | } 329 | 330 | jstring NewStringUTF(const char* str) 331 | { 332 | JNI_CALL_RETURN(jstring, str, true, env->NewStringUTF(str)); 333 | } 334 | 335 | jsize GetStringUTFLength(jstring string) 336 | { 337 | JNI_CALL_RETURN(jsize, string, true, env->GetStringUTFLength(string)); 338 | } 339 | 340 | const char* GetStringUTFChars(jstring str, jboolean* isCopy) 341 | { 342 | JNI_CALL_RETURN(const char*, str, true, env->GetStringUTFChars(str, isCopy)); 343 | } 344 | 345 | void ReleaseStringUTFChars(jstring str, const char* utfchars) 346 | { 347 | JNI_CALL(str && utfchars, false, env->ReleaseStringUTFChars(str, utfchars)); 348 | } 349 | 350 | size_t GetArrayLength(jarray obj) 351 | { 352 | JNI_CALL_RETURN(size_t, obj, true, env->GetArrayLength(obj)); 353 | } 354 | 355 | jobjectArray NewObjectArray(jsize length, jclass elementClass, jobject initialElement) 356 | { 357 | JNI_CALL_RETURN(jobjectArray, elementClass, true, env->NewObjectArray(length, elementClass, initialElement)); 358 | } 359 | 360 | jobject GetObjectArrayElement(jobjectArray obj, size_t index) 361 | { 362 | JNI_CALL_RETURN(jobject, obj, true, env->GetObjectArrayElement(obj, index)); 363 | } 364 | 365 | void SetObjectArrayElement(jobjectArray obj, size_t index, jobject val) 366 | { 367 | JNI_CALL(obj, true, env->SetObjectArrayElement(obj, index, val)); 368 | } 369 | 370 | void* GetPrimitiveArrayCritical(jarray obj, jboolean *isCopy) 371 | { 372 | JNI_CALL_RETURN(void*, obj, false, env->GetPrimitiveArrayCritical(obj, isCopy)); 373 | } 374 | 375 | void ReleasePrimitiveArrayCritical(jarray obj, void *carray, jint mode) 376 | { 377 | JNI_CALL(obj, false, env->ReleasePrimitiveArrayCritical(obj, carray, mode)); 378 | } 379 | 380 | jobject NewDirectByteBuffer(void* buffer, jlong size) 381 | { 382 | JNI_CALL_RETURN(jobject, buffer, true, env->NewDirectByteBuffer(buffer, size)); 383 | } 384 | 385 | void* GetDirectBufferAddress(jobject byteBuffer) 386 | { 387 | JNI_CALL_RETURN(void*, byteBuffer, true, env->GetDirectBufferAddress(byteBuffer)); 388 | } 389 | 390 | jlong GetDirectBufferCapacity(jobject byteBuffer) 391 | { 392 | JNI_CALL_RETURN(jlong, byteBuffer, true, env->GetDirectBufferCapacity(byteBuffer)); 393 | } 394 | 395 | // -------------------------------------------------------------------------------------- 396 | // ThreadScope 397 | // -------------------------------------------------------------------------------------- 398 | ThreadScope::ThreadScope() 399 | { 400 | m_NeedDetach = !jni::GetEnv(); 401 | } 402 | 403 | ThreadScope::~ThreadScope() 404 | { 405 | if (m_NeedDetach) 406 | jni::DetachCurrentThread(); 407 | } 408 | 409 | // -------------------------------------------------------------------------------------- 410 | // LocalFrame 411 | // -------------------------------------------------------------------------------------- 412 | LocalFrame::LocalFrame(jint capacity) 413 | { 414 | if (PushLocalFrame(capacity) < 0) 415 | FatalError("Out of memory: Unable to allocate local frame(64)"); 416 | m_FramePushed = (PeekError() == kJNI_NO_ERROR); 417 | } 418 | LocalFrame::~LocalFrame() 419 | { 420 | if (m_FramePushed) 421 | PopLocalFrame(NULL); 422 | } 423 | 424 | } 425 | -------------------------------------------------------------------------------- /JNIBridge.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #if 0 // ANDROID 5 | #include 6 | #define JNI_TRACE(...) __android_log_print(ANDROID_LOG_VERBOSE, "JNIBridge", __VA_ARGS__) 7 | #else 8 | #define JNI_TRACE(...) do {} while (false) 9 | #endif 10 | 11 | typedef void* jvoid; // make it possible to return void 12 | 13 | namespace jni 14 | { 15 | 16 | enum Errno 17 | { 18 | kJNI_NO_ERROR = 0, 19 | kJNI_ATTACH_FAILED, 20 | kJNI_INVALID_PARAMETERS, 21 | kJNI_EXCEPTION_THROWN 22 | }; 23 | 24 | extern jobject kNull; 25 | 26 | // -------------------------------------------------------------------------------------- 27 | // Initialization and error functions 28 | // -------------------------------------------------------------------------------------- 29 | void Initialize(JavaVM& vm); 30 | 31 | Errno CheckError(); 32 | Errno PeekError(); 33 | const char* GetErrorMessage(); 34 | 35 | jthrowable ExceptionThrown(jclass clazz = 0); 36 | 37 | // Internalish 38 | bool CheckForParameterError(bool valid); 39 | bool CheckForExceptionError(JNIEnv* env); 40 | 41 | // -------------------------------------------------------------------------------------- 42 | // Oracle JNI functions (a selection of) 43 | // http://docs.oracle.com/javase/6/docs/technotes/guides/jni/spec/functions.html#wp9502 44 | // -------------------------------------------------------------------------------------- 45 | 46 | JavaVM* GetJavaVM(); 47 | JNIEnv* GetEnv(); 48 | JNIEnv* AttachCurrentThread(); 49 | void DetachCurrentThread(); 50 | 51 | jclass FindClass(const char* name); 52 | 53 | jint Throw(jthrowable object); 54 | jint ThrowNew(jclass exception, const char* message); 55 | void FatalError(const char* str); 56 | 57 | jobject NewLocalRef(jobject obj); 58 | void DeleteLocalRef(jobject obj); 59 | jobject NewGlobalRef(jobject obj); 60 | void DeleteGlobalRef(jobject obj); 61 | jobject NewWeakGlobalRef(jobject obj); 62 | void DeleteWeakGlobalRef(jobject obj); 63 | 64 | jclass GetObjectClass(jobject object); 65 | jboolean IsInstanceOf(jobject object, jclass clazz); 66 | jboolean IsSameObject(jobject object1, jobject object2); 67 | 68 | jmethodID GetMethodID(jclass clazz, const char* name, const char* signature); 69 | jfieldID GetFieldID(jclass clazz, const char* name, const char* signature); 70 | jmethodID GetStaticMethodID(jclass clazz, const char* name, const char* signature); 71 | jfieldID GetStaticFieldID(jclass clazz, const char* name, const char* signature); 72 | 73 | jobject ToReflectedMethod(jclass clazz, jmethodID methodID, bool isStatic); 74 | 75 | jobject NewObject(jclass clazz, jmethodID methodID, ...); 76 | 77 | jstring NewStringUTF(const char* str); 78 | jsize GetStringUTFLength(jstring string); 79 | const char* GetStringUTFChars(jstring str, jboolean* isCopy = 0); 80 | void ReleaseStringUTFChars(jstring str, const char* utfchars); 81 | 82 | size_t GetArrayLength(jarray obj); 83 | jobject GetObjectArrayElement(jobjectArray obj, size_t index); 84 | void SetObjectArrayElement(jobjectArray obj, size_t index, jobject val); 85 | void* GetPrimitiveArrayCritical(jarray obj, jboolean *isCopy); 86 | void ReleasePrimitiveArrayCritical(jarray obj, void *carray, jint mode); 87 | jobjectArray NewObjectArray(jsize length, jclass elementClass, jobject initialElement = 0); 88 | 89 | jobject NewDirectByteBuffer(void* buffer, jlong size); 90 | void* GetDirectBufferAddress(jobject byteBuffer); 91 | jlong GetDirectBufferCapacity(jobject byteBuffer); 92 | 93 | class ThreadScope 94 | { 95 | public: 96 | ThreadScope(); 97 | ~ThreadScope(); 98 | 99 | private: 100 | bool m_NeedDetach; 101 | }; 102 | 103 | class LocalFrame 104 | { 105 | public: 106 | LocalFrame(jint capacity = 64); 107 | ~LocalFrame(); 108 | 109 | private: 110 | LocalFrame(const LocalFrame& frame); 111 | LocalFrame& operator=(const LocalFrame& rhs); 112 | bool m_FramePushed; 113 | }; 114 | 115 | // Some logic explanation 116 | // Only invoke function if thread can be attached 117 | // Only invoke function if 'parameters' == true 118 | // If function is 'exception safe' only check for exceptions after function has been invoked 119 | // Only adjust return value on exception if function is not exception safe 120 | #define JNI_CALL(parameters, check_exception, function) \ 121 | JNI_TRACE("%d:%d:%s", static_cast(parameters), check_exception, #function); \ 122 | JNIEnv* env(AttachCurrentThread()); \ 123 | if (env && !CheckForParameterError(parameters) && !(check_exception && CheckForExceptionError(env)))\ 124 | { \ 125 | function; \ 126 | CheckForExceptionError(env); \ 127 | } 128 | 129 | #define JNI_CALL_RETURN(type, parameters, check_exception, function) \ 130 | JNI_TRACE("%d:%d:%s %s", static_cast(parameters), check_exception, #type, #function); \ 131 | JNIEnv* env(AttachCurrentThread()); \ 132 | if (env && !CheckForParameterError(parameters) && !(check_exception && CheckForExceptionError(env)))\ 133 | { \ 134 | type JNI_CALL_result = function; \ 135 | if (!(CheckForExceptionError(env) && check_exception)) \ 136 | return JNI_CALL_result; \ 137 | } \ 138 | return 0 139 | 140 | #define JNI_CALL_DECLARE(type, result, parameters, check_exception, function) \ 141 | JNI_TRACE("%d:%d:%s %s", static_cast(parameters), check_exception, #type, #function); \ 142 | JNIEnv* env(AttachCurrentThread()); \ 143 | type result = 0; \ 144 | if (env && !CheckForParameterError(parameters) && !(check_exception && CheckForExceptionError(env)))\ 145 | { \ 146 | result = function; \ 147 | if (CheckForExceptionError(env) && check_exception) \ 148 | result = 0; \ 149 | } 150 | 151 | 152 | //---------------------------------------------------------------------------- 153 | // JNI Operations 154 | // Heavily inspired by https://github.com/kohsuke/jnitl 155 | //---------------------------------------------------------------------------- 156 | #if defined(__NDK_FPABI__) 157 | # define JNITL_FUNCTION_ATTRIBUTES __NDK_FPABI__ 158 | #else 159 | # define JNITL_FUNCTION_ATTRIBUTES 160 | #endif 161 | 162 | template 167 | class MethodOps 168 | { 169 | public: 170 | static JT CallMethod(jobject object, jmethodID id, ...) 171 | { 172 | va_list args; 173 | va_start(args, id); 174 | JNI_CALL_DECLARE(JT, result, object && id, true, static_cast((env->*CallMethodOP)(object, id, args))); 175 | va_end(args); 176 | return result; 177 | } 178 | static JT CallNonVirtualMethod(jobject object, jclass clazz, jmethodID id, ...) 179 | { 180 | va_list args; 181 | va_start(args, id); 182 | JNI_CALL_DECLARE(JT, result, object && clazz && id, true, static_cast((env->*CallNonvirtualMethodOP)(object, clazz, id, args))); 183 | va_end(args); 184 | return result; 185 | } 186 | static JT CallStaticMethod(jclass clazz, jmethodID id, ...) 187 | { 188 | va_list args; 189 | va_start(args, id); 190 | JNI_CALL_DECLARE(JT, result, clazz && id, true, static_cast((env->*CallStaticMethodOP)(clazz, id, args))); 191 | va_end(args); 192 | return result; 193 | } 194 | }; 195 | 196 | template 202 | class FieldOps 203 | { 204 | public: 205 | static JT GetField(jobject object, jfieldID id) 206 | { 207 | JNI_CALL_RETURN(JT, object && id, true, static_cast((env->*GetFieldOP)(object, id))); 208 | } 209 | static void SetField(jobject object, jfieldID id, const RT& value) 210 | { 211 | JNI_CALL(object && id, true, (env->*SetFieldOP)(object, id, value)); 212 | } 213 | static JT GetStaticField(jclass clazz, jfieldID id) 214 | { 215 | JNI_CALL_RETURN(JT, clazz && id, true, static_cast((env->*GetStaticFieldOP)(clazz, id))); 216 | } 217 | static void SetStaticField(jclass clazz, jfieldID id, const RT& value) 218 | { 219 | JNI_CALL(clazz && id, true, (env->*SetStaticFieldOP)(clazz, id, value)); 220 | } 221 | }; 222 | 223 | template 229 | class FloatFieldOps 230 | { 231 | public: 232 | static JT GetField(jobject object, jfieldID id) 233 | { 234 | JNI_CALL_RETURN(JT, object && id, true, static_cast((env->*GetFieldOP)(object, id))); 235 | } 236 | static void SetField(jobject object, jfieldID id, const RT& value) 237 | { 238 | JNI_CALL(object && id, true, (env->*SetFieldOP)(object, id, value)); 239 | } 240 | static JT GetStaticField(jclass clazz, jfieldID id) 241 | { 242 | JNI_CALL_RETURN(JT, clazz && id, true, static_cast((env->*GetStaticFieldOP)(clazz, id))); 243 | } 244 | static void SetStaticField(jclass clazz, jfieldID id, const RT& value) 245 | { 246 | JNI_CALL(clazz && id, true, (env->*SetStaticFieldOP)(clazz, id, value)); 247 | } 248 | }; 249 | 250 | template 257 | class ArrayOps 258 | { 259 | public: 260 | static RAT NewArray(jsize size) 261 | { 262 | JNI_CALL_RETURN(RAT, true, true, static_cast((env->*NewArrayOP)(size))); 263 | } 264 | static RT* GetArrayElements(RAT array, jboolean* isCopy = NULL) 265 | { 266 | JNI_CALL_RETURN(RT*, array, true, static_cast((env->*GetArrayElementsOP)(array, isCopy))); 267 | } 268 | static void ReleaseArrayElements(RAT array, RT* elements, jint mode = 0) 269 | { 270 | JNI_CALL(array && elements, true, (env->*ReleaseArrayElementsOP)(array, elements, mode)); 271 | } 272 | static void GetArrayRegion(RAT array, jsize start, jsize len, RT* buffer) 273 | { 274 | JNI_CALL(array && buffer, true, (env->*GetArrayRegionOP)(array, start, len, buffer)); 275 | } 276 | static void SetArrayRegion(RAT array, jsize start, jsize len, RT* buffer) 277 | { 278 | JNI_CALL(array && buffer, true, (env->*SetArrayRegionOP)(array, start, len, buffer)); 279 | } 280 | }; 281 | 282 | template 291 | class Object_Op : 292 | public MethodOps, 293 | public FieldOps 294 | { }; 295 | 296 | template 310 | class Primitive_Op : 311 | public MethodOps, 312 | public FieldOps, 313 | public ArrayOps 314 | { }; 315 | 316 | template 330 | class FloatPrimitive_Op : 331 | public MethodOps, 332 | public FloatFieldOps, 333 | public ArrayOps 334 | { }; 335 | 336 | 337 | #define JNITL_DEF_OP_LIST(t) \ 338 | &JNIEnv::Call##t##MethodV, \ 339 | &JNIEnv::CallNonvirtual##t##MethodV, \ 340 | &JNIEnv::CallStatic##t##MethodV, \ 341 | &JNIEnv::Get##t##Field, \ 342 | &JNIEnv::Set##t##Field, \ 343 | &JNIEnv::GetStatic##t##Field, \ 344 | &JNIEnv::SetStatic##t##Field 345 | 346 | #define JNITL_DEF_PRIMITIVE_OP_LIST(t) \ 347 | JNITL_DEF_OP_LIST(t), \ 348 | &JNIEnv::New##t##Array, \ 349 | &JNIEnv::Get##t##ArrayElements, \ 350 | &JNIEnv::Release##t##ArrayElements, \ 351 | &JNIEnv::Get##t##ArrayRegion, \ 352 | &JNIEnv::Set##t##ArrayRegion 353 | 354 | #define JNITL_DEF_PRIMITIVE_OP(jt,t) \ 355 | template <> \ 356 | class Op : public Primitive_Op {}; 359 | 360 | #define JNITL_DEF_FLOAT_PRIMITIVE_OP(jt,t) \ 361 | template <> \ 362 | class Op : public FloatPrimitive_Op {}; 365 | 366 | // it defaults to jobject 367 | template 368 | class Op : public Object_Op {}; 369 | 370 | // specialization for primitives 371 | JNITL_DEF_PRIMITIVE_OP(jboolean,Boolean) 372 | JNITL_DEF_PRIMITIVE_OP(jint,Int) 373 | JNITL_DEF_PRIMITIVE_OP(jshort,Short) 374 | JNITL_DEF_PRIMITIVE_OP(jbyte,Byte) 375 | JNITL_DEF_PRIMITIVE_OP(jlong,Long) 376 | JNITL_DEF_PRIMITIVE_OP(jchar,Char) 377 | JNITL_DEF_FLOAT_PRIMITIVE_OP(jfloat,Float) 378 | JNITL_DEF_FLOAT_PRIMITIVE_OP(jdouble,Double) 379 | 380 | #undef JNITL_FUNCTION_ATTRIBUTES 381 | #undef JNITL_DEF_FLOAT_PRIMITIVE_OP 382 | #undef JNITL_DEF_PRIMITIVE_OP 383 | #undef JNITL_DEF_PRIMITIVE_OP_LIST 384 | #undef JNITL_DEF_OP_LIST 385 | 386 | // void requires a specialization. 387 | template <> 388 | class Op 389 | { 390 | public: 391 | static jvoid CallMethod(jobject object, jmethodID id, ...) 392 | { 393 | va_list args; 394 | va_start(args, id); 395 | JNI_CALL(object && id, true, env->CallVoidMethodV(object, id, args)); 396 | va_end(args); 397 | return 0; 398 | } 399 | static jvoid CallStaticMethod(jclass clazz, jmethodID id, ...) 400 | { 401 | va_list args; 402 | va_start(args, id); 403 | JNI_CALL(clazz && id, true, env->CallStaticVoidMethodV(clazz, id, args)); 404 | va_end(args); 405 | return 0; 406 | } 407 | }; 408 | 409 | } 410 | -------------------------------------------------------------------------------- /JNIBridge.java: -------------------------------------------------------------------------------- 1 | package bitter.jnibridge; 2 | 3 | import java.lang.reflect.*; 4 | 5 | public class JNIBridge 6 | { 7 | static native Object invoke(long ptr, Class clazz, Method method, Object[] args); 8 | static native void delete(long ptr); 9 | 10 | static Object newInterfaceProxy(final long ptr, final Class[] interfaces) 11 | { 12 | return Proxy.newProxyInstance(JNIBridge.class.getClassLoader(), interfaces, new InterfaceProxy(ptr)); 13 | } 14 | 15 | static void disableInterfaceProxy(final Object proxy) 16 | { 17 | ((InterfaceProxy) Proxy.getInvocationHandler(proxy)).disable(); 18 | } 19 | 20 | private static class InterfaceProxy implements InvocationHandler 21 | { 22 | private Object m_InvocationLock = new Object[0]; 23 | private long m_Ptr; 24 | 25 | public InterfaceProxy(final long ptr) { m_Ptr = ptr; } 26 | 27 | public Object invoke(Object proxy, Method method, Object[] args) 28 | { 29 | synchronized (m_InvocationLock) 30 | { 31 | if (m_Ptr == 0) 32 | return null; 33 | return JNIBridge.invoke(m_Ptr, method.getDeclaringClass(), method, args); 34 | } 35 | } 36 | 37 | public void finalize() 38 | { 39 | synchronized (m_InvocationLock) 40 | { 41 | if (m_Ptr == 0) 42 | return; 43 | JNIBridge.delete(m_Ptr); 44 | } 45 | } 46 | 47 | public void disable() 48 | { 49 | synchronized (m_InvocationLock) 50 | { 51 | m_Ptr = 0; 52 | } 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015, Martin Sternevald - Unity Technologies 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PLATFORM ?= $(shell echo $(shell uname) | tr '[:upper:]' '[:lower:]') 2 | ifeq (${PLATFORM}, darwin) 3 | include Makefile.osx 4 | endif 5 | ifeq (${PLATFORM}, android) 6 | include Makefile.android 7 | endif 8 | 9 | LIBNAME = lib${APINAME}-jni.a 10 | 11 | ifdef JAVA_HOME 12 | JAVA = ${JAVA_HOME}/bin/java 13 | JAVAC = ${JAVA_HOME}/bin/javac 14 | else 15 | JAVA = java 16 | JAVAC = javac 17 | endif 18 | 19 | JAVAFLAGS = -XX:MaxPermSize=128M 20 | JAVACFLAGS = -source 1.6 -target 1.6 21 | 22 | CPPFLAGS += -g0 -O2 -Wall -Werror -Wno-long-long -std=c++11 23 | 24 | BUILDDIR = build 25 | GENDIR = ${BUILDDIR}/${APINAME}/source 26 | 27 | PLATFORM_BUILDDIR = $(abspath ${BUILDDIR}/${APINAME}/${PLATFORM}) 28 | 29 | APIGENERATOR_SRCS := $(wildcard *.java) 30 | APIGENERATOR_CLASSES := $(addprefix $(BUILDDIR)/,$(APIGENERATOR_SRCS:%.java=%.class)) 31 | 32 | static-apilib: ${GENDIR}/API.h ${GENDIR}/Makefile 33 | @make -C ${GENDIR} LIBNAME=${LIBNAME} BUILDDIR=${PLATFORM_BUILDDIR} static-lib 34 | 35 | compile-static-apilib: 36 | @make -C ${GENDIR} LIBNAME=${LIBNAME} BUILDDIR=${PLATFORM_BUILDDIR} static-lib 37 | 38 | api: ${GENDIR}/API.h ${GENDIR}/Makefile 39 | @make -C ${GENDIR} BUILDDIR=${PLATFORM_BUILDDIR} compile 40 | 41 | api-module: ${GENDIR}/Makefile ; 42 | ${GENDIR}/Makefile: Makefile.api *.cpp *.h | ${GENDIR} 43 | cp *.h *.cpp ${GENDIR}/ 44 | cp Makefile.api ${GENDIR}/Makefile 45 | 46 | api-source: ${GENDIR}/API.h ; 47 | ${GENDIR}/API.h: ${APIJAR} ${GPSJAR} ${APIGENERATOR_CLASSES} templates/* | ${GENDIR} 48 | ${JAVA} ${JAVAFLAGS} -cp ${BUILDDIR} APIGenerator ${GENDIR} "${APIJAR};${GPSJAR}" ${APICLASSES} 49 | 50 | api-generator: ${APIGENERATOR_CLASSES} ; 51 | ${BUILDDIR}/%.class: %.java | ${BUILDDIR} 52 | ${JAVAC} ${JAVACFLAGS} -d ${BUILDDIR} $< 53 | 54 | clean: 55 | @rm -fr ${BUILDDIR} 56 | 57 | ${GENDIR}: 58 | @mkdir -p ${GENDIR} 59 | 60 | ${BUILDDIR}: 61 | @mkdir -p ${BUILDDIR} 62 | -------------------------------------------------------------------------------- /Makefile.android: -------------------------------------------------------------------------------- 1 | 2 | APINAME = android-23 3 | APIJAR = ${ANDROID_SDK_ROOT}/platforms/${APINAME}/android.jar 4 | GPSJAR = ${ANDROID_SDK_ROOT}/extras/google/google_play_services/libproject/google-play-services_lib/libs/google-play-services.jar 5 | 6 | API = android-9 7 | SDK = ${ANDROID_SDK_ROOT} 8 | NDK = ${ANDROID_NDK_ROOT} 9 | UNAME := $(shell uname) 10 | HOST := $(shell echo ${UNAME} | tr '[:upper:]' '[:lower:]')-x86_64 11 | 12 | LLVM_VERSION = 3.5 13 | LLVM_TOOLCHAIN := llvm-${LLVM_VERSION} 14 | 15 | GCC_VERSION = 4.8 16 | 17 | ifeq (${ABI}, x86) 18 | NDK_ARCH := arch-x86 19 | GCC_BIN_PREFIX := i686-linux-android- 20 | GCC_TOOLCHAIN := x86-${GCC_VERSION} 21 | CPPFLAGS := ${CPPFLAGS} -target i686-none-linux-android 22 | LLVM_FLAGS = -gcc-toolchain ${GCC_TOOLCHAIN_PATH} 23 | else ifeq (${ABI}, mips) 24 | NDK_ARCH := arch-mips 25 | GCC_BIN_PREFIX := mipsel-linux-android- 26 | GCC_TOOLCHAIN := mipsel-linux-android-${GCC_VERSION} 27 | CPPFLAGS := ${CPPFLAGS} -target mipsel-none-linux-android 28 | LLVM_FLAGS = -gcc-toolchain ${GCC_TOOLCHAIN_PATH} 29 | else 30 | NDK_ARCH := arch-arm 31 | GCC_BIN_PREFIX := arm-linux-androideabi- 32 | GCC_TOOLCHAIN := arm-linux-androideabi-${GCC_VERSION} 33 | CPPFLAGS := ${CPPFLAGS} -marm 34 | LLVM_FLAGS = -gcc-toolchain ${GCC_TOOLCHAIN_PATH} 35 | 36 | ifeq (${ABI}, armeabi) 37 | DEFINES := ${DEFINES} -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__ 38 | CPPFLAGS := ${CPPFLAGS} -target armv5te-none-linux-androideabi -march=armv5te -mtune=xscale -mfloat-abi=soft 39 | else 40 | override ABI = armeabi-v7a 41 | DEFINES := ${DEFINES} -DARCH_ARMEABIV7A 42 | CPPFLAGS := ${CPPFLAGS} -target armv5te-none-linux-androideabi-v7a -march=armv7-a -mfpu=vfp -mfloat-abi=softfp 43 | LDFLAGS := -Wl,--fix-cortex-a8 44 | endif 45 | endif 46 | 47 | DEFINES := ${DEFINES} -DANDROID 48 | override PLATFORM = android/${ABI} 49 | LLVM_TOOLCAHIN_PATH = ${NDK}/toolchains/${LLVM_TOOLCHAIN}/prebuilt/${HOST} 50 | GCC_TOOLCHAIN_PATH = ${NDK}/toolchains/${GCC_TOOLCHAIN}/prebuilt/${HOST} 51 | LLVM_TOOL_PREFIX = ${LLVM_TOOLCAHIN_PATH}/bin/ 52 | GCC_TOOL_PREFIX = ${GCC_TOOLCHAIN_PATH}/bin/${GCC_BIN_PREFIX} 53 | SYSROOT = ${NDK}/platforms/${API}/${NDK_ARCH} 54 | CPPFLAGS := ${CPPFLAGS} --sysroot=${SYSROOT} -fpic -ffunction-sections -fdata-sections -fstrict-aliasing -fvisibility=hidden -MMD 55 | CXXFLAGS := ${CXXFLAGS} -fno-rtti -fno-exceptions 56 | LDFLAGS := ${LDFLAGS} --sysroot=${SYSROOT} -Wl,--no-undefined -Wl,--gc-sections 57 | CXX = ${LLVM_TOOL_PREFIX}clang++ ${DEFINES} ${LLVM_FLAGS} 58 | CC = ${LLVM_TOOL_PREFIX}clang ${DEFINES} ${LLVM_FLAGS} 59 | STRIP = ${LLVM_TOOL_PREFIX}strip ${STRIPFLAGS} 60 | LD = ${LLVM_TOOL_PREFIX}clang++ ${LDFLAGS} ${LLVM_FLAGS} 61 | AR = ${GCC_TOOL_PREFIX}ar crs 62 | 63 | export CPPFLAGS CXXFLAGS LDFLAGS CXX CC STRIP LD AR DEFINES PLATFORM 64 | -------------------------------------------------------------------------------- /Makefile.api: -------------------------------------------------------------------------------- 1 | 2 | BUILDDIR = . 3 | SRCS := $(wildcard *.cpp) 4 | OBJS := $(addprefix ${BUILDDIR}/,$(SRCS:%.cpp=%.o)) 5 | 6 | static-lib: ${BUILDDIR}/${LIBNAME} ; 7 | 8 | compile: ${OBJS} ; 9 | 10 | ${BUILDDIR}/${LIBNAME}: ${OBJS} 11 | ${AR} $@ $^ 12 | 13 | ${BUILDDIR}/%.o: %.cpp *.h | ${BUILDDIR} 14 | $(COMPILE.cpp) $(OUTPUT_OPTION) $< 15 | 16 | ${BUILDDIR}: 17 | @mkdir -p ${BUILDDIR} -------------------------------------------------------------------------------- /Makefile.osx: -------------------------------------------------------------------------------- 1 | 2 | JAVA_HOME ?= $(shell /usr/libexec/java_home -v 1.7) 3 | 4 | APINAME = jdk 5 | APIJAR = ${JAVA_HOME}/jre/lib/rt.jar 6 | 7 | DEFINES := ${DEFINES} -DMACOSX 8 | CPPFLAGS := ${CPPFLAGS} -fpic -ffunction-sections -fdata-sections -fvisibility=hidden -MMD 9 | CPPFLAGS := ${CPPFLAGS} -I${JAVA_HOME}/include -I${JAVA_HOME}/include/darwin 10 | CXXFLAGS := ${CXXFLAGS} -fno-rtti -fno-exceptions 11 | LDFLAGS := ${LDFLAGS} 12 | CXX = g++ ${DEFINES} 13 | CC = gcc ${DEFINES} 14 | STRIP = strip ${STRIPFLAGS} 15 | LD = g++ ${LDFLAGS} 16 | AR = ar crs 17 | 18 | export CPPFLAGS CXXFLAGS LDFLAGS CXX CC STRIP LD AR DEFINES 19 | -------------------------------------------------------------------------------- /PrepareAndroidSDK.pm: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | package PrepareAndroidSDK; 4 | 5 | use strict; 6 | use warnings; 7 | use Carp qw(croak carp); 8 | use File::Path qw(mkpath rmtree); 9 | use File::Spec::Functions; 10 | use File::Copy; 11 | use File::Basename; 12 | 13 | require Exporter; 14 | our @ISA = qw(Exporter); 15 | our @EXPORT_OK=qw(GetAndroidSDK); 16 | 17 | our $SDK_ROOT_ENV = "ANDROID_SDK_ROOT"; 18 | our $NDK_ROOT_ENV = "ANDROID_NDK_ROOT"; 19 | 20 | # based on https://dl.google.com/android/repository/repository-11.xml 21 | 22 | our $BASE_URL_SDK = "http://dl.google.com/android/repository/"; 23 | our $BASE_URL_NDK = "http://dl.google.com/android/ndk/"; 24 | 25 | our $sdks = 26 | { 27 | "android-7" => "android-2.1_r03-linux.zip", 28 | "android-8" => "android-2.2_r03-linux.zip", 29 | "android-9" => "android-2.3.1_r02-linux.zip", 30 | "android-10" => "android-2.3.3_r02-linux.zip", 31 | "android-11" => "android-3.0_r02-linux.zip", 32 | "android-12" => "android-3.1_r03-linux.zip", 33 | "android-13" => "android-3.2_r01-linux.zip", 34 | "android-14" => "android-14_r03.zip", 35 | "android-15" => "android-15_r03.zip", 36 | "android-16" => "android-16_r02.zip", 37 | "android-17" => "android-17_r01.zip", 38 | "android-18" => "android-18_r02.zip", 39 | "android-19" => "android-19_r03.zip", 40 | "android-21" => "android-21_r02.zip", 41 | "android-23" => "android-23_r02.zip", 42 | }; 43 | 44 | our $sdk_tools = 45 | { 46 | "version" => "24.4.1", 47 | "windows" => "tools_r24.4.1-windows.zip", 48 | "linux" => "tools_r24.4.1-linux.zip", 49 | "macosx" => "tools_r24.4.1-macosx.zip", 50 | }; 51 | 52 | our $platform_tools = 53 | { 54 | "version" => "23.1.0", 55 | "windows" => "platform-tools_r23.1.0-windows.zip", 56 | "linux" => "platform-tools_r23.1.0-linux.zip", 57 | "macosx" => "platform-tools_r23.1.0-macosx.zip", 58 | }; 59 | 60 | our $build_tools = 61 | { 62 | "version" => "23.0.2", 63 | "windows" => "build-tools_r23.0.2-windows.zip", 64 | "linux" => "build-tools_r23.0.2-linux.zip", 65 | "macosx" => "build-tools_r23.0.2-macosx.zip", 66 | }; 67 | 68 | our $ndks = 69 | { 70 | "r5" => 71 | { 72 | "windows" => "android-ndk-r5-windows.zip", 73 | "macosx" => "android-ndk-r5-darwin-x86.tar.bz2", 74 | "linux" => "android-ndk-r5-linux-x86.tar.bz2", 75 | }, 76 | "r5b" => 77 | { 78 | "windows" => "android-ndk-r5b-windows.zip", 79 | "macosx" => "android-ndk-r5b-darwin-x86.tar.bz2", 80 | "linux" => "android-ndk-r5b-linux-x86.tar.bz2", 81 | }, 82 | "r5c" => 83 | { 84 | "windows" => "android-ndk-r5c-windows.zip", 85 | "macosx" => "android-ndk-r5c-darwin-x86.tar.bz2", 86 | "linux" => "android-ndk-r5c-linux-x86.tar.bz2", 87 | }, 88 | "r6" => 89 | { 90 | "windows" => "android-ndk-r6-windows.zip", 91 | "macosx" => "android-ndk-r6-darwin-x86.tar.bz2", 92 | "linux" => "android-ndk-r6-linux-x86.tar.bz2", 93 | }, 94 | "r6b" => 95 | { 96 | "windows" => "android-ndk-r6b-windows.zip", 97 | "macosx" => "android-ndk-r6b-darwin-x86.tar.bz2", 98 | "linux" => "android-ndk-r6b-linux-x86.tar.bz2", 99 | }, 100 | "r7" => 101 | { 102 | "windows" => "android-ndk-r7-windows.zip", 103 | "macosx" => "android-ndk-r7-darwin-x86.tar.bz2", 104 | "linux" => "android-ndk-r7-linux-x86.tar.bz2", 105 | }, 106 | "r7b" => 107 | { 108 | "windows" => "android-ndk-r7b-windows.zip", 109 | "macosx" => "android-ndk-r7b-darwin-x86.tar.bz2", 110 | "linux" => "android-ndk-r7b-linux-x86.tar.bz2", 111 | }, 112 | "r7c" => 113 | { 114 | "windows" => "android-ndk-r7c-windows.zip", 115 | "macosx" => "android-ndk-r7c-darwin-x86.tar.bz2", 116 | "linux" => "android-ndk-r7c-linux-x86.tar.bz2", 117 | }, 118 | "r8" => 119 | { 120 | "windows" => "android-ndk-r8-windows.zip", 121 | "macosx" => "android-ndk-r8-darwin-x86.tar.bz2", 122 | "linux" => "android-ndk-r8-linux-x86.tar.bz2", 123 | }, 124 | "r8b" => 125 | { 126 | "windows" => "android-ndk-r8b-windows.zip", 127 | "macosx" => "android-ndk-r8b-darwin-x86.tar.bz2", 128 | "linux" => "android-ndk-r8b-linux-x86.tar.bz2", 129 | }, 130 | "r8c" => 131 | { 132 | "windows" => "android-ndk-r8c-windows.zip", 133 | "macosx" => "android-ndk-r8c-darwin-x86.tar.bz2", 134 | "linux" => "android-ndk-r8c-linux-x86.tar.bz2", 135 | }, 136 | "r8e" => 137 | { 138 | "windows" => "android-ndk-r8e-windows.zip", 139 | "macosx" => "android-ndk-r8e-darwin-x86.tar.bz2", 140 | "linux" => "android-ndk-r8e-linux-x86.tar.bz2", 141 | }, 142 | "r9" => 143 | { 144 | "windows" => "android-ndk-r9-windows-x86.zip", 145 | "macosx" => "android-ndk-r9-darwin-x86.tar.bz2", 146 | "linux" => "android-ndk-r9-linux-x86.tar.bz2", 147 | }, 148 | "r10b" => 149 | { 150 | "windows" => "android-ndk32-r10b-windows-x86.zip", 151 | "macosx" => "android-ndk32-r10b-darwin-x86.tar.bz2", 152 | "linux" => "android-ndk32-r10b-linux-x86.tar.bz2", 153 | }, 154 | "r10e" => 155 | { 156 | "windows" => "android-ndk-r10e-windows-x86.exe", 157 | "macosx" => "android-ndk-r10e-darwin-x86_64.bin", 158 | "linux" => "android-ndk-r10e-linux-x86.bin", 159 | }, 160 | }; 161 | 162 | our $google_play_services = 163 | { 164 | "24" => "google_play_services_7327000_r24.zip" 165 | }; 166 | 167 | our ($HOST_ENV, $TMP, $HOME, $WINZIP); 168 | 169 | sub GetAndroidSDK 170 | { 171 | if(lc $^O eq 'darwin') 172 | { 173 | $HOST_ENV = "macosx"; 174 | $TMP = $ENV{"TMPDIR"}; 175 | $HOME = $ENV{"HOME"}; 176 | } 177 | elsif(lc $^O eq 'linux') 178 | { 179 | $HOST_ENV = "linux"; 180 | $TMP = "/tmp"; 181 | $HOME = $ENV{"HOME"}; 182 | } 183 | elsif(lc $^O eq 'mswin32') 184 | { 185 | $HOST_ENV = "windows"; 186 | $TMP = $ENV{"TMP"}; 187 | $HOME = $ENV{"USERPROFILE"}; 188 | if (-e "Tools/WinUtils/7z/7z.exe") 189 | { 190 | $WINZIP = "Tools/WinUtils/7z/7z.exe"; 191 | } 192 | } 193 | elsif(lc $^O eq 'cygwin') 194 | { 195 | $HOST_ENV = "windows"; 196 | $TMP = $ENV{"TMP"}; 197 | $HOME = $ENV{"HOME"}; 198 | } 199 | else 200 | { 201 | die "UNKNOWN " . $^O; 202 | } 203 | 204 | print "Environment:\n"; 205 | print "\tHost = $HOST_ENV\n"; 206 | print "\tTemporary = $TMP\n"; 207 | print "\tHome = $HOME\n"; 208 | print "\n"; 209 | print "\t\$$SDK_ROOT_ENV = $ENV{$SDK_ROOT_ENV}\n" if ($ENV{$SDK_ROOT_ENV}); 210 | print "\t\$$NDK_ROOT_ENV = $ENV{$NDK_ROOT_ENV}\n" if ($ENV{$NDK_ROOT_ENV}); 211 | print "\n"; 212 | 213 | my ($sdk, $tools, $ndk, $gps, $setenv) = @_; 214 | 215 | # Getopt::Long::GetOptions("sdk=s"=>\$sdk, "ndk=s"=>\$ndk) or die ("Illegal cmdline options"); 216 | 217 | if ($sdk or $tools) 218 | { 219 | if ($sdk) 220 | { 221 | print "Installing SDK '$sdk':\n"; 222 | } 223 | elsif($tools) 224 | { 225 | print "Installing SDK Tools '$tools':\n"; 226 | } 227 | 228 | if (!$ENV{$SDK_ROOT_ENV}) 229 | { 230 | $ENV{$SDK_ROOT_ENV} = catfile($HOME, "android-sdk_auto"); 231 | print "\t\$$SDK_ROOT_ENV not set; using $ENV{$SDK_ROOT_ENV} instead\n"; 232 | } 233 | 234 | if (not $tools and $sdk) 235 | { 236 | my @split = split('-', $sdk); 237 | $tools = $split[1]; 238 | } 239 | if ($tools) 240 | { 241 | PrepareSDKTools($tools); 242 | } 243 | if ($sdk) 244 | { 245 | PrepareSDK($sdk); 246 | } 247 | if ($gps) 248 | { 249 | print "Installing Google Play Services '$gps':\n"; 250 | PrepareGPS($gps); 251 | print "\n"; 252 | } 253 | print "\n"; 254 | } 255 | 256 | if ($ndk) 257 | { 258 | print "Installing NDK '$ndk':\n"; 259 | if (!$ENV{$NDK_ROOT_ENV}) 260 | { 261 | $ENV{$NDK_ROOT_ENV} = catfile($HOME, "android-ndk_auto-" . $ndk); 262 | print "\t\$$NDK_ROOT_ENV not set; using $ENV{$NDK_ROOT_ENV} instead\n"; 263 | } 264 | PrepareNDK($ndk); 265 | print "\n"; 266 | } 267 | 268 | my $export = "export"; 269 | if (lc $^O eq 'mswin32') 270 | { 271 | $export = "set"; 272 | } 273 | 274 | if ($setenv and ($ENV{$SDK_ROOT_ENV} or $ENV{$SDK_ROOT_ENV})) 275 | { 276 | print "Outputing updated environment:\n"; 277 | print "\t'$setenv'\n"; 278 | open (SETENV, '>' . $setenv); 279 | print SETENV "$export $SDK_ROOT_ENV=$ENV{$SDK_ROOT_ENV}\n" if ($ENV{$SDK_ROOT_ENV}); 280 | print SETENV "$export $NDK_ROOT_ENV=$ENV{$NDK_ROOT_ENV}\n" if ($ENV{$NDK_ROOT_ENV}); 281 | close (SETENV); 282 | print "\n"; 283 | } 284 | 285 | print "Environment:\n" if ($ENV{$SDK_ROOT_ENV} or $ENV{$SDK_ROOT_ENV}); 286 | print "\t\$$SDK_ROOT_ENV = $ENV{$SDK_ROOT_ENV}\n" if ($ENV{$SDK_ROOT_ENV}); 287 | print "\t\$$NDK_ROOT_ENV = $ENV{$NDK_ROOT_ENV}\n" if ($ENV{$NDK_ROOT_ENV}); 288 | print "\n"; 289 | } 290 | 291 | sub PrepareSDKTools 292 | { 293 | my $sdk_root = $ENV{$SDK_ROOT_ENV}; 294 | my $sdk_tool_path = catfile($sdk_root, "tools"); 295 | my $sdk_platform_tool_path = catfile($sdk_root, "platform-tools"); 296 | my $sdk_build_tool_path = catfile($sdk_root, "build-tools", $build_tools->{'version'}); 297 | 298 | my $sdk_tool_version = GetToolsRevisionMajor("$sdk_tool_path"); 299 | my $sdk_platform_tool_version = GetToolsRevisionMajor("$sdk_platform_tool_path"); 300 | my $sdk_build_tool_version = GetToolsRevisionMajor("$sdk_build_tool_path"); 301 | 302 | my $sdk_tool = $sdk_tools->{$HOST_ENV}; 303 | my $platform_tool = $platform_tools->{$HOST_ENV}; 304 | my $build_tool = $build_tools->{$HOST_ENV}; 305 | die ("Unknown host environment '$HOST_ENV'") if (!$sdk_tool or !$platform_tool or !$build_tool); 306 | 307 | if (ParseMajor($sdk_tools->{'version'}) != $sdk_tool_version) 308 | { 309 | print "\tInstalling Tools\n"; 310 | DownloadAndUnpackArchive($BASE_URL_SDK . $sdk_tool, "$sdk_tool_path"); 311 | } 312 | 313 | if (ParseMajor($platform_tools->{'version'}) != $sdk_platform_tool_version) 314 | { 315 | print "\tInstalling Platform Tools\n"; 316 | DownloadAndUnpackArchive($BASE_URL_SDK . $platform_tool, "$sdk_platform_tool_path"); 317 | } 318 | 319 | if (ParseMajor($build_tools->{'version'}) != $sdk_build_tool_version) 320 | { 321 | print "\tInstalling Build Tools\n"; 322 | DownloadAndUnpackArchive($BASE_URL_SDK . $build_tool, "$sdk_build_tool_path"); 323 | } 324 | } 325 | 326 | sub ParseMajor 327 | { 328 | my ($version_str) = @_; 329 | my @version_numbers = split('\.', $version_str); 330 | return int($version_numbers[0]); 331 | } 332 | 333 | sub GetToolsRevisionMajor 334 | { 335 | my ($tools_dir) = @_; 336 | if (open PROPS, "<", catfile("$tools_dir", "source.properties")) 337 | { 338 | my @content = ; 339 | close PROPS; 340 | chomp(@content); 341 | foreach (@content) 342 | { 343 | if (index($_, "Pkg.Revision") != -1) 344 | { 345 | my @tokens = split('=', $_); 346 | return ParseMajor($tokens[1]); 347 | } 348 | } 349 | } 350 | return 0; 351 | } 352 | 353 | sub PrepareSDK 354 | { 355 | my $sdk_root = $ENV{$SDK_ROOT_ENV}; 356 | 357 | my ($sdk) = @_; 358 | 359 | if (IsPlatformInstalled($sdk)) 360 | { 361 | print "\tPlatform '$sdk' is already installed\n"; 362 | return; 363 | } 364 | 365 | my $platform = $sdks->{$sdk}; 366 | die ("Unknown platform API '$sdk'") if (!$platform); 367 | 368 | my $output = catfile($sdk_root, "platforms", $sdk); 369 | print "\tDownloading '$platform' to '$output'\n"; 370 | DownloadAndUnpackArchive($BASE_URL_SDK . $platform, $output); 371 | } 372 | 373 | sub PrepareGPS 374 | { 375 | my $sdk_root = $ENV{$SDK_ROOT_ENV}; 376 | 377 | my ($gps_version) = @_; 378 | 379 | my $gps = $google_play_services->{$gps_version}; 380 | die ("Unknown Google Play Services version '$gps_version'") if (!$gps); 381 | 382 | my $output = catfile($sdk_root, "extras", "google", "google_play_services"); 383 | my $gps_jar = catfile($output, "libproject", "google-play-services_lib", "libs", "google-play-services.jar"); 384 | if (-e $gps_jar) 385 | { 386 | print "\tGoogle Play Services version '$gps_version' is already installed\n"; 387 | return; 388 | } 389 | 390 | print "\tDownloading '$gps' to '$output'\n"; 391 | DownloadAndUnpackArchive($BASE_URL_SDK . $gps, $output); 392 | } 393 | 394 | sub IsPlatformInstalled 395 | { 396 | my $sdk_root = $ENV{$SDK_ROOT_ENV}; 397 | my ($sdk) = @_; 398 | if (! $sdk_root) 399 | { 400 | return 0; 401 | } 402 | unless (grep {$_ eq $sdk} GetCurrentSDKPlatforms($sdk_root)) 403 | { 404 | return 0; 405 | } 406 | return 1; 407 | } 408 | 409 | sub GetCurrentSDKPlatforms 410 | { 411 | my ($sdk_root) = @_; 412 | my $platform_root = $sdk_root . "/platforms"; 413 | opendir(my $dh, $platform_root) || return; 414 | my @platforms = grep { !/^\.\.?$/ && -e catfile($platform_root, $_, "android.jar") } readdir($dh); 415 | closedir $dh; 416 | 417 | return @platforms; 418 | } 419 | 420 | sub DownloadAndUnpackArchive 421 | { 422 | my ($url, $output) = @_; 423 | my ($base,$base_url,$suffix) = fileparse($url, qr/\.[^.]*/); 424 | my ($dest_name,$dest_path) = fileparse($output); 425 | 426 | my $temporary_download_path = catfile($TMP, $base . $suffix); 427 | my $temporary_unpack_path = catfile($TMP, $base . "_unpack"); 428 | 429 | print "\t\tURL: " . $url . "\n"; 430 | print "\t\tOutput: " . $output . "\n"; 431 | print "\t\tBase: " . $base . "\n"; 432 | print "\t\tURL base: " . $base_url . "\n"; 433 | print "\t\tSuffix: " . $suffix . "\n"; 434 | print "\t\tTmp DL: " . $temporary_download_path . "\n"; 435 | print "\t\tTmp unpack: " . $temporary_unpack_path . "\n"; 436 | print "\t\tDest path: " . $dest_path . "\n"; 437 | print "\t\tDest name: " . $dest_name . "\n"; 438 | 439 | # remove old output 440 | rmtree($output); 441 | mkpath($dest_path); 442 | 443 | # create temporary locations 444 | unlink($temporary_download_path); 445 | rmtree($temporary_unpack_path); 446 | mkpath($temporary_unpack_path); 447 | 448 | system("lwp-download", $url, $temporary_download_path); 449 | 450 | if ($WINZIP) 451 | { 452 | system($WINZIP, "x", $temporary_download_path, "-o" . $temporary_unpack_path); 453 | } 454 | else 455 | { 456 | if (lc $suffix eq '.zip') 457 | { 458 | system("unzip", "-q", $temporary_download_path, "-d", $temporary_unpack_path); 459 | } 460 | elsif (lc $suffix eq '.bz2') 461 | { 462 | system("tar", "-xf", $temporary_download_path, "-C", $temporary_unpack_path); 463 | } 464 | elsif (lc $suffix eq '.bin') 465 | { chmod(0755, $temporary_download_path); 466 | system($temporary_download_path, "-o" . $temporary_unpack_path); 467 | } 468 | elsif (lc $suffix eq '.exe') 469 | { chmod(0755, $temporary_download_path); 470 | system($temporary_download_path, "-o" . $temporary_unpack_path); 471 | } 472 | else 473 | { 474 | die "Unknown file extension '" . $suffix . "'\n"; 475 | } 476 | } 477 | 478 | opendir(my $dh, $temporary_unpack_path); 479 | my @dirs = grep { !/^\.\.?$/ && -d catfile($temporary_unpack_path, $_) } readdir($dh); 480 | closedir $dh; 481 | my $unpacked_subdir = catfile($temporary_unpack_path, $dirs[0]); 482 | 483 | if(move($unpacked_subdir, $output) == 0) 484 | { 485 | # move failed. Try to do a recursive copy instead 486 | if(File::Copy::Recursive::dircopy($unpacked_subdir, $output) == 0) 487 | { 488 | print "\t\tMove/Copy Error: " . $! . "\n"; 489 | } 490 | } 491 | 492 | # clean up 493 | unlink($temporary_download_path); 494 | rmtree($temporary_unpack_path); 495 | } 496 | 497 | 498 | sub PrepareNDK 499 | { 500 | my ($ndk) = @_; 501 | my $ndk_root = $ENV{$NDK_ROOT_ENV}; 502 | $ndk_root = $1 if($ndk_root=~/(.*)\/$/); 503 | 504 | if (-e $ndk_root and open RELEASE, "<", catfile("$ndk_root", "RELEASE.TXT")) 505 | { 506 | my @content = ; 507 | close RELEASE; 508 | chomp(@content); 509 | my $current = $content[0]; 510 | print "\tCurrently installed = " . $current . "\n"; 511 | 512 | # remove the possible '-rcX' or ' (64-bit)' from the end 513 | my @curr_arr = split(/ |-/, $current); 514 | $current = $curr_arr[0]; 515 | print "\tShort name = " . $current . "\n"; 516 | 517 | if ($ndk eq $current) 518 | { 519 | print "\tNDK '$ndk' is already installed\n"; 520 | return; 521 | } 522 | else 523 | { 524 | my ($current_name,$path) = fileparse($ndk_root); 525 | 526 | $ENV{$NDK_ROOT_ENV} = catfile($path, "android-ndk-" . $ndk); 527 | print "\t\$$NDK_ROOT_ENV is pointing to a mismatching NDK; using $ENV{$NDK_ROOT_ENV} instead\n"; 528 | PrepareNDK($ndk); 529 | return; 530 | } 531 | } 532 | 533 | rmtree($ndk_root); 534 | 535 | my $archive = $ndks->{$ndk}->{$HOST_ENV}; 536 | die ("Unknown NDK release '$ndk' (for $HOST_ENV)") if (!$archive); 537 | 538 | print "\tDownloading '$ndk' to '$ndk_root'\n"; 539 | DownloadAndUnpackArchive($BASE_URL_NDK . $archive, $ndk_root); 540 | } 541 | 542 | 1; 543 | -------------------------------------------------------------------------------- /Proxy.cpp: -------------------------------------------------------------------------------- 1 | #include "Proxy.h" 2 | 3 | namespace jni 4 | { 5 | 6 | jni::Class s_JNIBridgeClass("bitter/jnibridge/JNIBridge"); 7 | 8 | JNIEXPORT jobject JNICALL Java_bitter_jnibridge_JNIBridge_00024InterfaceProxy_invoke(JNIEnv* env, jobject thiz, jlong ptr, jclass clazz, jobject method, jobjectArray args) 9 | { 10 | jmethodID methodID = env->FromReflectedMethod(method); 11 | ProxyInvoker* proxy = (ProxyInvoker*)ptr; 12 | return proxy->__Invoke(clazz, methodID, args); 13 | } 14 | 15 | JNIEXPORT void JNICALL Java_bitter_jnibridge_JNIBridge_00024InterfaceProxy_delete(JNIEnv* env, jobject thiz, jlong ptr) 16 | { 17 | delete (ProxyInvoker*)ptr; 18 | } 19 | 20 | bool ProxyInvoker::__Register() 21 | { 22 | jni::LocalFrame frame; 23 | jni::Class nativeProxyClass("bitter/jnibridge/JNIBridge"); 24 | char invokeMethodName[] = "invoke"; 25 | char invokeMethodSignature[] = "(JLjava/lang/Class;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;"; 26 | char deleteMethodName[] = "delete"; 27 | char deleteMethodSignature[] = "(J)V"; 28 | 29 | JNINativeMethod nativeProxyFunction[] = { 30 | {invokeMethodName, invokeMethodSignature, (void*) Java_bitter_jnibridge_JNIBridge_00024InterfaceProxy_invoke}, 31 | {deleteMethodName, deleteMethodSignature, (void*) Java_bitter_jnibridge_JNIBridge_00024InterfaceProxy_delete} 32 | }; 33 | 34 | if (nativeProxyClass) jni::GetEnv()->RegisterNatives(nativeProxyClass, nativeProxyFunction, 2); // <-- fix this 35 | return !jni::CheckError(); 36 | } 37 | 38 | ::jint ProxyObject::HashCode() const 39 | { 40 | return java::lang::System::IdentityHashCode(java::lang::Object(__ProxyObject())); 41 | } 42 | 43 | ::jboolean ProxyObject::Equals(const ::jobject arg0) const 44 | { 45 | return jni::IsSameObject(__ProxyObject(), arg0); 46 | } 47 | 48 | java::lang::String ProxyObject::ToString() const 49 | { 50 | return java::lang::String(""); 51 | } 52 | 53 | jobject ProxyObject::__Invoke(jclass clazz, jmethodID mid, jobjectArray args) 54 | { 55 | jobject result; 56 | if (!__InvokeInternal(clazz, mid, args, &result)) 57 | { 58 | java::lang::reflect::Method method(jni::ToReflectedMethod(clazz, mid, false)); 59 | jni::ThrowNew(method.ToString().c_str()); 60 | } 61 | 62 | return result; 63 | } 64 | 65 | bool ProxyObject::__TryInvoke(jclass clazz, jmethodID methodID, jobjectArray args, bool* success, jobject* result) 66 | { 67 | if (*success) 68 | return false; 69 | 70 | // in case jmethodIDs are not unique across classes - couldn't find any spec regarding this 71 | if (!jni::IsSameObject(clazz, java::lang::Object::__CLASS)) 72 | return false; 73 | 74 | static jmethodID methodIDs[] = { 75 | jni::GetMethodID(java::lang::Object::__CLASS, "hashCode", "()I"), 76 | jni::GetMethodID(java::lang::Object::__CLASS, "equals", "(Ljava/lang/Object;)Z"), 77 | jni::GetMethodID(java::lang::Object::__CLASS, "toString", "()Ljava/lang/String;") 78 | }; 79 | if (methodIDs[0] == methodID) { *result = jni::NewLocalRef(static_cast(HashCode())); *success = true; return true; } 80 | if (methodIDs[1] == methodID) { *result = jni::NewLocalRef(static_cast(Equals(::java::lang::Object(jni::GetObjectArrayElement(args, 0))))); *success = true; return true; } 81 | if (methodIDs[2] == methodID) { *result = jni::NewLocalRef(static_cast(ToString())); *success = true; return true; } 82 | 83 | return false; 84 | } 85 | 86 | jobject ProxyObject::NewInstance(void* nativePtr, const jobject* interfaces, size_t interfaces_len) 87 | { 88 | Array interfaceArray(java::lang::Class::__CLASS, interfaces_len, interfaces); 89 | 90 | static jmethodID newProxyMID = jni::GetStaticMethodID(s_JNIBridgeClass, "newInterfaceProxy", "(J[Ljava/lang/Class;)Ljava/lang/Object;"); 91 | return jni::Op::CallStaticMethod(s_JNIBridgeClass, newProxyMID, (jlong) nativePtr, static_cast(interfaceArray)); 92 | } 93 | 94 | void ProxyObject::DisableInstance(jobject proxy) 95 | { 96 | static jmethodID disableProxyMID = jni::GetStaticMethodID(s_JNIBridgeClass, "disableInterfaceProxy", "(Ljava/lang/Object;)V"); 97 | jni::Op::CallStaticMethod(s_JNIBridgeClass, disableProxyMID, proxy); 98 | } 99 | 100 | } -------------------------------------------------------------------------------- /Proxy.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "API.h" 4 | 5 | namespace jni 6 | { 7 | 8 | class ProxyObject : public virtual ProxyInvoker 9 | { 10 | // Dispatch invoke calls 11 | public: 12 | virtual jobject __Invoke(jclass clazz, jmethodID mid, jobjectArray args); 13 | 14 | // These functions are special and always forwarded 15 | protected: 16 | virtual ::jint HashCode() const; 17 | virtual ::jboolean Equals(const ::jobject arg0) const; 18 | virtual java::lang::String ToString() const; 19 | 20 | bool __TryInvoke(jclass clazz, jmethodID methodID, jobjectArray args, bool* success, jobject* result); 21 | virtual bool __InvokeInternal(jclass clazz, jmethodID mid, jobjectArray args, jobject* result) = 0; 22 | 23 | // Factory stuff 24 | protected: 25 | static jobject NewInstance(void* nativePtr, const jobject* interfaces, size_t interfaces_len); 26 | static void DisableInstance(jobject proxy); 27 | }; 28 | 29 | template 30 | class ProxyGenerator : public ProxyObject, public TX::__Proxy... 31 | { 32 | protected: 33 | ProxyGenerator() : m_ProxyObject(NewInstance(this, (jobject[]){TX::__CLASS...}, sizeof...(TX))) { } 34 | 35 | virtual ~ProxyGenerator() 36 | { 37 | DisableInstance(__ProxyObject()); 38 | } 39 | 40 | virtual ::jobject __ProxyObject() const { return m_ProxyObject; } 41 | 42 | private: 43 | template inline void DummyInvoke(Args&&...) {} 44 | virtual bool __InvokeInternal(jclass clazz, jmethodID mid, jobjectArray args, jobject* result) 45 | { 46 | bool success = false; 47 | DummyInvoke(ProxyObject::__TryInvoke(clazz, mid, args, &success, result), TX::__Proxy::__TryInvoke(clazz, mid, args, &success, result)...); 48 | return success; 49 | } 50 | 51 | Ref m_ProxyObject; 52 | }; 53 | 54 | template class Proxy : public ProxyGenerator {}; 55 | template class WeakProxy : public ProxyGenerator {}; 56 | 57 | } -------------------------------------------------------------------------------- /build-osx.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl -w 2 | 3 | use PrepareAndroidSDK; 4 | use File::Path; 5 | use strict; 6 | use warnings; 7 | 8 | my $api = "jdk-7"; 9 | 10 | my @classes = ( 11 | '::java::lang::System', 12 | '::java::lang::UnsupportedOperationException' 13 | ); 14 | 15 | sub BuildOSX 16 | { 17 | my $class_names = join(' ', @classes); 18 | my $threads = 8; 19 | 20 | system("make clean") && die("Clean failed"); 21 | system("make -j$threads PLATFORM=darwin APINAME=\"$api\" APICLASSES=\"$class_names\"") && die("Failed to make osx library"); 22 | } 23 | 24 | sub ZipIt 25 | { 26 | system("mkdir -p build/temp/include") && die("Failed to create temp directory."); 27 | 28 | # write build info 29 | my $git_info = qx(git symbolic-ref -q HEAD && git rev-parse HEAD); 30 | open(BUILD_INFO_FILE, '>', "build/temp/build.txt") or die("Unable to write build information to build/temp/build.txt"); 31 | print BUILD_INFO_FILE "$git_info"; 32 | close(BUILD_INFO_FILE); 33 | 34 | # create zip 35 | system("cp build/$api/source/*.h build/temp/include") && die("Failed to copy headers."); 36 | system("cd build && jar cf temp/jnibridge.jar bitter") && die("Failed to create java class archive."); 37 | system("cd build/$api && zip ../builds.zip -r darwin/*.a") && die("Failed to package libraries into zip file."); 38 | system("cd build/temp && zip ../builds.zip -r jnibridge.jar build.txt include") && die("Failed to package headers into zip file."); 39 | system("rm -r build/temp") && die("Unable to remove temp directory."); 40 | system("cd test; unzip -o ../build/builds.zip; touch Test.cpp") && die("Unable to prepare for tests"); 41 | } 42 | 43 | BuildOSX(); 44 | ZipIt(); 45 | -------------------------------------------------------------------------------- /build.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl -w 2 | use PrepareAndroidSDK; 3 | use File::Path; 4 | use strict; 5 | use warnings; 6 | 7 | my $api = "android-23"; 8 | 9 | my @classes = ( 10 | '::android::Manifest_permission', 11 | '::android::R_attr', 12 | '::android::app::Activity', 13 | '::android::app::AlertDialog_Builder', 14 | '::android::app::NotificationManager', 15 | '::android::app::Presentation', 16 | '::android::content::Context', 17 | '::android::graphics::Color', 18 | '::android::graphics::ImageFormat', 19 | '::android::graphics::drawable::ColorDrawable', 20 | '::android::hardware::display::DisplayManager', 21 | '::android::hardware::Camera', 22 | '::android::hardware::input::InputManager', 23 | '::android::hardware::GeomagneticField', 24 | '::android::location::LocationManager', 25 | '::android::media::AudioManager', 26 | '::android::media::MediaCodec', 27 | '::android::media::MediaCodec::BufferInfo', 28 | '::android::media::MediaExtractor', 29 | '::android::media::MediaFormat', 30 | '::android::media::MediaRouter', 31 | '::android::net::ConnectivityManager', 32 | '::android::os::Build', 33 | '::android::os::Build_VERSION', 34 | '::android::os::HandlerThread', 35 | '::android::os::Environment', 36 | '::android::os::PowerManager', 37 | '::android::os::Vibrator', 38 | '::android::provider::Settings_Secure', 39 | '::android::provider::Settings_System', 40 | '::android::telephony::TelephonyManager', 41 | '::android::telephony::SubscriptionManager', 42 | '::android::telephony::SubscriptionInfo', 43 | '::android::view::Choreographer', 44 | '::android::view::Display', 45 | '::android::view::Gravity', 46 | '::android::view::SurfaceView', 47 | '::android::view::WindowManager', 48 | '::android::webkit::MimeTypeMap', 49 | '::android::widget::CheckBox', 50 | '::android::widget::CompoundButton_OnCheckedChangeListener', 51 | '::android::widget::ProgressBar', 52 | '::java::lang::Character', 53 | '::java::lang::System', 54 | '::java::lang::SecurityException', 55 | '::java::lang::NoSuchMethodError', 56 | '::java::lang::ClassCastException', 57 | '::java::lang::UnsatisfiedLinkError', 58 | '::java::io::FileNotFoundException', 59 | '::java::net::HttpURLConnection', 60 | '::java::nio::channels::Channels', 61 | '::java::util::HashSet', 62 | '::java::util::Map_Entry', 63 | '::java::util::NoSuchElementException', 64 | '::java::util::Scanner', 65 | 66 | '::com::google::android::gms::ads::identifier::AdvertisingIdClient', 67 | '::com::google::android::gms::common::GooglePlayServicesAvailabilityException', 68 | '::com::google::android::gms::common::GooglePlayServicesNotAvailableException', 69 | ); 70 | 71 | sub BuildAndroid 72 | { 73 | my $class_names = join(' ', @classes); 74 | my $threads = 8; 75 | 76 | PrepareAndroidSDK::GetAndroidSDK("$api", "21", "r10e", "24"); 77 | 78 | system("make clean") && die("Clean failed"); 79 | system("make api-source PLATFORM=android APINAME=\"$api\" APICLASSES=\"$class_names\"") && die("Failed to make API source"); 80 | system("make api-module PLATFORM=android APINAME=\"$api\" APICLASSES=\"$class_names\"") && die("Failed to make API module"); 81 | system("make compile-static-apilib -j$threads PLATFORM=android ABI=armeabi-v7a APINAME=\"$api\" APICLASSES=\"$class_names\"") && die("Failed to make android armv7 library"); 82 | system("make compile-static-apilib -j$threads PLATFORM=android ABI=x86 APINAME=\"$api\" APICLASSES=\"$class_names\"") && die("Failed to make android x86 library"); 83 | system("make compile-static-apilib -j$threads PLATFORM=android ABI=armeabi APINAME=\"$api\" APICLASSES=\"$class_names\"") && die("Failed to make android armv5 library"); 84 | system("make compile-static-apilib -j$threads PLATFORM=android ABI=mips APINAME=\"$api\" APICLASSES=\"$class_names\"") && die("Failed to make android mips library"); 85 | } 86 | 87 | sub ZipIt 88 | { 89 | system("mkdir -p build/temp/include") && die("Failed to create temp directory."); 90 | 91 | # write build info 92 | my $git_info = qx(git symbolic-ref -q HEAD && git rev-parse HEAD); 93 | open(BUILD_INFO_FILE, '>', "build/temp/build.txt") or die("Unable to write build information to build/temp/build.txt"); 94 | print BUILD_INFO_FILE "$git_info"; 95 | close(BUILD_INFO_FILE); 96 | 97 | # create zip 98 | system("cp build/$api/source/*.h build/temp/include") && die("Failed to copy headers."); 99 | system("cd build && jar cf temp/jnibridge.jar bitter") && die("Failed to create java class archive."); 100 | system("cd build/$api && zip ../builds.zip -r android/*/*.a") && die("Failed to package libraries into zip file."); 101 | system("cd build/temp && zip ../builds.zip -r build.txt jnibridge.jar include") && die("Failed to package zip file."); 102 | system("rm -r build/temp") && die("Unable to remove temp directory."); 103 | } 104 | 105 | BuildAndroid(); 106 | ZipIt(); 107 | -------------------------------------------------------------------------------- /templates/android.os.Bundle.cpp: -------------------------------------------------------------------------------- 1 | void Bundle::__Initialize() { } 2 | 3 | // -------------------------------------------------------- 4 | // API-21 moved these functions to BaseBundle but that 5 | // class is not available in earlier versions of android 6 | // -------------------------------------------------------- 7 | // Copied from android::os::BaseBundle 8 | // -------------------------------------------------------- 9 | ::jvoid Bundle::Remove(const ::java::lang::String& arg0) const 10 | { 11 | static jmethodID methodID = jni::GetMethodID(__CLASS, "remove", "(Ljava/lang/String;)V"); 12 | return ::jvoid(jni::Op::CallMethod(m_Object, methodID, (jobject)arg0)); 13 | } 14 | ::java::lang::Object Bundle::Get(const ::java::lang::String& arg0) const 15 | { 16 | static jmethodID methodID = jni::GetMethodID(__CLASS, "get", "(Ljava/lang/String;)Ljava/lang/Object;"); 17 | return ::java::lang::Object(jni::Op::CallMethod(m_Object, methodID, (jobject)arg0)); 18 | } 19 | ::jboolean Bundle::GetBoolean(const ::java::lang::String& arg0) const 20 | { 21 | static jmethodID methodID = jni::GetMethodID(__CLASS, "getBoolean", "(Ljava/lang/String;)Z"); 22 | return ::jboolean(jni::Op::CallMethod(m_Object, methodID, (jobject)arg0)); 23 | } 24 | ::jboolean Bundle::GetBoolean(const ::java::lang::String& arg0, const ::jboolean& arg1) const 25 | { 26 | static jmethodID methodID = jni::GetMethodID(__CLASS, "getBoolean", "(Ljava/lang/String;Z)Z"); 27 | return ::jboolean(jni::Op::CallMethod(m_Object, methodID, (jobject)arg0, arg1)); 28 | } 29 | ::jvoid Bundle::PutBoolean(const ::java::lang::String& arg0, const ::jboolean& arg1) const 30 | { 31 | static jmethodID methodID = jni::GetMethodID(__CLASS, "putBoolean", "(Ljava/lang/String;Z)V"); 32 | return ::jvoid(jni::Op::CallMethod(m_Object, methodID, (jobject)arg0, arg1)); 33 | } 34 | ::jint Bundle::GetInt(const ::java::lang::String& arg0) const 35 | { 36 | static jmethodID methodID = jni::GetMethodID(__CLASS, "getInt", "(Ljava/lang/String;)I"); 37 | return ::jint(jni::Op::CallMethod(m_Object, methodID, (jobject)arg0)); 38 | } 39 | ::jint Bundle::GetInt(const ::java::lang::String& arg0, const ::jint& arg1) const 40 | { 41 | static jmethodID methodID = jni::GetMethodID(__CLASS, "getInt", "(Ljava/lang/String;I)I"); 42 | return ::jint(jni::Op::CallMethod(m_Object, methodID, (jobject)arg0, arg1)); 43 | } 44 | ::jvoid Bundle::PutInt(const ::java::lang::String& arg0, const ::jint& arg1) const 45 | { 46 | static jmethodID methodID = jni::GetMethodID(__CLASS, "putInt", "(Ljava/lang/String;I)V"); 47 | return ::jvoid(jni::Op::CallMethod(m_Object, methodID, (jobject)arg0, arg1)); 48 | } 49 | ::jlong Bundle::GetLong(const ::java::lang::String& arg0, const ::jlong& arg1) const 50 | { 51 | static jmethodID methodID = jni::GetMethodID(__CLASS, "getLong", "(Ljava/lang/String;J)J"); 52 | return ::jlong(jni::Op::CallMethod(m_Object, methodID, (jobject)arg0, arg1)); 53 | } 54 | ::jlong Bundle::GetLong(const ::java::lang::String& arg0) const 55 | { 56 | static jmethodID methodID = jni::GetMethodID(__CLASS, "getLong", "(Ljava/lang/String;)J"); 57 | return ::jlong(jni::Op::CallMethod(m_Object, methodID, (jobject)arg0)); 58 | } 59 | ::jvoid Bundle::PutLong(const ::java::lang::String& arg0, const ::jlong& arg1) const 60 | { 61 | static jmethodID methodID = jni::GetMethodID(__CLASS, "putLong", "(Ljava/lang/String;J)V"); 62 | return ::jvoid(jni::Op::CallMethod(m_Object, methodID, (jobject)arg0, arg1)); 63 | } 64 | ::jdouble Bundle::GetDouble(const ::java::lang::String& arg0) const 65 | { 66 | static jmethodID methodID = jni::GetMethodID(__CLASS, "getDouble", "(Ljava/lang/String;)D"); 67 | return ::jdouble(jni::Op::CallMethod(m_Object, methodID, (jobject)arg0)); 68 | } 69 | ::jdouble Bundle::GetDouble(const ::java::lang::String& arg0, const ::jdouble& arg1) const 70 | { 71 | static jmethodID methodID = jni::GetMethodID(__CLASS, "getDouble", "(Ljava/lang/String;D)D"); 72 | return ::jdouble(jni::Op::CallMethod(m_Object, methodID, (jobject)arg0, arg1)); 73 | } 74 | ::jvoid Bundle::PutDouble(const ::java::lang::String& arg0, const ::jdouble& arg1) const 75 | { 76 | static jmethodID methodID = jni::GetMethodID(__CLASS, "putDouble", "(Ljava/lang/String;D)V"); 77 | return ::jvoid(jni::Op::CallMethod(m_Object, methodID, (jobject)arg0, arg1)); 78 | } 79 | ::jboolean Bundle::IsEmpty() const 80 | { 81 | static jmethodID methodID = jni::GetMethodID(__CLASS, "isEmpty", "()Z"); 82 | return ::jboolean(jni::Op::CallMethod(m_Object, methodID)); 83 | } 84 | ::jint Bundle::Size() const 85 | { 86 | static jmethodID methodID = jni::GetMethodID(__CLASS, "size", "()I"); 87 | return ::jint(jni::Op::CallMethod(m_Object, methodID)); 88 | } 89 | ::jvoid Bundle::PutAll(const ::android::os::PersistableBundle& arg0) const 90 | { 91 | static jmethodID methodID = jni::GetMethodID(__CLASS, "putAll", "(Landroid/os/PersistableBundle;)V"); 92 | return ::jvoid(jni::Op::CallMethod(m_Object, methodID, (jobject)arg0)); 93 | } 94 | ::java::util::Set Bundle::KeySet() const 95 | { 96 | static jmethodID methodID = jni::GetMethodID(__CLASS, "keySet", "()Ljava/util/Set;"); 97 | return ::java::util::Set(jni::Op::CallMethod(m_Object, methodID)); 98 | } 99 | ::jboolean Bundle::ContainsKey(const ::java::lang::String& arg0) const 100 | { 101 | static jmethodID methodID = jni::GetMethodID(__CLASS, "containsKey", "(Ljava/lang/String;)Z"); 102 | return ::jboolean(jni::Op::CallMethod(m_Object, methodID, (jobject)arg0)); 103 | } 104 | ::java::lang::String Bundle::GetString(const ::java::lang::String& arg0, const ::java::lang::String& arg1) const 105 | { 106 | static jmethodID methodID = jni::GetMethodID(__CLASS, "getString", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"); 107 | return ::java::lang::String(jni::Op::CallMethod(m_Object, methodID, (jobject)arg0, (jobject)arg1)); 108 | } 109 | ::java::lang::String Bundle::GetString(const ::java::lang::String& arg0) const 110 | { 111 | static jmethodID methodID = jni::GetMethodID(__CLASS, "getString", "(Ljava/lang/String;)Ljava/lang/String;"); 112 | return ::java::lang::String(jni::Op::CallMethod(m_Object, methodID, (jobject)arg0)); 113 | } 114 | jni::Array< ::jlong > Bundle::GetLongArray(const ::java::lang::String& arg0) const 115 | { 116 | static jmethodID methodID = jni::GetMethodID(__CLASS, "getLongArray", "(Ljava/lang/String;)[J"); 117 | return jni::Array< ::jlong >(jni::Op::CallMethod(m_Object, methodID, (jobject)arg0)); 118 | } 119 | ::jvoid Bundle::PutString(const ::java::lang::String& arg0, const ::java::lang::String& arg1) const 120 | { 121 | static jmethodID methodID = jni::GetMethodID(__CLASS, "putString", "(Ljava/lang/String;Ljava/lang/String;)V"); 122 | return ::jvoid(jni::Op::CallMethod(m_Object, methodID, (jobject)arg0, (jobject)arg1)); 123 | } 124 | ::jvoid Bundle::PutLongArray(const ::java::lang::String& arg0, const jni::Array< ::jlong >& arg1) const 125 | { 126 | static jmethodID methodID = jni::GetMethodID(__CLASS, "putLongArray", "(Ljava/lang/String;[J)V"); 127 | return ::jvoid(jni::Op::CallMethod(m_Object, methodID, (jobject)arg0, (jobject)arg1)); 128 | } 129 | ::jvoid Bundle::PutStringArray(const ::java::lang::String& arg0, const jni::Array< ::java::lang::String >& arg1) const 130 | { 131 | static jmethodID methodID = jni::GetMethodID(__CLASS, "putStringArray", "(Ljava/lang/String;[Ljava/lang/String;)V"); 132 | return ::jvoid(jni::Op::CallMethod(m_Object, methodID, (jobject)arg0, (jobject)arg1)); 133 | } 134 | jni::Array< ::jint > Bundle::GetIntArray(const ::java::lang::String& arg0) const 135 | { 136 | static jmethodID methodID = jni::GetMethodID(__CLASS, "getIntArray", "(Ljava/lang/String;)[I"); 137 | return jni::Array< ::jint >(jni::Op::CallMethod(m_Object, methodID, (jobject)arg0)); 138 | } 139 | ::jvoid Bundle::PutIntArray(const ::java::lang::String& arg0, const jni::Array< ::jint >& arg1) const 140 | { 141 | static jmethodID methodID = jni::GetMethodID(__CLASS, "putIntArray", "(Ljava/lang/String;[I)V"); 142 | return ::jvoid(jni::Op::CallMethod(m_Object, methodID, (jobject)arg0, (jobject)arg1)); 143 | } 144 | ::jvoid Bundle::PutBooleanArray(const ::java::lang::String& arg0, const jni::Array< ::jboolean >& arg1) const 145 | { 146 | static jmethodID methodID = jni::GetMethodID(__CLASS, "putBooleanArray", "(Ljava/lang/String;[Z)V"); 147 | return ::jvoid(jni::Op::CallMethod(m_Object, methodID, (jobject)arg0, (jobject)arg1)); 148 | } 149 | ::jvoid Bundle::PutDoubleArray(const ::java::lang::String& arg0, const jni::Array< ::jdouble >& arg1) const 150 | { 151 | static jmethodID methodID = jni::GetMethodID(__CLASS, "putDoubleArray", "(Ljava/lang/String;[D)V"); 152 | return ::jvoid(jni::Op::CallMethod(m_Object, methodID, (jobject)arg0, (jobject)arg1)); 153 | } 154 | jni::Array< ::jboolean > Bundle::GetBooleanArray(const ::java::lang::String& arg0) const 155 | { 156 | static jmethodID methodID = jni::GetMethodID(__CLASS, "getBooleanArray", "(Ljava/lang/String;)[Z"); 157 | return jni::Array< ::jboolean >(jni::Op::CallMethod(m_Object, methodID, (jobject)arg0)); 158 | } 159 | jni::Array< ::jdouble > Bundle::GetDoubleArray(const ::java::lang::String& arg0) const 160 | { 161 | static jmethodID methodID = jni::GetMethodID(__CLASS, "getDoubleArray", "(Ljava/lang/String;)[D"); 162 | return jni::Array< ::jdouble >(jni::Op::CallMethod(m_Object, methodID, (jobject)arg0)); 163 | } 164 | jni::Array< ::java::lang::String > Bundle::GetStringArray(const ::java::lang::String& arg0) const 165 | { 166 | static jmethodID methodID = jni::GetMethodID(__CLASS, "getStringArray", "(Ljava/lang/String;)[Ljava/lang/String;"); 167 | return jni::Array< ::java::lang::String >(jni::Op::CallMethod(m_Object, methodID, (jobject)arg0)); 168 | } 169 | -------------------------------------------------------------------------------- /templates/android.os.Bundle.h: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------------- 2 | // API-21 moved these functions to BaseBundle but that 3 | // class is not available in earlier versions of android 4 | // -------------------------------------------------------- 5 | // Copied from android::os::BaseBundle 6 | // -------------------------------------------------------- 7 | ::jvoid Remove(const ::java::lang::String& arg0) const; 8 | ::java::lang::Object Get(const ::java::lang::String& arg0) const; 9 | ::jboolean GetBoolean(const ::java::lang::String& arg0) const; 10 | ::jboolean GetBoolean(const ::java::lang::String& arg0, const ::jboolean& arg1) const; 11 | ::jvoid PutBoolean(const ::java::lang::String& arg0, const ::jboolean& arg1) const; 12 | ::jint GetInt(const ::java::lang::String& arg0) const; 13 | ::jint GetInt(const ::java::lang::String& arg0, const ::jint& arg1) const; 14 | ::jvoid PutInt(const ::java::lang::String& arg0, const ::jint& arg1) const; 15 | ::jlong GetLong(const ::java::lang::String& arg0, const ::jlong& arg1) const; 16 | ::jlong GetLong(const ::java::lang::String& arg0) const; 17 | ::jvoid PutLong(const ::java::lang::String& arg0, const ::jlong& arg1) const; 18 | ::jdouble GetDouble(const ::java::lang::String& arg0) const; 19 | ::jdouble GetDouble(const ::java::lang::String& arg0, const ::jdouble& arg1) const; 20 | ::jvoid PutDouble(const ::java::lang::String& arg0, const ::jdouble& arg1) const; 21 | ::jboolean IsEmpty() const; 22 | ::jint Size() const; 23 | ::jvoid PutAll(const ::android::os::PersistableBundle& arg0) const; 24 | ::java::util::Set KeySet() const; 25 | ::jboolean ContainsKey(const ::java::lang::String& arg0) const; 26 | ::java::lang::String GetString(const ::java::lang::String& arg0, const ::java::lang::String& arg1) const; 27 | ::java::lang::String GetString(const ::java::lang::String& arg0) const; 28 | jni::Array< ::jlong > GetLongArray(const ::java::lang::String& arg0) const; 29 | ::jvoid PutString(const ::java::lang::String& arg0, const ::java::lang::String& arg1) const; 30 | ::jvoid PutLongArray(const ::java::lang::String& arg0, const jni::Array< ::jlong >& arg1) const; 31 | ::jvoid PutStringArray(const ::java::lang::String& arg0, const jni::Array< ::java::lang::String >& arg1) const; 32 | jni::Array< ::jint > GetIntArray(const ::java::lang::String& arg0) const; 33 | ::jvoid PutIntArray(const ::java::lang::String& arg0, const jni::Array< ::jint >& arg1) const; 34 | ::jvoid PutBooleanArray(const ::java::lang::String& arg0, const jni::Array< ::jboolean >& arg1) const; 35 | ::jvoid PutDoubleArray(const ::java::lang::String& arg0, const jni::Array< ::jdouble >& arg1) const; 36 | jni::Array< ::jboolean > GetBooleanArray(const ::java::lang::String& arg0) const; 37 | jni::Array< ::jdouble > GetDoubleArray(const ::java::lang::String& arg0) const; 38 | jni::Array< ::java::lang::String > GetStringArray(const ::java::lang::String& arg0) const; -------------------------------------------------------------------------------- /templates/android.view.Display.cpp: -------------------------------------------------------------------------------- 1 | void Display::__Initialize() { } 2 | 3 | jint Display::__GetRawWidth() const 4 | { 5 | static jmethodID methodID = jni::GetMethodID(__CLASS, "getRawWidth", "()I"); 6 | return methodID != 0 ? jni::Op::CallMethod(m_Object, methodID) : 0; 7 | } 8 | 9 | jint Display::__GetRawHeight() const 10 | { 11 | static jmethodID methodID = jni::GetMethodID(__CLASS, "getRawHeight", "()I"); 12 | return methodID != 0 ? jni::Op::CallMethod(m_Object, methodID) : 0; 13 | } 14 | -------------------------------------------------------------------------------- /templates/android.view.Display.h: -------------------------------------------------------------------------------- 1 | jint __GetRawWidth() const; 2 | jint __GetRawHeight() const; 3 | -------------------------------------------------------------------------------- /templates/java.lang.CharSequence.cpp: -------------------------------------------------------------------------------- 1 | CharSequence::CharSequence(const char* str) : ::java::lang::Object(java::lang::String(str)) { __Initialize(); } 2 | 3 | void CharSequence::__Initialize() { } 4 | -------------------------------------------------------------------------------- /templates/java.lang.CharSequence.h: -------------------------------------------------------------------------------- 1 | CharSequence(const char* str); 2 | -------------------------------------------------------------------------------- /templates/java.lang.Number.cpp: -------------------------------------------------------------------------------- 1 | void Number::__Initialize() { } 2 | 3 | Number::operator ::jbyte() const { return ByteValue(); } 4 | Number::operator ::jshort() const { return ShortValue(); } 5 | Number::operator ::jint() const { return IntValue(); } 6 | Number::operator ::jlong() const { return LongValue(); } 7 | Number::operator ::jfloat() const { return FloatValue(); } 8 | Number::operator ::jdouble() const { return DoubleValue(); } 9 | 10 | -------------------------------------------------------------------------------- /templates/java.lang.Number.h: -------------------------------------------------------------------------------- 1 | 2 | operator ::jbyte() const; 3 | operator ::jshort() const; 4 | operator ::jint() const; 5 | operator ::jlong() const; 6 | operator ::jfloat() const; 7 | operator ::jdouble() const; 8 | -------------------------------------------------------------------------------- /templates/java.lang.String.cpp: -------------------------------------------------------------------------------- 1 | String::String(const char* str) : ::java::lang::Object(str ? jni::NewStringUTF(str) : NULL) { __Initialize(); } 2 | String::~String() 3 | { 4 | if (m_Str) 5 | jni::ReleaseStringUTFChars(*this, m_Str); 6 | m_Str = 0; 7 | } 8 | 9 | void String::__Initialize() 10 | { 11 | m_Str = 0; 12 | } 13 | 14 | String::operator jstring () const 15 | { 16 | return (jstring)(jobject)m_Object; 17 | } 18 | 19 | String& String::operator = (const String& other) 20 | { 21 | if (m_Object == other.m_Object) 22 | return *this; 23 | 24 | if (m_Str) 25 | jni::ReleaseStringUTFChars(*this, m_Str); 26 | m_Str = 0; 27 | 28 | m_Object = other.m_Object; 29 | return *this; 30 | } 31 | 32 | const char* String::c_str() 33 | { 34 | if (m_Object && !m_Str) 35 | m_Str = jni::GetStringUTFChars(*this); 36 | return m_Str; 37 | } 38 | 39 | bool String::EmptyOrNull() 40 | { 41 | if (!m_Object) 42 | return true; 43 | const char* str = c_str(); 44 | return !str || !str[0]; 45 | } 46 | -------------------------------------------------------------------------------- /templates/java.lang.String.h: -------------------------------------------------------------------------------- 1 | String(const char* str); 2 | ~String(); 3 | 4 | String& operator = (const String& other); 5 | bool EmptyOrNull(); 6 | 7 | const char* c_str(); 8 | 9 | operator jstring () const; 10 | 11 | private: 12 | const char* m_Str; -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | JAVA_HOME ?= $(shell /usr/libexec/java_home -v 1.7) 2 | 3 | OBJDIR := build 4 | 5 | CPPFLAGS := ${CPPFLAGS} -Iinclude -I${JAVA_HOME}/include -I${JAVA_HOME}/include/darwin -std=c++11 -Wall -Werror 6 | CXXFLAGS := ${CXXFLAGS} -fno-rtti -fno-exceptions -g 7 | LDFLAGS := ${LDFLAGS} -L${OBJDIR} -Ldarwin -ljdk-7-jni -ljvm -L${JAVA_HOME}/jre/lib/server -Wl,-rpath,${JAVA_HOME}/jre/lib/server 8 | SRCS := $(wildcard *.cpp) 9 | OBJS := $(addprefix $(OBJDIR)/,$(SRCS:%.cpp=%.o)) 10 | 11 | LD = g++ ${LDFLAGS} 12 | 13 | all: ${OBJDIR}/test 14 | ${OBJDIR}/test 15 | 16 | ${OBJDIR}/test: ${OBJS} 17 | ${LD} ${LDFLAGS} -o $@ $^ 18 | 19 | ${OBJDIR}/%.o: %.cpp 20 | $(COMPILE.cpp) $(OUTPUT_OPTION) $< 21 | 22 | ${OBJS}: | ${OBJDIR} 23 | 24 | ${OBJDIR}: 25 | mkdir -p ${OBJDIR} 26 | 27 | clean: 28 | rm -fr ${OBJDIR} 29 | -------------------------------------------------------------------------------- /test/Test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "API.h" 6 | #include "Proxy.h" 7 | 8 | using namespace java::lang; 9 | using namespace java::io; 10 | using namespace java::util; 11 | 12 | int main(int,char**) 13 | { 14 | JavaVM* vm; 15 | void* envPtr; 16 | JavaVMInitArgs vm_args; 17 | memset(&vm_args, 0, sizeof(vm_args)); 18 | 19 | char classPath[] = {"-Djava.class.path=../build"}; 20 | 21 | JavaVMOption options[1]; 22 | options[0].optionString = classPath; 23 | 24 | vm_args.options = options; 25 | vm_args.nOptions = 1; 26 | vm_args.version = JNI_VERSION_1_6; 27 | JNI_CreateJavaVM(&vm, &envPtr, &vm_args); 28 | 29 | JNIEnv* env = (JNIEnv*)envPtr; 30 | 31 | jni::Initialize(*vm); 32 | 33 | // ------------------------------------------------------------------------ 34 | // System.out.println("Hello world"); 35 | // ------------------------------------------------------------------------ 36 | 37 | // pure JNI 38 | { 39 | jstring helloWorldString = env->NewStringUTF("RAW"); 40 | jclass javaLangSystem = env->FindClass("java/lang/System"); 41 | jclass javaIoPrintStream = env->FindClass("java/io/PrintStream"); 42 | jfieldID javaLangSystem_outFID = env->GetStaticFieldID(javaLangSystem, "out", "Ljava/io/PrintStream;"); 43 | jobject javaLangSystem_out = env->GetStaticObjectField(javaLangSystem, javaLangSystem_outFID); 44 | jmethodID javaIoPrintStream_printlnMID = env->GetMethodID(javaIoPrintStream, "println", "(Ljava/lang/String;)V"); 45 | env->CallVoidMethod(javaLangSystem_out, javaIoPrintStream_printlnMID, helloWorldString); 46 | } 47 | 48 | // JNI.h 49 | jni::Errno error; 50 | { 51 | jni::Initialize(*vm); 52 | if ((env = jni::AttachCurrentThread())) 53 | { 54 | jni::LocalFrame frame; 55 | jstring helloWorldString = env->NewStringUTF("JNI"); 56 | jclass javaLangSystem = env->FindClass("java/lang/System"); 57 | jclass javaIoPrintStream = env->FindClass("java/io/PrintStream"); 58 | jfieldID javaLangSystem_outFID = env->GetStaticFieldID(javaLangSystem, "out", "Ljava/io/PrintStream;"); 59 | jobject javaLangSystem_out = env->GetStaticObjectField(javaLangSystem, javaLangSystem_outFID); 60 | jmethodID javaIoPrintStream_printlnMID = env->GetMethodID(javaIoPrintStream, "println", "(Ljava/lang/String;)V"); 61 | env->CallVoidMethod(javaLangSystem_out, javaIoPrintStream_printlnMID, helloWorldString); 62 | if ((error = jni::CheckError())) 63 | printf("JNI %s\n", jni::GetErrorMessage()); 64 | } 65 | } 66 | 67 | // Ops.h 68 | { 69 | jni::LocalFrame frame; 70 | jstring helloWorldString = env->NewStringUTF("Ops"); 71 | jclass javaLangSystem = env->FindClass("java/lang/System"); 72 | jclass javaIoPrintStream = env->FindClass("java/io/PrintStream"); 73 | jfieldID javaLangSystem_outFID = env->GetStaticFieldID(javaLangSystem, "out", "Ljava/io/PrintStream;"); 74 | jobject javaLangSystem_out = jni::Op::GetStaticField(javaLangSystem, javaLangSystem_outFID); 75 | jmethodID javaIoPrintStream_printlnMID = env->GetMethodID(javaIoPrintStream, "println", "(Ljava/lang/String;)V"); 76 | jni::Op::CallMethod(javaLangSystem_out, javaIoPrintStream_printlnMID, helloWorldString); 77 | if ((error = jni::CheckError())) 78 | printf("Ops %d:%s\n", error, jni::GetErrorMessage()); 79 | } 80 | 81 | { 82 | System::fOut().Println("Api"); 83 | } 84 | 85 | // ------------------------------------------------------------------------ 86 | // import java.io.PrintStream; 87 | // import java.util.Properties; 88 | // import java.util.Enumerator; 89 | // 90 | // PrintStream out = System.out; 91 | // Properties properties = System.getPropertes(); 92 | // Enumerator keys = properties.keys(); 93 | // while (keys.hasMoreElements()) 94 | // out.println(properties.getProperty((String)keys.next())); 95 | // ------------------------------------------------------------------------ 96 | timeval start, stop; 97 | 98 | // Optimized version 99 | gettimeofday(&start, NULL); 100 | { 101 | jni::LocalFrame frame; 102 | PrintStream out = System::fOut(); 103 | Properties properties = System::GetProperties(); 104 | Enumeration keys = properties.Keys(); 105 | while (keys.HasMoreElements()) 106 | out.Println(properties.GetProperty(jni::Cast(keys.NextElement()))); 107 | } 108 | gettimeofday(&stop, NULL); 109 | printf("%f ms.\n", (stop.tv_sec - start.tv_sec) * 1000.0 + (stop.tv_usec - start.tv_usec) / 1000.0); 110 | 111 | // CharSequence test 112 | java::lang::CharSequence string = "hello world"; 113 | printf("%s\n", string.ToString().c_str()); 114 | 115 | // ------------------------------------------------------------- 116 | // Util functions 117 | // ------------------------------------------------------------- 118 | java::lang::Object object = java::lang::Integer(23754); 119 | if (jni::InstanceOf(object) && jni::Cast(object)) 120 | { 121 | printf("%d\n", static_cast(java::lang::Number(object))); 122 | } 123 | else 124 | { 125 | int* p = 0; *p = 3; 126 | } 127 | 128 | // ------------------------------------------------------------- 129 | // Array Test 130 | // ------------------------------------------------------------- 131 | { 132 | jni::LocalFrame frame; 133 | jni::Array test01(4, (int[]){1, 2, 3, 4}); 134 | for (int i = 0; i < test01.Length(); ++i) 135 | printf("ArrayTest01[%d],", test01[i]); 136 | printf("\n"); 137 | 138 | jni::Array test02(4, (java::lang::Integer[]){1, 2, 3, 4}); 139 | for (int i = 0; i < test02.Length(); ++i) 140 | printf("ArrayTest02[%d],", test02[i].IntValue()); 141 | printf("\n"); 142 | 143 | jni::Array test03(java::lang::Integer::__CLASS, 4, (jobject[]){java::lang::Integer(1), java::lang::Integer(2), java::lang::Integer(3), java::lang::Integer(4)}); 144 | for (int i = 0; i < test03.Length(); ++i) 145 | printf("ArrayTest03[%d],", java::lang::Integer(test03[i]).IntValue()); 146 | printf("\n"); 147 | 148 | jni::Array test04(java::lang::Integer::__CLASS, 4, (java::lang::Integer[]){1, 2, 3, 4}); 149 | for (int i = 0; i < test04.Length(); ++i) 150 | printf("ArrayTest04[%d],", java::lang::Integer(test04[i]).IntValue()); 151 | printf("\n"); 152 | 153 | jni::Array test05(4, (java::lang::Integer[]){1, 2, 3, 4}); 154 | for (int i = 0; i < test05.Length(); ++i) 155 | printf("ArrayTest05[%d],", test05[i]); 156 | printf("\n"); 157 | 158 | jni::Array test10(4, 4733); 159 | for (int i = 0; i < test10.Length(); ++i) 160 | printf("ArrayTest10[%d],", test10[i].IntValue()); 161 | printf("\n"); 162 | 163 | jni::Array test11(java::lang::Integer::__CLASS, 4, java::lang::Integer(4733)); 164 | for (int i = 0; i < test11.Length(); ++i) 165 | printf("ArrayTest11[%d],", java::lang::Integer(test11[i]).IntValue()); 166 | printf("\n"); 167 | } 168 | 169 | // ------------------------------------------------------------- 170 | // Proxy test 171 | // ------------------------------------------------------------- 172 | if (!jni::ProxyInvoker::__Register()) 173 | printf("%s\n", jni::GetErrorMessage()); 174 | 175 | struct PretendRunnable : jni::Proxy 176 | { 177 | virtual void Run() {printf("%s\n", "hello world!!!!"); } 178 | }; 179 | 180 | PretendRunnable pretendRunnable; 181 | Runnable runnable = pretendRunnable; 182 | 183 | Thread thread(pretendRunnable); 184 | thread.Start(); 185 | thread.Join(); 186 | 187 | runnable.Run(); 188 | 189 | // Make sure we don't get crashes from deleting the native object. 190 | PretendRunnable* pretendRunnable2 = new PretendRunnable; 191 | Runnable runnable2 = *pretendRunnable2; 192 | runnable2.Run(); 193 | delete pretendRunnable2; 194 | runnable2.Run(); // <-- should not log anything 195 | 196 | // ------------------------------------------------------------- 197 | // Performance Proxy Test 198 | // ------------------------------------------------------------- 199 | struct PerformanceRunnable : jni::Proxy 200 | { 201 | int i; 202 | PerformanceRunnable() : i(0) {} 203 | virtual void Run() { ++i; } 204 | }; 205 | PerformanceRunnable* perfRunner = new PerformanceRunnable; 206 | Runnable perfRunnable = *perfRunner; 207 | gettimeofday(&start, NULL); 208 | for (int i = 0; i < 1024; ++i) 209 | perfRunnable.Run(); 210 | gettimeofday(&stop, NULL); 211 | printf("count: %d, time: %f ms.\n", perfRunner->i, (stop.tv_sec - start.tv_sec) * 1000.0 + (stop.tv_usec - start.tv_usec) / 1000.0); 212 | 213 | delete perfRunner; 214 | gettimeofday(&start, NULL); 215 | for (int i = 0; i < 1024; ++i) 216 | perfRunnable.Run(); 217 | gettimeofday(&stop, NULL); 218 | printf("count: %d, time: %f ms.\n", 1024, (stop.tv_sec - start.tv_sec) * 1000.0 + (stop.tv_usec - start.tv_usec) / 1000.0); 219 | 220 | // ------------------------------------------------------------- 221 | // Weak Proxy Test 222 | // ------------------------------------------------------------- 223 | struct KillMePleazeRunnable : jni::WeakProxy 224 | { 225 | virtual ~KillMePleazeRunnable() { printf("%s\n", "KillMePleazeRunnable");} 226 | virtual void Run() { } 227 | }; 228 | 229 | { 230 | jni::LocalFrame frame; 231 | new KillMePleazeRunnable; 232 | } 233 | for (int i = 0; i < 32; ++i) // Do a couple of loops to massage the GC 234 | { 235 | jni::LocalFrame frame; 236 | jni::Array array(1024*1024); 237 | System::Gc(); 238 | } 239 | 240 | // ------------------------------------------------------------- 241 | // Multiple Proxy Interface Test 242 | // ------------------------------------------------------------- 243 | { 244 | class MultipleInterfaces : public jni::WeakProxy 245 | { 246 | public: 247 | MultipleInterfaces() 248 | : m_Count(10) 249 | { 250 | 251 | } 252 | 253 | virtual ~MultipleInterfaces() 254 | { 255 | printf("destroyed[%p]\n", this); 256 | } 257 | 258 | virtual void Run() 259 | { 260 | printf("Run[%p]!\n", this); 261 | } 262 | 263 | virtual void Remove() 264 | { 265 | jni::ThrowNew(UnsupportedOperationException::__CLASS, "This iterator does not support remove."); 266 | } 267 | 268 | virtual ::jboolean HasNext() 269 | { 270 | printf("HasNext[%p]!\n", this); 271 | bool result = (--m_Count != 0); 272 | printf("m_Count[%d][%d]\n", m_Count, result); 273 | return result; 274 | } 275 | 276 | virtual ::java::lang::Object Next() 277 | { 278 | return ::java::lang::String("this is a string"); 279 | } 280 | 281 | private: 282 | unsigned m_Count; 283 | }; 284 | 285 | { 286 | jni::LocalFrame frame; 287 | MultipleInterfaces* testProxy = new MultipleInterfaces(); 288 | 289 | Runnable runnable = *testProxy; 290 | runnable.Run(); 291 | 292 | Iterator iterator = *testProxy; 293 | while (iterator.HasNext()) 294 | { 295 | String javaString = jni::Cast(iterator.Next()); 296 | printf("%s\n", javaString.c_str()); 297 | } 298 | } 299 | for (int i = 0; i < 32; ++i) // Do a couple of loops to massage the GC 300 | { 301 | jni::LocalFrame frame; 302 | jni::Array array(1024*1024); 303 | System::Gc(); 304 | } 305 | 306 | printf("%s", "end of multi interface test\n"); 307 | } 308 | 309 | // ------------------------------------------------------------- 310 | // Proxy Object Test 311 | // ------------------------------------------------------------- 312 | { 313 | jni::LocalFrame frame; 314 | struct PretendRunnable : jni::Proxy 315 | { 316 | virtual void Run() {printf("%s\n", "hello world!!!!"); } 317 | }; 318 | 319 | PretendRunnable pretendRunnable; 320 | Runnable runnable = pretendRunnable; 321 | 322 | printf("equals: %d\n", runnable.Equals(runnable)); 323 | printf("hashcode: %d\n", runnable.HashCode()); 324 | printf("toString: %s\n", runnable.ToString().c_str()); 325 | } 326 | 327 | printf("%s\n", "EOP"); 328 | 329 | // print resolution of clock() 330 | jni::DetachCurrentThread(); 331 | 332 | vm->DestroyJavaVM(); 333 | return 0; 334 | } 335 | --------------------------------------------------------------------------------