/* 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 java.io.IOException; import java.lang.reflect.Method; import java.util.List; import java.util.regex.Pattern; import org.rococoa.AlreadyRetained; import org.rococoa.cocoa.foundation.NSObject; import com.ochafik.lang.jnaerator.JNAeratorConfig.GenFeatures; 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.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.CompoundCollection; import com.nativelibs4java.jalico.Pair; import com.ochafik.util.string.StringUtils; import java.net.URL; import java.net.URLConnection; import java.text.MessageFormat; import static com.ochafik.lang.jnaerator.parser.ElementsHelper.*; import static com.ochafik.lang.jnaerator.TypeConversion.*; import com.ochafik.lang.jnaerator.parser.Function.SignatureType; import com.ochafik.lang.jnaerator.parser.Identifier.SimpleIdentifier; import com.ochafik.lang.jnaerator.runtime.LibraryExtractor; import com.ochafik.lang.jnaerator.runtime.MangledFunctionMapper; import com.sun.jna.Native; import com.sun.jna.NativeLibrary; import com.sun.jna.PointerType; import com.sun.jna.win32.StdCallLibrary; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; public class JNADeclarationsConverter extends DeclarationsConverter { private static final Pattern manglingCommentPattern = Pattern.compile("@mangling (.*)$", Pattern.MULTILINE); public JNADeclarationsConverter(Result result) { super(result); } final JNATypeConversion typeConverter() { return (JNATypeConversion) result.typeConverter; } ; //protected abstract SimpleTypeRef getCallbackType(FunctionSignature functionSignature, Identifier name); @Override public Struct convertCallback(FunctionSignature functionSignature, Signatures signatures, Identifier callerLibraryName) { Struct decl = super.convertCallback(functionSignature, signatures, callerLibraryName); if (decl != null) { List<Modifier> mods = functionSignature.getFunction().getModifiers(); decl.setParents(Arrays.asList((SimpleTypeRef) typeRef( functionSignature.getFunction().hasModifier(ModifierType.__stdcall) ? StdCallLibrary.StdCallCallback.class : result.config.runtime.callbackClass))); } return decl; } static Map<Class<?>, Pair<List<Pair<Function, String>>, Set<String>>> cachedForcedMethodsAndTheirSignatures; public static synchronized Pair<List<Pair<Function, String>>, Set<String>> getMethodsAndTheirSignatures(Class<?> originalLib) { if (cachedForcedMethodsAndTheirSignatures == null) { cachedForcedMethodsAndTheirSignatures = new LinkedHashMap<Class<?>, Pair<List<Pair<Function, String>>, Set<String>>>(); } Pair<List<Pair<Function, String>>, Set<String>> pair = cachedForcedMethodsAndTheirSignatures.get(originalLib); if (pair == null) { pair = new Pair<List<Pair<Function, String>>, Set<String>>(new ArrayList<Pair<Function, String>>(), new HashSet<String>()); for (Method m : originalLib.getDeclaredMethods()) { Function f = Function.fromMethod(m); String sig = f.computeSignature(SignatureType.JavaStyle); //if (m.getDeclaringClass().equals(NSObject.class) && f.getName().equals("as")) { // Declaration //} pair.getFirst().add(new Pair<Function, String>(f, sig)); pair.getSecond().add(sig); } } return pair; } public void addMissingMethods(Class<?> originalLib, Signatures existingSignatures, Struct outputLib) { for (Pair<Function, String> f : getMethodsAndTheirSignatures(originalLib).getFirst()) { if (existingSignatures.addMethod(f.getSecond())) { outputLib.addDeclaration(f.getFirst().clone()); } } } public EmptyDeclaration skipDeclaration(Element e, String... preMessages) { if (result.config.limitComments) { return null; } List<String> mess = new ArrayList<String>(); if (preMessages != null) { mess.addAll(Arrays.asList(preMessages)); } mess.addAll(Arrays.asList("SKIPPED:", new Printer(null).formatComments(e, true, true, false).toString(), getFileCommentContent(e), e.toString().replace("*/", "* /"))); return new EmptyDeclaration(mess.toArray(new String[0])); } 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) { Identifier enumName = result.typeConverter.getValidJavaIdentifier(rawEnumName); if (!signatures.addClass(enumName)) { return; } hasEnumClass = true; Struct struct = publicStaticClass(enumName, null, Struct.Type.JavaInterface, e); out.addDeclaration(new TaggedTypeRefDeclaration(struct)); if (!result.config.noComments) { struct.addToCommentBefore("enum values"); } out = struct; signatures = new Signatures(); } outputEnumItemsAsConstants(results, out, signatures, libraryClassName, hasEnumClass); } Map<String, Pair<Function, List<Function>>> functionAlternativesByNativeSignature = new LinkedHashMap<String, Pair<Function, List<Function>>>(); @Override protected void convertFunction(Function function, Signatures signatures, boolean isCallback, DeclarationsHolder declarations, DeclarationsHolder implementations, Identifier libraryClassName, String sig, Identifier functionName, String library, int iConstructor) { assert implementations == declarations || declarations == null; Pair<Function, List<Function>> alternativesPair = functionAlternativesByNativeSignature.get(sig); if (alternativesPair != null) { if (result.config.choicesInputFile != null) { for (Function alt : alternativesPair.getValue()) { implementations.addDeclaration(alt.clone()); } return; } } else { functionAlternativesByNativeSignature.put( sig, alternativesPair = new Pair<Function, List<Function>>( cleanClone(function), new ArrayList<Function>())); } List<Function> alternatives = alternativesPair.getValue(); Function natFunc = new Function(); Element parent = function.getParentElement(); boolean isMethod = parent instanceof Struct; if (isMethod) { switch (((Struct) parent).getType()) { case ObjCClass: case ObjCProtocol: break; } } if (!isMethod && library != null) { Boolean alreadyRetained = Result.getMap(result.retainedRetValFunctions, library).get(functionName.toString()); if (alreadyRetained != null && alreadyRetained) { natFunc.addAnnotation(new Annotation(typeRef(AlreadyRetained.class), expr(alreadyRetained))); } } //String namespaceArrayStr = "{\"" + StringUtils.implode(ns, "\", \"") + "\"}"; //if (!ns.isEmpty()) // natFunc.addAnnotation(new Annotation(Namespace.class, "(value=" + namespaceArrayStr + (isMethod ? ", isClass=true" : "") + ")")); boolean isObjectiveC = function.getType() == Type.ObjCMethod; natFunc.setType(Function.Type.JavaMethod); if (result.config.synchronizedMethods && !isCallback && result.config.useJNADirectCalls) { natFunc.addModifiers(ModifierType.Synchronized); } if (result.config.useJNADirectCalls && !isCallback && !isObjectiveC) { natFunc.addModifiers(ModifierType.Public, ModifierType.Static, ModifierType.Native); } try { //StringBuilder outPrefix = new StringBuilder(); TypeRef returnType = null; if (!isObjectiveC) { returnType = function.getValueType(); if (returnType == null) { returnType = new TypeRef.Primitive("int"); } if (returnType != null) { returnType.addModifiers(function.getModifiers()); } } else { returnType = RococoaUtils.fixReturnType(function); functionName = ident(RococoaUtils.getMethodName(function)); } Identifier modifiedMethodName; if (isCallback) { modifiedMethodName = ident(result.config.callbackInvokeMethodName); } else { modifiedMethodName = result.typeConverter.getValidJavaMethodName(ident(functionName)); } Set<String> names = new LinkedHashSet<String>(); //if (ns.isEmpty()) if (function.getName() != null && !modifiedMethodName.equals(function.getName().toString())) { names.add(function.getName().toString()); } if (function.getAsmName() != null) { names.add(function.getAsmName()); } if (!isCallback && !modifiedMethodName.equals(functionName)) { annotateActualName(natFunc, functionName); } natFunc.setName(modifiedMethodName); natFunc.setValueType(typeConverter().convertTypeToJNA(returnType, TypeConversionMode.ReturnType, libraryClassName)); if (!result.config.noComments) { natFunc.importComments(function, isCallback ? null : getFileCommentContent(function)); } if (function.getName() != null) { Object[] name = new Object[]{function.getName().toString()}; for (Pair<MessageFormat, MessageFormat> mf : result.config.onlineDocumentationURLFormats) { try { MessageFormat urlFormat = mf.getSecond(); URL url = new URL(urlFormat.format(name)); URLConnection con = url.openConnection(); con.getInputStream().close(); MessageFormat displayFormat = mf.getFirst(); natFunc.addToCommentBefore("@see <a href=\"" + url + "\">" + displayFormat.format(name) + "</a>"); break; } catch (Exception ex) { //ex.printStackTrace(); } } } boolean alternativeOutputs = !isCallback; Function primOrBufFunc = alternativeOutputs ? natFunc.clone() : null; Function natStructFunc = alternativeOutputs ? natFunc.clone() : null; Set<String> argNames = new TreeSet<String>(); for (int iArg = 0, nArgs = function.getArgs().size(); iArg < nArgs; iArg++) { Arg arg = function.getArgs().get(iArg); boolean isVarArgs = isVarArgs(arg); if (isVarArgs && iArg == nArgs - 1) { //TODO choose vaname dynamically ! Identifier vaType = ident(isObjectiveC ? NSObject.class : Object.class); String argName = chooseJavaArgName(arg.getName() == null ? "varargs" : arg.getName(), iArg + 1, argNames); natFunc.addArg(new Arg(argName, typeRef(vaType.clone()))).setVarArg(true); if (alternativeOutputs) { primOrBufFunc.addArg(new Arg(argName, typeRef(vaType.clone()))).setVarArg(true); natStructFunc.addArg(new Arg(argName, typeRef(vaType.clone()))).setVarArg(true); } } else { String argName = chooseJavaArgName(arg.getName(), iArg + 1, argNames); TypeRef mutType = arg.createMutatedType(); if (mutType == null) { throw new UnsupportedConversionException(function, "Argument " + arg.getName() + " cannot be converted"); } if (isVarArgs) { mutType = new TypeRef.Pointer(typeRef(void.class), PointerStyle.Pointer); } natFunc.addArg(new Arg(argName, typeConverter().convertTypeToJNA(mutType, TypeConversionMode.NativeParameter, libraryClassName))); if (alternativeOutputs) { primOrBufFunc.addArg(new Arg(argName, typeConverter().convertTypeToJNA(mutType, TypeConversionMode.PrimitiveOrBufferParameter, libraryClassName))); natStructFunc.addArg(new Arg(argName, typeConverter().convertTypeToJNA(mutType, TypeConversionMode.NativeParameterWithStructsPtrPtrs, libraryClassName))); } } } String natSign = natFunc.computeSignature(SignatureType.JavaStyle), primOrBufSign = alternativeOutputs ? primOrBufFunc.computeSignature(SignatureType.JavaStyle) : null, bufSign = alternativeOutputs ? natStructFunc.computeSignature(SignatureType.JavaStyle) : null; if (signatures == null || signatures.addMethod(natSign)) { boolean isDeprecated = alternativeOutputs && !primOrBufSign.equals(natSign); if (!(isDeprecated && result.config.skipDeprecated)) { if (isDeprecated) { if (!result.config.noComments) { if (primOrBufSign.equals(bufSign)) { natFunc.addToCommentBefore(Arrays.asList("@deprecated use the safer method {@link #" + primOrBufSign + "} instead")); } else { natFunc.addToCommentBefore(Arrays.asList("@deprecated use the safer methods {@link #" + primOrBufSign + "} and {@link #" + bufSign + "} instead")); } } natFunc.addAnnotation(new Annotation(typeRef(Deprecated.class))); } collectParamComments(natFunc); implementations.addDeclaration(natFunc); alternatives.add(cleanClone(natFunc)); } } if (alternativeOutputs) { if (signatures == null || signatures.addMethod(primOrBufSign)) { collectParamComments(primOrBufFunc); implementations.addDeclaration(primOrBufFunc); alternatives.add(cleanClone(primOrBufFunc)); } if (signatures == null || signatures.addMethod(bufSign)) { collectParamComments(natStructFunc); implementations.addDeclaration(natStructFunc); alternatives.add(cleanClone(natStructFunc)); } } } catch (UnsupportedConversionException ex) { if (!result.config.limitComments) { implementations.addDeclaration(new EmptyDeclaration(getFileCommentContent(function), ex.toString())); } } } @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("MonoSymbolFile")) // 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; if (!onlyFields) { if (!struct.getParents().isEmpty()) { for (SimpleTypeRef parentName : struct.getParents()) { Struct parent = result.structsByName.get(parentName.getName()); if (parent == null) { // TODO report error continue; } baseClass = result.typeConverter.getTaggedTypeIdentifierInJava(parent); if (baseClass != null) { inheritsFromStruct = true; break; // TODO handle multiple and virtual inheritage } } } if (baseClass == null) { Class<?> c = isUnion ? result.config.runtime.unionClass : result.config.runtime.structClass; if (result.config.runtime != JNAeratorConfig.Runtime.JNA) { baseClass = ident( c, expr(typeRef(structName.clone())), expr(typeRef(ident(structName.clone(), "ByValue"))), expr(typeRef(ident(structName.clone(), "ByReference")))); } else { baseClass = ident(c); } } } Struct structJavaClass = publicStaticClass(structName, baseClass, Struct.Type.JavaClass, struct); final int iChild[] = new int[]{0}; //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)); // //VariablesDeclaration vptr = new VariablesDeclaration(typeRef(result.config.runtime.pointerClass), new Declarator.DirectDeclarator(vptrName)); // vptr.addModifiers(ModifierType.Public); // structJavaClass.addDeclaration(vptr); // childSignatures.variablesSignatures.add(vptrName); // // TODO add vptr grabber to constructor ! // } //List<Declaration> children = new ArrayList<Declaration>(); boolean succeeded = true; for (Declaration d : struct.getDeclarations()) { 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; 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, -1); for (Declaration md : decls) { if (!(md instanceof Function)) { continue; } Function method = (Function) md; Identifier methodImplName = method.getName().clone(); Identifier methodName = result.typeConverter.getValidJavaMethodName(f.getName()); method.setName(methodName); List<Expression> args = new ArrayList<Expression>(); boolean isStatic = f.hasModifier(ModifierType.Static); int iArg = 0; for (Arg arg : new ArrayList<Arg>(method.getArgs())) { if (iArg == 0 && !isStatic) { arg.replaceBy(null); args.add(thisRef()); } else { args.add(varRef(arg.getName())); } iArg++; } Expression implCall = methodCall(result.getLibraryInstanceReferenceExpression(library), MemberRefStyle.Dot, methodImplName.toString(), args.toArray(new Expression[args.size()])); method.setBody(block( "void".equals(String.valueOf(method.getValueType())) ? stat(implCall) : new Statement.Return(implCall))); method.addModifiers(ModifierType.Public, isStatic ? ModifierType.Static : null); structJavaClass.addDeclaration(method); } } } } if (!onlyFields) { if (result.config.features.contains(GenFeatures.StructConstructors)) { addStructConstructors(structName, structJavaClass/*, byRef, byVal*/, struct); } Struct byRef = publicStaticClass(ident("ByReference"), structName, Struct.Type.JavaClass, null, ident(ident(result.config.runtime.structClass), "ByReference")); Struct byVal = publicStaticClass(ident("ByValue"), structName, Struct.Type.JavaClass, null, ident(ident(result.config.runtime.structClass), "ByValue")); if (!succeeded) { byRef.addModifiers(ModifierType.Abstract); byVal.addModifiers(ModifierType.Abstract); } if (succeeded && result.config.runtime != JNAeratorConfig.Runtime.JNA) { if (!inheritsFromStruct) { structJavaClass.addDeclaration(createNewStructMethod("newByReference", byRef)); structJavaClass.addDeclaration(createNewStructMethod("newByValue", byVal)); } structJavaClass.addDeclaration(createNewStructMethod("newInstance", structJavaClass)); structJavaClass.addDeclaration(createNewStructArrayMethod(structJavaClass, isUnion)); } structJavaClass.addDeclaration(decl(byRef)); structJavaClass.addDeclaration(decl(byVal)); } if (!succeeded) { structJavaClass.addModifiers(ModifierType.Abstract); } return structJavaClass; } protected Function createNewStructMethod(String name, Struct byRef) { TypeRef tr = typeRef(byRef.getTag().clone()); Function f = new Function(Function.Type.JavaMethod, ident(name), tr); String varName = "s"; f.addModifiers(ModifierType.Protected); if (result.config.runtime != JNAeratorConfig.Runtime.JNA) { f.setBody(block( //new Statement.Return(methodCall("setupClone", new Expression.New(tr.clone(), methodCall(null)))) new Statement.Return(new Expression.New(tr.clone(), methodCall((String) null)))).setCompact(true)); } else { f.setBody(block( stat(tr.clone(), varName, new Expression.New(tr.clone(), methodCall((String) null))), stat(methodCall(varRef(varName), MemberRefStyle.Dot, "useMemory", methodCall("getPointer"))), stat(methodCall("write")), stat(methodCall(varRef(varName), MemberRefStyle.Dot, "read")), new Statement.Return(varRef(varName)))); } return f; } protected Function createNewStructArrayMethod(Struct struct, boolean isUnion) { if (result.config.runtime == JNAeratorConfig.Runtime.JNA) { return null; } TypeRef tr = typeRef(struct.getTag().clone()); TypeRef ar = new TypeRef.ArrayRef(tr); String varName = "arrayLength"; Function f = new Function(Function.Type.JavaMethod, ident("newArray"), ar, new Arg(varName, typeRef(Integer.TYPE))); f.addModifiers(ModifierType.Public, ModifierType.Static); f.setBody(block( new Statement.Return( methodCall( expr(typeRef(isUnion ? result.config.runtime.unionClass : result.config.runtime.structClass)), MemberRefStyle.Dot, "newArray", result.typeConverter.typeLiteral(tr), varRef(varName))))); return f; } public int countFieldsInStruct(Struct s) throws UnsupportedConversionException { int count = 0; for (Declaration declaration : s.getDeclarations()) { if (declaration instanceof VariablesDeclaration) { count += ((VariablesDeclaration) declaration).getDeclarators().size(); } } for (SimpleTypeRef parentName : s.getParents()) { Struct parent = result.structsByName.get(parentName.getName()); if (parent == null) { throw new UnsupportedConversionException(s, "Cannot find parent " + parentName + " of struct " + s); } count += countFieldsInStruct(parent); } return count; } public VariablesDeclaration convertVariablesDeclarationToJNA(String name, TypeRef mutatedType, int[] iChild, Identifier callerLibraryName, Element... toImportDetailsFrom) throws UnsupportedConversionException { name = result.typeConverter.getValidJavaArgumentName(ident(name)).toString(); //convertVariablesDeclaration(name, mutatedType, out, iChild, callerLibraryName); Expression initVal = null; TypeRef javaType = typeConverter().convertTypeToJNA( mutatedType, TypeConversion.TypeConversionMode.FieldType, callerLibraryName); mutatedType = ((JNATypeConversion)result.typeConverter).resolveTypeDef(mutatedType, callerLibraryName, true, false); VariablesDeclaration convDecl = new VariablesDeclaration(); convDecl.addModifiers(ModifierType.Public); if (javaType instanceof ArrayRef && mutatedType instanceof ArrayRef) { ArrayRef mr = (ArrayRef) mutatedType; ArrayRef jr = (ArrayRef) javaType; Expression mul = null; List<Expression> dims = mr.flattenDimensions(); for (int i = dims.size(); i-- != 0;) { Expression x = dims.get(i); if (x == null || x instanceof EmptyArraySize) { javaType = jr = new ArrayRef(typeRef(com.sun.jna.Pointer.class)); break; } else { Pair<Expression, TypeRef> c = result.typeConverter.convertExpressionToJava(x, callerLibraryName, false, true, null); c.getFirst().setParenthesis(dims.size() != 1); if (mul == null) { mul = c.getFirst(); } else { mul = expr(c.getFirst(), BinaryOperator.Multiply, mul); } } } initVal = new Expression.NewArray(jr.getTarget(), Arrays.asList(mul), Collections.EMPTY_LIST); } if (javaType == null) { throw new UnsupportedConversionException(mutatedType, "failed to convert type to Java"); } else if (javaType.toString().equals("void")) { throw new UnsupportedConversionException(mutatedType, "void type !"); //out.add(new EmptyDeclaration("SKIPPED:", v.formatComments("", true, true, false), v.toString())); } else { 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.setValueType(javaType); convDecl.addDeclarator(new DirectDeclarator(name, initVal)); return convDecl;//out.addDeclaration(convDecl); } } int nextAnonymousFieldId; @Override public boolean convertVariablesDeclaration(VariablesDeclaration v, Signatures signatures, DeclarationsHolder out, int[] iChild, boolean isGlobal, Identifier holderName, Identifier callerLibraryClass, String callerLibrary) { //List<Declaration> out = new ArrayList<Declaration>(); try { TypeRef valueType = v.getValueType(); for (Declarator vs : v.getDeclarators()) { 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()); } VariablesDeclaration vd = convertVariablesDeclarationToJNA(name, mutatedType, iChild, callerLibraryClass, v, vs); if (vd != null) { Declarator d = v.getDeclarators().get(0); if (d.getBits() > 0) { int bits = d.getBits(); if (!result.config.runtime.hasBitFields) { throw new UnsupportedConversionException(d, "This runtime does not support bit fields : " + result.config.runtime + " (please use BridJ instead)"); } vd.addAnnotation(new Annotation(result.config.runtime.typeRef(JNAeratorConfig.Runtime.Ann.Bits), expr(bits))); String st = vd.getValueType().toString(), mst = st; if (st.equals("int") || st.equals("long") || st.equals("short") || st.equals("long")) { if (bits <= 8) { mst = "byte"; } else if (bits <= 16) { mst = "short"; } else if (bits <= 32) { mst = "int"; } else { mst = "long"; // should not happen } } if (!st.equals(mst)) { vd.setValueType(new Primitive(mst)); } } if (!(mutatedType instanceof Primitive) && !result.config.noComments) { vd.addToCommentBefore("C type : " + mutatedType); } out.addDeclaration(vd); } if (result.config.beanStructs) { Function getMethod = new Function(Function.Type.JavaMethod, ident("get" + StringUtils.capitalize(name)), vd.getValueType().clone()).setBody(block( new Statement.Return(varRef(name)))).addModifiers(ModifierType.Public); if (signatures.addMethod(getMethod)) { out.addDeclaration(getMethod); } Function setMethod = new Function(Function.Type.JavaMethod, ident("set" + StringUtils.capitalize(name)), typeRef(Void.TYPE), new Arg(name, vd.getValueType().clone())).setBody(block( stat(expr(memberRef(thisRef(), MemberRefStyle.Dot, ident(name)), AssignmentOperator.Equal, varRef(name))))).addModifiers(ModifierType.Public); if (signatures.addMethod(setMethod)) { out.addDeclaration(setMethod); } } iChild[0]++; } return true; } catch (UnsupportedConversionException e) { //if (!result.config.limitComments) out.addDeclaration(new EmptyDeclaration(e.toString())); return false; } } TaggedTypeRefDeclaration publicStaticClassDecl(Identifier name, Identifier parentName, Struct.Type type, Element toCloneCommentsFrom, Identifier... interfaces) { return decl(publicStaticClass(name, parentName, type, toCloneCommentsFrom, interfaces)); } Struct publicStaticClass(Identifier name, Identifier parentName, Struct.Type type, Element toCloneCommentsFrom, Identifier... interfaces) { Struct cl = new Struct(); cl.setType(type); cl.setTag(name); if (parentName != null) { cl.setParents(typeRef(parentName)); } if (type == Struct.Type.JavaInterface) { for (Identifier inter : interfaces) { cl.addParent(typeRef(inter)); } } else { for (Identifier inter : interfaces) { cl.addProtocol(typeRef(inter)); } } if (!result.config.noComments) { cl.importComments(toCloneCommentsFrom, getFileCommentContent(toCloneCommentsFrom)); } cl.addModifiers(ModifierType.Public, ModifierType.Static); return cl; } public Pair<List<VariablesDeclaration>, List<VariablesDeclaration>> getParentAndOwnDeclarations(Struct structJavaClass, Struct nativeStruct) throws IOException { Pair<List<VariablesDeclaration>, List<VariablesDeclaration>> ret = new Pair<List<VariablesDeclaration>, List<VariablesDeclaration>>( new ArrayList<VariablesDeclaration>(), new ArrayList<VariablesDeclaration>()); if (!nativeStruct.getParents().isEmpty()) { for (SimpleTypeRef parentName : nativeStruct.getParents()) { Struct parent = result.structsByName.get(parentName.getName()); if (parent == null) { // TODO report error continue; } Struct parentJavaClass = convertStruct(parent, new Signatures(), null, null, true); Pair<List<VariablesDeclaration>, List<VariablesDeclaration>> parentDecls = getParentAndOwnDeclarations(parentJavaClass, parent); ret.getFirst().addAll(parentDecls.getFirst()); ret.getFirst().addAll(parentDecls.getSecond()); } } for (Declaration d : structJavaClass.getDeclarations()) { if (!(d instanceof VariablesDeclaration)) { continue; } VariablesDeclaration vd = (VariablesDeclaration) d; if (vd.getDeclarators().size() != 1) { continue; // should not happen ! } if (!isField(vd)) { continue; } ret.getSecond().add(vd); } return ret; } private boolean isJNAPointer(TypeRef tr) { if (!(tr instanceof SimpleTypeRef)) return false; SimpleTypeRef str = (SimpleTypeRef) tr; Identifier name = str.getName().eraseTemplateArguments(); return name.toString().equals(com.sun.jna.Pointer.class.getName()); } @SuppressWarnings("unchecked") private void addStructConstructors(Identifier structName, Struct structJavaClass/*, Struct byRef, Struct byVal*/, Struct nativeStruct) throws IOException { List<Declaration> initialMembers = new ArrayList<Declaration>(structJavaClass.getDeclarations()); Set<String> signatures = new TreeSet<String>(); Function emptyConstructor = new Function(Function.Type.JavaMethod, structName.clone(), null).addModifiers(ModifierType.Public); emptyConstructor.setBody(block(stat(methodCall("super")))); addConstructor(structJavaClass, emptyConstructor); boolean isUnion = nativeStruct.getType() == Struct.Type.CUnion; boolean addPointerConstructor = true; if (isUnion) { Map<String, Pair<TypeRef, List<Pair<String, String>>>> fieldsAndCommentsByTypeStr = new LinkedHashMap<String, Pair<TypeRef, List<Pair<String, String>>>>(); for (Declaration d : initialMembers) { if (!(d instanceof VariablesDeclaration)) { continue; } VariablesDeclaration vd = (VariablesDeclaration) d; if (vd.getDeclarators().size() != 1) { continue; // should not happen ! } String name = vd.getDeclarators().get(0).resolveName(); TypeRef tr = vd.getValueType(); if (!isField(vd)) { continue; } if (isJNAPointer(tr)) addPointerConstructor = false; String trStr = tr.toString(); Pair<TypeRef, List<Pair<String, String>>> pair = fieldsAndCommentsByTypeStr.get(trStr); if (pair == null) { fieldsAndCommentsByTypeStr.put(trStr, pair = new Pair<TypeRef, List<Pair<String, String>>>(tr, new ArrayList<Pair<String, String>>())); } pair.getSecond().add(new Pair<String, String>(vd.getCommentBefore(), name)); } for (Pair<TypeRef, List<Pair<String, String>>> pair : fieldsAndCommentsByTypeStr.values()) { List<String> commentBits = new ArrayList<String>(), nameBits = new ArrayList<String>(); for (Pair<String, String> p : pair.getValue()) { if (p.getFirst() != null) { commentBits.add(p.getFirst()); } nameBits.add(p.getValue()); } String name = StringUtils.implode(nameBits, "_or_"); TypeRef tr = pair.getFirst(); Function unionValConstr = new Function(Function.Type.JavaMethod, structName.clone(), null, new Arg(name, tr.clone())); if (!result.config.noComments) { if (!commentBits.isEmpty()) { unionValConstr.addToCommentBefore("@param " + name + " " + StringUtils.implode(commentBits, ", or ")); } } unionValConstr.addModifiers(ModifierType.Public); Expression assignmentExpr = varRef(name); for (Pair<String, String> p : pair.getValue()) { assignmentExpr = new Expression.AssignmentOp(memberRef(thisRef(), MemberRefStyle.Dot, ident(p.getValue())), AssignmentOperator.Equal, assignmentExpr); } unionValConstr.setBody(block( stat(methodCall("super")), tr instanceof TypeRef.ArrayRef ? throwIfArraySizeDifferent(name) : null, stat(assignmentExpr), stat(methodCall("setType", result.typeConverter.getJavaClassLitteralExpression(tr))))); if (signatures.add(unionValConstr.computeSignature(SignatureType.JavaStyle))) { structJavaClass.addDeclaration(unionValConstr); // byRef.addDeclaration(unionValConstr.clone().setName(byRef.getTag().clone())); // byVal.addDeclaration(unionValConstr.clone().setName(byVal.getTag().clone())); } } } else { Function fieldsConstr = new Function(Function.Type.JavaMethod, structName.clone(), null); fieldsConstr.setBody(new Block()).addModifiers(ModifierType.Public); Pair<List<VariablesDeclaration>, List<VariablesDeclaration>> decls = getParentAndOwnDeclarations(structJavaClass, nativeStruct); Map<Integer, String> namesById = new TreeMap<Integer, String>(); Set<String> names = new HashSet<String>(); List<Expression> orderedFieldNames = new ArrayList<Expression>(); int iArg = 0; for (VariablesDeclaration vd : new CompoundCollection<VariablesDeclaration>(decls.getFirst(), decls.getSecond())) { String name = chooseJavaArgName(vd.getDeclarators().get(0).resolveName(), iArg, names); namesById.put(vd.getId(), name); fieldsConstr.addArg(new Arg(name, vd.getValueType().clone())); iArg++; } if (iArg == 1 && isJNAPointer(fieldsConstr.getArgs().get(0).getValueType())) addPointerConstructor = false; FunctionCall superCall = methodCall("super"); // Adding parent fields for (VariablesDeclaration vd : decls.getFirst()) { String name = vd.getDeclarators().get(0).resolveName(), uname = namesById.get(vd.getId()); Struct parent = (Struct) vd.getParentElement(); Identifier parentTgName = typeConverter().getTaggedTypeIdentifierInJava(parent); if (!result.config.noComments) { fieldsConstr.addToCommentBefore("@param " + name + " @see " + parentTgName + "#" + vd.getDeclarators().get(0).resolveName()); } superCall.addArgument(varRef(uname)); //orderedFieldNames.add(expr(name)); } fieldsConstr.getBody().addStatement(stat(superCall)); // Adding class' own fields for (VariablesDeclaration vd : decls.getSecond()) { String name = vd.getDeclarators().get(0).resolveName(), uname = namesById.get(vd.getId()); if (!result.config.noComments) { if (vd.getCommentBefore() != null) { fieldsConstr.addToCommentBefore("@param " + uname + " " + vd.getCommentBefore()); } } if (vd.getValueType() instanceof TypeRef.ArrayRef) { fieldsConstr.getBody().addStatement(throwIfArraySizeDifferent(uname)); } fieldsConstr.getBody().addStatement(stat( new Expression.AssignmentOp(memberRef(thisRef(), MemberRefStyle.Dot, ident(name)), AssignmentOperator.Equal, varRef(uname)))); orderedFieldNames.add(expr(name)); } String getFieldOrderName = "getFieldOrder"; Expression selfList = methodCall( expr(typeRef(Arrays.class)), "asList", orderedFieldNames.toArray(new Expression[orderedFieldNames.size()])); Block getFieldOrderImpl; if (nativeStruct.getParents().isEmpty()) { getFieldOrderImpl = block(new Statement.Return(selfList)); } else { String fieldOrderName = "fieldOrder"; VariablesDeclaration vd = new VariablesDeclaration( typeRef(List.class), new DirectDeclarator( fieldOrderName, new Expression.New( typeRef(ArrayList.class), (Expression) methodCall(varRef("super"), getFieldOrderName)))); getFieldOrderImpl = block( vd, stat(methodCall(varRef(fieldOrderName), "addAll", selfList)), new Statement.Return(varRef(fieldOrderName))); } TypeRef listRef = typeRef(ident(List.class, expr(typeRef("?")))); Function getFieldOrder = new Function( Type.JavaMethod, ident(getFieldOrderName), listRef) .setBody(getFieldOrderImpl).addModifiers(ModifierType.Protected); if (signatures.add(getFieldOrder.computeSignature(SignatureType.JavaStyle))) { structJavaClass.addDeclaration(getFieldOrder); } int nArgs = fieldsConstr.getArgs().size(); if (nArgs == 0) { System.err.println("Struct with no field : " + structName); } if (nArgs > 0 && nArgs < result.config.maxConstructedFields) { if (signatures.add(fieldsConstr.computeSignature(SignatureType.JavaStyle))) { structJavaClass.addDeclaration(fieldsConstr); } } } if (addPointerConstructor) { Function addressConstructor = new Function(Function.Type.JavaMethod, structName.clone(), null).addModifiers(ModifierType.Public); String pointerVarName = "peer"; addressConstructor.addArg(new Arg(pointerVarName, typeRef(com.sun.jna.Pointer.class))); FunctionCall superPointerCall = methodCall("super"); superPointerCall.addArgument(varRef(pointerVarName)); addressConstructor.setBody(block(stat(superPointerCall))); addConstructor(structJavaClass, addressConstructor); } } @Override protected void configureCallbackStruct(Struct callbackStruct) { callbackStruct.setType(Struct.Type.JavaInterface); callbackStruct.addModifiers(ModifierType.Public); } @Override protected Struct createFakePointerClass(Identifier fakePointer) { Struct ptClass = result.declarationsConverter.publicStaticClass(fakePointer, ident(PointerType.class), Struct.Type.JavaClass, null); String pointerVarName = "address"; ptClass.addDeclaration(new Function(Function.Type.JavaMethod, fakePointer, null, new Arg(pointerVarName, typeRef(com.sun.jna.Pointer.class))).addModifiers(ModifierType.Public).setBody( block(stat(methodCall("super", varRef(pointerVarName)))))); ptClass.addDeclaration(new Function(Function.Type.JavaMethod, fakePointer, null) .addModifiers(ModifierType.Public) .setBody( block(stat(methodCall("super"))))); return ptClass; } private void annotateActualName(ModifiableElement e, Identifier name) { e.addAnnotation(new Annotation(result.config.runtime.typeRef(JNAeratorConfig.Runtime.Ann.Name), expr(name.toString()))); } @Override public void generateLibraryFiles(SourceFiles sourceFiles, Result result, JNAeratorConfig config) throws IOException { Struct librariesHub = null; PrintWriter hubOut = null; if (result.config.entryName != null) { librariesHub = new Struct(); librariesHub.addToCommentBefore("JNA Wrappers instances"); librariesHub.setType(Struct.Type.JavaClass); librariesHub.addModifiers(ModifierType.Public, ModifierType.Abstract); Identifier hubName = result.getHubFullClassName(); librariesHub.setTag(hubName.resolveLastSimpleIdentifier()); hubOut = result.classOutputter.getClassSourceWriter(hubName.toString()); hubOut.println("package " + hubName.resolveAllButLastIdentifier() + ";"); for (Identifier pn : result.javaPackages) { if (!pn.equals("")) { hubOut.println("import " + pn + ".*;"); } } } for (String library : result.libraries) { if (library == null) { continue; // to handle code defined in macro-expanded expressions }// library = ""; Identifier javaPackage = result.javaPackageByLibrary.get(library); Identifier simpleLibraryClassName = result.getLibraryClassSimpleName(library); Identifier fullLibraryClassName = result.getLibraryClassFullName(library);//ident(javaPackage, libraryClassName); //if (!result.objCClasses.isEmpty()) // out.println("import org.rococoa.ID;"); Struct interf = new Struct(); interf.setResolvedJavaIdentifier(fullLibraryClassName); interf.addToCommentBefore("JNA Wrapper for library <b>" + library + "</b>", result.declarationsConverter.getFileCommentContent(result.config.libraryProjectSources.get(library), null)); if (hubOut != null) { interf.addToCommentBefore("@see " + result.config.entryName + "." + library); } interf.addModifiers(ModifierType.Public); interf.setTag(simpleLibraryClassName); Expression nativeLibFieldExpr = null; if (!result.config.skipLibraryInstanceDeclarations) { Expression libNameExpr = opaqueExpr(result.getLibraryFileExpression(library)); TypeRef libTypeRef = typeRef(fullLibraryClassName); Expression libClassLiteral = result.typeConverter.typeLiteral(libTypeRef); boolean isJNAerator = result.config.runtime == JNAeratorConfig.Runtime.JNAerator; Expression libraryPathGetterExpr; if (isJNAerator) { libraryPathGetterExpr = methodCall( expr(typeRef(LibraryExtractor.class)), Expression.MemberRefStyle.Dot, "getLibraryPath", libNameExpr, expr(true), libClassLiteral); } else { libraryPathGetterExpr = libNameExpr; } String libNameStringFieldName = "JNA_LIBRARY_NAME", nativeLibFieldName = "JNA_NATIVE_LIB"; interf.addDeclaration(new VariablesDeclaration(typeRef(String.class), new Declarator.DirectDeclarator( libNameStringFieldName, libraryPathGetterExpr)).addModifiers(ModifierType.Public, ModifierType.Static, ModifierType.Final)); Expression libraryNameFieldExpr = memberRef(expr(libTypeRef.clone()), Expression.MemberRefStyle.Dot, ident(libNameStringFieldName)); Expression optionsMapExpr = memberRef(expr(typeRef(MangledFunctionMapper.class)), Expression.MemberRefStyle.Dot, "DEFAULT_OPTIONS"); Expression[] getInstArgs = isJNAerator ? new Expression[]{libraryNameFieldExpr.clone(), optionsMapExpr.clone()} : new Expression[]{libraryNameFieldExpr.clone()}; interf.addDeclaration(new VariablesDeclaration(typeRef(NativeLibrary.class), new Declarator.DirectDeclarator( nativeLibFieldName, methodCall( expr(typeRef(NativeLibrary.class)), Expression.MemberRefStyle.Dot, "getInstance", getInstArgs))).addModifiers(ModifierType.Public, ModifierType.Static, ModifierType.Final)); nativeLibFieldExpr = memberRef(expr(libTypeRef.clone()), Expression.MemberRefStyle.Dot, ident(nativeLibFieldName)); if (result.config.useJNADirectCalls) { interf.addDeclaration(new Function(Function.Type.StaticInit, null, null).setBody(block( stat( methodCall( expr(typeRef(Native.class)), Expression.MemberRefStyle.Dot, "register", memberRef(expr(libTypeRef.clone()), MemberRefStyle.Dot, ident("class")), //TODO: use this line instead when okay to break JNA 3.x (see https://github.com/ochafik/nativelibs4java/pull/432): //libraryNameFieldExpr.clone() nativeLibFieldExpr.clone() ) ) )).addModifiers(ModifierType.Static)); } else { Expression[] loadLibArgs = isJNAerator ? new Expression[]{libraryNameFieldExpr.clone(), libClassLiteral, optionsMapExpr.clone()} : new Expression[]{libraryNameFieldExpr.clone(), libClassLiteral}; VariablesDeclaration instanceDecl = new VariablesDeclaration(libTypeRef, new Declarator.DirectDeclarator( librariesHub == null ? "INSTANCE" : library, cast( libTypeRef, methodCall( expr(typeRef(Native.class)), Expression.MemberRefStyle.Dot, "loadLibrary", loadLibArgs)))).addModifiers(ModifierType.Public, ModifierType.Static, ModifierType.Final); if (librariesHub != null) { librariesHub.addDeclaration(instanceDecl); librariesHub.addProtocol(fullLibraryClassName.clone()); } else { interf.addDeclaration(instanceDecl); } } } boolean stdcall = false; List<Function> functions = result.functionsByLibrary.get(library); if (functions != null) { for (Function function : functions) { if (function.hasModifier(ModifierType.__stdcall)) { stdcall = true; break; } } } Identifier libSuperInter = ident(stdcall ? StdCallLibrary.class : config.runtime.libraryClass); if (result.config.useJNADirectCalls) { interf.addProtocol(libSuperInter); interf.setType(Struct.Type.JavaClass); } else { interf.addParent(libSuperInter); interf.setType(Struct.Type.JavaInterface); } fillLibraryMapping(result, sourceFiles, interf, interf, library, javaPackage, nativeLibFieldExpr); writeLibraryInterface(result, sourceFiles, interf, library, javaPackage); } if (hubOut != null) { hubOut.println(librariesHub.toString()); hubOut.close(); } } }