JniCodeEmitter.java revision 6cbca50b6a438151a6c5f4dbdccc7057c19b73fa
1import java.io.PrintStream; 2import java.util.ArrayList; 3import java.util.HashSet; 4import java.util.Iterator; 5import java.util.List; 6 7/** 8 * Emits a Java interface and Java & C implementation for a C function. 9 * 10 * <p> The Java interface will have Buffer and array variants for functions that 11 * have a typed pointer argument. The array variant will convert a single "<type> *data" 12 * argument to a pair of arguments "<type>[] data, int offset". 13 */ 14public class JniCodeEmitter implements CodeEmitter { 15 16 // If true, use C++ style for calling through a JNIEnv *: 17 // env->Func(...) 18 // If false, use C style: 19 // (*env)->Func(env, ...) 20 static final boolean mUseCPlusPlus = true; 21 22 boolean mUseContextPointer = true; 23 24 String mClassPathName; 25 26 ParameterChecker mChecker; 27 PrintStream mJava10InterfaceStream; 28 PrintStream mJava10ExtInterfaceStream; 29 PrintStream mJava11InterfaceStream; 30 PrintStream mJava11ExtInterfaceStream; 31 PrintStream mJava11ExtPackInterfaceStream; 32 PrintStream mJavaImplStream; 33 PrintStream mCStream; 34 35 PrintStream mJavaInterfaceStream; 36 37 List<String> nativeRegistrations = new ArrayList<String>(); 38 39 boolean needsExit; 40 41 static String indent = " "; 42 43 HashSet<String> mFunctionsEmitted = new HashSet<String>(); 44 45 /** 46 * @param java10InterfaceStream the PrintStream to which to emit the Java interface for GL 1.0 functions 47 * @param java10ExtInterfaceStream the PrintStream to which to emit the Java interface for GL 1.0 extension functions 48 * @param java11InterfaceStream the PrintStream to which to emit the Java interface for GL 1.1 functions 49 * @param java11ExtInterfaceStream the PrintStream to which to emit the Java interface for GL 1.1 Extension functions 50 * @param java11ExtPackInterfaceStream the PrintStream to which to emit the Java interface for GL 1.1 Extension Pack functions 51 * @param javaImplStream the PrintStream to which to emit the Java implementation 52 * @param cStream the PrintStream to which to emit the C implementation 53 */ 54 public JniCodeEmitter(String classPathName, 55 ParameterChecker checker, 56 PrintStream java10InterfaceStream, 57 PrintStream java10ExtInterfaceStream, 58 PrintStream java11InterfaceStream, 59 PrintStream java11ExtInterfaceStream, 60 PrintStream java11ExtPackInterfaceStream, 61 PrintStream javaImplStream, 62 PrintStream cStream, 63 boolean useContextPointer) { 64 mClassPathName = classPathName; 65 mChecker = checker; 66 mJava10InterfaceStream = java10InterfaceStream; 67 mJava10ExtInterfaceStream = java10ExtInterfaceStream; 68 mJava11InterfaceStream = java11InterfaceStream; 69 mJava11ExtInterfaceStream = java11ExtInterfaceStream; 70 mJava11ExtPackInterfaceStream = java11ExtPackInterfaceStream; 71 mJavaImplStream = javaImplStream; 72 mCStream = cStream; 73 mUseContextPointer = useContextPointer; 74 } 75 76 public void setVersion(int version, boolean ext, boolean pack) { 77 if (version == 0) { 78 mJavaInterfaceStream = ext ? mJava10ExtInterfaceStream : 79 mJava10InterfaceStream; 80 } else if (version == 1) { 81 mJavaInterfaceStream = ext ? 82 (pack ? mJava11ExtPackInterfaceStream : 83 mJava11ExtInterfaceStream) : 84 mJava11InterfaceStream; 85 } else { 86 throw new RuntimeException("Bad version: " + version); 87 } 88 } 89 90 public void emitCode(CFunc cfunc, String original) { 91 JFunc jfunc; 92 String signature; 93 boolean duplicate; 94 95 if (cfunc.hasTypedPointerArg()) { 96 jfunc = JFunc.convert(cfunc, true); 97 98 // Don't emit duplicate functions 99 // These may appear because they are defined in multiple 100 // Java interfaces (e.g., GL11/GL11ExtensionPack) 101 signature = jfunc.toString(); 102 duplicate = false; 103 if (mFunctionsEmitted.contains(signature)) { 104 duplicate = true; 105 } else { 106 mFunctionsEmitted.add(signature); 107 } 108 109 if (!duplicate) { 110 emitNativeDeclaration(jfunc, mJavaImplStream); 111 emitJavaCode(jfunc, mJavaImplStream); 112 } 113 emitJavaInterfaceCode(jfunc, mJavaInterfaceStream); 114 if (!duplicate) { 115 emitJniCode(jfunc, mCStream); 116 } 117 } 118 119 jfunc = JFunc.convert(cfunc, false); 120 121 signature = jfunc.toString(); 122 duplicate = false; 123 if (mFunctionsEmitted.contains(signature)) { 124 duplicate = true; 125 } else { 126 mFunctionsEmitted.add(signature); 127 } 128 129 if (!duplicate) { 130 emitNativeDeclaration(jfunc, mJavaImplStream); 131 } 132 emitJavaInterfaceCode(jfunc, mJavaInterfaceStream); 133 if (!duplicate) { 134 emitJavaCode(jfunc, mJavaImplStream); 135 emitJniCode(jfunc, mCStream); 136 } 137 } 138 139 public void emitNativeDeclaration(JFunc jfunc, PrintStream out) { 140 out.println(" // C function " + jfunc.getCFunc().getOriginal()); 141 out.println(); 142 143 emitFunction(jfunc, out, true, false); 144 } 145 146 public void emitJavaInterfaceCode(JFunc jfunc, PrintStream out) { 147 emitFunction(jfunc, out, false, true); 148 } 149 150 public void emitJavaCode(JFunc jfunc, PrintStream out) { 151 emitFunction(jfunc, out, false, false); 152 } 153 154 void emitFunctionCall(JFunc jfunc, PrintStream out, String iii, boolean grabArray ) { 155 boolean isVoid = jfunc.getType().isVoid(); 156 boolean isPointerFunc = jfunc.getName().endsWith("Pointer") && 157 jfunc.getCFunc().hasPointerArg(); 158 159 if (!isVoid) { 160 out.println(iii + 161 jfunc.getType() + " _returnValue;"); 162 } 163 out.println(iii + 164 (isVoid ? "" : "_returnValue = ") + 165 jfunc.getName() + 166 (isPointerFunc ? "Bounds" : "" ) + 167 "("); 168 169 int numArgs = jfunc.getNumArgs(); 170 for (int i = 0; i < numArgs; i++) { 171 String argName = jfunc.getArgName(i); 172 JType argType = jfunc.getArgType(i); 173 174 if (grabArray && argType.isTypedBuffer()) { 175 String typeName = argType.getBaseType(); 176 typeName = typeName.substring(9, typeName.length() - 6); 177 out.println(iii + indent + "get" + typeName + "Array(" + argName + "),"); 178 out.print(iii + indent + "getOffset(" + argName + ")"); 179 } else { 180 out.print(iii + indent + argName); 181 } 182 if (i == numArgs - 1) { 183 if (isPointerFunc) { 184 out.println(","); 185 out.println(iii + indent + argName + ".remaining()"); 186 } else { 187 out.println(); 188 } 189 } else { 190 out.println(","); 191 } 192 } 193 194 out.println(iii + ");"); 195 } 196 197 void printIfcheckPostamble(PrintStream out, boolean isBuffer, 198 boolean emitExceptionCheck, String iii) { 199 printIfcheckPostamble(out, isBuffer, emitExceptionCheck, 200 "offset", "_remaining", iii); 201 } 202 203 void printIfcheckPostamble(PrintStream out, boolean isBuffer, 204 boolean emitExceptionCheck, 205 String offset, String remaining, String iii) { 206 out.println(iii + " default:"); 207 out.println(iii + " _needed = 0;"); 208 out.println(iii + " break;"); 209 out.println(iii + "}"); 210 211 out.println(iii + "if (" + remaining + " < _needed) {"); 212 if (emitExceptionCheck) { 213 out.println(iii + indent + "_exception = 1;"); 214 } 215 out.println(iii + indent + 216 (mUseCPlusPlus ? "_env" : "(*_env)") + 217 "->ThrowNew(" + 218 (mUseCPlusPlus ? "" : "_env, ") + 219 "IAEClass, " + 220 "\"" + 221 (isBuffer ? 222 "remaining()" : "length - " + offset) + 223 " < needed\");"); 224 out.println(iii + indent + "goto exit;"); 225 needsExit = true; 226 out.println(iii + "}"); 227 } 228 229 boolean isNullAllowed(CFunc cfunc) { 230 String[] checks = mChecker.getChecks(cfunc.getName()); 231 int index = 1; 232 if (checks != null) { 233 while (index < checks.length) { 234 if (checks[index].equals("return")) { 235 index += 2; 236 } else if (checks[index].startsWith("check")) { 237 index += 3; 238 } else if (checks[index].equals("ifcheck")) { 239 index += 5; 240 } else if (checks[index].equals("unsupported")) { 241 index += 1; 242 } else if (checks[index].equals("nullAllowed")) { 243 return true; 244 } else { 245 System.out.println("Error: unknown keyword \"" + 246 checks[index] + "\""); 247 System.exit(0); 248 } 249 } 250 } 251 return false; 252 } 253 254 String getErrorReturnValue(CFunc cfunc) { 255 CType returnType = cfunc.getType(); 256 boolean isVoid = returnType.isVoid(); 257 if (isVoid) { 258 return null; 259 } 260 261 String[] checks = mChecker.getChecks(cfunc.getName()); 262 263 int index = 1; 264 if (checks != null) { 265 while (index < checks.length) { 266 if (checks[index].equals("return")) { 267 return checks[index + 1]; 268 } else if (checks[index].startsWith("check")) { 269 index += 3; 270 } else if (checks[index].equals("ifcheck")) { 271 index += 5; 272 } else if (checks[index].equals("unsupported")) { 273 index += 1; 274 } else if (checks[index].equals("nullAllowed")) { 275 index += 1; 276 } else { 277 System.out.println("Error: unknown keyword \"" + 278 checks[index] + "\""); 279 System.exit(0); 280 } 281 } 282 } 283 284 return null; 285 } 286 287 boolean isUnsupportedFunc(CFunc cfunc) { 288 String[] checks = mChecker.getChecks(cfunc.getName()); 289 int index = 1; 290 if (checks != null) { 291 while (index < checks.length) { 292 if (checks[index].equals("unsupported")) { 293 return true; 294 } else if (checks[index].equals("return")) { 295 index += 2; 296 } else if (checks[index].startsWith("check")) { 297 index += 3; 298 } else if (checks[index].equals("ifcheck")) { 299 index += 5; 300 } else if (checks[index].equals("nullAllowed")) { 301 index += 1; 302 } else { 303 System.out.println("Error: unknown keyword \"" + 304 checks[index] + "\""); 305 System.exit(0); 306 } 307 } 308 } 309 return false; 310 } 311 312 void emitNativeBoundsChecks(CFunc cfunc, String cname, PrintStream out, 313 boolean isBuffer, boolean emitExceptionCheck, 314 String offset, String remaining, String iii) { 315 CType returnType = cfunc.getType(); 316 boolean isVoid = returnType.isVoid(); 317 318 String[] checks = mChecker.getChecks(cfunc.getName()); 319 String checkVar; 320 String retval = getErrorReturnValue(cfunc); 321 322 boolean lastWasIfcheck = false; 323 324 int index = 1; 325 if (checks != null) { 326 boolean remainingDeclared = false; 327 boolean nullCheckDeclared = false; 328 boolean offsetChecked = false; 329 while (index < checks.length) { 330 if (checks[index].startsWith("check")) { 331 if (lastWasIfcheck) { 332 printIfcheckPostamble(out, isBuffer, emitExceptionCheck, 333 offset, remaining, iii); 334 } 335 lastWasIfcheck = false; 336 if (cname != null && !cname.equals(checks[index + 1])) { 337 index += 3; 338 continue; 339 } 340 out.println(iii + "if (" + remaining + " < " + 341 checks[index + 2] + 342 ") {"); 343 if (emitExceptionCheck) { 344 out.println(iii + indent + "_exception = 1;"); 345 } 346 String exceptionClassName = "IAEClass"; 347 // If the "check" keyword was of the form 348 // "check_<class name>", use the class name in the 349 // exception to be thrown 350 int underscore = checks[index].indexOf('_'); 351 if (underscore >= 0) { 352 exceptionClassName = checks[index].substring(underscore + 1) + "Class"; 353 } 354 out.println(iii + indent + 355 (mUseCPlusPlus ? "_env" : "(*_env)") + 356 "->ThrowNew(" + 357 (mUseCPlusPlus ? "" : "_env, ") + 358 exceptionClassName + ", " + 359 "\"" + 360 (isBuffer ? 361 "remaining()" : "length - " + offset) + 362 " < " + checks[index + 2] + 363 "\");"); 364 365 out.println(iii + indent + "goto exit;"); 366 needsExit = true; 367 out.println(iii + "}"); 368 369 index += 3; 370 } else if (checks[index].equals("ifcheck")) { 371 String[] matches = checks[index + 4].split(","); 372 373 if (!lastWasIfcheck) { 374 out.println(iii + "int _needed;"); 375 out.println(iii + 376 "switch (" + 377 checks[index + 3] + 378 ") {"); 379 } 380 381 for (int i = 0; i < matches.length; i++) { 382 out.println("#if defined(" + matches[i] + ")"); 383 out.println(iii + 384 " case " + 385 matches[i] + 386 ":"); 387 out.println("#endif // defined(" + matches[i] + ")"); 388 } 389 out.println(iii + 390 " _needed = " + 391 checks[index + 2] + 392 ";"); 393 out.println(iii + 394 " break;"); 395 396 lastWasIfcheck = true; 397 index += 5; 398 } else if (checks[index].equals("return")) { 399 // ignore 400 index += 2; 401 } else if (checks[index].equals("unsupported")) { 402 // ignore 403 index += 1; 404 } else if (checks[index].equals("nullAllowed")) { 405 // ignore 406 index += 1; 407 } else { 408 System.out.println("Error: unknown keyword \"" + 409 checks[index] + "\""); 410 System.exit(0); 411 } 412 } 413 } 414 415 if (lastWasIfcheck) { 416 printIfcheckPostamble(out, isBuffer, emitExceptionCheck, iii); 417 } 418 } 419 420 boolean hasNonConstArg(JFunc jfunc, CFunc cfunc, 421 List<Integer> nonPrimitiveArgs) { 422 if (nonPrimitiveArgs.size() > 0) { 423 for (int i = nonPrimitiveArgs.size() - 1; i >= 0; i--) { 424 int idx = nonPrimitiveArgs.get(i).intValue(); 425 int cIndex = jfunc.getArgCIndex(idx); 426 if (jfunc.getArgType(idx).isArray()) { 427 if (!cfunc.getArgType(cIndex).isConst()) { 428 return true; 429 } 430 } else if (jfunc.getArgType(idx).isBuffer()) { 431 if (!cfunc.getArgType(cIndex).isConst()) { 432 return true; 433 } 434 } 435 } 436 } 437 438 return false; 439 } 440 441 /** 442 * Emit a function in several variants: 443 * 444 * if nativeDecl: public native <returntype> func(args); 445 * 446 * if !nativeDecl: 447 * if interfaceDecl: public <returntype> func(args); 448 * if !interfaceDecl: public <returntype> func(args) { body } 449 */ 450 void emitFunction(JFunc jfunc, 451 PrintStream out, 452 boolean nativeDecl, boolean interfaceDecl) { 453 boolean isPointerFunc = 454 jfunc.getName().endsWith("Pointer") && 455 jfunc.getCFunc().hasPointerArg(); 456 457 if (!nativeDecl && !interfaceDecl && !isPointerFunc) { 458 // If it's not a pointer function, we've already emitted it 459 // with nativeDecl == true 460 return; 461 } 462 463 if (isPointerFunc) { 464 out.println(indent + 465 (nativeDecl ? "private native " : 466 (interfaceDecl ? "" : "public ")) + 467 jfunc.getType() + " " + 468 jfunc.getName() + 469 (nativeDecl ? "Bounds" : "") + 470 "("); 471 } else { 472 out.println(indent + 473 (nativeDecl ? "public native " : 474 (interfaceDecl ? "" : "public ")) + 475 jfunc.getType() + " " + 476 jfunc.getName() + 477 "("); 478 } 479 480 int numArgs = jfunc.getNumArgs(); 481 for (int i = 0; i < numArgs; i++) { 482 String argName = jfunc.getArgName(i); 483 JType argType = jfunc.getArgType(i); 484 485 out.print(indent + indent + argType + " " + argName); 486 if (i == numArgs - 1) { 487 if (isPointerFunc && nativeDecl) { 488 out.println(","); 489 out.println(indent + indent + "int remaining"); 490 } else { 491 out.println(); 492 } 493 } else { 494 out.println(","); 495 } 496 } 497 498 if (nativeDecl || interfaceDecl) { 499 out.println(indent + ");"); 500 } else { 501 out.println(indent + ") {"); 502 503 String iii = indent + indent; 504 505 String fname = jfunc.getName(); 506 if (isPointerFunc) { 507 // TODO - deal with VBO variants 508 if (fname.equals("glColorPointer")) { 509 out.println(iii + "if ((size == 4) &&"); 510 out.println(iii + " ((type == GL_FLOAT) ||"); 511 out.println(iii + " (type == GL_UNSIGNED_BYTE) ||"); 512 out.println(iii + " (type == GL_FIXED)) &&"); 513 out.println(iii + " (stride >= 0)) {"); 514 out.println(iii + indent + "_colorPointer = pointer;"); 515 out.println(iii + "}"); 516 } else if (fname.equals("glNormalPointer")) { 517 out.println(iii + "if (((type == GL_FLOAT) ||"); 518 out.println(iii + " (type == GL_BYTE) ||"); 519 out.println(iii + " (type == GL_SHORT) ||"); 520 out.println(iii + " (type == GL_FIXED)) &&"); 521 out.println(iii + " (stride >= 0)) {"); 522 out.println(iii + indent + "_normalPointer = pointer;"); 523 out.println(iii + "}"); 524 } else if (fname.equals("glTexCoordPointer")) { 525 out.println(iii + "if (((size == 2) ||"); 526 out.println(iii + " (size == 3) ||"); 527 out.println(iii + " (size == 4)) &&"); 528 out.println(iii + " ((type == GL_FLOAT) ||"); 529 out.println(iii + " (type == GL_BYTE) ||"); 530 out.println(iii + " (type == GL_SHORT) ||"); 531 out.println(iii + " (type == GL_FIXED)) &&"); 532 out.println(iii + " (stride >= 0)) {"); 533 out.println(iii + indent + "_texCoordPointer = pointer;"); 534 out.println(iii + "}"); 535 } else if (fname.equals("glVertexPointer")) { 536 out.println(iii + "if (((size == 2) ||"); 537 out.println(iii + " (size == 3) ||"); 538 out.println(iii + " (size == 4)) &&"); 539 out.println(iii + " ((type == GL_FLOAT) ||"); 540 out.println(iii + " (type == GL_BYTE) ||"); 541 out.println(iii + " (type == GL_SHORT) ||"); 542 out.println(iii + " (type == GL_FIXED)) &&"); 543 out.println(iii + " (stride >= 0)) {"); 544 out.println(iii + indent + "_vertexPointer = pointer;"); 545 out.println(iii + "}"); 546 } 547 } 548 549 // emitBoundsChecks(jfunc, out, iii); 550 emitFunctionCall(jfunc, out, iii, false); 551 552 boolean isVoid = jfunc.getType().isVoid(); 553 554 if (!isVoid) { 555 out.println(indent + indent + "return _returnValue;"); 556 } 557 out.println(indent + "}"); 558 } 559 out.println(); 560 } 561 562 public static String getJniName(JType jType) { 563 String jniName = ""; 564 if (jType.isClass()) { 565 return "L" + jType.getBaseType() + ";"; 566 } else if (jType.isArray()) { 567 jniName = "["; 568 } 569 570 String baseType = jType.getBaseType(); 571 if (baseType.equals("int")) { 572 jniName += "I"; 573 } else if (baseType.equals("float")) { 574 jniName += "F"; 575 } else if (baseType.equals("boolean")) { 576 jniName += "Z"; 577 } else if (baseType.equals("short")) { 578 jniName += "S"; 579 } else if (baseType.equals("long")) { 580 jniName += "L"; 581 } else if (baseType.equals("byte")) { 582 jniName += "B"; 583 } 584 return jniName; 585 } 586 587 String getJniType(JType jType) { 588 if (jType.isVoid()) { 589 return "void"; 590 } 591 592 String baseType = jType.getBaseType(); 593 if (jType.isPrimitive()) { 594 if (baseType.equals("String")) { 595 return "jstring"; 596 } else { 597 return "j" + baseType; 598 } 599 } else if (jType.isArray()) { 600 return "j" + baseType + "Array"; 601 } else { 602 return "jobject"; 603 } 604 } 605 606 String getJniMangledName(String name) { 607 name = name.replaceAll("_", "_1"); 608 name = name.replaceAll(";", "_2"); 609 name = name.replaceAll("\\[", "_3"); 610 return name; 611 } 612 613 public void emitJniCode(JFunc jfunc, PrintStream out) { 614 CFunc cfunc = jfunc.getCFunc(); 615 616 // Emit comment identifying original C function 617 // 618 // Example: 619 // 620 // /* void glClipPlanef ( GLenum plane, const GLfloat *equation ) */ 621 // 622 out.println("/* " + cfunc.getOriginal() + " */"); 623 624 // Emit JNI signature (name) 625 // 626 // Example: 627 // 628 // void 629 // android_glClipPlanef__I_3FI 630 // 631 632 String outName = "android_" + jfunc.getName(); 633 boolean isPointerFunc = outName.endsWith("Pointer") && 634 jfunc.getCFunc().hasPointerArg(); 635 boolean isVBOPointerFunc = (outName.endsWith("Pointer") || 636 outName.endsWith("DrawElements")) && 637 !jfunc.getCFunc().hasPointerArg(); 638 if (isPointerFunc) { 639 outName += "Bounds"; 640 } 641 642 out.print("static "); 643 out.println(getJniType(jfunc.getType())); 644 out.print(outName); 645 646 String rsignature = getJniName(jfunc.getType()); 647 648 String signature = ""; 649 int numArgs = jfunc.getNumArgs(); 650 for (int i = 0; i < numArgs; i++) { 651 JType argType = jfunc.getArgType(i); 652 signature += getJniName(argType); 653 } 654 if (isPointerFunc) { 655 signature += "I"; 656 } 657 658 // Append signature to function name 659 String sig = getJniMangledName(signature).replace('.', '_'); 660 out.print("__" + sig); 661 outName += "__" + sig; 662 663 signature = signature.replace('.', '/'); 664 rsignature = rsignature.replace('.', '/'); 665 666 out.println(); 667 if (rsignature.length() == 0) { 668 rsignature = "V"; 669 } 670 671 String s = "{\"" + 672 jfunc.getName() + 673 (isPointerFunc ? "Bounds" : "") + 674 "\", \"(" + signature +")" + 675 rsignature + 676 "\", (void *) " + 677 outName + 678 " },"; 679 nativeRegistrations.add(s); 680 681 List<Integer> nonPrimitiveArgs = new ArrayList<Integer>(); 682 int numBufferArgs = 0; 683 List<String> bufferArgNames = new ArrayList<String>(); 684 685 // Emit JNI signature (arguments) 686 // 687 // Example: 688 // 689 // (JNIEnv *_env, jobject this, jint plane, jfloatArray equation_ref, jint offset) { 690 // 691 out.print(" (JNIEnv *_env, jobject _this"); 692 for (int i = 0; i < numArgs; i++) { 693 out.print(", "); 694 JType argType = jfunc.getArgType(i); 695 String suffix; 696 if (!argType.isPrimitive()) { 697 if (argType.isArray()) { 698 suffix = "_ref"; 699 } else { 700 suffix = "_buf"; 701 } 702 nonPrimitiveArgs.add(new Integer(i)); 703 if (jfunc.getArgType(i).isBuffer()) { 704 int cIndex = jfunc.getArgCIndex(i); 705 String cname = cfunc.getArgName(cIndex); 706 bufferArgNames.add(cname); 707 numBufferArgs++; 708 } 709 } else { 710 suffix = ""; 711 } 712 713 out.print(getJniType(argType) + " " + jfunc.getArgName(i) + suffix); 714 } 715 if (isPointerFunc) { 716 out.print(", jint remaining"); 717 } 718 out.println(") {"); 719 720 int numArrays = 0; 721 int numBuffers = 0; 722 for (int i = 0; i < nonPrimitiveArgs.size(); i++) { 723 int idx = nonPrimitiveArgs.get(i).intValue(); 724 int cIndex = jfunc.getArgCIndex(idx); 725 String cname = cfunc.getArgName(cIndex); 726 if (jfunc.getArgType(idx).isArray()) { 727 ++numArrays; 728 } 729 if (jfunc.getArgType(idx).isBuffer()) { 730 ++numBuffers; 731 } 732 } 733 734 // Emit method body 735 736 // Emit local variable declarations for _exception and _returnValue 737 // 738 // Example: 739 // 740 // android::gl::ogles_context_t *ctx; 741 // 742 // jint _exception; 743 // GLenum _returnValue; 744 // 745 CType returnType = cfunc.getType(); 746 boolean isVoid = returnType.isVoid(); 747 748 boolean isUnsupported = isUnsupportedFunc(cfunc); 749 if (isUnsupported) { 750 out.println(indent + 751 "_env->ThrowNew(UOEClass,"); 752 out.println(indent + 753 " \"" + cfunc.getName() + "\");"); 754 if (!isVoid) { 755 String retval = getErrorReturnValue(cfunc); 756 out.println(indent + "return " + retval + ";"); 757 } 758 out.println("}"); 759 out.println(); 760 return; 761 } 762 763 if (mUseContextPointer) { 764 out.println(indent + 765 "android::gl::ogles_context_t *ctx = getContext(_env, _this);"); 766 } 767 768 boolean emitExceptionCheck = (numArrays > 0 || numBuffers > 0) && 769 hasNonConstArg(jfunc, cfunc, nonPrimitiveArgs); 770 // mChecker.getChecks(cfunc.getName()) != null 771 772 // Emit an _exeption variable if there will be error checks 773 if (emitExceptionCheck) { 774 out.println(indent + "jint _exception = 0;"); 775 } 776 777 // Emit a single _array or multiple _XXXArray variables 778 if (numBufferArgs == 1) { 779 out.println(indent + "jarray _array = (jarray) 0;"); 780 } else { 781 for (int i = 0; i < numBufferArgs; i++) { 782 out.println(indent + "jarray _" + bufferArgNames.get(i) + 783 "Array = (jarray) 0;"); 784 } 785 } 786 if (!isVoid) { 787 String retval = getErrorReturnValue(cfunc); 788 if (retval != null) { 789 out.println(indent + returnType.getDeclaration() + 790 " _returnValue = " + retval + ";"); 791 } else { 792 out.println(indent + returnType.getDeclaration() + 793 " _returnValue;"); 794 } 795 } 796 797 // Emit local variable declarations for pointer arguments 798 // 799 // Example: 800 // 801 // GLfixed *eqn_base; 802 // GLfixed *eqn; 803 // 804 String offset = "offset"; 805 String remaining = "_remaining"; 806 if (nonPrimitiveArgs.size() > 0) { 807 for (int i = 0; i < nonPrimitiveArgs.size(); i++) { 808 int idx = nonPrimitiveArgs.get(i).intValue(); 809 int cIndex = jfunc.getArgCIndex(idx); 810 String cname = cfunc.getArgName(cIndex); 811 812 CType type = cfunc.getArgType(jfunc.getArgCIndex(idx)); 813 String decl = type.getDeclaration(); 814 if (jfunc.getArgType(idx).isArray()) { 815 out.println(indent + 816 decl + 817 (decl.endsWith("*") ? "" : " ") + 818 jfunc.getArgName(idx) + 819 "_base = (" + decl + ") 0;"); 820 } 821 remaining = (numArrays <= 1 && numBuffers <= 1) ? "_remaining" : 822 "_" + cname + "Remaining"; 823 out.println(indent + 824 "jint " + remaining + ";"); 825 out.println(indent + 826 decl + 827 (decl.endsWith("*") ? "" : " ") + 828 jfunc.getArgName(idx) + 829 " = (" + decl + ") 0;"); 830 } 831 832 out.println(); 833 } 834 835 String retval = isVoid ? "" : " _returnValue"; 836 837 // Emit 'GetPrimitiveArrayCritical' for arrays 838 // Emit 'GetPointer' calls for Buffer pointers 839 int bufArgIdx = 0; 840 if (nonPrimitiveArgs.size() > 0) { 841 for (int i = 0; i < nonPrimitiveArgs.size(); i++) { 842 int idx = nonPrimitiveArgs.get(i).intValue(); 843 int cIndex = jfunc.getArgCIndex(idx); 844 845 String cname = cfunc.getArgName(cIndex); 846 offset = numArrays <= 1 ? "offset" : 847 cname + "Offset"; 848 remaining = (numArrays <= 1 && numBuffers <= 1) ? "_remaining" : 849 "_" + cname + "Remaining"; 850 851 if (jfunc.getArgType(idx).isArray()) { 852 out.println(indent + 853 "if (!" + 854 cname + 855 "_ref) {"); 856 if (emitExceptionCheck) { 857 out.println(indent + indent + "_exception = 1;"); 858 } 859 out.println(indent + " " + 860 (mUseCPlusPlus ? "_env" : "(*_env)") + 861 "->ThrowNew(" + 862 (mUseCPlusPlus ? "" : "_env, ") + 863 "IAEClass, " + 864 "\"" + cname + 865 " == null\");"); 866 out.println(indent + " goto exit;"); 867 needsExit = true; 868 out.println(indent + "}"); 869 870 out.println(indent + "if (" + offset + " < 0) {"); 871 if (emitExceptionCheck) { 872 out.println(indent + indent + "_exception = 1;"); 873 } 874 out.println(indent + " " + 875 (mUseCPlusPlus ? "_env" : "(*_env)") + 876 "->ThrowNew(" + 877 (mUseCPlusPlus ? "" : "_env, ") + 878 "IAEClass, " + 879 "\"" + offset + " < 0\");"); 880 out.println(indent + " goto exit;"); 881 needsExit = true; 882 out.println(indent + "}"); 883 884 out.println(indent + remaining + " = " + 885 (mUseCPlusPlus ? "_env" : "(*_env)") + 886 "->GetArrayLength(" + 887 (mUseCPlusPlus ? "" : "_env, ") + 888 cname + "_ref) - " + offset + ";"); 889 890 emitNativeBoundsChecks(cfunc, cname, out, false, 891 emitExceptionCheck, 892 offset, remaining, " "); 893 894 out.println(indent + 895 cname + 896 "_base = (" + 897 cfunc.getArgType(cIndex).getDeclaration() + 898 ")"); 899 out.println(indent + " " + 900 (mUseCPlusPlus ? "_env" : "(*_env)") + 901 "->GetPrimitiveArrayCritical(" + 902 (mUseCPlusPlus ? "" : "_env, ") + 903 jfunc.getArgName(idx) + 904 "_ref, (jboolean *)0);"); 905 out.println(indent + 906 cname + " = " + cname + "_base + " + offset + 907 ";"); 908 out.println(); 909 } else { 910 String array = numBufferArgs <= 1 ? "_array" : 911 "_" + bufferArgNames.get(bufArgIdx++) + "Array"; 912 913 boolean nullAllowed = isNullAllowed(cfunc); 914 if (nullAllowed) { 915 out.println(indent + "if (" + cname + "_buf) {"); 916 out.print(indent); 917 } 918 919 out.println(indent + 920 cname + 921 " = (" + 922 cfunc.getArgType(cIndex).getDeclaration() + 923 ")getPointer(_env, " + 924 cname + 925 "_buf, &" + array + ", &" + remaining + ");"); 926 927 if (nullAllowed) { 928 out.println(indent + "}"); 929 } 930 931 emitNativeBoundsChecks(cfunc, cname, out, true, 932 emitExceptionCheck, 933 offset, remaining, " "); 934 } 935 } 936 } 937 938 if (!isVoid) { 939 out.print(indent + "_returnValue = "); 940 } else { 941 out.print(indent); 942 } 943 String name = cfunc.getName(); 944 945 if (mUseContextPointer) { 946 name = name.substring(2, name.length()); // Strip off 'gl' prefix 947 name = name.substring(0, 1).toLowerCase() + 948 name.substring(1, name.length()); 949 out.print("ctx->procs."); 950 } 951 952 out.print(name + (isPointerFunc ? "Bounds" : "") + "("); 953 954 numArgs = cfunc.getNumArgs(); 955 if (numArgs == 0) { 956 if (mUseContextPointer) { 957 out.println("ctx);"); 958 } else { 959 out.println(");"); 960 } 961 } else { 962 if (mUseContextPointer) { 963 out.println("ctx,"); 964 } else { 965 out.println(); 966 } 967 for (int i = 0; i < numArgs; i++) { 968 String typecast; 969 if (i == numArgs - 1 && isVBOPointerFunc) { 970 typecast = "const GLvoid *"; 971 } else { 972 typecast = cfunc.getArgType(i).getDeclaration(); 973 } 974 out.print(indent + indent + 975 "(" + 976 typecast + 977 ")" + 978 cfunc.getArgName(i)); 979 980 if (i == numArgs - 1) { 981 if (isPointerFunc) { 982 out.println(","); 983 out.println(indent + indent + "(GLsizei)remaining"); 984 } else { 985 out.println(); 986 } 987 } else { 988 out.println(","); 989 } 990 } 991 out.println(indent + ");"); 992 } 993 994 if (needsExit) { 995 out.println(); 996 out.println("exit:"); 997 needsExit = false; 998 } 999 1000 bufArgIdx = 0; 1001 if (nonPrimitiveArgs.size() > 0) { 1002 for (int i = nonPrimitiveArgs.size() - 1; i >= 0; i--) { 1003 int idx = nonPrimitiveArgs.get(i).intValue(); 1004 1005 int cIndex = jfunc.getArgCIndex(idx); 1006 if (jfunc.getArgType(idx).isArray()) { 1007 1008 // If the argument is 'const', GL will not write to it. 1009 // In this case, we can use the 'JNI_ABORT' flag to avoid 1010 // the need to write back to the Java array 1011 out.println(indent + 1012 "if (" + jfunc.getArgName(idx) + "_base) {"); 1013 out.println(indent + indent + 1014 (mUseCPlusPlus ? "_env" : "(*_env)") + 1015 "->ReleasePrimitiveArrayCritical(" + 1016 (mUseCPlusPlus ? "" : "_env, ") + 1017 jfunc.getArgName(idx) + "_ref, " + 1018 cfunc.getArgName(cIndex) + 1019 "_base,"); 1020 out.println(indent + indent + indent + 1021 (cfunc.getArgType(cIndex).isConst() ? 1022 "JNI_ABORT" : 1023 "_exception ? JNI_ABORT: 0") + 1024 ");"); 1025 out.println(indent + "}"); 1026 } else if (jfunc.getArgType(idx).isBuffer()) { 1027 String array = numBufferArgs <= 1 ? "_array" : 1028 "_" + bufferArgNames.get(bufArgIdx++) + "Array"; 1029 out.println(indent + "if (" + array + ") {"); 1030 out.println(indent + indent + 1031 "releasePointer(_env, " + array + ", " + 1032 cfunc.getArgName(cIndex) + 1033 ", " + 1034 (cfunc.getArgType(cIndex).isConst() ? 1035 "JNI_FALSE" : "_exception ? JNI_FALSE : JNI_TRUE") + 1036 ");"); 1037 out.println(indent + "}"); 1038 } 1039 } 1040 } 1041 1042 if (!isVoid) { 1043 out.println(indent + "return _returnValue;"); 1044 } 1045 1046 out.println("}"); 1047 out.println(); 1048 } 1049 1050 public void addNativeRegistration(String s) { 1051 nativeRegistrations.add(s); 1052 } 1053 1054 public void emitNativeRegistration() { 1055 mCStream.println("static const char *classPathName = \"" + 1056 mClassPathName + 1057 "\";"); 1058 mCStream.println(); 1059 1060 mCStream.println("static JNINativeMethod methods[] = {"); 1061 1062 mCStream.println("{\"_nativeClassInit\", \"()V\", (void*)nativeClassInit },"); 1063 1064 Iterator<String> i = nativeRegistrations.iterator(); 1065 while (i.hasNext()) { 1066 mCStream.println(i.next()); 1067 } 1068 1069 mCStream.println("};"); 1070 mCStream.println(); 1071 1072 1073 mCStream.println("int register_com_google_android_gles_jni_GLImpl(JNIEnv *_env)"); 1074 mCStream.println("{"); 1075 mCStream.println(indent + 1076 "int err;"); 1077 1078 mCStream.println(indent + 1079 "err = android::AndroidRuntime::registerNativeMethods(_env, classPathName, methods, NELEM(methods));"); 1080 1081 mCStream.println(indent + "return err;"); 1082 mCStream.println("}"); 1083 } 1084} 1085