Skip to content
Draft
Show file tree
Hide file tree
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
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,17 @@ Alternatively, to build jextract from the latest sources (which include all the
> Over time, new branches will be added, each targeting a specific JDK version.
> </details>

`jextract` can be built using `gradle`, as follows (on Windows, `gradlew.bat` should be used instead).
`jextract` can be built using `gradlew`, as follows (on Windows, `gradlew.bat` should be used instead).

We currently use gradle version 8.11.1 which is fetched automatically by the gradle wrapper. Please refer to the [compatibility matrix](https://docs.gradle.org/current/userguide/compatibility.html) to see which version of java is needed in `PATH`/`JAVA_HOME` to run gradle. Note that the JDK we use to build (the toolchain JDK) is passed in separately as a property.
We currently use Gradle version 8.11.1 which is fetched automatically by the Gradle wrapper. Please refer to the [compatibility matrix](https://docs.gradle.org/current/userguide/compatibility.html) to see which version of java is needed in `PATH`/`JAVA_HOME` to run Gradle. Note that the JDK we use to build (the toolchain JDK) is passed in separately as a property.



```sh
$ sh ./gradlew -Pjdk_home=<jdk_home_dir> -Pllvm_home=<libclang_dir> clean verify
```

Alternatively these properties can be specified in the [`gradle.properties` file](https://docs.gradle.org/current/userguide/build_environment.html#the_gradle_properties_file).

> <details><summary><strong>Using a local installation of LLVM</strong></summary>
>
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/org/openjdk/jextract/clang/Type.java
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,18 @@ public Type canonicalType() {
return new Type(canonicalType, owner);
}

/**
* For a typedef returns the underlying type.
*/
public Type typeDefUnderlyingType() {
var cursor = getDeclarationCursor();
if (cursor.isInvalid()) {
throw new AssertionError("TODO");
}
var underlyingType = Index_h.clang_getTypedefDeclUnderlyingType(cursor.owner, cursor.segment);
return new Type(underlyingType, owner);
}

/**
* Determine whether a Type has the "const" qualifier set,
* without looking through typedefs that may have added "const" at a
Expand Down
18 changes: 18 additions & 0 deletions src/main/java/org/openjdk/jextract/impl/ClassSourceBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import java.lang.invoke.MethodType;
import java.lang.invoke.VarHandle;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
Expand Down Expand Up @@ -211,13 +212,30 @@ String layoutString(Type type, long align) {
case Declared d when Utils.isEnum(d) -> layoutString(ClangEnumType.get(d.tree()).get(), align);
case Declared d when Utils.isStructOrUnion(d) -> alignIfNeeded(JavaName.getFullNameOrThrow(d.tree()) + ".layout()", ClangAlignOf.getOrThrow(d.tree()) / 8, align);
case Delegated d when d.kind() == Delegated.Kind.POINTER -> alignIfNeeded(runtimeHelperName() + ".C_POINTER", 8, align);
case Delegated d when d.kind() == Delegated.Kind.TYPEDEF -> typedefLayoutString(d, align);
case Delegated d -> layoutString(d.type(), align);
case Function _ -> alignIfNeeded(runtimeHelperName() + ".C_POINTER", 8, align);
case Array a -> String.format("MemoryLayout.sequenceLayout(%1$d, %2$s)", a.elementCount().orElse(0L), layoutString(a.elementType(), align));
default -> throw new UnsupportedOperationException();
};
}

private String typedefLayoutString(Type.Delegated type, long align) {
String name = type.name().orElse("");
String layoutName = runtimeHelperName() + ".C_" + name.toUpperCase(Locale.ROOT);
return switch (name) {
// https://en.cppreference.com/w/c/types/integer.html
case "int8_t", "uint8_t" -> layoutName;
case "int16_t", "uint16_t" -> alignIfNeeded(layoutName, 2, align);
case "int32_t", "uint32_t" -> alignIfNeeded(layoutName, 4, align);
case "int64_t", "uint64_t" -> alignIfNeeded(layoutName, 8, align);
// Other types which are common enough and have an entry in FFM `Linker#canonicalLayouts()`
case "size_t" -> alignIfNeeded(layoutName, 8, align);
// Fall back to the layout for the underlying type
default -> layoutString(type.type(), align);
};
}

String functionDescriptorString(int textBoxIndent, Type.Function functionType) {
final MethodType type = Utils.methodTypeFor(functionType);
boolean noArgs = type.parameterCount() == 0;
Expand Down
6 changes: 5 additions & 1 deletion src/main/java/org/openjdk/jextract/impl/DeclarationImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,11 @@ public List<Variable> parameters() {

@Override
public Type.Function type() {
return type;
// Use actual types from parameters, since they preserve typedef information
// This is needed for example for `malloc` to preserve the parameter type `size_t` and avoid it becoming `long`
// TODO: Does that also affect return type? maybe not, for C function `mbstowcs` it properly emits `size_t` as return type
// TODO: Maybe this is not safe / correct
return Type.function(type.varargs(), type.returnType(), params.stream().map(Variable::type).toArray(Type[]::new));
}

@Override
Expand Down
17 changes: 14 additions & 3 deletions src/main/java/org/openjdk/jextract/impl/HeaderFileBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -403,17 +403,28 @@ void emitBasicPrimitiveTypes(){
public static final ValueLayout.OfByte C_CHAR =(ValueLayout.OfByte)Linker.nativeLinker().canonicalLayouts().get("char");
public static final ValueLayout.OfShort C_SHORT = (ValueLayout.OfShort) Linker.nativeLinker().canonicalLayouts().get("short");
public static final ValueLayout.OfInt C_INT = (ValueLayout.OfInt) Linker.nativeLinker().canonicalLayouts().get("int");
// For C_LONG use unspecific ValueLayout to make generated code portable; because on Linux it is OfLong but on Windows it is OfInt
public static final ValueLayout C_LONG = (ValueLayout) Linker.nativeLinker().canonicalLayouts().get("long");
public static final ValueLayout.OfLong C_LONG_LONG = (ValueLayout.OfLong) Linker.nativeLinker().canonicalLayouts().get("long long");
public static final ValueLayout.OfFloat C_FLOAT = (ValueLayout.OfFloat) Linker.nativeLinker().canonicalLayouts().get("float");
public static final ValueLayout.OfDouble C_DOUBLE = (ValueLayout.OfDouble) Linker.nativeLinker().canonicalLayouts().get("double");
public static final AddressLayout C_POINTER = ((AddressLayout) Linker.nativeLinker().canonicalLayouts().get("void*"))
.withTargetLayout(MemoryLayout.sequenceLayout(java.lang.Long.MAX_VALUE, C_CHAR));

public static final ValueLayout.OfByte C_INT8_T = (ValueLayout.OfByte) Linker.nativeLinker().canonicalLayouts().get("int8_t");
public static final ValueLayout.OfByte C_UINT8_T = C_INT8_T;
public static final ValueLayout.OfShort C_INT16_T = (ValueLayout.OfShort) Linker.nativeLinker().canonicalLayouts().get("int16_t");
public static final ValueLayout.OfShort C_UINT16_T = C_INT16_T;
public static final ValueLayout.OfInt C_INT32_T = (ValueLayout.OfInt) Linker.nativeLinker().canonicalLayouts().get("int32_t");
public static final ValueLayout.OfInt C_UINT32_T = C_INT32_T;
public static final ValueLayout.OfLong C_INT64_T = (ValueLayout.OfLong) Linker.nativeLinker().canonicalLayouts().get("int64_t");
public static final ValueLayout.OfLong C_UINT64_T = C_INT64_T;

// For C_SIZE_T use unspecific ValueLayout to make generated code portable; because its layout is platform-specific
public static final ValueLayout C_SIZE_T = (ValueLayout) Linker.nativeLinker().canonicalLayouts().get("size_t");
""");
if (TypeImpl.IS_WINDOWS) {
appendIndentedLines("public static final ValueLayout.OfInt C_LONG = (ValueLayout.OfInt) Linker.nativeLinker().canonicalLayouts().get(\"long\");");
appendIndentedLines("public static final ValueLayout.OfDouble C_LONG_DOUBLE = (ValueLayout.OfDouble) Linker.nativeLinker().canonicalLayouts().get(\"double\");");
} else {
appendIndentedLines("public static final ValueLayout.OfLong C_LONG = (ValueLayout.OfLong) Linker.nativeLinker().canonicalLayouts().get(\"long\");");
}
}
private void emitGlobalGetter(String holderClass, String javaName,
Expand Down
4 changes: 3 additions & 1 deletion src/main/java/org/openjdk/jextract/impl/TypeMaker.java
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,9 @@ static Type makeType(org.openjdk.jextract.clang.Type t, TreeMaker treeMaker) {
}
}
case Typedef: {
Type __type = makeType(t.canonicalType(), treeMaker);
// Don't use `canonicalType()` here since that would lose information about intermediate typedefs,
// for example `typedef uint16_t MyType` would otherwise be resolved as `short` and not `uint16_t`
Type __type = makeType(t.typeDefUnderlyingType(), treeMaker);
return Type.typedef(t.spelling(), __type);
}
case Complex: {
Expand Down