
com.ochafik.lang.jnaerator.JNADeclarationsConverter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jnaerator Show documentation
Show all versions of jnaerator Show documentation
JNAerator (pronounce "generator") simply parses C and Objective-C headers and generates the corresponding JNA and Rococoa Java interfaces (it also has a very limited support for C++).
This lets Java programmers access native libraries transparently, with full IDE support and little to no hand-tweaking.
Users who are looking for ready-to-use libraries should check the NativeLibs4Java project instead.
The newest version!
/*
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 .
*/
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.ochafik.util.CompoundCollection;
import com.ochafik.util.listenable.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 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, Pair>, Set>> cachedForcedMethodsAndTheirSignatures;
public static synchronized Pair>, Set> getMethodsAndTheirSignatures(Class> originalLib) {
if (cachedForcedMethodsAndTheirSignatures == null) {
cachedForcedMethodsAndTheirSignatures = new LinkedHashMap, Pair>, Set>>();
}
Pair>, Set> pair = cachedForcedMethodsAndTheirSignatures.get(originalLib);
if (pair == null) {
pair = new Pair>, Set>(new ArrayList>(), new HashSet());
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(f, sig));
pair.getSecond().add(sig);
}
}
return pair;
}
public void addMissingMethods(Class> originalLib, Signatures existingSignatures, Struct outputLib) {
for (Pair 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 mess = new ArrayList();
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 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>> functionAlternativesByNativeSignature = new LinkedHashMap>>();
@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> 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>(
cleanClone(function),
new ArrayList()));
}
List 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 names = new LinkedHashSet();
//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 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 " + displayFormat.format(name) + "");
break;
} catch (Exception ex) {
//ex.printStackTrace();
}
}
}
boolean alternativeOutputs = !isCallback;
Function primOrBufFunc = alternativeOutputs ? natFunc.clone() : null;
Function natStructFunc = alternativeOutputs ? natFunc.clone() : null;
Set argNames = new TreeSet();
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 children = new ArrayList();
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 decls = new ArrayList();
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 args = new ArrayList();
boolean isStatic = f.hasModifier(ModifierType.Static);
int iArg = 0;
for (Arg arg : new ArrayList(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 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 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 out = new ArrayList();
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> getParentAndOwnDeclarations(Struct structJavaClass, Struct nativeStruct) throws IOException {
Pair, List> ret =
new Pair, List>(
new ArrayList(),
new ArrayList());
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> 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 initialMembers = new ArrayList(structJavaClass.getDeclarations());
Set signatures = new TreeSet();
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>>> fieldsAndCommentsByTypeStr = new LinkedHashMap>>>();
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>> pair = fieldsAndCommentsByTypeStr.get(trStr);
if (pair == null) {
fieldsAndCommentsByTypeStr.put(trStr, pair = new Pair>>(tr, new ArrayList>()));
}
pair.getSecond().add(new Pair(vd.getCommentBefore(), name));
}
for (Pair>> pair : fieldsAndCommentsByTypeStr.values()) {
List commentBits = new ArrayList(), nameBits = new ArrayList();
for (Pair 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 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> decls = getParentAndOwnDeclarations(structJavaClass, nativeStruct);
Map namesById = new TreeMap();
Set names = new HashSet();
List orderedFieldNames = new ArrayList();
int iArg = 0;
for (VariablesDeclaration vd : new CompoundCollection(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 " + library + "",
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 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();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy