1 /* 2 * Copyright (c) 2002-2008 LWJGL Project 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * * Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * * Neither the name of 'LWJGL' nor the names of 17 * its contributors may be used to endorse or promote products derived 18 * from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 package org.lwjgl.util.generator; 33 34 /** 35 * 36 * This class generates the methods in the generated java source files. 37 * 38 * @author elias_naur <elias_naur@users.sourceforge.net> 39 * @version $Revision$ $Id$ 40 */ 41 import org.lwjgl.PointerBuffer; 42 import org.lwjgl.util.generator.opengl.GLreturn; 43 44 import java.io.PrintWriter; 45 import java.nio.*; 46 import java.util.*; 47 import java.util.regex.Matcher; 48 import java.util.regex.Pattern; 49 import javax.annotation.processing.ProcessingEnvironment; 50 import javax.lang.model.element.AnnotationMirror; 51 import javax.lang.model.element.ExecutableElement; 52 import javax.lang.model.element.TypeElement; 53 import javax.lang.model.element.VariableElement; 54 import javax.lang.model.type.TypeKind; 55 import javax.lang.model.type.TypeMirror; 56 57 public class JavaMethodsGenerator { 58 59 private static final String SAVED_PARAMETER_POSTFIX = "_saved"; 60 generateMethodsJava(ProcessingEnvironment env, TypeMap type_map, PrintWriter writer, TypeElement interface_decl, boolean generate_error_checks, boolean context_specific)61 public static void generateMethodsJava(ProcessingEnvironment env, TypeMap type_map, PrintWriter writer, TypeElement interface_decl, boolean generate_error_checks, boolean context_specific) { 62 for ( ExecutableElement method : Utils.getMethods(interface_decl) ) { 63 generateMethodJava(env, type_map, writer, interface_decl, method, generate_error_checks, context_specific); 64 } 65 } 66 67 /** 68 * TODO : fix info multi-type methods print. 69 */ generateMethodJava(ProcessingEnvironment env, TypeMap type_map, PrintWriter writer, TypeElement interface_decl, ExecutableElement method, boolean generate_error_checks, boolean context_specific)70 private static void generateMethodJava(ProcessingEnvironment env, TypeMap type_map, PrintWriter writer, TypeElement interface_decl, ExecutableElement method, boolean generate_error_checks, boolean context_specific) { 71 writer.println(); 72 if ( Utils.isMethodIndirect(generate_error_checks, context_specific, method) ) { 73 if ( method.getAnnotation(GenerateAutos.class) != null ) { 74 printMethodWithMultiType(env, type_map, writer, interface_decl, method, TypeInfo.getDefaultTypeInfoMap(method), Mode.AUTOS, generate_error_checks, context_specific); 75 } 76 Collection<Map<VariableElement, TypeInfo>> cross_product = TypeInfo.getTypeInfoCrossProduct(type_map, method); 77 for ( Map<VariableElement, TypeInfo> typeinfos_instance : cross_product ) { 78 printMethodWithMultiType(env, type_map, writer, interface_decl, method, typeinfos_instance, Mode.NORMAL, generate_error_checks, context_specific); 79 } 80 } 81 if ( method.getAnnotation(CachedResult.class) != null && !method.getAnnotation(CachedResult.class).isRange() ) { 82 printMethodWithMultiType(env, type_map, writer, interface_decl, method, TypeInfo.getDefaultTypeInfoMap(method), Mode.CACHEDRESULT, generate_error_checks, context_specific); 83 } 84 85 Reuse reuse_annotation = method.getAnnotation(Reuse.class); 86 Alternate alt_annotation = method.getAnnotation(Alternate.class); 87 if ( alt_annotation == null || (alt_annotation.nativeAlt() && !alt_annotation.skipNative()) ) { 88 if ( alt_annotation != null && method.getSimpleName().toString().equals(alt_annotation.value()) ) { 89 throw new RuntimeException("An alternate function with native code should have a different name than the main function."); 90 } 91 92 if ( reuse_annotation == null ) { 93 printJavaNativeStub(env, writer, method, Mode.NORMAL, generate_error_checks, context_specific); 94 } 95 96 if ( Utils.hasMethodBufferObjectParameter(method) ) { 97 printMethodWithMultiType(env, type_map, writer, interface_decl, method, TypeInfo.getDefaultTypeInfoMap(method), Mode.BUFFEROBJECT, generate_error_checks, context_specific); 98 if ( reuse_annotation == null ) { 99 printJavaNativeStub(env, writer, method, Mode.BUFFEROBJECT, generate_error_checks, context_specific); 100 } 101 } 102 } 103 } 104 printJavaNativeStub(ProcessingEnvironment env, PrintWriter writer, ExecutableElement method, Mode mode, boolean generate_error_checks, boolean context_specific)105 private static void printJavaNativeStub(ProcessingEnvironment env, PrintWriter writer, ExecutableElement method, Mode mode, boolean generate_error_checks, boolean context_specific) { 106 if ( Utils.isMethodIndirect(generate_error_checks, context_specific, method) ) { 107 writer.print("\tstatic native "); 108 } else { 109 Utils.printDocComment(writer, method, env); 110 writer.print("\tpublic static native "); 111 } 112 writer.print(getResultType(method, true)); 113 writer.print(" " + Utils.getSimpleNativeMethodName(method, generate_error_checks, context_specific)); 114 if ( mode == Mode.BUFFEROBJECT ) { 115 writer.print(Utils.BUFFER_OBJECT_METHOD_POSTFIX); 116 } 117 writer.print("("); 118 boolean first_parameter = generateParametersJava(writer, method, TypeInfo.getDefaultTypeInfoMap(method), true, true, mode); 119 if ( context_specific ) { 120 if ( !first_parameter ) { 121 writer.print(", "); 122 } 123 writer.print("long " + Utils.FUNCTION_POINTER_VAR_NAME); 124 } 125 writer.println(");"); 126 } 127 generateParametersJava(PrintWriter writer, ExecutableElement method, Map<VariableElement, TypeInfo> typeinfos_instance, boolean native_stub, final boolean printTypes, Mode mode)128 private static boolean generateParametersJava(PrintWriter writer, ExecutableElement method, Map<VariableElement, TypeInfo> typeinfos_instance, boolean native_stub, final boolean printTypes, Mode mode) { 129 boolean first_parameter = true; 130 for ( VariableElement param : method.getParameters() ) { 131 if ( native_stub && (param.getAnnotation(Helper.class) != null && !param.getAnnotation(Helper.class).passToNative()) ) { 132 continue; 133 } 134 final Constant constant_annotation = param.getAnnotation(Constant.class); 135 if ( constant_annotation != null && constant_annotation.isNative() ) { 136 continue; 137 } 138 AnnotationMirror auto_annotation_mirror = Utils.getParameterAutoAnnotation(param); 139 boolean hide_auto_parameter = mode == Mode.NORMAL && !native_stub && auto_annotation_mirror != null; 140 if ( hide_auto_parameter ) { 141 AutoType auto_type_annotation = param.getAnnotation(AutoType.class); 142 if ( auto_type_annotation != null ) { 143 VariableElement auto_parameter = Utils.findParameter(method, auto_type_annotation.value()); 144 TypeInfo auto_param_type_info = typeinfos_instance.get(auto_parameter); 145 if ( auto_param_type_info.getSignedness() == Signedness.BOTH ) { 146 if ( !first_parameter ) { 147 writer.print(", "); 148 } 149 first_parameter = false; 150 if ( printTypes ) { 151 writer.print("boolean "); 152 } 153 writer.print(TypeInfo.UNSIGNED_PARAMETER_NAME); 154 } 155 } 156 } else if ( param.getAnnotation(Result.class) == null 157 && (native_stub || ((param.getAnnotation(Constant.class) == null || param.getAnnotation(Constant.class).keepParam()) && !Utils.isReturnParameter(method, param))) 158 && (mode != Mode.AUTOS || getAutoTypeParameter(method, param) == null) ) { 159 first_parameter = generateParameterJava(writer, param, typeinfos_instance.get(param), native_stub, printTypes, first_parameter, mode); 160 } 161 } 162 CachedResult cached_result_annotation = method.getAnnotation(CachedResult.class); 163 TypeMirror result_type = Utils.getMethodReturnType(method); 164 if ( (native_stub && Utils.getNIOBufferType(result_type) != null) || Utils.needResultSize(method) ) { 165 AutoSize auto_size_annotation = method.getAnnotation(AutoSize.class); 166 if ( auto_size_annotation == null || !auto_size_annotation.isNative() ) { 167 if ( cached_result_annotation == null || !cached_result_annotation.isRange() ) { 168 if ( !first_parameter ) { 169 writer.print(", "); 170 } 171 first_parameter = false; 172 if ( printTypes ) { 173 writer.print("long "); 174 } 175 writer.print(Utils.RESULT_SIZE_NAME); 176 } 177 } 178 } 179 if ( cached_result_annotation != null ) { 180 if ( !first_parameter ) { 181 writer.print(", "); 182 } 183 184 if ( mode == Mode.CACHEDRESULT ) { 185 if ( printTypes ) { 186 writer.print("long "); 187 } 188 writer.print(Utils.CACHED_BUFFER_LENGTH_NAME + ", "); 189 } 190 191 first_parameter = false; 192 if ( printTypes ) { 193 writer.print(getResultType(method, native_stub)); 194 } 195 writer.print(" " + Utils.CACHED_BUFFER_NAME); 196 } 197 return first_parameter; 198 } 199 generateParameterJava(PrintWriter writer, VariableElement param, TypeInfo type_info, boolean native_stub, final boolean printTypes, boolean first_parameter, Mode mode)200 private static boolean generateParameterJava(PrintWriter writer, VariableElement param, TypeInfo type_info, boolean native_stub, final boolean printTypes, boolean first_parameter, Mode mode) { 201 Class buffer_type = Utils.getNIOBufferType(param.asType()); 202 if ( !first_parameter ) { 203 writer.print(", "); 204 } 205 BufferObject bo_annotation = param.getAnnotation(BufferObject.class); 206 if ( bo_annotation != null && mode == Mode.BUFFEROBJECT ) { 207 if ( buffer_type == null ) { 208 throw new RuntimeException("type of " + param + " is not a nio Buffer parameter but is annotated as buffer object"); 209 } 210 if ( printTypes ) { 211 writer.print("long "); 212 } 213 writer.print(param.getSimpleName() + Utils.BUFFER_OBJECT_PARAMETER_POSTFIX); 214 } else { 215 if ( native_stub && param.getAnnotation(PointerWrapper.class) != null ) { 216 writer.print("long "); 217 } else { 218 Class type = type_info.getType(); 219 if ( native_stub && (type == CharSequence.class || type == CharSequence[].class || type == PointerBuffer.class || Buffer.class.isAssignableFrom(type)) ) { 220 writer.print("long "); 221 } else if ( printTypes ) { 222 writer.print(type.getSimpleName() + " "); 223 } 224 } 225 AutoSize auto_size_annotation = param.getAnnotation(AutoSize.class); 226 if ( auto_size_annotation != null ) { 227 writer.print(auto_size_annotation.value() + "_"); 228 } 229 writer.print(param.getSimpleName()); 230 } 231 return false; 232 } 233 printBufferObjectCheck(PrintWriter writer, BufferKind kind, Mode mode, boolean context_specific)234 private static void printBufferObjectCheck(PrintWriter writer, BufferKind kind, Mode mode, boolean context_specific) { 235 String bo_check_method_name = kind.toString(); 236 writer.print("\t\t" + Utils.CHECKS_CLASS_NAME + ".ensure" + bo_check_method_name); 237 if ( mode == Mode.BUFFEROBJECT ) { 238 writer.print("enabled"); 239 } else { 240 writer.print("disabled"); 241 } 242 243 if ( context_specific ) { 244 writer.println("(caps);"); 245 } else { 246 writer.println("();"); 247 } 248 } 249 printBufferObjectChecks(PrintWriter writer, ExecutableElement method, Mode mode, boolean context_specific)250 private static void printBufferObjectChecks(PrintWriter writer, ExecutableElement method, Mode mode, boolean context_specific) { 251 EnumSet<BufferKind> check_set = EnumSet.noneOf(BufferKind.class); 252 for ( VariableElement param : method.getParameters() ) { 253 BufferObject bo_annotation = param.getAnnotation(BufferObject.class); 254 if ( bo_annotation != null ) { 255 check_set.add(bo_annotation.value()); 256 } 257 } 258 for ( BufferKind kind : check_set ) { 259 printBufferObjectCheck(writer, kind, mode, context_specific); 260 } 261 } 262 printMethodWithMultiType(ProcessingEnvironment env, TypeMap type_map, PrintWriter writer, TypeElement interface_decl, ExecutableElement method, Map<VariableElement, TypeInfo> typeinfos_instance, Mode mode, boolean generate_error_checks, boolean context_specific)263 private static void printMethodWithMultiType(ProcessingEnvironment env, TypeMap type_map, PrintWriter writer, TypeElement interface_decl, ExecutableElement method, Map<VariableElement, TypeInfo> typeinfos_instance, Mode mode, boolean generate_error_checks, boolean context_specific) { 264 Utils.printDocComment(writer, method, env); 265 if ( method.getAnnotation(Deprecated.class) != null ) { 266 writer.println("\t@Deprecated"); 267 } 268 if ( interface_decl.getAnnotation(Private.class) == null && method.getAnnotation(Private.class) == null ) { 269 writer.print("\tpublic static "); 270 } else { 271 writer.print("\tstatic "); 272 } 273 writer.print(getResultType(method, false)); 274 StripPostfix strip_annotation = method.getAnnotation(StripPostfix.class); 275 String method_name; 276 Alternate alt_annotation = method.getAnnotation(Alternate.class); 277 method_name = alt_annotation == null || alt_annotation.javaAlt() ? method.getSimpleName().toString() : alt_annotation.value(); 278 if ( strip_annotation != null && mode == Mode.NORMAL ) { 279 method_name = getPostfixStrippedName(type_map, interface_decl, method); 280 } 281 writer.print(" " + method_name + "("); 282 generateParametersJava(writer, method, typeinfos_instance, false, true, mode); 283 writer.println(") {"); 284 285 final TypeMirror result_type = Utils.getMethodReturnType(method); 286 boolean has_result = !result_type.equals(env.getTypeUtils().getNoType(TypeKind.VOID)); 287 288 final Reuse reuse_annotation = method.getAnnotation(Reuse.class); 289 if ( reuse_annotation != null ) { 290 writer.print("\t\t"); 291 if ( has_result || method.getAnnotation(GLreturn.class) != null ) { 292 writer.print("return "); 293 } 294 295 writer.print(reuse_annotation.value() + "." + (reuse_annotation.method().length() > 0 ? reuse_annotation.method() : method_name) + "("); 296 generateParametersJava(writer, method, typeinfos_instance, false, false, mode); 297 writer.println(");\n\t}"); 298 return; 299 } 300 301 if ( context_specific ) { 302 type_map.printCapabilitiesInit(writer); 303 writer.print("\t\tlong " + Utils.FUNCTION_POINTER_VAR_NAME + " = " + type_map.getCapabilities() + "."); 304 writer.println(Utils.getFunctionAddressName(interface_decl, method, true) + ";"); 305 writer.print("\t\tBufferChecks.checkFunctionAddress("); 306 writer.println(Utils.FUNCTION_POINTER_VAR_NAME + ");"); 307 } 308 final Code code_annotation = method.getAnnotation(Code.class); 309 if ( code_annotation != null && code_annotation.value().length() > 0 ) { 310 writer.println(code_annotation.value()); 311 } 312 printBufferObjectChecks(writer, method, mode, context_specific); 313 printParameterChecks(writer, method, typeinfos_instance, mode, generate_error_checks); 314 printParameterCaching(writer, interface_decl, method, mode, context_specific); 315 316 if ( code_annotation != null && code_annotation.javaBeforeNative().length() > 0 ) { 317 writer.println(code_annotation.javaBeforeNative()); 318 } 319 writer.print("\t\t"); 320 321 final PointerWrapper pointer_wrapper_annotation = method.getAnnotation(PointerWrapper.class); 322 if ( has_result ) { 323 writer.print(getResultType(method, false) + " " + Utils.RESULT_VAR_NAME); 324 325 if ( code_annotation != null && code_annotation.tryBlock() ) { 326 writer.print(" = " + getDefaultResultValue(method)); 327 writer.println(";\n\t\ttry {"); 328 writer.print("\t\t\t" + Utils.RESULT_VAR_NAME); 329 } 330 331 writer.print(" = "); 332 if ( pointer_wrapper_annotation != null ) { 333 if ( pointer_wrapper_annotation.factory().length() > 0 ) { 334 writer.print(pointer_wrapper_annotation.factory() + "("); 335 } else { 336 writer.print("new " + getResultType(method, false) + "("); 337 } 338 } 339 } else if ( method.getAnnotation(GLreturn.class) != null ) { 340 has_result = true; 341 Utils.printGLReturnPre(writer, method, method.getAnnotation(GLreturn.class), type_map); 342 } 343 writer.print(Utils.getSimpleNativeMethodName(method, generate_error_checks, context_specific)); 344 if ( mode == Mode.BUFFEROBJECT ) { 345 writer.print(Utils.BUFFER_OBJECT_METHOD_POSTFIX); 346 } 347 writer.print("("); 348 boolean first_parameter = printMethodCallArguments(writer, method, typeinfos_instance, mode, type_map); 349 if ( context_specific ) { 350 if ( !first_parameter ) { 351 writer.print(", "); 352 } 353 writer.print(Utils.FUNCTION_POINTER_VAR_NAME); 354 } 355 if ( has_result && pointer_wrapper_annotation != null ) { 356 writer.print(")"); 357 if ( pointer_wrapper_annotation.params().length() > 0 ) { 358 writer.print(", " + pointer_wrapper_annotation.params()); 359 } 360 } 361 writer.println(");"); 362 363 if ( code_annotation != null && code_annotation.javaAfterNative().length() > 0 ) { 364 writer.println(code_annotation.javaAfterNative()); 365 } 366 367 final String tabs = code_annotation != null && code_annotation.tryBlock() ? "\t\t\t" : "\t\t"; 368 if ( generate_error_checks && method.getAnnotation(NoErrorCheck.class) == null ) { 369 type_map.printErrorCheckMethod(writer, method, tabs); 370 } 371 // DISABLED: indirect buffer support 372 //printNondirectParameterCopies(writer, method, mode); 373 if ( has_result ) { 374 if ( method.getAnnotation(GLreturn.class) == null ) { 375 if ( ByteBuffer.class.equals(Utils.getJavaType(result_type)) ) { 376 writer.println(tabs + "return LWJGLUtil.CHECKS && " + Utils.RESULT_VAR_NAME + " == null ? null : " + Utils.RESULT_VAR_NAME + ".order(ByteOrder.nativeOrder());"); // safeNewBuffer returns a direct ByteBuffer with BIG_ENDIAN order. 377 } else { 378 writer.println(tabs + "return " + Utils.RESULT_VAR_NAME + ";"); 379 } 380 } else { 381 Utils.printGLReturnPost(writer, method, method.getAnnotation(GLreturn.class), type_map); 382 } 383 } 384 385 if ( code_annotation != null && code_annotation.tryBlock() ) { 386 writer.println("\t\t} finally {"); 387 writer.println(code_annotation.javaFinally()); 388 writer.println("\t\t}"); 389 } 390 writer.println("\t}"); 391 } 392 getExtensionPostfix(TypeElement interface_decl)393 private static String getExtensionPostfix(TypeElement interface_decl) { 394 String interface_simple_name = interface_decl.getSimpleName().toString(); 395 Extension extension_annotation = interface_decl.getAnnotation(Extension.class); 396 if ( extension_annotation == null ) { 397 int underscore_index = interface_simple_name.indexOf("_"); 398 if ( underscore_index != -1 ) { 399 return interface_simple_name.substring(0, underscore_index); 400 } else { 401 return ""; 402 } 403 } else { 404 return extension_annotation.postfix(); 405 } 406 } 407 getAutoTypeParameter(ExecutableElement method, VariableElement target_parameter)408 private static VariableElement getAutoTypeParameter(ExecutableElement method, VariableElement target_parameter) { 409 for ( VariableElement param : method.getParameters() ) { 410 AnnotationMirror auto_annotation = Utils.getParameterAutoAnnotation(param); 411 if ( auto_annotation != null ) { 412 Class annotation_type = NativeTypeTranslator.getClassFromType(auto_annotation.getAnnotationType()); 413 String parameter_name; 414 if ( annotation_type.equals(AutoType.class) ) { 415 parameter_name = param.getAnnotation(AutoType.class).value(); 416 } else if ( annotation_type.equals(AutoSize.class) ) { 417 parameter_name = param.getAnnotation(AutoSize.class).value(); 418 } else { 419 throw new RuntimeException("Unknown annotation type " + annotation_type); 420 } 421 if ( target_parameter.getSimpleName().toString().equals(parameter_name) ) { 422 return param; 423 } 424 } 425 } 426 return null; 427 } 428 hasAnyParameterAutoTypeAnnotation(ExecutableElement method, VariableElement target_param)429 private static boolean hasAnyParameterAutoTypeAnnotation(ExecutableElement method, VariableElement target_param) { 430 for ( VariableElement param : method.getParameters() ) { 431 AutoType auto_type_annotation = param.getAnnotation(AutoType.class); 432 if ( auto_type_annotation != null ) { 433 VariableElement type_target_param = Utils.findParameter(method, auto_type_annotation.value()); 434 if ( target_param.equals(type_target_param) ) { 435 return true; 436 } 437 } 438 } 439 return false; 440 } 441 442 private static final Map<String, Pattern> postfixPatterns = new HashMap<String, Pattern>(); 443 getPostfixPattern(String regex)444 private static Pattern getPostfixPattern(String regex) { 445 Pattern pattern = postfixPatterns.get(regex); 446 if ( pattern == null ) { 447 postfixPatterns.put(regex, pattern = Pattern.compile(regex)); 448 } 449 return pattern; 450 } 451 getPostfixStrippedName(TypeMap type_map, TypeElement interface_decl, ExecutableElement method)452 private static String getPostfixStrippedName(TypeMap type_map, TypeElement interface_decl, ExecutableElement method) { 453 StripPostfix strip_annotation = method.getAnnotation(StripPostfix.class); 454 VariableElement postfix_parameter = Utils.findParameter(method, strip_annotation.value()); 455 String postfix = strip_annotation.postfix(); 456 boolean postfixOverride = !("NULL".equals(postfix) && strip_annotation.hasPostfix()); 457 if ( !postfixOverride ) { 458 PostfixTranslator translator = new PostfixTranslator(type_map, postfix_parameter); 459 postfix_parameter.asType().accept(translator, null); 460 postfix = translator.getSignature(); 461 } else if ( !strip_annotation.hasPostfix() ) { 462 postfix = ""; 463 } 464 465 String method_name; 466 Alternate alt_annotation = method.getAnnotation(Alternate.class); 467 method_name = alt_annotation == null || alt_annotation.javaAlt() ? method.getSimpleName().toString() : alt_annotation.value(); 468 469 String extension_postfix = "NULL".equals(strip_annotation.extension()) ? getExtensionPostfix(interface_decl) : strip_annotation.extension(); 470 471 Matcher matcher = getPostfixPattern( 472 postfixOverride 473 ? (postfix + "(?:v)?" + extension_postfix + "$") 474 : ("(?:" + postfix + "(?:v)?|i(?:64)?_v|v)" + extension_postfix + "$") 475 ).matcher(method_name); 476 477 if ( !matcher.find() ) { 478 throw new RuntimeException(method_name + " is specified as being postfix stripped on parameter " + postfix_parameter + ", but it's postfix is neither '" + postfix + "' nor 'v'"); 479 } 480 481 return method_name.substring(0, matcher.start()) + extension_postfix; 482 } 483 getBufferElementSizeExponent(Class c)484 private static int getBufferElementSizeExponent(Class c) { 485 if ( IntBuffer.class.equals(c) ) { 486 return 2; 487 } else if ( LongBuffer.class.equals(c) ) { 488 return 3; 489 } else if ( DoubleBuffer.class.equals(c) ) { 490 return 3; 491 } else if ( ShortBuffer.class.equals(c) ) { 492 return 1; 493 } else if ( ByteBuffer.class.equals(c) ) { 494 return 0; 495 } else if ( FloatBuffer.class.equals(c) ) { 496 return 2; 497 } else { 498 throw new RuntimeException(c + " is not allowed"); 499 } 500 } 501 printMethodCallArgument(PrintWriter writer, ExecutableElement method, VariableElement param, Map<VariableElement, TypeInfo> typeinfos_instance, Mode mode, boolean first_parameter, TypeMap type_map)502 private static boolean printMethodCallArgument(PrintWriter writer, ExecutableElement method, VariableElement param, Map<VariableElement, TypeInfo> typeinfos_instance, Mode mode, boolean first_parameter, TypeMap type_map) { 503 if ( !first_parameter ) { 504 writer.print(", "); 505 } 506 507 AnnotationMirror auto_annotation = Utils.getParameterAutoAnnotation(param); 508 Constant constant_annotation = param.getAnnotation(Constant.class); 509 if ( constant_annotation != null ) { 510 writer.print(constant_annotation.value()); 511 } else if ( auto_annotation != null && mode == Mode.NORMAL ) { 512 Class param_type = NativeTypeTranslator.getClassFromType(auto_annotation.getAnnotationType()); 513 if ( AutoType.class.equals(param_type) ) { 514 final AutoType auto_type_annotation = param.getAnnotation(AutoType.class); 515 final VariableElement auto_parameter = Utils.findParameter(method, auto_type_annotation.value()); 516 final String auto_type = typeinfos_instance.get(auto_parameter).getAutoType(); 517 if ( auto_type == null ) { 518 throw new RuntimeException("No auto type for parameter " + param.getSimpleName() + " in method " + method); 519 } 520 writer.print(auto_type); 521 } else if ( AutoSize.class.equals(param_type) ) { 522 final AutoSize auto_size_annotation = param.getAnnotation(AutoSize.class); 523 if ( !auto_size_annotation.useExpression() ) { 524 final String auto_parameter_name = auto_size_annotation.value(); 525 final VariableElement auto_target_param = Utils.findParameter(method, auto_parameter_name); 526 final TypeInfo auto_target_type_info = typeinfos_instance.get(auto_target_param); 527 final boolean shift_remaining = !hasAnyParameterAutoTypeAnnotation(method, auto_target_param) && Utils.isParameterMultiTyped(auto_target_param); 528 int shifting = 0; 529 if ( shift_remaining ) { 530 shifting = getBufferElementSizeExponent(auto_target_type_info.getType()); 531 if ( shifting > 0 ) { 532 writer.print("("); 533 } 534 } 535 if ( auto_size_annotation.canBeNull() ) { 536 writer.print("(" + auto_parameter_name + " == null ? 0 : " + auto_parameter_name + ".remaining())"); 537 } else { 538 writer.print(auto_parameter_name + ".remaining()"); 539 } 540 // Shift the remaining if the target parameter is multityped and there's no AutoType to track type 541 if ( shift_remaining && shifting > 0 ) { 542 writer.print(" << " + shifting); 543 writer.print(")"); 544 } 545 } 546 writer.print(auto_size_annotation.expression()); 547 } else { 548 throw new RuntimeException("Unknown auto annotation " + param_type); 549 } 550 } else { 551 if ( mode == Mode.BUFFEROBJECT && param.getAnnotation(BufferObject.class) != null ) { 552 writer.print(param.getSimpleName() + Utils.BUFFER_OBJECT_PARAMETER_POSTFIX); 553 } else { 554 Class type = typeinfos_instance.get(param).getType(); 555 Check check_annotation = param.getAnnotation(Check.class); 556 boolean hide_buffer = mode == Mode.AUTOS && getAutoTypeParameter(method, param) != null; 557 if ( hide_buffer ) { 558 writer.print("0L"); 559 } else { 560 if ( type == CharSequence.class || type == CharSequence[].class ) { 561 final String offset = Utils.getStringOffset(method, param); 562 563 writer.print("APIUtil.getBuffer"); 564 if ( param.getAnnotation(NullTerminated.class) != null ) { 565 writer.print("NT"); 566 } 567 writer.print('('); 568 writer.print(type_map.getAPIUtilParam(true)); 569 writer.print(param.getSimpleName()); 570 if ( offset != null ) { 571 writer.print(", " + offset); 572 } 573 writer.print(")"); 574 } else { 575 final AutoSize auto_size_annotation = param.getAnnotation(AutoSize.class); 576 if ( auto_size_annotation != null ) { 577 writer.print(auto_size_annotation.value() + "_"); 578 } 579 580 final Class buffer_type = Utils.getNIOBufferType(param.asType()); 581 if ( buffer_type == null ) { 582 writer.print(param.getSimpleName()); 583 } else { 584 writer.print("MemoryUtil.getAddress"); 585 if ( check_annotation != null && check_annotation.canBeNull() ) { 586 writer.print("Safe"); 587 } 588 writer.print("("); 589 writer.print(param.getSimpleName()); 590 writer.print(")"); 591 } 592 } 593 } 594 if ( type != long.class ) { 595 PointerWrapper pointer_annotation = param.getAnnotation(PointerWrapper.class); 596 if ( pointer_annotation != null ) { 597 if ( pointer_annotation.canBeNull() ) { 598 writer.print(" == null ? 0 : " + param.getSimpleName()); 599 } 600 writer.print(".getPointer()"); 601 } 602 } 603 } 604 } 605 return false; 606 } 607 printMethodCallArguments(PrintWriter writer, ExecutableElement method, Map<VariableElement, TypeInfo> typeinfos_instance, Mode mode, TypeMap type_map)608 private static boolean printMethodCallArguments(PrintWriter writer, ExecutableElement method, Map<VariableElement, TypeInfo> typeinfos_instance, Mode mode, TypeMap type_map) { 609 boolean first_parameter = true; 610 for ( VariableElement param : method.getParameters() ) { 611 if ( param.getAnnotation(Result.class) != null || (param.getAnnotation(Helper.class) != null && !param.getAnnotation(Helper.class).passToNative()) ) { 612 continue; 613 } 614 615 final Constant constant_annotation = param.getAnnotation(Constant.class); 616 if ( constant_annotation == null || !constant_annotation.isNative() ) { 617 first_parameter = printMethodCallArgument(writer, method, param, typeinfos_instance, mode, first_parameter, type_map); 618 } 619 } 620 if ( Utils.getNIOBufferType(Utils.getMethodReturnType(method)) != null ) { 621 if ( method.getAnnotation(CachedResult.class) != null && method.getAnnotation(CachedResult.class).isRange() ) { 622 first_parameter = false; 623 Utils.printExtraCallArguments(writer, method, ""); 624 } else { 625 AutoSize auto_size_annotation = method.getAnnotation(AutoSize.class); 626 if ( auto_size_annotation == null || !auto_size_annotation.isNative() ) { 627 if ( !first_parameter ) { 628 writer.print(", "); 629 } 630 first_parameter = false; 631 632 String result_size_expression; 633 if ( mode == Mode.CACHEDRESULT ) { 634 result_size_expression = Utils.CACHED_BUFFER_LENGTH_NAME; 635 } else if ( auto_size_annotation == null ) { 636 result_size_expression = Utils.RESULT_SIZE_NAME; 637 } else { 638 result_size_expression = auto_size_annotation.value(); 639 } 640 641 Utils.printExtraCallArguments(writer, method, result_size_expression); 642 } 643 } 644 } 645 return first_parameter; 646 } 647 printParameterCaching(PrintWriter writer, TypeElement interface_decl, ExecutableElement method, Mode mode, boolean context_specific)648 private static void printParameterCaching(PrintWriter writer, TypeElement interface_decl, ExecutableElement method, Mode mode, boolean context_specific) { 649 for ( VariableElement param : method.getParameters() ) { 650 Class java_type = Utils.getJavaType(param.asType()); 651 CachedReference cachedReference = param.getAnnotation(CachedReference.class); 652 if ( Buffer.class.isAssignableFrom(java_type) 653 && cachedReference != null 654 && (mode != Mode.BUFFEROBJECT || param.getAnnotation(BufferObject.class) == null) 655 && param.getAnnotation(Result.class) == null ) { 656 writer.print("\t\tif ( LWJGLUtil.CHECKS ) StateTracker."); 657 if ( context_specific ) { 658 writer.print("getReferences(caps)."); 659 } else { 660 writer.print("getTracker()."); 661 } 662 if ( cachedReference.name().length() > 0 ) { 663 writer.print(cachedReference.name()); 664 } else { 665 writer.print(Utils.getReferenceName(interface_decl, method, param)); 666 } 667 if ( cachedReference.index().length() > 0 ) { 668 writer.print("[" + cachedReference.index() + "]"); 669 } 670 writer.println(" = " + param.getSimpleName() + ";"); 671 } 672 } 673 } 674 printParameterChecks(PrintWriter writer, ExecutableElement method, Map<VariableElement, TypeInfo> typeinfos, Mode mode, final boolean generate_error_checks)675 private static void printParameterChecks(PrintWriter writer, ExecutableElement method, Map<VariableElement, TypeInfo> typeinfos, Mode mode, final boolean generate_error_checks) { 676 if ( mode == Mode.NORMAL ) { 677 final GenerateAutos gen_autos_annotation = method.getAnnotation(GenerateAutos.class); 678 if ( gen_autos_annotation != null && gen_autos_annotation.sizeVariables().length > 0 ) { 679 // For the auto-generated parameters, declare and init a size variable (that can be reused by @Code) 680 for ( final VariableElement param : method.getParameters() ) { 681 if ( Arrays.binarySearch(gen_autos_annotation.sizeVariables(), param.getSimpleName().toString()) >= 0 ) { 682 final int shifting = getBufferElementSizeExponent(typeinfos.get(param).getType()); 683 final Check check_annotation = param.getAnnotation(Check.class); 684 685 writer.print("\t\tlong " + param.getSimpleName() + "_size = "); 686 if ( check_annotation == null || !check_annotation.canBeNull() ) { 687 writer.println(param.getSimpleName() + ".remaining() << " + shifting + ";"); 688 } else { 689 writer.println(param.getSimpleName() + " == null ? 0 : " + param.getSimpleName() + ".remaining() << " + shifting + ";"); 690 } 691 } 692 } 693 } 694 } 695 696 for ( VariableElement param : method.getParameters() ) { 697 Class java_type = Utils.getJavaType(param.asType()); 698 if ( java_type.isArray() || (Utils.isAddressableType(java_type) 699 && (mode != Mode.BUFFEROBJECT || param.getAnnotation(BufferObject.class) == null) 700 && (mode != Mode.AUTOS || getAutoTypeParameter(method, param) == null) 701 && param.getAnnotation(Result.class) == null 702 && !Utils.isReturnParameter(method, param)) ) { 703 String check_value = null; 704 boolean can_be_null = false; 705 Check check_annotation = param.getAnnotation(Check.class); 706 if ( check_annotation != null ) { 707 check_value = check_annotation.value(); 708 can_be_null = check_annotation.canBeNull(); 709 } 710 if ( (Buffer.class.isAssignableFrom(java_type) || PointerBuffer.class.isAssignableFrom(java_type)) && param.getAnnotation(Constant.class) == null ) { 711 TypeInfo typeinfo = typeinfos.get(param); 712 printParameterCheck(writer, method, param.getSimpleName().toString(), typeinfo.getType().getSimpleName(), check_value, can_be_null, param.getAnnotation(NullTerminated.class), generate_error_checks); 713 } else if ( String.class.equals(java_type) ) { 714 if ( !can_be_null ) { 715 writer.println("\t\tBufferChecks.checkNotNull(" + param.getSimpleName() + ");"); 716 } 717 } else if ( java_type.isArray() ) { 718 printArrayParameterCheck(writer, param.getSimpleName().toString(), check_value, can_be_null); 719 } 720 } 721 } 722 if ( method.getAnnotation(CachedResult.class) != null ) { 723 printParameterCheck(writer, method, Utils.CACHED_BUFFER_NAME, null, null, true, null, generate_error_checks); 724 } 725 } 726 printParameterCheck(PrintWriter writer, ExecutableElement method, String name, String type, String check_value, boolean can_be_null, NullTerminated null_terminated, boolean generate_error_checks)727 private static void printParameterCheck(PrintWriter writer, ExecutableElement method, String name, String type, String check_value, boolean can_be_null, NullTerminated null_terminated, boolean generate_error_checks) { 728 String tabs; 729 if ( can_be_null ) { 730 writer.print("\t\tif (" + name + " != null)"); 731 if ( null_terminated != null ) { 732 writer.println(" {"); 733 } else { 734 writer.println(); 735 } 736 tabs = "\t\t\t"; 737 } else { 738 tabs = "\t\t"; 739 } 740 writer.print(tabs + "BufferChecks.check"); 741 if ( check_value != null && check_value.length() > 0 ) { 742 writer.print("Buffer"); 743 if ( "Buffer".equals(type) ) { 744 writer.print("Size"); // Check size only, Buffer.isDirect() was added in 1.6, cannot use yet. TODO: Remove? 745 } 746 writer.print("(" + name + ", " + check_value); 747 } else { 748 writer.print("Direct(" + name); 749 } 750 writer.println(");"); 751 if ( can_be_null && generate_error_checks ) { 752 final Check check_annotation = method.getAnnotation(Check.class); 753 if ( check_annotation != null && check_annotation.value().equals(name) ) { 754 writer.println("\t\telse"); 755 writer.println("\t\t\t" + name + " = APIUtil.getBufferIntDebug();"); // Use an exclusive buffer here 756 } 757 } 758 if ( null_terminated != null ) { 759 writer.print(tabs + "BufferChecks.checkNullTerminated("); 760 writer.print(name); 761 if ( null_terminated.value().length() > 0 ) { 762 writer.print(", "); 763 writer.print(null_terminated.value()); 764 } 765 writer.println(");"); 766 if ( can_be_null ) { 767 writer.println("\t\t}"); 768 } 769 } 770 } 771 printArrayParameterCheck(PrintWriter writer, String name, String check_value, boolean can_be_null)772 private static void printArrayParameterCheck(PrintWriter writer, String name, String check_value, boolean can_be_null) { 773 String tabs; 774 if ( can_be_null ) { 775 writer.println("\t\tif (" + name + " != null)"); 776 tabs = "\t\t\t"; 777 } else { 778 tabs = "\t\t"; 779 } 780 781 writer.print(tabs + "BufferChecks.checkArray(" + name); 782 if ( check_value != null && check_value.length() > 0 ) { 783 writer.print(", " + check_value); 784 } 785 writer.println(");"); 786 } 787 getResultType(ExecutableElement method, boolean native_stub)788 private static String getResultType(ExecutableElement method, boolean native_stub) { 789 if ( native_stub && method.getAnnotation(PointerWrapper.class) != null ) { 790 return "long"; 791 } else if ( !native_stub && method.getAnnotation(GLreturn.class) != null ) { 792 return Utils.getMethodReturnType(method, method.getAnnotation(GLreturn.class), false); 793 } else { 794 return Utils.getJavaType(Utils.getMethodReturnType(method)).getSimpleName(); 795 } 796 } 797 getDefaultResultValue(ExecutableElement method)798 private static String getDefaultResultValue(ExecutableElement method) { 799 if ( method.getAnnotation(GLreturn.class) != null ) { 800 final String type = Utils.getMethodReturnType(method, method.getAnnotation(GLreturn.class), false); 801 if ( "boolean".equals(type) ) { 802 return "false"; 803 } else if ( Character.isLowerCase(type.charAt(0)) ) { 804 return "0"; 805 } else { 806 return "null"; 807 } 808 } else { 809 final Class type = Utils.getJavaType(Utils.getMethodReturnType(method)); 810 if ( type.isPrimitive() ) { 811 if ( type == boolean.class ) { 812 return "false"; 813 } else { 814 return "0"; 815 } 816 } else { 817 return "null"; 818 } 819 } 820 } 821 822 } 823