/* * Copyright (c) 2002-2008 LWJGL Project * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of 'LWJGL' nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.lwjgl.util.generator; /** * * This class generates the functions in the native source files. * * @author elias_naur * @version $Revision$ * $Id$ */ import org.lwjgl.PointerBuffer; import java.io.PrintWriter; import java.nio.Buffer; import java.util.List; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; public final class NativeMethodStubsGenerator { private static final String BUFFER_ADDRESS_POSTFIX = "_address"; public static final String BUFFER_POSITION_POSTFIX = "_position"; private static final String STRING_LIST_NAME = "_str"; private static final String POINTER_LIST_NAME = "_ptr"; public static void generateNativeMethodStubs(ProcessingEnvironment env, TypeMap type_map, PrintWriter writer, TypeElement d, boolean generate_error_checks, boolean context_specific) { for ( ExecutableElement method : Utils.getMethods(d) ) { Alternate alt_annotation = method.getAnnotation(Alternate.class); if ( (alt_annotation != null && (!alt_annotation.nativeAlt() || alt_annotation.skipNative())) || method.getAnnotation(Reuse.class) != null ) continue; generateMethodStub(env, type_map, writer, Utils.getQualifiedClassName(d), method, Mode.NORMAL, generate_error_checks, context_specific); if ( Utils.hasMethodBufferObjectParameter(method) ) generateMethodStub(env, type_map, writer, Utils.getQualifiedClassName(d), method, Mode.BUFFEROBJECT, generate_error_checks, context_specific); } } private static void generateParameters(PrintWriter writer, List params, Mode mode) { for ( VariableElement param : params ) { if ( param.getAnnotation(Result.class) != null || (param.getAnnotation(Helper.class) != null && !param.getAnnotation(Helper.class).passToNative()) ) continue; final Constant constant_annotation = param.getAnnotation(Constant.class); if ( constant_annotation == null || !constant_annotation.isNative() ) generateParameter(writer, param, mode); } } private static void generateParameter(PrintWriter writer, VariableElement param, Mode mode) { writer.print(", "); if ( mode == Mode.BUFFEROBJECT && param.getAnnotation(BufferObject.class) != null ) { writer.append("jlong ").append(param.getSimpleName()).append(Utils.BUFFER_OBJECT_PARAMETER_POSTFIX); } else if ( param.getAnnotation(PointerWrapper.class) != null ) { writer.append("jlong ").append(param.getSimpleName()); } else { JNITypeTranslator translator = new JNITypeTranslator(); param.asType().accept(translator, null); writer.append(translator.getSignature()).append(' ').append(param.getSimpleName()); } } private static void generateMethodStub(ProcessingEnvironment env, TypeMap type_map, PrintWriter writer, String interface_name, ExecutableElement method, Mode mode, boolean generate_error_checks, boolean context_specific) { if ( !context_specific && method.getAnnotation(Alternate.class) == null ) writer.print("static "); else writer.print("JNIEXPORT "); final TypeMirror result_type = Utils.getMethodReturnType(method); final CachedResult cached_result_annotation = method.getAnnotation(CachedResult.class); final AutoSize auto_size_annotation = method.getAnnotation(AutoSize.class); if ( method.getAnnotation(PointerWrapper.class) != null ) { writer.print("jlong"); } else { JNITypeTranslator translator = new JNITypeTranslator(); result_type.accept(translator, null); writer.print(translator.getReturnSignature()); } writer.print(" JNICALL "); writer.print(Utils.getQualifiedNativeMethodName(interface_name, method, generate_error_checks, context_specific)); if ( mode == Mode.BUFFEROBJECT ) writer.print(Utils.BUFFER_OBJECT_METHOD_POSTFIX); writer.print("(JNIEnv *env, jclass clazz"); generateParameters(writer, method.getParameters(), mode); if ( Utils.getNIOBufferType(result_type) != null ) { if ( (cached_result_annotation == null || !cached_result_annotation.isRange()) && (auto_size_annotation == null || !auto_size_annotation.isNative()) ) writer.print(", jlong " + Utils.RESULT_SIZE_NAME); if ( cached_result_annotation != null ) writer.print(", jobject " + Utils.CACHED_BUFFER_NAME); } if ( context_specific ) { writer.print(", jlong " + Utils.FUNCTION_POINTER_VAR_NAME); } writer.append(") {\n"); generateBufferParameterAddresses(type_map, writer, method, mode); Alternate alt_annotation = method.getAnnotation(Alternate.class); if ( context_specific ) { String typedef_name = Utils.getTypedefName(method); writer.append('\t').append(typedef_name).append(' ').append(alt_annotation == null ? method.getSimpleName() : alt_annotation.value()); writer.append(" = (").append(typedef_name).append(")((intptr_t)"); writer.append(Utils.FUNCTION_POINTER_VAR_NAME + ");\n"); } final Code code_annotation = method.getAnnotation(Code.class); final boolean hasResult = !result_type.equals(env.getTypeUtils().getNoType(TypeKind.VOID)); final boolean resultPreDeclare = hasResult && (hasPointerArrayInits(method.getParameters()) || (code_annotation != null && (code_annotation.nativeAfterVars().length() > 0 || code_annotation.nativeBeforeCall().length() > 0))); if ( resultPreDeclare ) printResultParam(type_map, writer, method, result_type, true); if ( code_annotation != null && code_annotation.nativeAfterVars().length() > 0 ) writer.append(code_annotation.nativeAfterVars()).append('\n'); generatePointerArrayInits(type_map, writer, method.getParameters()); if ( code_annotation != null && code_annotation.nativeBeforeCall().length() > 0 ) writer.append(code_annotation.nativeBeforeCall()).append('\n'); writer.print("\t"); if ( resultPreDeclare ) writer.print(Utils.RESULT_VAR_NAME + " = "); else if ( hasResult ) printResultParam(type_map, writer, method, result_type, false); writer.append(alt_annotation == null ? method.getSimpleName() : alt_annotation.value()).append('('); generateCallParameters(writer, type_map, method.getParameters()); writer.append(");\n"); if ( code_annotation != null && code_annotation.nativeAfterCall().length() > 0 ) writer.append(code_annotation.nativeAfterCall()).append('\n'); generateStringDeallocations(writer, method.getParameters()); if ( !result_type.equals(env.getTypeUtils().getNoType(TypeKind.VOID)) ) { writer.append("\treturn "); Class java_result_type = Utils.getJavaType(result_type); if ( Buffer.class.isAssignableFrom(java_result_type) ) { if ( cached_result_annotation != null ) writer.print("safeNewBufferCached(env, "); else writer.print("safeNewBuffer(env, "); } else if ( String.class.equals(java_result_type) ) { writer.print("NewStringNativeUnsigned(env, "); } else if ( method.getAnnotation(PointerWrapper.class) != null ) { writer.print("(intptr_t)"); } writer.print(Utils.RESULT_VAR_NAME); if ( Buffer.class.isAssignableFrom(java_result_type) ) { final String size_parameter_name; if ( auto_size_annotation != null && (auto_size_annotation.isNative() || (cached_result_annotation != null && cached_result_annotation.isRange())) ) size_parameter_name = auto_size_annotation.value(); else size_parameter_name = Utils.RESULT_SIZE_NAME; writer.print(", "); Utils.printExtraCallArguments(writer, method, size_parameter_name); } if ( Buffer.class.isAssignableFrom(java_result_type) || String.class.equals(java_result_type) ) writer.print(")"); writer.append(";\n"); } writer.append("}\n"); } private static void printResultParam(final TypeMap type_map, final PrintWriter writer, final ExecutableElement method, final TypeMirror result_type, final boolean preDeclare) { final VariableElement result_param = Utils.getResultParameter(method); final Element return_declaration = result_param == null ? method : result_param; final NativeTypeTranslator result_translator = new NativeTypeTranslator(type_map, return_declaration); result_type.accept(result_translator, null); if ( preDeclare ) writer.print("\t"); writer.append(result_translator.getSignature()).append(' ').append(Utils.RESULT_VAR_NAME); if ( preDeclare ) writer.append(";\n"); else writer.append(result_param == null ? " = " : ";\n\t"); } private static void generateCallParameters(PrintWriter writer, TypeMap type_map, List params) { if ( params.size() > 0 ) { boolean first = true; for ( VariableElement param : params ) { if ( param.getAnnotation(Helper.class) != null ) continue; if ( first ) first = false; else writer.append(", "); generateCallParameter(writer, type_map, param); } } } private static void generateCallParameter(PrintWriter writer, TypeMap type_map, VariableElement param) { if ( param.getAnnotation(Helper.class) != null ) return; final Constant constant_annotation = param.getAnnotation(Constant.class); if ( constant_annotation != null && constant_annotation.isNative() ) { writer.append(constant_annotation.value()); return; } boolean is_indirect = param.getAnnotation(Indirect.class) != null; if ( is_indirect || param.getAnnotation(PointerArray.class) != null ) { writer.append('('); final NativeTypeTranslator translator = new NativeTypeTranslator(type_map, param); param.asType().accept(translator, null); writer.append(translator.getSignature()); writer.append("*)"); } if ( param.getAnnotation(PointerWrapper.class) != null ) writer.append('(').append(param.getAnnotation(PointerWrapper.class).value()).append(")(intptr_t)"); if ( param.getAnnotation(Result.class) != null || is_indirect ) writer.append('&'); if ( param.getAnnotation(Result.class) != null ) { writer.append(Utils.RESULT_VAR_NAME); } else { writer.append(param.getSimpleName()); if ( param.getAnnotation(PointerArray.class) != null ) writer.append(getPointerArrayName(Utils.getJavaType(param.asType()))); else if ( Utils.isAddressableType(param.asType()) ) writer.append(BUFFER_ADDRESS_POSTFIX); } } private static void generateStringDeallocations(PrintWriter writer, List params) { for ( VariableElement param : params ) { final Class java_type = Utils.getJavaType(param.asType()); if ( java_type.equals(String.class) && param.getAnnotation(Result.class) == null ) writer.append("\tfree(" + param.getSimpleName() + BUFFER_ADDRESS_POSTFIX + ");\n"); else if ( param.getAnnotation(PointerArray.class) != null ) // Free the string array mem writer.append("\tfree(" + param.getSimpleName() + getPointerArrayName(java_type) + ");\n"); } } private static void generateBufferParameterAddresses(TypeMap type_map, PrintWriter writer, ExecutableElement method, Mode mode) { strLoopDeclared = false; ptrLoopDeclared = false; for ( VariableElement param : method.getParameters() ) { final Constant constant_annotation = param.getAnnotation(Constant.class); if ( param.getAnnotation(Result.class) == null && (constant_annotation == null || !constant_annotation.isNative()) && Utils.isAddressableType(param.asType()) ) generateBufferParameterAddress(type_map, writer, param, mode); } } private static boolean strLoopDeclared; private static boolean ptrLoopDeclared; private static void generateBufferParameterAddress(TypeMap type_map, PrintWriter writer, VariableElement param, Mode mode) { final Check check_annotation = param.getAnnotation(Check.class); final PointerArray array_annotation = param.getAnnotation(PointerArray.class); final Class java_type = Utils.getJavaType(param.asType()); final NativeTypeTranslator translator = new NativeTypeTranslator(type_map, param); param.asType().accept(translator, null); final String native_type = translator.getSignature(); if ( !java_type.isArray() || CharSequence.class.isAssignableFrom(java_type.getComponentType()) ) { writer.append('\t').append(native_type).append(param.getSimpleName()); writer.append(BUFFER_ADDRESS_POSTFIX + " = ("); writer.append(native_type); writer.append(")(intptr_t)"); if ( mode == Mode.BUFFEROBJECT && param.getAnnotation(BufferObject.class) != null ) { writer.append("offsetToPointer(").append(param.getSimpleName()).append(Utils.BUFFER_OBJECT_PARAMETER_POSTFIX + ')'); } else { if ( Buffer.class.isAssignableFrom(java_type) || java_type.equals(CharSequence.class) || java_type.equals(CharSequence[].class) || PointerBuffer.class.isAssignableFrom(java_type) ) { writer.append(param.getSimpleName()); } else if ( java_type.equals(String.class) ) { writer.append("GetStringNativeChars(env, ").append(param.getSimpleName()).append(')'); } else if ( array_annotation == null ) throw new RuntimeException("Illegal type " + java_type); } writer.append(";\n"); } if ( array_annotation != null ) { final String n = getPointerArrayName(java_type); final String arrayType; if ( POINTER_LIST_NAME.equals(n) ) { if ( n.equals(param.getSimpleName().toString()) ) throw new RuntimeException("The name '" + n + "' is not valid for object array arguments annotated with PointerArray"); arrayType = translator.getSignature(true) + (org.lwjgl.PointerWrapper.class.isAssignableFrom(java_type.getComponentType()) ? " " : ""); // Declare loop counters and allocate object array if ( !ptrLoopDeclared ) { writer.append("\tint ").append(n).append("_i;\n"); writer.append("\tjobject ").append(n).append("_object;\n"); ptrLoopDeclared = true; } } else { if ( n.equals(param.getSimpleName().toString()) ) throw new RuntimeException("The name '" + n + "' is not valid for arguments annotated with PointerArray"); arrayType = translator.getSignature(true); // Declare loop counters and allocate string array if ( !strLoopDeclared ) { writer.append("\tint ").append(n).append("_i;\n"); writer.append("\t").append(arrayType).append(n).append("_address;\n"); strLoopDeclared = true; } } writer.append('\t').append(arrayType).append('*').append(param.getSimpleName()).append(n).append(" = "); if ( check_annotation != null && check_annotation.canBeNull() ) writer.append(array_annotation.value()).append(" == 0 ? NULL : "); writer.append('(').append(arrayType).append("*) malloc(").append(array_annotation.value()).append(" * sizeof(" + arrayType + "));\n"); } } private static String getPointerArrayName(final Class java_type) { final Class component_type = java_type.getComponentType(); if ( component_type != null && (Buffer.class.isAssignableFrom(component_type) || org.lwjgl.PointerWrapper.class.isAssignableFrom(component_type)) ) return POINTER_LIST_NAME; else return STRING_LIST_NAME; } private static boolean hasPointerArrayInits(List params) { for ( VariableElement param : params ) { PointerArray pointerArray_annotation = param.getAnnotation(PointerArray.class); if ( pointerArray_annotation != null ) return true; } return false; } private static void generatePointerArrayInits(TypeMap type_map, PrintWriter writer, List params) { for ( VariableElement param : params ) { PointerArray pointerArray_annotation = param.getAnnotation(PointerArray.class); if ( pointerArray_annotation != null ) { final Class java_type = Utils.getJavaType(param.asType()); final Class component_type = java_type.isArray() ? java_type.getComponentType() : null; final NativeTypeTranslator translator = new NativeTypeTranslator(type_map, param); param.asType().accept(translator, null); final String n = getPointerArrayName(java_type); if ( POINTER_LIST_NAME.equals(n) ) { // Init vars writer.append('\t').append(n).append("_i = 0;\n"); // Fill pointer array with the buffer pointers writer.append("\twhile ( ").append(n).append("_i < ").append(pointerArray_annotation.value()).append(" ) {\n"); writer.append("\t\t").append(n).append("_object = (*env)->GetObjectArrayElement(env, ").append(param.getSimpleName()).append(", ").append(n).append("_i);\n"); writer.append("\t\t").append(param.getSimpleName()).append(n).append('[').append(n).append("_i++] = (").append(translator.getSignature(true)).append(')'); if ( Buffer.class.isAssignableFrom(component_type) ) writer.append("(*env)->GetDirectBufferAddress(env, "); else writer.append("(intptr_t)getPointerWrapperAddress(env, "); writer.append(n).append("_object);\n") .append("\t}\n"); } else { final String lengths = pointerArray_annotation.lengths(); // Init vars writer.append('\t').append(n).append("_i = 0;\n"); writer.append('\t').append(n).append("_address = (").append(translator.getSignature(true)).append(')').append(param.getSimpleName()).append(BUFFER_ADDRESS_POSTFIX + ";\n"); // Fill string array with the string pointers writer.append("\twhile ( ").append(n).append("_i < ").append(pointerArray_annotation.value()).append(" ) {\n"); if ( lengths.length() == 0 ) { writer.append("\t\t").append(param.getSimpleName()).append(n).append('[').append(n).append("_i++] = ").append(n).append("_address;\n"); writer.append("\t\t").append(n).append("_address += strlen((const char *)").append(n).append("_address) + 1;\n"); } else { writer.append("\t\t").append(param.getSimpleName()).append(n).append('[').append(n).append("_i] = ").append(n).append("_address;\n"); writer.append("\t\t").append(n).append("_address += ").append(lengths).append(BUFFER_ADDRESS_POSTFIX + "[").append(n).append("_i++];\n"); } writer.append("\t}\n"); } } } } }