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