Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8252634: jextract should generate type annotations for C types #300

Closed
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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 General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

package jdk.internal.jextract.impl;

import jdk.incubator.jextract.Type;
import java.util.stream.Collectors;

public class AnnotationWriter implements Type.Visitor<String, Void> {
@Override
public String visitPrimitive(Type.Primitive t, Void aVoid) {
if (t.kind().layout().isEmpty()) {
return "void"; //skip for now
} else {
return t.kind().typeName();
}
}

@Override
public String visitDelegated(Type.Delegated t, Void aVoid) {
if (t.kind() == Type.Delegated.Kind.TYPEDEF) {
return t.name().get();
} else if (t.kind() == Type.Delegated.Kind.POINTER) {
String typeStr = t.type().accept(this, null);
// FIXME Revisit this logic for pointer to function types
if (t.type() instanceof Type.Function) {
return typeStr.replace("(", "(*)(");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be problematic for multiple nested function types, e.g.

void (int, void (*)(void)) -> void (*)(int, void (*)(*)(*)(void))

The return type could also be a function pointer, so it's not always the first occurrence that needs to be replaced either.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might just want to revisit the return type and argumentTypes, similar to what you do in visitFunction, but use (*)( as a joiner instead of (.

} else {
return typeStr + "*";
}
} else {
String prefix = switch (t.kind()) {
case ATOMIC -> "_Atomic";
case COMPLEX -> "complex";
case SIGNED -> "signed";
case UNSIGNED -> "unsigned";
case VOLATILE -> "volatile";
default -> throw new IllegalStateException("Invalid input" + t);
};
return prefix + " " + t.type().accept(this, null);
}
}

@Override
public String visitFunction(Type.Function t, Void aVoid) {
String ret = t.returnType().accept(this, null);
String args = t.argumentTypes().stream().map(p -> p.accept(this, null))
.collect(Collectors.joining(",", "(", ")"));
return ret + args;
}

@Override
public String visitDeclared(Type.Declared t, Void aVoid) {
String name = t.tree().name();
return switch (t.tree().kind()) {
case STRUCT -> "struct " + name;
case UNION -> "union " + name;
case ENUM -> "enum " + name;
default -> name;
};
}

@Override
public String visitArray(Type.Array t, Void aVoid) {
if (t.kind() == Type.Array.Kind.VECTOR) {
return ""; //skip for now
} else {
return t.elementType().accept(this, null) + "[]";
}
}

@Override
public String visitType(Type t, Void aVoid) {
throw new UnsupportedOperationException();
}

String getCAnnotation(Type t) {
return "@C(\"" + t.accept(this, null) + "\")";
}
}
Original file line number Diff line number Diff line change
@@ -88,7 +88,7 @@ void decrAlign() {
align--;
}

