/* Copyright (c) 2009-2013 Olivier Chafik, All Rights Reserved This file is part of JNAerator (http://jnaerator.googlecode.com/). JNAerator is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. JNAerator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with JNAerator. If not, see <http://www.gnu.org/licenses/>. */ package com.ochafik.lang.jnaerator; import com.google.common.collect.ImmutableMap; import org.bridj.ann.Name; import org.bridj.BridJ; import org.bridj.FlagSet; import org.bridj.IntValuedEnum; import org.bridj.StructObject; import org.bridj.cpp.CPPObject; import org.bridj.cpp.com.IUnknown; import com.ochafik.lang.jnaerator.BridJTypeConversion.NL4JConversion; import com.ochafik.lang.jnaerator.TypeConversion.ConvType; import com.ochafik.lang.jnaerator.TypeConversion.EnumItemResult; //import org.bridj.structs.StructIO; //import org.bridj.structs.Array; import java.io.IOException; import java.util.*; import com.ochafik.lang.jnaerator.parser.*; import com.ochafik.lang.jnaerator.parser.Enum; import com.ochafik.lang.jnaerator.parser.Statement.Block; import com.ochafik.lang.jnaerator.parser.StoredDeclarations.*; import com.ochafik.lang.jnaerator.parser.Struct.MemberVisibility; import com.ochafik.lang.jnaerator.parser.TypeRef.*; import com.ochafik.lang.jnaerator.parser.Expression.*; import com.ochafik.lang.jnaerator.parser.Function.Type; import com.ochafik.lang.jnaerator.parser.DeclarationsHolder.ListWrapper; import com.ochafik.lang.jnaerator.parser.Declarator.*; import com.nativelibs4java.jalico.Pair; import com.ochafik.util.string.StringUtils; import static com.ochafik.lang.jnaerator.parser.ElementsHelper.*; import com.ochafik.lang.jnaerator.parser.Function.SignatureType; import org.bridj.*; import org.bridj.ann.Convention; import org.bridj.ann.Optional; import org.bridj.cpp.CPPRuntime; import org.bridj.objc.NSObject; public class BridJDeclarationsConverter extends DeclarationsConverter { public BridJDeclarationsConverter(Result result) { super(result); } public void annotateActualName(ModifiableElement e, Identifier name) { e.addAnnotation(new Annotation(typeRef(org.bridj.ann.Name.class), expr(name.toString()))); } @Override public Struct convertCallback(FunctionSignature functionSignature, Signatures signatures, Identifier callerLibraryName) { Struct decl = super.convertCallback(functionSignature, signatures, callerLibraryName); if (decl != null) { decl.setParents(Arrays.asList((SimpleTypeRef) (FunctionSignature.Type.ObjCBlock.equals(functionSignature.getType()) ? result.config.runtime.typeRef(JNAeratorConfig.Runtime.Ann.ObjCBlock) : (SimpleTypeRef) typeRef(ident(result.config.runtime.callbackClass, expr(typeRef(decl.getTag().clone()))))))); //addCallingConventionAnnotation(functionSignature.getFunction(), decl); } return decl; } @Override public void convertEnum(Enum e, Signatures signatures, DeclarationsHolder out, Identifier libraryClassName) { if (e.isForwardDeclaration()) { return; } Identifier rawEnumName = getActualTaggedTypeName(e); Map<String, EnumItemResult> results = result.typeConverter.getEnumValuesAndCommentsByName(e, libraryClassName); boolean hasEnumClass = false; if (rawEnumName != null && rawEnumName.resolveLastSimpleIdentifier().getName() != null) { hasEnumClass = true; Identifier enumName = result.typeConverter.getValidJavaIdentifier(rawEnumName); if (!signatures.addClass(enumName)) { return; } signatures = new Signatures(); Enum en = new Enum(); if (result.config.forceNames || !rawEnumName.equals(enumName)) annotateActualName(en, rawEnumName); addParentNamespaceAnnotation(en, e.getParentNamespace()); if (!result.config.noComments) { en.importComments(e, "enum values", getFileCommentContent(e)); } en.setType(Enum.Type.Java); en.setTag(enumName.clone()); en.addModifiers(ModifierType.Public); out.addDeclaration(new TaggedTypeRefDeclaration(en)); Struct body = new Struct(); en.setBody(body); boolean hasValidItem = false; for (EnumItemResult er : results.values()) { if (er.errorElement != null) { out.addDeclaration(er.errorElement); continue; } String itemName = result.typeConverter.getValidJavaIdentifierString(ident(er.originalItem.getName())); Enum.EnumItem item = new Enum.EnumItem(itemName, er.convertedValue); en.addItem(item); hasValidItem = true; if (!result.config.noComments) { if (item != null) {// && hasEnumClass) { item.importComments(er.originalItem); } } } if (hasValidItem) { en.addInterface(ident(IntValuedEnum.class, expr(typeRef(enumName.clone())))); String valueArgName = "value"; body.addDeclaration(new Function(Type.JavaMethod, enumName.clone(), null, new Arg(valueArgName, typeRef(Long.TYPE))).setBody(block( stat(expr(memberRef(thisRef(), MemberRefStyle.Dot, valueArgName), AssignmentOperator.Equal, varRef(valueArgName)))))); body.addDeclaration(new VariablesDeclaration(typeRef(Long.TYPE), new DirectDeclarator(valueArgName)).addModifiers(ModifierType.Public, ModifierType.Final)); body.addDeclaration(new Function(Type.JavaMethod, ident(valueArgName), typeRef(Long.TYPE)).setBody(block( new Statement.Return(memberRef(thisRef(), MemberRefStyle.Dot, valueArgName)))).addModifiers(ModifierType.Public)); body.addDeclaration(new Function(Type.JavaMethod, ident("iterator"), typeRef(ident(Iterator.class, expr(typeRef(enumName.clone()))))).setBody(block( new Statement.Return( methodCall( methodCall( expr(typeRef(Collections.class)), MemberRefStyle.Dot, "singleton", thisRef()), MemberRefStyle.Dot, "iterator")))).addModifiers(ModifierType.Public)); body.addDeclaration(new Function(Type.JavaMethod, ident("fromValue"), typeRef(ident(IntValuedEnum.class, expr(typeRef(enumName.clone())))), new Arg(valueArgName, typeRef(Integer.TYPE))).setBody(block( new Statement.Return( methodCall( expr(typeRef(FlagSet.class)), MemberRefStyle.Dot, "fromValue", varRef(valueArgName), methodCall( "values"))))).addModifiers(ModifierType.Public, ModifierType.Static)); } } else { outputEnumItemsAsConstants(results, out, signatures, libraryClassName, hasEnumClass); } } void addCallingConventionAnnotation(Function originalFunction, ModifiableElement target) { Convention.Style cc = null; if (originalFunction.hasModifier(ModifierType.__stdcall)) { cc = Convention.Style.StdCall; } else if (originalFunction.hasModifier(ModifierType.__fastcall)) { cc = Convention.Style.FastCall; } else if (originalFunction.hasModifier(ModifierType.__thiscall)) { cc = Convention.Style.ThisCall; } else if (originalFunction.hasModifier(ModifierType.__pascal)) { cc = Convention.Style.Pascal; } if (cc != null) { target.addAnnotation(new Annotation(typeRef(Convention.class), enumRef(cc))); } } @Override public void convertFunction(Function function, Signatures signatures, boolean isCallback, DeclarationsHolder declarations, DeclarationsHolder implementations, Identifier libraryClassName, String sig, Identifier functionName, String library, int iConstructor) throws UnsupportedConversionException { assert implementations != null; boolean extractingDeclarations = declarations != null && declarations.resolveHolder() != implementations.resolveHolder(); Element parent = function.getParentElement(); MemberVisibility visibility = function.getVisibility(); boolean isPublic = visibility == MemberVisibility.Public || function.hasModifier(ModifierType.Public); boolean isPrivate = visibility == MemberVisibility.Private || function.hasModifier(ModifierType.Private); boolean isProtected = visibility == MemberVisibility.Protected || function.hasModifier(ModifierType.Protected); boolean isInStruct = parent instanceof Struct; if (isInStruct && result.config.skipPrivateMembers && (isPrivate || !isPublic && !isProtected)) { return; } boolean isStatic = function.hasModifier(ModifierType.Static); boolean isConstructor = iConstructor != -1; Function nativeMethod = new Function(Type.JavaMethod, ident(functionName), null); if (result.config.synchronizedMethods && !isCallback) { nativeMethod.addModifiers(ModifierType.Synchronized); } addCallingConventionAnnotation(function, nativeMethod); boolean annotatedActualName = false; if (function.getName() != null && !isCallback && (!functionName.toString().equals(function.getName().toString()) || result.config.forceNames)) { annotateActualName(nativeMethod, function.getName()); annotatedActualName = true; } Function rawMethod = nativeMethod.clone(); //Map<String, NL4JConversion> argTypes = new LinkedHashMap<String, NL4JConversion>(); boolean isObjectiveC = function.getType() == Type.ObjCMethod; Set<String> argNames = new TreeSet<String>(); List<Expression> superConstructorArgs = null; if (isConstructor) { superConstructorArgs = new ArrayList<Expression>(); superConstructorArgs.add(cast(typeRef(Void.class), nullExpr())); superConstructorArgs.add(expr(iConstructor)); } Identifier varArgType = null; String varArgName = null; NL4JConversion returnType = null; List<NL4JConversion> paramTypes = new ArrayList<NL4JConversion>(); List<String> paramNames = new ArrayList<String>(); if (!isConstructor) { returnType = ((BridJTypeConversion) result.typeConverter).convertTypeToNL4J(function.getValueType(), libraryClassName, null, null, -1, -1); } for (int iArg = 0, nArgs = function.getArgs().size(); iArg < nArgs; iArg++) { Arg arg = function.getArgs().get(iArg); String paramName; boolean isVarArgs = isVarArgs(arg); if (isVarArgs && iArg == nArgs - 1) { // assert arg.getValueType() == null; paramName = varArgName = chooseJavaArgName(arg.getName() == null ? "varargs" : arg.getName(), iArg + 1, argNames); varArgType = ident(isObjectiveC ? NSObject.class : Object.class); } else { TypeRef argType = arg.getValueType(); if (isVarArgs) { argType = new TypeRef.Pointer(typeRef(void.class), PointerStyle.Pointer); } paramName = chooseJavaArgName(arg.getName(), iArg + 1, argNames); paramTypes.add(((BridJTypeConversion) result.typeConverter).convertTypeToNL4J(argType, libraryClassName, null, null, -1, -1)); } paramNames.add(paramName); if (isConstructor) { superConstructorArgs.add(varRef(paramName)); } } fillIn(signatures, functionName, nativeMethod, returnType, paramTypes, paramNames, varArgType, varArgName, isCallback, false); List<Declaration> extractibleDecls = new ArrayList<Declaration>(); Block convertedBody = null; if (isConstructor) { convertedBody = block(stat(methodCall("super", superConstructorArgs.toArray(new Expression[superConstructorArgs.size()])))); } else if (result.config.convertBodies && function.getBody() != null) { try { Pair<Element, List<Declaration>> bodyAndExtraDeclarations = result.bridjer.convertToJava(function.getBody(), libraryClassName); convertedBody = (Block) bodyAndExtraDeclarations.getFirst(); for (Declaration d : bodyAndExtraDeclarations.getSecond()) { implementations.addDeclaration(d); // extractibleDecls.add(d); } } catch (Exception ex) { ex.printStackTrace(System.out); nativeMethod.addToCommentBefore("TRANSLATION OF BODY FAILED: " + ex); } } if (!result.config.noComments) { nativeMethod.importComments(function, isCallback ? null : getFileCommentContent(function)); } boolean generateStaticMethod = (isStatic || !isCallback && !isInStruct) && (declarations == null || implementations == declarations); nativeMethod.addModifiers( isProtected && !extractingDeclarations && !result.config.publicRawBindings ? ModifierType.Protected : ModifierType.Public, generateStaticMethod ? ModifierType.Static : null); boolean isOptional = isOptionalFunction(function.getName() + ""); implementations.addDeclaration(nativeMethod); boolean forwardedToRaw = false; if (convertedBody == null) { if (result.config.genRawBindings) {// && !isCallback) { // Function rawMethod = nativeMethod.clone(); rawMethod.setArgs(Collections.EMPTY_LIST); fillIn(signatures, functionName, rawMethod, returnType, paramTypes, paramNames, varArgType, varArgName, isCallback, true); rawMethod.addModifiers( extractingDeclarations || isCallback || result.config.publicRawBindings ? ModifierType.Public : ModifierType.Protected, isCallback ? ModifierType.Abstract : ModifierType.Native); if (generateStaticMethod) { rawMethod.addModifiers(ModifierType.Static); } if (!nativeMethod.computeSignature(SignatureType.ArgsAndRet).equals(rawMethod.computeSignature(SignatureType.ArgsAndRet))) { implementations.addDeclaration(rawMethod); extractibleDecls.add(rawMethod); if (isOptional && !isCallback) { rawMethod.addAnnotation(new Annotation(typeRef(Optional.class))); } List<Expression> objectToRawFollowedArgs = new ArrayList<Expression>(); List<Expression> rawToObjectFollowedArgs = new ArrayList<Expression>(); for (int i = 0, n = paramTypes.size(); i < n; i++) { NL4JConversion paramType = paramTypes.get(i); String paramName = paramNames.get(i); switch (paramType.type) { case Pointer: objectToRawFollowedArgs.add(createGetPeerExpression(varRef(paramName))); rawToObjectFollowedArgs.add(createPointerToAddressExpression(paramType, varRef(paramName))); break; case Enum: objectToRawFollowedArgs.add(createGetEnumValueExpression(varRef(paramName))); rawToObjectFollowedArgs.add(createEnumExpression(paramType, varRef(paramName))); break; // case NativeSize: // case NativeLong: // followedArg = methodCall(varRef(paramName), "longValue"); // break; default: objectToRawFollowedArgs.add(varRef(paramName)); rawToObjectFollowedArgs.add(varRef(paramName)); break; } } if (varArgType != null) { objectToRawFollowedArgs.add(varRef(varArgName)); rawToObjectFollowedArgs.add(varRef(varArgName)); } Expression objectToRawFollowedCall = methodCall(rawMethod.getName().toString(), objectToRawFollowedArgs.toArray(new Expression[objectToRawFollowedArgs.size()])); Expression rawToObjectFollowedCall = methodCall(nativeMethod.getName().toString(), rawToObjectFollowedArgs.toArray(new Expression[rawToObjectFollowedArgs.size()])); boolean isVoid = "void".equals(String.valueOf(nativeMethod.getValueType())); if (isVoid) { nativeMethod.setBody(block(stat(objectToRawFollowedCall))); if (isCallback) { rawMethod.removeModifiers(ModifierType.Abstract); rawMethod.setBody(block(stat(rawToObjectFollowedCall))); } } else { switch (returnType.type) { case Pointer: objectToRawFollowedCall = createPointerToAddressExpression(returnType, objectToRawFollowedCall); rawToObjectFollowedCall = createGetPeerExpression(rawToObjectFollowedCall); // if (returnType.isTypedPointer) { // objectToRawFollowedCall = new New(nativeMethod.getValueType(), objectToRawFollowedCall); // } else { // Expression ptrExpr = expr(typeRef(org.bridj.Pointer.class)); // Expression targetTypeExpr = result.typeConverter.typeLiteral(getSingleTypeParameter(nativeMethod.getValueType())); // if (targetTypeExpr == null || (returnType.targetTypeConversion != null && returnType.targetTypeConversion.type == ConvType.Void)) { // objectToRawFollowedCall = methodCall(ptrExpr, "pointerToAddress", objectToRawFollowedCall); // } else { // objectToRawFollowedCall = methodCall(ptrExpr, "pointerToAddress", objectToRawFollowedCall, targetTypeExpr); // } // } break; case Enum: objectToRawFollowedCall = createEnumExpression(returnType, objectToRawFollowedCall); rawToObjectFollowedCall = createGetEnumValueExpression(rawToObjectFollowedCall); // objectToRawFollowedCall = methodCall(expr(typeRef(org.bridj.FlagSet.class)), "fromValue", objectToRawFollowedCall, result.typeConverter.typeLiteral(getSingleTypeParameter(nativeMethod.getValueType()))); break; case NativeLong: case NativeSize: if (!rawMethod.getValueType().toString().equals("long")) { objectToRawFollowedCall = new New(nativeMethod.getValueType().clone(), objectToRawFollowedCall); rawToObjectFollowedCall = createGetIntegralValueExpression(rawToObjectFollowedCall); } default: break; } nativeMethod.setBody(block(new Statement.Return(objectToRawFollowedCall))); if (isCallback) { rawMethod.removeModifiers(ModifierType.Abstract); rawMethod.setBody(block(new Statement.Return(rawToObjectFollowedCall))); } else if (function.getName() != null && !function.getName().equals(rawMethod.getName()) && !annotatedActualName) { annotateActualName(rawMethod, function.getName()); annotatedActualName = true; } } forwardedToRaw = true; } } if (!forwardedToRaw) { nativeMethod.addModifiers(isCallback ? ModifierType.Abstract : ModifierType.Native); if (isOptional && !isCallback) { nativeMethod.addAnnotation(new Annotation(typeRef(Optional.class))); } } } else { nativeMethod.setBody(convertedBody); } if (!forwardedToRaw && convertedBody == null) { extractibleDecls.add(nativeMethod); } for (Declaration d : extractibleDecls) { if (extractingDeclarations) { if (d instanceof Function) { Function m = (Function)d.clone(); m.setBody(null); m.removeModifiers(ModifierType.Abstract, ModifierType.Final, ModifierType.Static, ModifierType.Native, ModifierType.Public, ModifierType.Protected); declarations.addDeclaration(m); // d.addAnnotation(new Annotation(typeRef(Override.class))); } } } } private Expression createGetEnumValueExpression(Expression value) { return cast(typeRef(int.class), methodCall(value, "value")); } private Expression createGetIntegralValueExpression(Expression value) { return methodCall(value, "longValue"); } private Expression createGetPeerExpression(Expression value) { return methodCall(expr(typeRef(org.bridj.Pointer.class)), "getPeer", value); } private Expression createPointerToAddressExpression(NL4JConversion pointerType, Expression value) { TypeRef tr = pointerType.getTypeRef(false); if (pointerType.isTypedPointer) { return new New(tr, value); } else { Expression ptrExpr = expr(typeRef(org.bridj.Pointer.class)); Expression targetTypeExpr = result.typeConverter.typeLiteral(getSingleTypeParameter(tr)); if (targetTypeExpr == null || (pointerType.targetTypeConversion != null && pointerType.targetTypeConversion.type == ConvType.Void)) { return methodCall(ptrExpr, "pointerToAddress", value); } else { // Cast to unparameterized Pointer to avoid weird cast cases return cast(typeRef(org.bridj.Pointer.class), methodCall(ptrExpr, "pointerToAddress", value, targetTypeExpr)); } } } private Expression createEnumExpression(NL4JConversion enumType, Expression value) { TypeRef tr = enumType.getTypeRef(false); return methodCall(expr(typeRef(org.bridj.FlagSet.class)), "fromValue", value, result.typeConverter.typeLiteral(getSingleTypeParameter(tr))); } private void fillIn(Signatures signatures, Identifier functionName, Function nativeMethod, NL4JConversion returnType, List<NL4JConversion> paramTypes, List<String> paramNames, Identifier varArgType, String varArgName, boolean isCallback, boolean useRawTypes) { for (int i = 0, n = paramTypes.size(); i < n; i++) { NL4JConversion paramType = paramTypes.get(i); String paramName = paramNames.get(i); nativeMethod.addArg(paramType.annotateTypedType(new Arg(paramName, paramType.getTypeRef(useRawTypes)), useRawTypes));//.getTypedTypeRef()))); } if (varArgType != null) { nativeMethod.addArg(new Arg(varArgName, typeRef(varArgType.clone()))).setVarArg(true); } if (returnType != null) { returnType.annotateTypedType(nativeMethod, useRawTypes); nativeMethod.setValueType(returnType.getTypeRef(useRawTypes)); } String natSig = nativeMethod.computeSignature(SignatureType.JavaStyle); Identifier javaMethodName = signatures == null ? functionName : signatures.findNextMethodName(natSig, functionName); if (!javaMethodName.equals(functionName)) { nativeMethod.setName(javaMethodName); } } @Override public Struct convertStruct(Struct struct, Signatures signatures, Identifier callerLibraryClass, String callerLibrary, boolean onlyFields) throws IOException { Identifier structName = getActualTaggedTypeName(struct); if (structName == null) { return null; } //if (structName.toString().contains("MonoObject")) // structName.toString(); if (struct.isForwardDeclaration())// && !result.structsByName.get(structName).isForwardDeclaration()) { return null; } if (!signatures.addClass(structName)) { return null; } boolean isUnion = struct.getType() == Struct.Type.CUnion; boolean inheritsFromStruct = false; Identifier baseClass = null; int parentFieldsCount = 0; List<String> preComments = new ArrayList<String>(); for (SimpleTypeRef parentName : struct.getParents()) { Struct parent = result.structsByName.get(parentName.getName()); if (parent == null) { // TODO report error continue; } try { parentFieldsCount += countFieldsInStruct(parent); } catch (UnsupportedConversionException ex) { preComments.add("Error: " + ex); } baseClass = result.typeConverter.getTaggedTypeIdentifierInJava(parent); if (baseClass != null) { inheritsFromStruct = true; break; // TODO handle multiple and virtual inheritage } } boolean hasMemberFunctions = false; for (Declaration d : struct.getDeclarations()) { if (d instanceof Function) { hasMemberFunctions = true; break; } } Constant uuid = (Constant) struct.getModifierValue(ModifierType.UUID); if (baseClass == null) { switch (struct.getType()) { case CStruct: case CUnion: if (!hasMemberFunctions) { baseClass = ident(StructObject.class); break; } case CPPClass: baseClass = ident(uuid == null ? CPPObject.class : IUnknown.class); result.hasCPlusPlus = true; break; default: throw new UnsupportedOperationException(); } } Struct structJavaClass = publicStaticClass(structName, baseClass, Struct.Type.JavaClass, struct); if (!result.config.noStaticInit) structJavaClass.addDeclaration(newStaticInit()); //if (result.config.microsoftCOM) { if (uuid != null) { structJavaClass.addAnnotation(new Annotation(result.config.runtime.typeRef(JNAeratorConfig.Runtime.Ann.IID), uuid)); } structJavaClass.setResolvedJavaIdentifier(ident(structName)); if (result.config.forceNames) annotateActualName(structJavaClass, structName); addParentNamespaceAnnotation(structJavaClass, struct.getParentNamespace()); structJavaClass.addToCommentBefore(preComments); //System.out.println("parentFieldsCount(structName = " + structName + ") = " + parentFieldsCount); final int iChild[] = new int[]{parentFieldsCount}; //cl.addDeclaration(new EmptyDeclaration()) Signatures childSignatures = new Signatures(); /*if (isVirtual(struct) && !onlyFields) { String vptrName = DEFAULT_VPTR_NAME; VariablesDeclaration vptr = new VariablesDeclaration(typeRef(VirtualTablePointer.class), new Declarator.DirectDeclarator(vptrName)); vptr.addModifiers(ModifierType.Public); structJavaClass.addDeclaration(vptr); childSignatures.variablesSignatures.add(vptrName); // TODO add vptr grabber to constructor ! }*/ // private static StructIO<MyStruct> io = StructIO.getInstance(MyStruct.class); if (isUnion) { structJavaClass.addAnnotation(new Annotation(result.config.runtime.typeRef(JNAeratorConfig.Runtime.Ann.Union))); } int iVirtual = 0, iConstructor = 0; //List<Declaration> children = new ArrayList<Declaration>(); boolean succeeded = true; for (Declaration d : struct.getDeclarations()) { //if (isUnion) // iChild[0] = 0; if (d instanceof VariablesDeclaration) { succeeded = convertVariablesDeclaration((VariablesDeclaration) d, childSignatures, structJavaClass, iChild, false, structName, callerLibraryClass, callerLibrary) && succeeded; } else if (!onlyFields) { if (d instanceof TaggedTypeRefDeclaration) { TaggedTypeRef tr = ((TaggedTypeRefDeclaration) d).getTaggedTypeRef(); if (tr instanceof Struct) { outputConvertedStruct((Struct) tr, childSignatures, structJavaClass, callerLibrary, false); } else if (tr instanceof Enum) { convertEnum((Enum) tr, childSignatures, structJavaClass, callerLibraryClass); } } else if (d instanceof TypeDef) { TypeDef td = (TypeDef) d; TypeRef tr = td.getValueType(); if (tr instanceof Struct) { outputConvertedStruct((Struct) tr, childSignatures, structJavaClass, callerLibrary, false); } else { FunctionSignature fs = null; if (tr instanceof FunctionSignature) { fs = (FunctionSignature) tr; } else if (tr instanceof TypeRef.Pointer) { TypeRef target = ((TypeRef.Pointer) tr).getTarget(); if (target instanceof FunctionSignature) { fs = (FunctionSignature) target; } } if (fs != null) { convertCallback(fs, childSignatures, structJavaClass, callerLibraryClass); } } } else if (result.config.genCPlusPlus && d instanceof Function) { Function f = (Function) d; boolean isVirtual = f.hasModifier(ModifierType.Virtual); boolean isConstructor = f.getName().equals(structName) && (f.getValueType() == null || f.getValueType().toString().equals("void")); if (isConstructor && f.getArgs().isEmpty()) { continue; // default constructor was already generated } String library = result.getLibrary(struct); if (library == null) { continue; } List<Declaration> decls = new ArrayList<Declaration>(); DeclarationsHolder out = new ListWrapper(decls); convertFunction(f, childSignatures, false, out, out, callerLibraryClass, isConstructor ? iConstructor : -1); for (Declaration md : decls) { if (!(md instanceof Function)) { continue; } Function method = (Function) md; boolean commentOut = false; if (isVirtual) { method.addAnnotation(new Annotation(result.config.runtime.typeRef(JNAeratorConfig.Runtime.Ann.Virtual), expr(iVirtual))); } else if (method.getValueType() == null) { method.addAnnotation(new Annotation(result.config.runtime.typeRef(JNAeratorConfig.Runtime.Ann.Constructor), expr(iConstructor))); isConstructor = true; } if (method.getName().toString().equals("operator")) { commentOut = true; } if (commentOut) { structJavaClass.addDeclaration(new EmptyDeclaration(method.toString())); } else { structJavaClass.addDeclaration(method); } } if (isVirtual) { iVirtual++; } if (isConstructor) { iConstructor++; } } } } if (succeeded) { Function defaultConstructor = new Function(Type.JavaMethod, ident(structName), null).setBody(block(stat(methodCall("super")))).addModifiers(ModifierType.Public); if (childSignatures.addMethod(defaultConstructor)) { structJavaClass.addDeclaration(defaultConstructor); } String ptrName = "pointer"; Function castConstructor = new Function(Type.JavaMethod, ident(structName), null, new Arg(ptrName, typeRef(result.config.runtime.pointerClass))).setBody(block(stat(methodCall("super", varRef(ptrName))))).addModifiers(ModifierType.Public); if (childSignatures.addMethod(castConstructor)) { structJavaClass.addDeclaration(castConstructor); } } else { structJavaClass.addModifiers(ModifierType.Abstract); } return structJavaClass; } Map<Identifier, Boolean> structsVirtuality = new LinkedHashMap<Identifier, Boolean>(); public boolean isVirtual(Struct struct) { Identifier name = getActualTaggedTypeName(struct); Boolean bVirtual = structsVirtuality.get(name); if (bVirtual == null) { boolean hasVirtualParent = false, hasVirtualMembers = false; for (SimpleTypeRef parentName : struct.getParents()) { Struct parentStruct = result.structsByName.get(parentName.getName()); if (parentStruct == null) { if (result.config.verbose) { System.out.println("Failed to resolve parent '" + parentName + "' for struct '" + name + "'"); } continue; } if (isVirtual(parentStruct)) { hasVirtualParent = true; break; } } for (Declaration mb : struct.getDeclarations()) { if (mb.hasModifier(ModifierType.Virtual)) { hasVirtualMembers = true; break; } } bVirtual = hasVirtualMembers && !hasVirtualParent; structsVirtuality.put(name, bVirtual); } return bVirtual; } protected String ioVarName = "io", ioStaticVarName = "IO"; public List<Declaration> convertVariablesDeclarationToBridJ(String name, TypeRef mutatedType, int[] iChild, int bits, boolean isGlobal, Identifier holderName, Identifier callerLibraryName, String callerLibrary, Element... toImportDetailsFrom) throws UnsupportedConversionException { name = result.typeConverter.getValidJavaArgumentName(ident(name)).toString(); //convertVariablesDeclaration(name, mutatedType, out, iChild, callerLibraryName); final boolean useRawTypes = false; //Expression initVal = null; int fieldIndex = iChild[0]; //convertTypeToNL4J(TypeRef valueType, Identifier libraryClassName, Expression structPeerExpr, Expression structIOExpr, Expression valueExpr, int fieldIndex, int bits) throws UnsupportedConversionException { NL4JConversion conv = ((BridJTypeConversion) result.typeConverter).convertTypeToNL4J( mutatedType, callerLibraryName, thisField("io"), varRef(name), fieldIndex, bits); if (conv == null) { throw new UnsupportedConversionException(mutatedType, "failed to convert type to Java"); } else if (conv.isUndefined) { throw new UnsupportedConversionException(mutatedType, "failed to convert type to Java (undefined type)"); } else if ("void".equals(String.valueOf(conv.getTypeRef(useRawTypes)))) { throw new UnsupportedConversionException(mutatedType, "void type !"); //out.add(new EmptyDeclaration("SKIPPED:", v.formatComments("", true, true, false), v.toString())); } Function convDecl = new Function(); conv.annotateTypedType(convDecl, useRawTypes); convDecl.setType(Type.JavaMethod); convDecl.addModifiers(ModifierType.Public); if (conv.arrayLengths != null) { convDecl.addAnnotation(new Annotation(result.config.runtime.typeRef(JNAeratorConfig.Runtime.Ann.Length), new OpaqueExpression("{" + StringUtils.implode(conv.arrayLengths, ", ") + "}"))); } if (conv.bits != null) { convDecl.addAnnotation(new Annotation(result.config.runtime.typeRef(JNAeratorConfig.Runtime.Ann.Bits), conv.bits)); } if (conv.byValue) { convDecl.addAnnotation(new Annotation(result.config.runtime.typeRef(JNAeratorConfig.Runtime.Ann.ByValue))); } for (Element e : toImportDetailsFrom) { convDecl.importDetails(e, false); } convDecl.importDetails(mutatedType, true); //convDecl.importDetails(javaType, true); // convDecl.importDetails(v, false); // convDecl.importDetails(vs, false); // convDecl.importDetails(valueType, false); // valueType.stripDetails(); convDecl.moveAllCommentsBefore(); convDecl.deDioxygenizeCommentBefore(); convDecl.setName(ident(name)); if (!isGlobal) { convDecl.addAnnotation(new Annotation(result.config.runtime.typeRef(JNAeratorConfig.Runtime.Ann.Field), expr(fieldIndex))); } convDecl.setValueType(conv.getTypeRef(useRawTypes)); TypeRef javaType = convDecl.getValueType(); String pointerGetSetMethodSuffix = StringUtils.capitalize(javaType.toString()); List<Declaration> out = new ArrayList<Declaration>(); boolean addedGetterOrSetter = false; Expression getGlobalPointerExpr = methodCall(methodCall(expr(typeRef(BridJ.class)), "getNativeLibrary", expr(callerLibrary)), "getSymbolPointer", expr(name)); String getterName = "get"; String setterName = "set"; if (isGlobal) { if (conv.pointerGetterName != null && conv.pointerSetterName != null) { getterName = conv.pointerGetterName; setterName = conv.pointerSetterName; } else { // if (conv.type == ConvType.NativeLong || conv.cLong) { // getterName = "getCLong"; // setterName = "setCLong"; // } else if (conv.type == ConvType.NativeSize || conv.nativeSize) { // getterName = "getSizeT"; // setterName = "setSizeT"; // } else { getGlobalPointerExpr = methodCall(getGlobalPointerExpr, "as", result.typeConverter.typeLiteral(javaType.clone())); } } if (conv.getFieldExpr != null) { Function getter = convDecl.clone(); if (isGlobal) { getter.setBody(block( tryRethrow(new Statement.Return(cast(javaType.clone(), methodCall(getGlobalPointerExpr, getterName)))))); } else { getter.setBody(block( new Statement.Return(conv.getFieldExpr))); } out.add(getter); addedGetterOrSetter = true; } if (!conv.readOnly && conv.setFieldExpr != null) { Function setter = convDecl.clone(); setter.setValueType(typeRef(holderName.clone()));//Void.TYPE)); setter.addArg(new Arg(name, javaType)); //setter.addModifiers(ModifierType.Native); if (isGlobal) { setter.setBody(block( tryRethrow(block( stat(methodCall(getGlobalPointerExpr, setterName, varRef(name))), new Statement.Return(thisRef()))))); } else { setter.setBody(block( stat(conv.setFieldExpr), new Statement.Return(thisRef()))); } out.add(setter); addedGetterOrSetter = true; if (result.config.scalaStructSetters) { setter = new Function(); setter.setType(Type.JavaMethod); setter.setName(ident(name + "_$eq")); setter.setValueType(javaType.clone()); setter.addArg(new Arg(name, javaType.clone())); setter.addModifiers(ModifierType.Public, ModifierType.Final); setter.setBody(block( stat(methodCall(name, varRef(name))), new Statement.Return(varRef(name)))); out.add(setter); } } if (!addedGetterOrSetter) { out.add(new EmptyDeclaration("Failed to convert value " + name + " of type " + mutatedType)); } return out; } @Override public boolean convertVariablesDeclaration(VariablesDeclaration v, Signatures signatures, DeclarationsHolder out, int[] iChild, boolean isGlobal, Identifier holderName, Identifier callerLibraryClass, String callerLibrary) { try { TypeRef valueType = v.getValueType(); for (Declarator vs : v.getDeclarators()) { if (vs.getDefaultValue() != null) { continue; } String name = vs.resolveName(); if (name == null || name.length() == 0) { name = "anonymous" + (nextAnonymousFieldId++); } TypeRef mutatedType = valueType; if (!(vs instanceof DirectDeclarator)) { mutatedType = (TypeRef) vs.mutateTypeKeepingParent(valueType); vs = new DirectDeclarator(vs.resolveName()); } //Declarator d = v.getDeclarators().get(0); List<Declaration> vds = convertVariablesDeclarationToBridJ(name, mutatedType, iChild, vs.getBits(), isGlobal, holderName, callerLibraryClass, callerLibrary, v, vs); if (vs.getBits() > 0) { for (Declaration vd : vds) { vd.addAnnotation(new Annotation(result.config.runtime.typeRef(JNAeratorConfig.Runtime.Ann.Bits), expr(vs.getBits()))); } } for (Declaration vd : vds) { if (vd instanceof Function) { if (!signatures.addMethod((Function) vd)) { continue; } } vd.importDetails(mutatedType, true); vd.moveAllCommentsBefore(); if (!(mutatedType instanceof Primitive) && !result.config.noComments) { vd.addToCommentBefore("C type : " + mutatedType); } out.addDeclaration(vd); } //} } return true; } catch (Throwable e) { if (!(e instanceof UnsupportedConversionException)) { e.printStackTrace(); } // if (!result.config.limitComments) out.addDeclaration(new EmptyDeclaration(e.toString())); return false; } finally { iChild[0]++; } } int nextAnonymousFieldId; @Override protected void configureCallbackStruct(Struct callbackStruct) { callbackStruct.setType(Struct.Type.JavaClass); callbackStruct.addModifiers(ModifierType.Public, ModifierType.Static, ModifierType.Abstract); } @Override protected Struct createFakePointerClass(Identifier fakePointer) { Struct ptClass = result.declarationsConverter.publicStaticClass(fakePointer, ident(TypedPointer.class), Struct.Type.JavaClass, null); String addressVarName = "address"; ptClass.addDeclaration(new Function(Function.Type.JavaMethod, fakePointer, null, new Arg(addressVarName, typeRef(long.class))).addModifiers(ModifierType.Public).setBody( block(stat(methodCall("super", varRef(addressVarName)))))); ptClass.addDeclaration(new Function(Function.Type.JavaMethod, fakePointer, null, new Arg(addressVarName, typeRef(org.bridj.Pointer.class))).addModifiers(ModifierType.Public).setBody( block(stat(methodCall("super", varRef(addressVarName)))))); return ptClass; } @Override protected void fillLibraryMapping(Result result, SourceFiles sourceFiles, DeclarationsHolder declarations, DeclarationsHolder implementations, String library, Identifier javaPackage, Expression nativeLibFieldExpr) throws IOException { super.fillLibraryMapping(result, sourceFiles, declarations, implementations, library, javaPackage, nativeLibFieldExpr); if (implementations instanceof ModifiableElement) { ModifiableElement minterf = (ModifiableElement) implementations; List<String> deps = result.config.dependencies.get(library); Map<String, Expression> namedArguments = null; if (deps != null) { List<Expression> depExprs = new ArrayList<Expression>(); for (String dep : deps) depExprs.add(expr(dep)); namedArguments = ImmutableMap.<String, Expression>of( "dependencies", NewArray.newAnnotationArrayValue(depExprs)); } minterf.addAnnotation(new Annotation(typeRef(org.bridj.ann.Library.class), expr(library), namedArguments)); minterf.addAnnotation(new Annotation(typeRef(org.bridj.ann.Runtime.class), classLiteral(result.hasCPlusPlus ? CPPRuntime.class : CRuntime.class))); } } @Override public void generateLibraryFiles(SourceFiles sourceFiles, Result result, JNAeratorConfig config) throws IOException { for (String library : result.libraries) { if (library == null) { continue; // to handle code defined in macro-expanded expressions } Identifier javaPackage = result.javaPackageByLibrary.get(library); Identifier implementationsSimpleClassName = result.getLibraryClassSimpleName(library); Identifier declarationsSimpleClassName = result.getLibraryDeclarationsClassSimpleName(library); Identifier implementationsFullClassName = result.getLibraryClassFullName(library);//ident(javaPackage, libraryClassName); Identifier declarationsFullClassName = result.getLibraryDeclarationsClassFullName(library); //if (!result.objCClasses.isEmpty()) // out.println("import org.rococoa.ID;"); Struct implementations = new Struct(); implementations.setType(Struct.Type.JavaClass); implementations.addToCommentBefore("Wrapper for library <b>" + library + "</b>", result.declarationsConverter.getFileCommentContent(result.config.libraryProjectSources.get(library), null)); implementations.addModifiers(ModifierType.Public); implementations.setTag(implementationsSimpleClassName); implementations.addParent(ident(config.runtime.libraryClass, expr(typeRef(implementationsSimpleClassName)))); if (declarationsFullClassName != null) { implementations.addProtocol(declarationsFullClassName.clone()); } if (!config.noStaticInit) implementations.addDeclaration(newStaticInit()); implementations.setResolvedJavaIdentifier(implementationsFullClassName); Struct declarations; if (declarationsFullClassName != null) { declarations = new Struct(); declarations.setType(Struct.Type.JavaInterface); declarations.addToCommentBefore("Interface for library <b>" + library + "</b>", result.declarationsConverter.getFileCommentContent(result.config.libraryProjectSources.get(library), null)); declarations.addModifiers(ModifierType.Public); declarations.setTag(declarationsSimpleClassName.clone()); declarations.setResolvedJavaIdentifier(declarationsFullClassName); } else { declarations = implementations; } // String libFileOrDirArgName = "libraryFileOrDirectory"; // Function constr = new Function(Function.Type.JavaMethod, fullLibraryClassName.resolveLastSimpleIdentifier().clone(), null, new Arg(libFileOrDirArgName, typeRef(File.class))); // constr.addModifiers(ModifierType.Public); // constr.setBody(block(stat(methodCall("super", varRef(libFileOrDirArgName))))); // interf.addDeclaration(constr); // // constr = new Function(Function.Type.JavaMethod, fullLibraryClassName.resolveLastSimpleIdentifier().clone(), null); // constr.addModifiers(ModifierType.Public); // constr.addThrown(typeRef(FileNotFoundException.class)); // constr.setBody(block(stat(methodCall("super", classLiteral(typeRef(fullLibraryClassName.clone())))))); // interf.addDeclaration(constr); fillLibraryMapping(result, sourceFiles, declarations, implementations, library, javaPackage, varRef("this")); writeLibraryInterface(result, sourceFiles, declarations, library, javaPackage); if (declarations != implementations) { writeLibraryInterface(result, sourceFiles, implementations, library, javaPackage); } } } private Function newStaticInit() { Function f = new Function(Function.Type.StaticInit, null, null).setBody(block( stat(methodCall( expr(typeRef(BridJ.class)), Expression.MemberRefStyle.Dot, "register")))).addModifiers(ModifierType.Static); return f; } private void addParentNamespaceAnnotation(ModifiableElement dest, Identifier parentNamespace) { if (parentNamespace != null) { dest.addAnnotation(new Annotation(typeRef(org.bridj.ann.Namespace.class), expr(parentNamespace.toString()))); } } }