diff --git a/README.md b/README.md index 89d58f19..55e9e0f6 100644 --- a/README.md +++ b/README.md @@ -26,9 +26,9 @@ 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. > -`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. @@ -36,6 +36,7 @@ We currently use gradle version 8.11.1 which is fetched automatically by the gra $ sh ./gradlew -Pjdk_home= -Pllvm_home= 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). >
Using a local installation of LLVM > diff --git a/src/main/java/org/openjdk/jextract/clang/Type.java b/src/main/java/org/openjdk/jextract/clang/Type.java index c5a61637..2662c0d4 100644 --- a/src/main/java/org/openjdk/jextract/clang/Type.java +++ b/src/main/java/org/openjdk/jextract/clang/Type.java @@ -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 diff --git a/src/main/java/org/openjdk/jextract/impl/ClassSourceBuilder.java b/src/main/java/org/openjdk/jextract/impl/ClassSourceBuilder.java index 7c1ae3a7..d84b2762 100644 --- a/src/main/java/org/openjdk/jextract/impl/ClassSourceBuilder.java +++ b/src/main/java/org/openjdk/jextract/impl/ClassSourceBuilder.java @@ -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; @@ -211,6 +212,7 @@ 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)); @@ -218,6 +220,22 @@ String layoutString(Type type, long align) { }; } + 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; diff --git a/src/main/java/org/openjdk/jextract/impl/DeclarationImpl.java b/src/main/java/org/openjdk/jextract/impl/DeclarationImpl.java index e8bee3f2..2ff47a1a 100644 --- a/src/main/java/org/openjdk/jextract/impl/DeclarationImpl.java +++ b/src/main/java/org/openjdk/jextract/impl/DeclarationImpl.java @@ -223,7 +223,11 @@ public List 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 diff --git a/src/main/java/org/openjdk/jextract/impl/HeaderFileBuilder.java b/src/main/java/org/openjdk/jextract/impl/HeaderFileBuilder.java index 16159091..3cb647c0 100644 --- a/src/main/java/org/openjdk/jextract/impl/HeaderFileBuilder.java +++ b/src/main/java/org/openjdk/jextract/impl/HeaderFileBuilder.java @@ -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, diff --git a/src/main/java/org/openjdk/jextract/impl/TypeMaker.java b/src/main/java/org/openjdk/jextract/impl/TypeMaker.java index c8788f7c..b12ca99d 100644 --- a/src/main/java/org/openjdk/jextract/impl/TypeMaker.java +++ b/src/main/java/org/openjdk/jextract/impl/TypeMaker.java @@ -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: {