void addFunctionalInterface(String name, MethodType mtype, FunctionDescriptor fDesc) {
void addFunctionalInterface(String name, MethodType mtype, FunctionDescriptor fDesc, String anno) {
incrAlign();
indent();
append("public interface " + name + " {\n");
@@ -101,18 +101,24 @@ void addFunctionalInterface(String name, MethodType mtype, FunctionDescriptor fD
delim = ", ";
}
append(");\n");
addFunctionalFactory(name, mtype, fDesc);
addFunctionalFactory(name, mtype, fDesc, anno);
decrAlign();
indent();
append("}\n");
decrAlign();
indent();
}

void addStaticFunctionWrapper(String javaName, String nativeName, MethodType mtype, FunctionDescriptor desc, boolean varargs, List<String> paramNames) {
void addStaticFunctionWrapper(String javaName, String nativeName, MethodType mtype, FunctionDescriptor desc,
boolean varargs, List<String> paramNames, List<String> annos, String returnAnno) {
incrAlign();
indent();
append(PUB_MODS + mtype.returnType().getName() + " " + javaName + " (");
append(PUB_MODS);
if (mtype.returnType() != void.class) {
append(returnAnno);
append(' ');
}
append(mtype.returnType().getSimpleName() + " " + javaName + " (");
String delim = "";
List<String> pExprs = new ArrayList<>();
final int numParams = paramNames.size();
@@ -130,7 +136,7 @@ void addStaticFunctionWrapper(String javaName, String nativeName, MethodType mty
if (pType.equals(MemoryAddress.class)) {
pType = Addressable.class;
}
append(delim + pType.getName() + " " + pName);
append(delim + annos.get(i) + " " + pType.getSimpleName() + " " + pName);
delim = ", ";
}
if (varargs) {
@@ -166,13 +172,13 @@ void addStaticFunctionWrapper(String javaName, String nativeName, MethodType mty
decrAlign();
}

void emitPrimitiveTypedef(Type.Primitive primType, String name) {
void emitPrimitiveTypedef(Type.Primitive primType, String name, String anno) {
Type.Primitive.Kind kind = primType.kind();
if (primitiveKindSupported(kind) && !kind.layout().isEmpty()) {
incrAlign();
indent();
append(PUB_MODS);
append("ValueLayout ");
append(anno + " ValueLayout ");
append(uniqueNestedClassName(name));
append(" = ");
append(TypeTranslator.typeToLayoutName(kind));
@@ -210,9 +216,9 @@ void emitTypedef(String className, String superClassName) {
decrAlign();
}

private void addFunctionalFactory(String className, MethodType mtype, FunctionDescriptor fDesc) {
private void addFunctionalFactory(String className, MethodType mtype, FunctionDescriptor fDesc, String anno) {
indent();
append(PUB_MODS + "MemorySegment allocate(" + className + " fi) {\n");
append(PUB_MODS + " " + anno + " MemorySegment allocate(" + className + " fi) {\n");
incrAlign();
indent();
append("return RuntimeHelper.upcallStub(" + className + ".class, fi, " + functionGetCallString(className, fDesc) + ", " +
@@ -222,7 +228,7 @@ private void addFunctionalFactory(String className, MethodType mtype, FunctionDe
append("}\n");

indent();
append(PUB_MODS + "MemorySegment allocate(" + className + " fi, NativeScope scope) {\n");
append(PUB_MODS + " " + anno + " MemorySegment allocate(" + className + " fi, NativeScope scope) {\n");
incrAlign();
indent();
append("return scope.register(allocate(fi));\n");
Original file line number Diff line number Diff line change
@@ -103,29 +103,29 @@ JavaSourceBuilder classEnd() {
}

void addLayoutGetter(String javaName, MemoryLayout layout) {
emitForwardGetter(constantHelper.addLayout(javaName, layout));
emitForwardGetter(constantHelper.addLayout(javaName, layout), "");
}

void addVarHandleGetter(String javaName, String nativeName, MemoryLayout layout, Class<?> type) {
emitForwardGetter(constantHelper.addGlobalVarHandle(javaName, nativeName, layout, type));
emitForwardGetter(constantHelper.addGlobalVarHandle(javaName, nativeName, layout, type), "");
}

void addMethodHandleGetter(String javaName, String nativeName, MethodType mtype, FunctionDescriptor desc, boolean varargs) {
emitForwardGetter(constantHelper.addMethodHandle(javaName, nativeName, mtype, desc, varargs));
emitForwardGetter(constantHelper.addMethodHandle(javaName, nativeName, mtype, desc, varargs), "");
}

void addSegmentGetter(String javaName, String nativeName, MemoryLayout layout) {
emitForwardGetter(constantHelper.addSegment(javaName, nativeName, layout));
emitForwardGetter(constantHelper.addSegment(javaName, nativeName, layout), "");
}

void addConstantGetter(String javaName, Class<?> type, Object value) {
emitForwardGetter(constantHelper.addConstant(javaName, type, value));
void addConstantGetter(String javaName, Class<?> type, Object value, String anno) {
emitForwardGetter(constantHelper.addConstant(javaName, type, value), anno);
}

void addGetter(String javaName, String nativeName, MemoryLayout layout, Class<?> type) {
void addGetter(String javaName, String nativeName, MemoryLayout layout, Class<?> type, String anno) {
incrAlign();
indent();
append(PUB_MODS + type.getName() + " " + javaName + "$get() {\n");
append(PUB_MODS + anno + " " + type.getSimpleName() + " " + javaName + "$get() {\n");
incrAlign();
indent();
String vhParam = addressGetCallString(javaName, nativeName, layout);
@@ -137,10 +137,10 @@ void addGetter(String javaName, String nativeName, MemoryLayout layout, Class<?>
decrAlign();
}

void addSetter(String javaName, String nativeName, MemoryLayout layout, Class<?> type) {
void addSetter(String javaName, String nativeName, MemoryLayout layout, Class<?> type, String anno) {
incrAlign();
indent();
append(PUB_MODS + "void " + javaName + "$set(" + type.getName() + " x) {\n");
append(PUB_MODS + "void " + javaName + "$set(" + anno + " " + type.getSimpleName() + " x) {\n");
incrAlign();
indent();
String vhParam = addressGetCallString(javaName, nativeName, layout);
@@ -173,10 +173,10 @@ protected void addImportSection() {
append(".*;\n");
}

protected void emitForwardGetter(DirectMethodHandleDesc desc) {
protected void emitForwardGetter(DirectMethodHandleDesc desc, String anno) {
incrAlign();
indent();
append(PUB_MODS + displayName(desc.invocationType().returnType()) + " " + desc.methodName() + "() {\n");
append(PUB_MODS + anno + " " + displayName(desc.invocationType().returnType()) + " " + desc.methodName() + "() {\n");
incrAlign();
indent();
append("return " + getCallString(desc) + ";\n");
Original file line number Diff line number Diff line change
@@ -64,6 +64,7 @@ public class OutputFactory implements Declaration.Visitor<Void, Declaration> {
protected JavaSourceBuilder currentBuilder;
protected final ConstantHelper constantHelper;
protected final TypeTranslator typeTranslator = new TypeTranslator();
protected final AnnotationWriter annotationWriter = new AnnotationWriter();
private final String pkgName;
private final Map<Declaration, String> structClassNames = new HashMap<>();
private final Set<Declaration.Typedef> unresolvedStructTypedefs = new HashSet<>();
@@ -142,6 +143,7 @@ JavaFileObject[] generate(Declaration.Scoped decl) {
files.add(toplevelBuilder.build());
files.addAll(constantHelper.getClasses());
files.add(jfoFromString(pkgName,"RuntimeHelper", getRuntimeHelperSource()));
files.add(jfoFromString(pkgName,"C", getCAnnotationSource()));
return files.toArray(new JavaFileObject[0]);
} catch (IOException ex) {
throw new UncheckedIOException(ex);
@@ -157,6 +159,12 @@ private String getRuntimeHelperSource() throws URISyntaxException, IOException {
.replace("${C_LANG}", C_LANG_CONSTANTS_HOLDER);
}

private String getCAnnotationSource() throws URISyntaxException, IOException {
URL cAnnotation = OutputFactory.class.getResource("resources/C.java.template");
return (pkgName.isEmpty()? "" : "package " + pkgName + ";\n") +
String.join("\n", Files.readAllLines(Paths.get(cAnnotation.toURI())));
}

private void generateDecl(Declaration tree) {
try {
tree.accept(this, null);
@@ -185,9 +193,10 @@ public Void visitConstant(Declaration.Constant constant, Declaration parent) {
return null;
}

String anno = annotationWriter.getCAnnotation(constant.type());
toplevelBuilder.addConstantGetter(Utils.javaSafeIdentifier(constant.name()),
constant.value() instanceof String ? MemorySegment.class :
typeTranslator.getJavaType(constant.type()), constant.value());
typeTranslator.getJavaType(constant.type()), constant.value(), anno);
return null;
}

@@ -207,7 +216,9 @@ public Void visitScoped(Declaration.Scoped d, Declaration parent) {
String className = d.name().isEmpty() ? parent.name() : d.name();
MemoryLayout parentLayout = parentLayout(d);
String parentLayoutFieldName = className + "$struct";
currentBuilder = new StructBuilder(currentBuilder, className, parentLayoutFieldName, parentLayout, pkgName, constantHelper);
String anno = annotationWriter.getCAnnotation(Type.declared(d));
String arrayAnno = annotationWriter.getCAnnotation(Type.array(Type.declared(d)));
currentBuilder = new StructBuilder(currentBuilder, className, parentLayoutFieldName, parentLayout, pkgName, constantHelper, anno, arrayAnno);
addStructDefinition(d, currentBuilder.className);
currentBuilder.incrAlign();
currentBuilder.classBegin();
@@ -295,8 +306,14 @@ public Void visitFunction(Declaration.Function funcTree, Declaration parent) {
.map(Declaration.Variable::name)
.map(p -> !p.isEmpty() ? Utils.javaSafeIdentifier(p) : p)
.collect(Collectors.toList());
List<String> annos = funcTree.parameters()
.stream()
.map(Declaration.Variable::type)
.map(annotationWriter::getCAnnotation)
.collect(Collectors.toList());
String returnAnno = annotationWriter.getCAnnotation(funcTree.type().returnType());
toplevelBuilder.addStaticFunctionWrapper(Utils.javaSafeIdentifier(funcTree.name()), funcTree.name(), mtype,
Type.descriptorFor(funcTree.type()).orElseThrow(), funcTree.type().varargs(), paramNames);
Type.descriptorFor(funcTree.type()).orElseThrow(), funcTree.type().varargs(), paramNames, annos, returnAnno);
int i = 0;
for (Declaration.Variable param : funcTree.parameters()) {
Type.Function f = getAsFunctionPointer(param.type());
@@ -308,7 +325,8 @@ public Void visitFunction(Declaration.Function funcTree, Declaration parent) {
warn("varargs in callbacks is not supported");
}
MethodType fitype = typeTranslator.getMethodType(f, false);
toplevelBuilder.addFunctionalInterface(name, fitype, Type.descriptorFor(f).orElseThrow());
String anno = annotationWriter.getCAnnotation(param.type());
toplevelBuilder.addFunctionalInterface(name, fitype, Type.descriptorFor(f).orElseThrow(), anno);
i++;
}
}
@@ -371,7 +389,8 @@ public Void visitTypedef(Declaration.Typedef tree, Declaration parent) {
}
}
} else if (type instanceof Type.Primitive) {
toplevelBuilder.emitPrimitiveTypedef((Type.Primitive)type, tree.name());
String anno = annotationWriter.getCAnnotation(type);
toplevelBuilder.emitPrimitiveTypedef((Type.Primitive)type, tree.name(), anno);
}
return null;
}
@@ -403,6 +422,7 @@ public Void visitVariable(Declaration.Variable tree, Declaration parent) {
}

Class<?> clazz = typeTranslator.getJavaType(type);
String anno = annotationWriter.getCAnnotation(type);
if (tree.kind() == Declaration.Variable.Kind.BITFIELD ||
(layout instanceof ValueLayout && layout.byteSize() > 8)) {
//skip
@@ -427,8 +447,8 @@ public Void visitVariable(Declaration.Variable tree, Declaration parent) {
}
} else {
currentBuilder.addVarHandleGetter(fieldName, tree.name(), treeLayout, clazz);
currentBuilder.addGetter(fieldName, tree.name(), treeLayout, clazz);
currentBuilder.addSetter(fieldName, tree.name(), treeLayout, clazz);
currentBuilder.addGetter(fieldName, tree.name(), treeLayout, clazz, anno);
currentBuilder.addSetter(fieldName, tree.name(), treeLayout, clazz, anno);
}
} else {
if (sizeAvailable) {
@@ -438,8 +458,8 @@ public Void visitVariable(Declaration.Variable tree, Declaration parent) {
toplevelBuilder.addLayoutGetter(fieldName, layout);
toplevelBuilder.addVarHandleGetter(fieldName, tree.name(), treeLayout, clazz);
toplevelBuilder.addSegmentGetter(fieldName, tree.name(), treeLayout);
toplevelBuilder.addGetter(fieldName, tree.name(), treeLayout, clazz);
toplevelBuilder.addSetter(fieldName, tree.name(), treeLayout, clazz);
toplevelBuilder.addGetter(fieldName, tree.name(), treeLayout, clazz, anno);
toplevelBuilder.addSetter(fieldName, tree.name(), treeLayout, clazz, anno);
}
} else {
warn("Layout size not available for " + fieldName);
Original file line number Diff line number Diff line change
@@ -35,13 +35,17 @@ class StructBuilder extends JavaSourceBuilder {
private final JavaSourceBuilder prev;
private final String parentLayoutFieldName;
private final MemoryLayout parentLayout;
private final String structAnno;
private final String structArrayAnno;

StructBuilder(JavaSourceBuilder prev, String className, String parentLayoutFieldName, MemoryLayout layout, String pkgName, ConstantHelper constantHelper) {
StructBuilder(JavaSourceBuilder prev, String className, String parentLayoutFieldName, MemoryLayout parentLayout, String pkgName,
ConstantHelper constantHelper, String structAnno, String structArrayAnno) {
super(prev.uniqueNestedClassName(className), pkgName, constantHelper);
this.prev = prev;
this.parentLayoutFieldName = parentLayoutFieldName;
this.parentLayout = layout;

this.parentLayout = parentLayout;
this.structAnno = structAnno;
this.structArrayAnno = structArrayAnno;
}

JavaSourceBuilder prev() {
@@ -138,37 +142,37 @@ void addLayoutGetter(String javaName, MemoryLayout layout) {
}

@Override
void addGetter(String javaName, String nativeName, MemoryLayout layout, Class<?> type) {
void addGetter(String javaName, String nativeName, MemoryLayout layout, Class<?> type, String anno) {
incrAlign();
indent();
append(PUB_MODS + type.getName() + " " + javaName + "$get(MemorySegment seg) {\n");
append(PUB_MODS + " " + anno + " " + type.getSimpleName() + " " + javaName + "$get(" + this.structAnno + " MemorySegment seg) {\n");
incrAlign();
indent();
append("return (" + type.getName() + ")"
+ fieldVarHandleGetCallString(getQualifiedName(javaName), nativeName, layout, type, parentLayoutFieldName, parentLayout) + ".get(seg);\n");
+ fieldVarHandleGetCallString(getQualifiedName(javaName), nativeName, layout, type) + ".get(seg);\n");
decrAlign();
indent();
append("}\n");
decrAlign();

addIndexGetter(javaName, nativeName, layout, type, parentLayoutFieldName, parentLayout);
addIndexGetter(javaName, nativeName, layout, type, anno);
}

@Override
void addSetter(String javaName, String nativeName, MemoryLayout layout, Class<?> type) {
void addSetter(String javaName, String nativeName, MemoryLayout layout, Class<?> type, String anno) {
incrAlign();
indent();
String param = MemorySegment.class.getName() + " seg";
append(PUB_MODS + "void " + javaName + "$set(" + param + ", " + type.getName() + " x) {\n");
String param = MemorySegment.class.getSimpleName() + " seg";
append(PUB_MODS + "void " + javaName + "$set(" + this.structAnno + " " + param + ", " + anno + " " + type.getSimpleName() + " x) {\n");
incrAlign();
indent();
append(fieldVarHandleGetCallString(getQualifiedName(javaName), nativeName, layout, type, parentLayoutFieldName, parentLayout) + ".set(seg, x);\n");
append(fieldVarHandleGetCallString(getQualifiedName(javaName), nativeName, layout, type) + ".set(seg, x);\n");
decrAlign();
indent();
append("}\n");
decrAlign();

addIndexSetter(javaName, nativeName, layout, type, parentLayoutFieldName, parentLayout);
addIndexSetter(javaName, nativeName, layout, type, anno);
}

@Override
@@ -201,23 +205,23 @@ private void emitAllocate() {
incrAlign();
indent();
append(PUB_MODS);
append("MemorySegment allocate() { return MemorySegment.allocateNative($LAYOUT()); }\n");
append(structAnno + " MemorySegment allocate() { return MemorySegment.allocateNative($LAYOUT()); }\n");
decrAlign();
}

private void emitScopeAllocate() {
incrAlign();
indent();
append(PUB_MODS);
append("MemorySegment allocate(NativeScope scope) { return scope.allocate($LAYOUT()); }\n");
append(structAnno + " MemorySegment allocate(NativeScope scope) { return scope.allocate($LAYOUT()); }\n");
decrAlign();
}

private void emitAllocateArray() {
incrAlign();
indent();
append(PUB_MODS);
append("MemorySegment allocateArray(int len) {\n");
append(structArrayAnno + " MemorySegment allocateArray(int len) {\n");
incrAlign();
indent();
append("return MemorySegment.allocateNative(MemoryLayout.ofSequence(len, $LAYOUT()));");
@@ -230,7 +234,7 @@ private void emitScopeAllocateArray() {
incrAlign();
indent();
append(PUB_MODS);
append("MemorySegment allocateArray(int len, NativeScope scope) {\n");
append(structArrayAnno + " MemorySegment allocateArray(int len, NativeScope scope) {\n");
incrAlign();
indent();
append("return scope.allocate(MemoryLayout.ofSequence(len, $LAYOUT()));");
@@ -239,38 +243,38 @@ private void emitScopeAllocateArray() {
decrAlign();
}

private void addIndexGetter(String javaName, String nativeName, MemoryLayout layout, Class<?> type, String parentJavaName, MemoryLayout parentLayout) {
private void addIndexGetter(String javaName, String nativeName, MemoryLayout layout, Class<?> type, String anno) {
incrAlign();
indent();
String params = MemorySegment.class.getName() + " addr, long index";
append(PUB_MODS + type.getName() + " " + javaName + "$get(" + params + ") {\n");
String params = this.structAnno + " " + MemorySegment.class.getSimpleName() + " seg, long index";
append(PUB_MODS + " " + anno + " " + type.getSimpleName() + " " + javaName + "$get(" + params + ") {\n");
incrAlign();
indent();
append("return (" + type.getName() + ")"
+ fieldVarHandleGetCallString(getQualifiedName(javaName), nativeName, layout, type, parentJavaName, parentLayout) +
".get(addr.asSlice(index*sizeof()));\n");
+ fieldVarHandleGetCallString(getQualifiedName(javaName), nativeName, layout, type) +
".get(seg.asSlice(index*sizeof()));\n");
decrAlign();
indent();
append("}\n");
decrAlign();
}

private void addIndexSetter(String javaName, String nativeName, MemoryLayout layout, Class<?> type, String parentJavaName, MemoryLayout parentLayout) {
private void addIndexSetter(String javaName, String nativeName, MemoryLayout layout, Class<?> type, String anno) {
incrAlign();
indent();
String params = MemorySegment.class.getName() + " addr, long index, " + type.getName() + " x";
String params = this.structAnno + " " + MemorySegment.class.getSimpleName() + " seg, long index, " + anno + " " + type.getSimpleName() + " x";
append(PUB_MODS + "void " + javaName + "$set(" + params + ") {\n");
incrAlign();
indent();
append(fieldVarHandleGetCallString(getQualifiedName(javaName), nativeName, layout, type, parentJavaName, parentLayout) +
".set(addr.asSlice(index*sizeof()), x);\n");
append(fieldVarHandleGetCallString(getQualifiedName(javaName), nativeName, layout, type) +
".set(seg.asSlice(index*sizeof()), x);\n");
decrAlign();
indent();
append("}\n");
decrAlign();
}

private String fieldVarHandleGetCallString(String javaName, String nativeName, MemoryLayout layout, Class<?> type, String parentJavaName, MemoryLayout parentLayout) {
return getCallString(constantHelper.addFieldVarHandle(javaName, nativeName, layout, type, parentJavaName, parentLayout));
private String fieldVarHandleGetCallString(String javaName, String nativeName, MemoryLayout layout, Class<?> type) {
return getCallString(constantHelper.addFieldVarHandle(javaName, nativeName, layout, type, parentLayoutFieldName, parentLayout));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Generated by jextract

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Annotation to indicate C types
*/
@Target({ ElementType.TYPE_USE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface C {
/**
* The C type associated with a given Java type
* @return The C type associated with a given Java type
*/
String value();
}
128 changes: 128 additions & 0 deletions test/jdk/tools/jextract/Test8252634.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code 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 General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemorySegment;
import java.nio.file.Path;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;

/*
* @test
* @bug 8252634
* @summary jextract should generate type annotations for C types
* @library /test/lib
* @modules jdk.incubator.jextract
* @build JextractToolRunner
* @run testng/othervm -Dforeign.restricted=permit Test8252634
*/
public class Test8252634 extends JextractToolRunner {
private Class<? extends Annotation> cAnnoClass;
private Method cValueMethod;

@Test
public void test() throws Throwable {
Path outputPath = getOutputFilePath("output8252634");
Path headerFile = getInputFilePath("test8252634.h");
run("-d", outputPath.toString(), headerFile.toString()).checkSuccess();
try(Loader loader = classLoader(outputPath)) {
this.cAnnoClass = (Class<? extends Annotation>)loader.loadClass("C");
this.cValueMethod = findMethod(cAnnoClass, "value");

Class<?> headerClass = loader.loadClass("test8252634_h");
checkGlobalFunctions(headerClass);
checkGlobalVariables(headerClass);

Class<?> pointClass = loader.loadClass("test8252634_h$Point");
checkPointGetters(pointClass);
checkPointSetters(pointClass);
checkPointAllocate(pointClass);
} finally {
deleteDir(outputPath);
}
}

private void checkGlobalFunctions(Class<?> headerClass) throws Throwable {
Method make = findMethod(headerClass, "make", int.class, int.class);
Parameter[] params = make.getParameters();
checkAnnotation(params[0].getAnnotatedType(), "int");
checkAnnotation(params[1].getAnnotatedType(), "int");
checkAnnotation(make.getAnnotatedReturnType(), "struct Point*");
Method func = findFirstMethod(headerClass, "func");
params = func.getParameters();
checkAnnotation(params[0].getAnnotatedType(), "int(*)(int)");
}

private void checkGlobalVariables(Class<?> headerClass) throws Throwable {
Method pGetter = findMethod(headerClass, "p$get");
checkAnnotation(pGetter.getAnnotatedReturnType(), "int_ptr");
Method pSetter = findMethod(headerClass, "p$set", MemoryAddress.class);
checkAnnotation(pSetter.getParameters()[0].getAnnotatedType(), "int_ptr");
}

private void checkPointGetters(Class<?> pointClass) throws Throwable {
Method xGetter = findMethod(pointClass, "x$get", MemorySegment.class);
checkAnnotation(xGetter.getParameters()[0].getAnnotatedType(), "struct Point");
checkAnnotation(xGetter.getAnnotatedReturnType(), "int");
Method yGetter = findMethod(pointClass, "y$get", MemorySegment.class);
checkAnnotation(yGetter.getParameters()[0].getAnnotatedType(), "struct Point");
checkAnnotation(yGetter.getAnnotatedReturnType(), "int");
Method xIndexedGetter = findMethod(pointClass, "x$get", MemorySegment.class, long.class);
checkAnnotation(xIndexedGetter.getParameters()[0].getAnnotatedType(), "struct Point");
checkAnnotation(xIndexedGetter.getAnnotatedReturnType(), "int");
Method yIndexedGetter = findMethod(pointClass, "y$get", MemorySegment.class, long.class);
checkAnnotation(yIndexedGetter.getParameters()[0].getAnnotatedType(), "struct Point");
checkAnnotation(yIndexedGetter.getAnnotatedReturnType(), "int");
}

private void checkPointSetters(Class<?> pointClass) throws Throwable {
Method xSetter = findMethod(pointClass, "x$set", MemorySegment.class, int.class);
checkAnnotation(xSetter.getParameters()[0].getAnnotatedType(), "struct Point");
checkAnnotation(xSetter.getParameters()[1].getAnnotatedType(), "int");
Method ySetter = findMethod(pointClass, "y$set", MemorySegment.class, int.class);
checkAnnotation(ySetter.getParameters()[0].getAnnotatedType(), "struct Point");
checkAnnotation(ySetter.getParameters()[1].getAnnotatedType(), "int");
Method xIndexedSetter = findMethod(pointClass, "x$set", MemorySegment.class, long.class, int.class);
checkAnnotation(xIndexedSetter.getParameters()[0].getAnnotatedType(), "struct Point");
checkAnnotation(xIndexedSetter.getParameters()[2].getAnnotatedType(), "int");
Method yIndexedSetter = findMethod(pointClass, "y$set", MemorySegment.class, long.class, int.class);
checkAnnotation(yIndexedSetter.getParameters()[0].getAnnotatedType(), "struct Point");
checkAnnotation(yIndexedSetter.getParameters()[2].getAnnotatedType(), "int");
}

private void checkPointAllocate(Class<?> pointClass) throws Throwable {
Method allocate = findMethod(pointClass, "allocate");
checkAnnotation(allocate.getAnnotatedReturnType(), "struct Point");
Method allocateArray = findMethod(pointClass, "allocateArray", int.class);
checkAnnotation(allocateArray.getAnnotatedReturnType(), "struct Point[]");
}

private void checkAnnotation(AnnotatedElement ae, String expected) throws Throwable {
Object anno = ae.getAnnotation(cAnnoClass);
assertEquals(cValueMethod.invoke(anno).toString(), expected);
}
}
41 changes: 41 additions & 0 deletions test/jdk/tools/jextract/test8252634.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code 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 General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus

struct Point {
int x; int y;
};

struct Point* make(int x, int y);

void func(int (*callback)(int));

typedef int* int_ptr;
int_ptr p;

#ifdef __cplusplus
}
#endif // __cplusplus