diff --git a/build.xml b/build.xml index 0199eb3..ee521aa 100644 --- a/build.xml +++ b/build.xml @@ -22,7 +22,8 @@ - + @@ -51,7 +52,8 @@ description="Compile the generated bindings sources" depends="generate-bindings-sources"> - + @@ -62,7 +64,8 @@ description="Compile the tests" depends="compile-bindings" > - + diff --git a/src/gir2java/Constants.java b/src/gir2java/Constants.java index 430665d..3baab1f 100644 --- a/src/gir2java/Constants.java +++ b/src/gir2java/Constants.java @@ -4,6 +4,7 @@ public class Constants { //Should we get this from the individual girs instead? public static final String GIR_XMLNS_C = "http://www.gtk.org/introspection/c/1.0"; + public static final String GIR_XMLNS_GLIB = "http://www.gtk.org/introspection/glib/1.0"; public static final String CONTEXT_EXTRA_NAMESPACE = "namespace"; public static final String CONTEXT_EXTRA_TYPE_REGISTRY = "types"; @@ -16,5 +17,7 @@ public class Constants { public static final String CONTEXT_EXTRA_UNDEFINED = "undefined"; public static final String CONTEXT_EXTRA_PREFIX = "prefix"; public static final String CONTEXT_EXTRA_NAMESPACE_TO_LIB = "ns-to-lib"; + public static final String CONTEXT_EXTRA_LOCAL_CALLBACK = "local-cb"; + public static final String CONTEXT_EXTRA_FIELD_NAME = "field-name"; } diff --git a/src/gir2java/GirParser.java b/src/gir2java/GirParser.java index 0838bb3..b59b10b 100644 --- a/src/gir2java/GirParser.java +++ b/src/gir2java/GirParser.java @@ -84,6 +84,7 @@ public class GirParser { elementParsers.put("instance-parameter", GirParser.class.getDeclaredMethod("parseParameter", Element.class, ParsingContext.class)); elementParsers.put("constructor", GirParser.class.getDeclaredMethod("parseMethodOrFunction", Element.class, ParsingContext.class)); elementParsers.put("union", GirParser.class.getDeclaredMethod("parseRecordOrClass", Element.class, ParsingContext.class)); + elementParsers.put("virtual-method", GirParser.class.getDeclaredMethod("parseVirtualMethod", Element.class, ParsingContext.class)); //Add other parser methods here } catch (NoSuchMethodException e) { @@ -494,7 +495,7 @@ private void parseEnumMember(Element root, ParsingContext context) { enumConstant.arg(JExpr.lit(value)); System.out.println("--> " + enumConstant.getName() + " (" + value + ")"); } - + @SuppressWarnings("unused") private void parseRecordOrClass(Element root, ParsingContext context) { /* @@ -502,7 +503,11 @@ private void parseRecordOrClass(Element root, ParsingContext context) { * an opaque struct. Classes have their own element, which is very similar, and also parsed here. */ - JCodeModel cm = (JCodeModel) context.getCmNode(); + if (root.getAttribute("is-gtype-struct-for", Constants.GIR_XMLNS_GLIB) != null) { + return; + } + + JCodeModel cm = (JCodeModel) context.getCm(); String name = root.getAttributeValue("name"); //Check if this is a retry @@ -711,6 +716,10 @@ private ConvertedType findType(Element root, ParsingContext context) { } System.out.println("found an array, treating it as " + convType); break; + } else if (childQualName.equals("callback")) { + parseElement(child, context); + convType = (ConvertedType) context.getExtra(Constants.CONTEXT_EXTRA_LOCAL_CALLBACK); + break; } } @@ -732,17 +741,21 @@ private ConvertedType findType(Element root, ParsingContext context) { private void parseRecordField(Element root, ParsingContext context) { JDefinedClass record = (JDefinedClass) context.getCmNode(); + String origName = root.getAttributeValue("name"); String name = record.name().toLowerCase() + "_field_" + root.getAttributeValue("name"); int fieldIdx = (int)context.getExtra(Constants.CONTEXT_EXTRA_NEXT_FIELD_INDEX); - ConvertedType convType = findType(root, context); + ParsingContext nextContext = context.copy(); + nextContext.putExtra(Constants.CONTEXT_EXTRA_FIELD_NAME, origName); + ConvertedType convType = findType(root, nextContext); - if (checkUndefined(root, context)) { + if (checkUndefined(root, nextContext)) { return; } if (convType == null) { + record.javadoc().add("Field " + origName + " skipped because no mapping was found\n"); return; } @@ -823,6 +836,11 @@ private boolean checkStructByValue(ConvertedType returnType, List, wrap, if not, then native method only + */ + + ConvertedType returnType = (ConvertedType) nextContext.getExtra(Constants.CONTEXT_EXTRA_RETURN_TYPE); + List parametersList = (List) nextContext.getExtra(Constants.CONTEXT_EXTRA_PARAM_TYPES); + + if (checkUndefined(root, nextContext)) { + return; + } + + if (checkStructByValue(returnType, parametersList)) { + System.out.println("Skipped " + nativeName + "() because it takes or returns struct by value"); + return; + } + + boolean returnsPointer = returnType.isPointer(); + boolean takesPointer = false; + + if (parametersList != null) { + for (ParameterDescriptor paramDesc : parametersList) { + if ((paramDesc.getType()) != null && paramDesc.getType().isPointer()) { + takesPointer = true; + break; + } + } + } + + int nativeVisibility = (returnsPointer || takesPointer) ? JMod.PROTECTED : JMod.PUBLIC; + int staticModifier = ParameterDescriptor.containsInstanceParameter(parametersList) ? 0 : JMod.STATIC; + int nativeModifiers = nativeVisibility | staticModifier | JMod.NATIVE; + + List parametersListJTypes = new ArrayList((parametersList == null) ? 0 : parametersList.size()); + + if (parametersList != null) { //null if the function has no arguments + for (ParameterDescriptor paramDesc : parametersList) { + if (paramDesc.isVarargs()) { + //XXX not entirely sure about this... + parametersListJTypes.add(cm._ref(Object[].class)); + } else if (paramDesc.getType().isPointer()) { + parametersListJTypes.add(nextContext.getCm()._ref(long.class)); + } else { + parametersListJTypes.add(paramDesc.getType().getJType()); + } + } + } + + JMethod nativeMethod = enclosing.getMethod(nativeName, parametersListJTypes.toArray(new JType[0])); + if (nativeMethod != null) { + //This can occur in the original girs + return; + } + + if (returnsPointer) { + nativeMethod = enclosing.method(nativeModifiers | JMod.NATIVE, long.class, nativeName); + nativeMethod.annotate(Ptr.class); + } else { + nativeMethod = enclosing.method(nativeModifiers | JMod.NATIVE, returnType.getJType(), nativeName); + } + + if (parametersList != null) { //null if the function has no arguments + for (ParameterDescriptor paramDesc : parametersList) { + if (paramDesc.isVarargs()) { + nativeMethod.varParam(Object.class, "varargs"); + } else if (paramDesc.getType().isPointer()) { + JType paramJType = nextContext.getCm()._ref(long.class); + JVar param = nativeMethod.param(paramJType, paramDesc.getName()); + param.annotate(Ptr.class); + } else { + //primitives only? + nativeMethod.param(paramDesc.getType().getJType(), paramDesc.getName()); + } + } + } + + //nativeMethod.javadoc().add(root.toXML()); + + //if there was any Pointer replaced above, here comes a pretty wrapper that wraps/unwraps between @Ptr long and Pointer + if (takesPointer || returnsPointer) { + if ("".equals(name)) { + name = nativeName; + } + name = NameUtils.neutralizeKeyword(name); + name = disambiguateMethodName(name, foundIn); + + JMethod wrapper = enclosing.method(JMod.PUBLIC | staticModifier, returnType.getJType(), name); + + JInvocation nativeCall; + if (staticModifier == 0) { + nativeCall = JExpr._this().invoke(nativeMethod); + } else { + nativeCall = enclosing.staticInvoke(nativeMethod); + } + if (parametersList != null) { + for (ParameterDescriptor paramDesc : parametersList) { + if (paramDesc.isInstance() && (foundIn.equals(enclosing)) ) { + //pass a pointer to this + nativeCall.arg( + nextContext + .getCm() + .ref(Pointer.class) + .staticInvoke("pointerTo") + .arg(JExpr._this()) + .arg(enclosing.dotclass()) + .invoke("getPeer") + ); + } else if (paramDesc.isVarargs()) { + JVar param = wrapper.varParam(Object.class, "varargs"); + nativeCall.arg(param); + } else if (paramDesc.getType().isPointer()) { + JVar param = wrapper.param(paramDesc.getType().getJType(), paramDesc.getName()); + nativeCall.arg(context.getCm().ref(Pointer.class).staticInvoke("getPeer").arg(param)); + } else { + JVar param = wrapper.param(paramDesc.getType().getJType(), paramDesc.getName()); + nativeCall.arg(param); + } + } + } + + if (returnsPointer) { + // Pointer.pointerToAddress(native(...), ReturnType.class) + ConvertedType returnConvType = returnType.forCType(nextContext, returnType.getCtype()); + JClass returnClass = (JClass)returnConvType.getJType(); + + JInvocation pointerToAddressCall = + context + .getCm() + .ref(Pointer.class) + .staticInvoke("pointerToAddress") + .arg(nativeCall); + + if (returnClass.isParameterized()) { + JClass parameter = returnClass.getTypeParameters().get(0); + if (parameter.isParameterized()) { + JInvocation paramTypeCall = context + .getCm() + .ref(DefaultParameterizedType.class) + .staticInvoke("paramType") + .arg(parameter.dotclass()) + .arg( parameter.getTypeParameters().get(0).dotclass() ); + + pointerToAddressCall.arg(paramTypeCall); + } else { + pointerToAddressCall.arg(parameter.dotclass()); + } + } + + wrapper.body()._return(pointerToAddressCall); + } else if ( "void".equals(returnType.getCtype()) ) { + wrapper.body().add(nativeCall); + } else { + wrapper.body()._return(nativeCall); + } + } + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + @SuppressWarnings("unused") + private void parseVirtualMethod(Element root, ParsingContext context) { String name = root.getAttributeValue("name"); - String nativeName = root.getAttributeValue("identifier", Constants.GIR_XMLNS_C); - Object cmNode = nextContext.getCmNode(); + name = NameUtils.neutralizeKeyword(name); + JDefinedClass enclosing = (JDefinedClass) context.getCmNode(); - JDefinedClass enclosing; - if (cmNode instanceof JDefinedClass) { - enclosing = (JDefinedClass) cmNode; - } else { - enclosing = context.getCurrentNamespaceClass(); - } + //Add an abstract method to the parent node + //For interfaces: + //Parse signature like functions, but only make the wrapper, as abstract + //For normal classes: + //Same, plus a body that calls the function through the pointer field + ParsingContext nextContext = context.copy(); parseElements(root.getChildElements(), nextContext); - /* The context should now have the return type, and parameter list. - * If one of these is a Pointer, wrap, if not, then native method only - */ ConvertedType returnType = (ConvertedType) nextContext.getExtra(Constants.CONTEXT_EXTRA_RETURN_TYPE); List parametersList = (List) nextContext.getExtra(Constants.CONTEXT_EXTRA_PARAM_TYPES); @@ -885,126 +1081,35 @@ private void parseMethodOrFunction(Element root, ParsingContext context) { } if (checkStructByValue(returnType, parametersList)) { - System.out.println("Skipped " + nativeName + "() because it takes or returns struct by value"); + System.out.println("Skipped " + name + "() because it takes or returns struct by value"); return; } - boolean returnsPointer = returnType.isPointer(); - boolean takesPointer = false; + JMethod method = enclosing.method(JMod.PUBLIC, returnType.getJType(), name); if (parametersList != null) { for (ParameterDescriptor paramDesc : parametersList) { - if ((paramDesc.getType()) != null && paramDesc.getType().isPointer()) { - takesPointer = true; - break; - } - } - } - - int nativeVisibility = (returnsPointer || takesPointer) ? JMod.PROTECTED : JMod.PUBLIC; - int staticModifier = ParameterDescriptor.containsInstanceParameter(parametersList) ? 0 : JMod.STATIC; - int nativeModifiers = nativeVisibility | staticModifier | JMod.NATIVE; - - JMethod nativeMethod; - if (returnsPointer) { - nativeMethod = enclosing.method(nativeModifiers | JMod.NATIVE, long.class, nativeName); - nativeMethod.annotate(Ptr.class); - } else { - nativeMethod = enclosing.method(nativeModifiers | JMod.NATIVE, returnType.getJType(), nativeName); - } - - if (parametersList != null) { //null if the function has no arguments - for (ParameterDescriptor paramDesc : parametersList) { - if (paramDesc.isVarargs()) { - nativeMethod.varParam(Object.class, "varargs"); - } else if (paramDesc.getType().isPointer()) { - JType paramJType = nextContext.getCm()._ref(long.class); - JVar param = nativeMethod.param(paramJType, paramDesc.getName()); - param.annotate(Ptr.class); + if (paramDesc.isInstance()) { + //FIXME: what to do with the this parameter? just skip, and pass in implementors? + } else if (paramDesc.isVarargs()) { + JVar param = method.varParam(Object.class, "varargs"); } else { - //primitives only? - nativeMethod.param(paramDesc.getType().getJType(), paramDesc.getName()); + JVar param = method.param(paramDesc.getType().getJType(), paramDesc.getName()); } } } - //nativeMethod.javadoc().add(root.toXML()); - - //if there was any Pointer replaced above, here comes a pretty wrapper that wraps/unwraps between @Ptr long and Pointer - if (takesPointer || returnsPointer) { - if ("".equals(name)) { - name = nativeName; + if (! (enclosing.isInterface()) ) { + JBlock body = method.body(); + JInvocation fptrGetterCall = body.invoke(enclosing.name().toLowerCase() + "_field_" + name); + JInvocation callbackCall = fptrGetterCall.invoke("apply"); + for (JVar param : method.params()) { + callbackCall.arg(param); } - name = NameUtils.neutralizeKeyword(name); - name = disambiguateMethodName(name, enclosing); - JMethod wrapper = enclosing.method(JMod.PUBLIC | staticModifier, returnType.getJType(), name); - - JInvocation nativeCall; - if (staticModifier == 0) { - nativeCall = JExpr._this().invoke(nativeMethod); - } else { - nativeCall = enclosing.staticInvoke(nativeMethod); - } - if (parametersList != null) { - for (ParameterDescriptor paramDesc : parametersList) { - if (paramDesc.isInstance()) { - //pass a pointer to this - nativeCall.arg( - nextContext - .getCm() - .ref(Pointer.class) - .staticInvoke("pointerTo") - .arg(JExpr._this()) - .arg(enclosing.dotclass()) - .invoke("getPeer") - ); - } else if (paramDesc.isVarargs()) { - JVar param = wrapper.varParam(Object.class, "varargs"); - nativeCall.arg(param); - } else if (paramDesc.getType().isPointer()) { - JVar param = wrapper.param(paramDesc.getType().getJType(), paramDesc.getName()); - nativeCall.arg(context.getCm().ref(Pointer.class).staticInvoke("getPeer").arg(param)); - } else { - JVar param = wrapper.param(paramDesc.getType().getJType(), paramDesc.getName()); - nativeCall.arg(param); - } - } - } - - if (returnsPointer) { - // Pointer.pointerToAddress(native(...), ReturnType.class) - ConvertedType returnConvType = returnType.forCType(nextContext, returnType.getCtype()); - JClass returnClass = (JClass)returnConvType.getJType(); - - JInvocation pointerToAddressCall = - context - .getCm() - .ref(Pointer.class) - .staticInvoke("pointerToAddress") - .arg(nativeCall); - - if (returnClass.isParameterized()) { - JClass parameter = returnClass.getTypeParameters().get(0); - if (parameter.isParameterized()) { - JInvocation paramTypeCall = context - .getCm() - .ref(DefaultParameterizedType.class) - .staticInvoke("paramType") - .arg(parameter.dotclass()) - .arg( parameter.getTypeParameters().get(0).dotclass() ); - - pointerToAddressCall.arg(paramTypeCall); - } else { - pointerToAddressCall.arg(parameter.dotclass()); - } - } - - wrapper.body()._return(pointerToAddressCall); - } else if ( "void".equals(returnType.getCtype()) ) { - wrapper.body().add(nativeCall); - } else { - wrapper.body()._return(nativeCall); + if (!returnType.getType().equals("none")) { + //virtual function is not void, we need to return the result + body._return(callbackCall); } } @@ -1012,10 +1117,23 @@ private void parseMethodOrFunction(Element root, ParsingContext context) { @SuppressWarnings("unused") private void parseInterface(Element root, ParsingContext context) { - // only log the fact that we have found this type for now String name = root.getAttributeValue("name"); Set foundTypes = (Set)context.getExtra(Constants.CONTEXT_EXTRA_DEFINED_TYPES); foundTypes.add("" + context.getExtra(Constants.CONTEXT_EXTRA_NAMESPACE) + '.' + name); + + String className = context.getCurrentPackage() + '.' + context.getExtra(Constants.CONTEXT_EXTRA_PREFIX) + NameUtils.neutralizeKeyword(name); + JDefinedClass iface; + try { + iface = context.getCm()._class(className, ClassType.INTERFACE); + } catch (JClassAlreadyExistsException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return; + } + + ParsingContext nextContext = context.withCmNode(iface); + parseElements(root.getChildElements(), nextContext); + ConvertedType convType = new ConvertedType( context.getCm(), (String)context.getExtra(Constants.CONTEXT_EXTRA_NAMESPACE), @@ -1023,7 +1141,7 @@ private void parseInterface(Element root, ParsingContext context) { root.getAttributeValue("type",Constants.GIR_XMLNS_C), false ); - convType.setJType(context.getCm().ref(Object.class)); + convType.setJType(iface); context.registerType(convType); } @@ -1063,21 +1181,50 @@ private void parseCallback(Element root, ParsingContext context) { //Abstract class //XXX: why neutralize, or why not together with the prefix? - String className = context.getCurrentPackage() + '.' + context.getExtra(Constants.CONTEXT_EXTRA_PREFIX) + NameUtils.neutralizeKeyword(name); - JDefinedClass callbackType; + JDefinedClass callbackType = null; + boolean local = false; + boolean ready = false; try { - callbackType = context.getCm()._class(JMod.PUBLIC | JMod.ABSTRACT, className, ClassType.CLASS); + if (context.getCmNode() instanceof JDefinedClass) { + JDefinedClass enclosing = (JDefinedClass) context.getCmNode(); + String className = context.getExtra(Constants.CONTEXT_EXTRA_FIELD_NAME) + "_callback"; + Iterator it = enclosing.classes(); + + //may already exist in the second pass + while (it.hasNext()) { + JDefinedClass nested = it.next(); + if (nested.name().equals(className)) { + callbackType = nested; + ready = true; + break; + } + } + + if (callbackType == null) { + callbackType = enclosing._class(JMod.PUBLIC | JMod.ABSTRACT, className, ClassType.CLASS); + } + + local = true; + } else { + String className = context.getCurrentPackage() + '.' + context.getExtra(Constants.CONTEXT_EXTRA_PREFIX) + NameUtils.neutralizeKeyword(name); + callbackType = context.getCm()._class(JMod.PUBLIC | JMod.ABSTRACT, className, ClassType.CLASS); + } } catch (JClassAlreadyExistsException e) { // TODO Auto-generated catch block e.printStackTrace(); return; } - callbackType._extends(context.getCm().ref(Callback.class).narrow(callbackType)); - //apply() method - JMethod applyMethod = callbackType.method(JMod.PUBLIC | JMod.ABSTRACT, returnType.getJType(), "apply"); - for (ParameterDescriptor param : parametersList) { - applyMethod.param(param.getType().getJType(), param.getName()); + if (!ready) { + callbackType._extends(context.getCm().ref(Callback.class).narrow(callbackType)); + + //apply() method + JMethod applyMethod = callbackType.method(JMod.PUBLIC | JMod.ABSTRACT, returnType.getJType(), "apply"); + if (parametersList != null) { + for (ParameterDescriptor param : parametersList) { + applyMethod.param(param.getType().getJType(), param.getName()); + } + } } ConvertedType convType = new ConvertedType( @@ -1088,6 +1235,11 @@ private void parseCallback(Element root, ParsingContext context) { false ); convType.setJType(context.getCm().ref(Pointer.class).narrow(callbackType)); - context.registerType(convType); + + if (local) { + context.putExtra(Constants.CONTEXT_EXTRA_LOCAL_CALLBACK, convType); + } else { + context.registerType(convType); + } } }