From 583bcf61d2468b2e7e93ca73091725be5be5c516 Mon Sep 17 00:00:00 2001 From: Nizar Benalla Date: Fri, 17 Jan 2025 15:42:13 +0100 Subject: [PATCH 01/10] move sharable items to a common class --- .../jextract/impl/ClassSourceBuilder.java | 42 +++-- .../jextract/impl/CommonBindingsBuilder.java | 171 ++++++++++++++++++ .../impl/FunctionalInterfaceBuilder.java | 6 +- .../jextract/impl/HeaderFileBuilder.java | 130 +++---------- .../jextract/impl/SourceFileBuilder.java | 22 +++ .../jextract/impl/ToplevelBuilder.java | 46 +++-- .../generator/clinitCycles/TestGlobal.java | 4 +- .../generator/clinitCycles/TestStruct.java | 5 +- .../generator/clinitCycles/TestTypedef.java | 4 +- test/jtreg/generator/dedup/TestDedup.java | 6 +- .../TestNestedStructTypedef.java | 4 +- .../nestedTypes/TestNestedTypes.java | 4 +- .../nestedTypes/TestNestedTypesNames.java | 6 +- .../TestNestedTypesUnsupported.java | 4 +- .../test8244412/LibTest8244412Test.java | 3 +- .../generator/test8245003/Test8245003.java | 8 +- .../test8246341/LibTest8246341Test.java | 3 +- .../generator/test8252121/Test8252121.java | 4 +- .../test8257892/LibUnsupportedTest.java | 4 +- .../generator/testPrintf/TestPrintf.java | 3 +- .../generator/testStruct/LibStructTest.java | 4 +- 21 files changed, 305 insertions(+), 178 deletions(-) create mode 100644 src/main/java/org/openjdk/jextract/impl/CommonBindingsBuilder.java diff --git a/src/main/java/org/openjdk/jextract/impl/ClassSourceBuilder.java b/src/main/java/org/openjdk/jextract/impl/ClassSourceBuilder.java index 051287ac..8808695e 100644 --- a/src/main/java/org/openjdk/jextract/impl/ClassSourceBuilder.java +++ b/src/main/java/org/openjdk/jextract/impl/ClassSourceBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, 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 @@ -160,6 +160,15 @@ final void emitDefaultConstructor() { """, className); } + final void emitPrivateConstructor() { + appendIndentedLines(""" + + private %1$s() { + // Should not be called directly + } + """, className); + } + final void emitDocComment(Declaration decl) { emitDocComment(decl, ""); } @@ -200,11 +209,14 @@ String layoutString(Type type, long align) { return switch (type) { case Primitive p -> primitiveLayoutString(p, 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 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(sb.layoutUtilsName() + "C_POINTER", 8, 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)); + case Function _ -> alignIfNeeded(sb.layoutUtilsName() + "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(); }; } @@ -243,16 +255,16 @@ String indentString(int size) { private String primitiveLayoutString(Primitive primitiveType, long align) { return switch (primitiveType.kind()) { - case Bool -> runtimeHelperName() + ".C_BOOL"; - case Char -> runtimeHelperName() + ".C_CHAR"; - case Short -> alignIfNeeded(runtimeHelperName() + ".C_SHORT", 2, align); - case Int -> alignIfNeeded(runtimeHelperName() + ".C_INT", 4, align); - case Long -> alignIfNeeded(runtimeHelperName() + ".C_LONG", TypeImpl.IS_WINDOWS ? 4 : 8, align); - case LongLong -> alignIfNeeded(runtimeHelperName() + ".C_LONG_LONG", 8, align); - case Float -> alignIfNeeded(runtimeHelperName() + ".C_FLOAT", 4, align); - case Double -> alignIfNeeded(runtimeHelperName() + ".C_DOUBLE", 8, align); + case Bool -> sb.layoutUtilsName() + "C_BOOL"; + case Char -> sb.layoutUtilsName() + "C_CHAR"; + case Short -> alignIfNeeded(sb.layoutUtilsName() + "C_SHORT", 2, align); + case Int -> alignIfNeeded(sb.layoutUtilsName() + "C_INT", 4, align); + case Long -> alignIfNeeded(sb.layoutUtilsName() + "C_LONG", TypeImpl.IS_WINDOWS ? 4 : 8, align); + case LongLong -> alignIfNeeded(sb.layoutUtilsName() + "C_LONG_LONG", 8, align); + case Float -> alignIfNeeded(sb.layoutUtilsName() + "C_FLOAT", 4, align); + case Double -> alignIfNeeded(sb.layoutUtilsName() + "C_DOUBLE", 8, align); case LongDouble -> TypeImpl.IS_WINDOWS ? - alignIfNeeded(runtimeHelperName() + ".C_LONG_DOUBLE", 8, align) : + alignIfNeeded(sb.layoutUtilsName() + "C_LONG_DOUBLE", 8, align) : paddingLayoutString(8, 0); case HalfFloat, Char16, WChar -> paddingLayoutString(2, 0); // unsupported case Float128, Int128 -> paddingLayoutString(16, 0); // unsupported @@ -262,7 +274,7 @@ private String primitiveLayoutString(Primitive primitiveType, long align) { private String alignIfNeeded(String layoutPrefix, long align, long expectedAlign) { return align > expectedAlign ? - String.format("%1$s.align(%2$s, %3$d)", runtimeHelperName(), layoutPrefix, expectedAlign) : + String.format("%3$salign(%1$s, %2$d)", layoutPrefix, expectedAlign, sourceFileBuilder().FFMUtilsName()) : layoutPrefix; } diff --git a/src/main/java/org/openjdk/jextract/impl/CommonBindingsBuilder.java b/src/main/java/org/openjdk/jextract/impl/CommonBindingsBuilder.java new file mode 100644 index 00000000..88c6746e --- /dev/null +++ b/src/main/java/org/openjdk/jextract/impl/CommonBindingsBuilder.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2025, 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 org.openjdk.jextract.impl; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Moving common layouts and static methods not relevant to component areas into a centralized location + */ + +final class CommonBindingsBuilder extends ClassSourceBuilder { + private CommonBindingsBuilder(SourceFileBuilder builder, String className, String runtimeHelperName) { + super(builder, "public final", Kind.CLASS, className, null, null, runtimeHelperName); + } + + public static void generate(SourceFileBuilder builder, String className, String runtimeHelperName) { + CommonBindingsBuilder cbd = new CommonBindingsBuilder(builder, className, runtimeHelperName); + cbd.appendBlankLine(); + cbd.classBegin(); + cbd.emitPrivateConstructor(); + cbd.appendBlankLine(); + cbd.emitPrimitiveTypes(); + cbd.classEnd(); + } + + public static void generate(SourceFileBuilder builder, String className, String runtimeHelperName, List libs, Boolean useSystemLoadLibrary) { + CommonBindingsBuilder cbd = new CommonBindingsBuilder(builder, className, runtimeHelperName); + cbd.appendBlankLine(); + cbd.classBegin(); + cbd.emitPrivateConstructor(); + cbd.emitCommonFinalFields(); + cbd.emitFirstHeaderPreamble(libs, useSystemLoadLibrary); + cbd.appendBlankLine(); + cbd.emitRuntimeHelperMethods(); + cbd.classEnd(); + } + + // emit basic primitive types + private void emitPrimitiveTypes() { + appendIndentedLines(""" + public static final ValueLayout.OfBoolean C_BOOL = (ValueLayout.OfBoolean) Linker.nativeLinker().canonicalLayouts().get("bool"); + 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"); + 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)); + """); + 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\");"); + } + } + + void emitFirstHeaderPreamble(List libraries, boolean useSystemLoadLibrary) { + List lookups = new ArrayList<>(); + // if legacy library loading is selected, load libraries (if any) into current loader + if (useSystemLoadLibrary) { + appendBlankLine(); + appendIndentedLines(""" + + static { + """); + incrAlign(); + for (Options.Library lib : libraries) { + String method = lib.specKind() == Options.Library.SpecKind.PATH ? "load" : "loadLibrary"; + appendIndentedLines("System.%1$s(\"%2$s\");", method, lib.toQuotedName()); + } + decrAlign(); + appendIndentedLines(""" + } + """); + } else { + // otherwise, add a library lookup per library (if any) + libraries.stream() // add library lookups (if any) + .map(l -> l.specKind() == Options.Library.SpecKind.PATH ? + String.format("SymbolLookup.libraryLookup(\"%1$s\", LIBRARY_ARENA)", l.toQuotedName()) : + String.format("SymbolLookup.libraryLookup(System.mapLibraryName(\"%1$s\"), LIBRARY_ARENA)", l.toQuotedName())) + .collect(Collectors.toCollection(() -> lookups)); + } + + lookups.add("SymbolLookup.loaderLookup()"); // fallback to loader lookup + lookups.add("Linker.nativeLinker().defaultLookup()"); // fallback to native lookup + + // wrap all lookups (but the first) with ".or(...)" + List lookupCalls = new ArrayList<>(); + boolean isFirst = true; + for (String lookup : lookups) { + lookupCalls.add(isFirst ? lookup : String.format(".or(%1$s)", lookup)); + isFirst = false; + } + + // chain all the calls together into a combined symbol lookup + appendBlankLine(); + appendIndentedLines(lookupCalls.stream() + .collect(Collectors.joining(String.format("\n%1$s", indentString(2)), "static final SymbolLookup SYMBOL_LOOKUP = ", ";"))); + } + + private void emitCommonFinalFields() { + appendIndentedLines(""" + static final Arena LIBRARY_ARENA = Arena.ofAuto(); + static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls"); + + """); + } + private void emitRuntimeHelperMethods() { + appendIndentedLines(""" + static void traceDowncall(String name, Object... args) { + String traceArgs = Arrays.stream(args) + .map(Object::toString) + .collect(Collectors.joining(", ")); + System.out.printf("%s(%s)\\n", name, traceArgs); + } + + static MemorySegment findOrThrow(String symbol) { + return SYMBOL_LOOKUP.findOrThrow(symbol); + } + + static MethodHandle upcallHandle(Class fi, String name, FunctionDescriptor fdesc) { + try { + return MethodHandles.lookup().findVirtual(fi, name, fdesc.toMethodType()); + } catch (ReflectiveOperationException ex) { + throw new AssertionError(ex); + } + } + + static MemoryLayout align(MemoryLayout layout, long align) { + return switch (layout) { + case PaddingLayout p -> p; + case ValueLayout v -> v.withByteAlignment(align); + case GroupLayout g -> { + MemoryLayout[] alignedMembers = g.memberLayouts().stream() + .map(m -> align(m, align)).toArray(MemoryLayout[]::new); + yield g instanceof StructLayout ? + MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers); + } + case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align)); + }; + } + """); + } +} diff --git a/src/main/java/org/openjdk/jextract/impl/FunctionalInterfaceBuilder.java b/src/main/java/org/openjdk/jextract/impl/FunctionalInterfaceBuilder.java index 8ef036c8..23896a25 100644 --- a/src/main/java/org/openjdk/jextract/impl/FunctionalInterfaceBuilder.java +++ b/src/main/java/org/openjdk/jextract/impl/FunctionalInterfaceBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, 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 @@ -83,7 +83,7 @@ public interface %1$s { private void emitFunctionalFactory(String fiName) { appendIndentedLines(""" - private static final MethodHandle UP$MH = %1$s.upcallHandle(%2$s.%3$s.class, "apply", $DESC); + private static final MethodHandle UP$MH = %1$supcallHandle(%2$s.%3$s.class, "apply", $DESC); /** * Allocates a new upcall stub, whose implementation is defined by {@code fi}. @@ -92,7 +92,7 @@ private void emitFunctionalFactory(String fiName) { public static MemorySegment allocate(%2$s.%3$s fi, Arena arena) { return Linker.nativeLinker().upcallStub(UP$MH.bindTo(fi), $DESC, arena); } - """, runtimeHelperName(), className(), fiName); + """, sourceFileBuilder().FFMUtilsName(), className(), fiName); } private void emitInvoke() { diff --git a/src/main/java/org/openjdk/jextract/impl/HeaderFileBuilder.java b/src/main/java/org/openjdk/jextract/impl/HeaderFileBuilder.java index 5d6f16ee..5d640e6b 100644 --- a/src/main/java/org/openjdk/jextract/impl/HeaderFileBuilder.java +++ b/src/main/java/org/openjdk/jextract/impl/HeaderFileBuilder.java @@ -180,12 +180,12 @@ private void emitFunctionWrapper(String javaName, String nativeName, boolean nee private static class %1$s { public static final FunctionDescriptor DESC = %2$s; - public static final MemorySegment ADDR = %3$s.findOrThrow("%4$s"); + public static final MemorySegment ADDR = %4$sfindOrThrow("%3$s"); public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } """, holderClass, functionDescriptorString(1, decl.type()), - runtimeHelperName(), lookupName(decl)); + lookupName(decl), sourceFileBuilder().FFMUtilsName()); appendBlankLine(); emitDocComment(decl, "Function descriptor for:"); appendLines(""" @@ -213,8 +213,8 @@ private static class %1$s { public static %1$s %2$s(%3$s) { var mh$ = %4$s.HANDLE; try { - if (TRACE_DOWNCALLS) { - traceDowncall(%5$s); + if (%8$sTRACE_DOWNCALLS) { + %8$straceDowncall(%5$s); } %6$smh$.invokeExact(%7$s); } catch (Throwable ex$) { @@ -223,7 +223,8 @@ private static class %1$s { } """, retType, javaName, paramExprs(declType, finalParamNames, isVarArg), - holderClass, traceArgList, returnWithCast, paramList); + holderClass, traceArgList, returnWithCast, paramList, + sourceFileBuilder().FFMUtilsName()); } else { String invokerClassName = newHolderClassName(javaName); String paramExprs = paramExprs(declType, finalParamNames, isVarArg); @@ -232,19 +233,19 @@ private static class %1$s { appendLines(""" public static class %1$s { private static final FunctionDescriptor BASE_DESC = %2$s; - private static final MemorySegment ADDR = %3$s.findOrThrow("%4$s"); + private static final MemorySegment ADDR = %4$sfindOrThrow("%3$s"); private final MethodHandle handle; private final FunctionDescriptor descriptor; private final MethodHandle spreader; - private %5$s(MethodHandle handle, FunctionDescriptor descriptor, MethodHandle spreader) { + private %1$s(MethodHandle handle, FunctionDescriptor descriptor, MethodHandle spreader) { this.handle = handle; this.descriptor = descriptor; this.spreader = spreader; } """, invokerClassName, functionDescriptorString(2, decl.type()), - runtimeHelperName(), lookupName(decl), invokerClassName); + lookupName(decl), sourceFileBuilder().FFMUtilsName()); incrAlign(); appendBlankLine(); emitDocComment(decl, "Variadic invoker factory for:"); @@ -283,8 +284,8 @@ public FunctionDescriptor descriptor() { public %1$s apply(%2$s) { try { - if (TRACE_DOWNCALLS) { - traceDowncall(%3$s); + if (%6$sTRACE_DOWNCALLS) { + %6$straceDowncall(%3$s); } %4$s spreader.invokeExact(%5$s); } catch(IllegalArgumentException | ClassCastException ex$) { @@ -294,7 +295,8 @@ public FunctionDescriptor descriptor() { } } } - """, retType, paramExprs, traceArgList, returnWithCast, paramList); + """, retType, paramExprs, traceArgList, + returnWithCast, paramList, sourceFileBuilder().FFMUtilsName()); } decrAlign(); } @@ -307,91 +309,6 @@ void emitPointerTypedef(Declaration.Typedef typedefTree, String name) { emitPrimitiveTypedefLayout(name, Type.pointer(), typedefTree); } - void emitFirstHeaderPreamble(List libraries, boolean useSystemLoadLibrary) { - List lookups = new ArrayList<>(); - // if legacy library loading is selected, load libraries (if any) into current loader - if (useSystemLoadLibrary) { - appendBlankLine(); - appendIndentedLines(""" - - static { - """); - incrAlign(); - for (Options.Library lib : libraries) { - String method = lib.specKind() == Options.Library.SpecKind.PATH ? "load" : "loadLibrary"; - appendIndentedLines("System.%1$s(\"%2$s\");", method, lib.toQuotedName()); - } - decrAlign(); - appendIndentedLines(""" - } - """); - } else { - // otherwise, add a library lookup per library (if any) - libraries.stream() // add library lookups (if any) - .map(l -> l.specKind() == Options.Library.SpecKind.PATH ? - String.format("SymbolLookup.libraryLookup(\"%1$s\", LIBRARY_ARENA)", l.toQuotedName()) : - String.format("SymbolLookup.libraryLookup(System.mapLibraryName(\"%1$s\"), LIBRARY_ARENA)", l.toQuotedName())) - .collect(Collectors.toCollection(() -> lookups)); - } - - lookups.add("SymbolLookup.loaderLookup()"); // fallback to loader lookup - lookups.add("Linker.nativeLinker().defaultLookup()"); // fallback to native lookup - - // wrap all lookups (but the first) with ".or(...)" - List lookupCalls = new ArrayList<>(); - boolean isFirst = true; - for (String lookup : lookups) { - lookupCalls.add(isFirst ? lookup : String.format(".or(%1$s)", lookup)); - isFirst = false; - } - - // chain all the calls together into a combined symbol lookup - appendBlankLine(); - appendIndentedLines(lookupCalls.stream() - .collect(Collectors.joining(String.format("\n%1$s", indentString(2)), "static final SymbolLookup SYMBOL_LOOKUP = ", ";"))); - } - - void emitRuntimeHelperMethods() { - appendIndentedLines(""" - - static final Arena LIBRARY_ARENA = Arena.ofAuto(); - static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls"); - - static void traceDowncall(String name, Object... args) { - String traceArgs = Arrays.stream(args) - .map(Object::toString) - .collect(Collectors.joining(", ")); - System.out.printf("%s(%s)\\n", name, traceArgs); - } - - static MemorySegment findOrThrow(String symbol) { - return SYMBOL_LOOKUP.findOrThrow(symbol); - } - - static MethodHandle upcallHandle(Class fi, String name, FunctionDescriptor fdesc) { - try { - return MethodHandles.lookup().findVirtual(fi, name, fdesc.toMethodType()); - } catch (ReflectiveOperationException ex) { - throw new AssertionError(ex); - } - } - - static MemoryLayout align(MemoryLayout layout, long align) { - return switch (layout) { - case PaddingLayout p -> p; - case ValueLayout v -> v.withByteAlignment(align); - case GroupLayout g -> { - MemoryLayout[] alignedMembers = g.memberLayouts().stream() - .map(m -> align(m, align)).toArray(MemoryLayout[]::new); - yield g instanceof StructLayout ? - MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers); - } - case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align)); - }; - } - """); - } - private void emitGlobalGetter(String holderClass, String javaName, Declaration.Variable decl, String docHeader) { appendBlankLine(); @@ -515,19 +432,20 @@ private String emitVarHolderClass(Declaration.Variable var, String javaName) { appendIndentedLines(""" private static class %1$s { public static final %2$s LAYOUT = %3$s; - public static final MemorySegment SEGMENT = %4$s.findOrThrow("%5$s").reinterpret(LAYOUT.byteSize()); - %6$s - public static final long[] DIMS = { %7$s }; + public static final MemorySegment SEGMENT = %7$sfindOrThrow("%4$s").reinterpret(LAYOUT.byteSize()); + %5$s + public static final long[] DIMS = { %6$s }; } - """, mangledName, layoutType, layoutString(varType), runtimeHelperName(), - lookupName(var), accessHandle, dimsString); + """, mangledName, layoutType, layoutString(varType), lookupName(var), + accessHandle, dimsString, sourceFileBuilder().FFMUtilsName()); } else { appendIndentedLines(""" private static class %1$s { public static final %2$s LAYOUT = %3$s; - public static final MemorySegment SEGMENT = %4$s.findOrThrow("%5$s").reinterpret(LAYOUT.byteSize()); + public static final MemorySegment SEGMENT = %5$sfindOrThrow("%4$s").reinterpret(LAYOUT.byteSize()); } - """, mangledName, layoutType, layoutString(varType), runtimeHelperName(), lookupName(var)); + """, mangledName, layoutType, layoutString(varType), + lookupName(var), sourceFileBuilder().FFMUtilsName()); } incrAlign(); appendBlankLine(); @@ -567,15 +485,15 @@ private void emitConstant(Class javaType, String constantName, Object value, public static %1$s %2$s() { class Holder { static final %1$s %2$s - = %3$s.LIBRARY_ARENA.allocateFrom("%4$s"); + = %4$sLIBRARY_ARENA.allocateFrom("%3$s"); } return Holder.%2$s; } """, javaType.getSimpleName(), constantName, - runtimeHelperName(), - Utils.quote(Objects.toString(value))); + Utils.quote(Objects.toString(value)), + sourceFileBuilder().FFMUtilsName()); } else { appendLines(""" private static final %1$s %2$s = %3$s; diff --git a/src/main/java/org/openjdk/jextract/impl/SourceFileBuilder.java b/src/main/java/org/openjdk/jextract/impl/SourceFileBuilder.java index 0d55f0f5..bf55b8e0 100644 --- a/src/main/java/org/openjdk/jextract/impl/SourceFileBuilder.java +++ b/src/main/java/org/openjdk/jextract/impl/SourceFileBuilder.java @@ -49,10 +49,25 @@ public String className() { return className; } + public String getPackageName(){ + return packageName; + } + + public String layoutUtilsName() { + return getPackageName().isEmpty() ? ToplevelBuilder.LAYOUT_UTILS + "." : ""; + } + + public String FFMUtilsName() { + return getPackageName().isEmpty() ? ToplevelBuilder.FFM_UTILS + "." : ""; + } + public static SourceFileBuilder newSourceFile(String packageName, String className) { SourceFileBuilder sfb = new SourceFileBuilder(packageName, className); sfb.emitPackagePrefix(); sfb.emitImportSection(); + if (!className.equals("FFMUtils") && !className.equals("LayoutUtils") && !packageName.isEmpty()){ + sfb.emitStaticImportSection(); + } return sfb; } @@ -81,9 +96,16 @@ void emitImportSection() { import static java.lang.foreign.ValueLayout.*; import static java.lang.foreign.MemoryLayout.PathElement.*; + """); } + void emitStaticImportSection() { + appendLines(String.format(""" + import static %1$s.FFMUtils.*; + import static %1$s.LayoutUtils.*; + """, packageName)); + } // Internal generation helpers (used by other builders) diff --git a/src/main/java/org/openjdk/jextract/impl/ToplevelBuilder.java b/src/main/java/org/openjdk/jextract/impl/ToplevelBuilder.java index 7893c63b..a0833ea7 100644 --- a/src/main/java/org/openjdk/jextract/impl/ToplevelBuilder.java +++ b/src/main/java/org/openjdk/jextract/impl/ToplevelBuilder.java @@ -41,12 +41,15 @@ */ class ToplevelBuilder implements OutputFactory.Builder { private static final int DECLS_PER_HEADER_CLASS = Integer.getInteger("jextract.decls.per.header", 1000); - public static final String PREV_SUFFIX = "#{PREV_SUFFIX}"; + private static final String PREV_SUFFIX = "#{PREV_SUFFIX}"; private static final String SUFFIX = "#{SUFFIX}"; + public static final String LAYOUT_UTILS = "LayoutUtils"; + public static final String FFM_UTILS = "FFMUtils"; private int declCount; private final List headerBuilders = new ArrayList<>(); private final List otherBuilders = new ArrayList<>(); + private final List commonUtilsBuilders = new ArrayList<>(); private HeaderFileBuilder lastHeader; private final ClassDesc headerDesc; @@ -55,35 +58,25 @@ class ToplevelBuilder implements OutputFactory.Builder { this.headerDesc = ClassDesc.of(packageName, headerClassName); SourceFileBuilder sfb = SourceFileBuilder.newSourceFile(packageName, headerClassName); headerBuilders.add(sfb); - lastHeader = createFirstHeader(sfb, libs, useSystemLoadLibrary); + lastHeader = createFirstHeader(sfb); + generateCommonBindings(libs, useSystemLoadLibrary); } - private static HeaderFileBuilder createFirstHeader(SourceFileBuilder sfb, List libs, boolean useSystemLoadLibrary) { - HeaderFileBuilder first = new HeaderFileBuilder(sfb, String.format("%1$s#{SUFFIX}",sfb.className()), null, sfb.className()); + private void generateCommonBindings(List libs, boolean useSystemLoadLibrary) { + var sfb = SourceFileBuilder.newSourceFile(packageName(), LAYOUT_UTILS); + commonUtilsBuilders.add(sfb); + CommonBindingsBuilder.generate(sfb, LAYOUT_UTILS, mainHeaderClassName()); + + sfb = SourceFileBuilder.newSourceFile(packageName(), FFM_UTILS); + commonUtilsBuilders.add(sfb); + CommonBindingsBuilder.generate(sfb, FFM_UTILS, mainHeaderClassName(), libs, useSystemLoadLibrary); + } + + private static HeaderFileBuilder createFirstHeader(SourceFileBuilder sfb) { + HeaderFileBuilder first = new HeaderFileBuilder(sfb, String.format("%1$s%2$s", sfb.className(), SUFFIX), null, sfb.className()); first.appendBlankLine(); first.classBegin(); first.emitDefaultConstructor(); - first.emitRuntimeHelperMethods(); - first.emitFirstHeaderPreamble(libs, useSystemLoadLibrary); - // emit basic primitive types - first.appendIndentedLines(""" - - public static final ValueLayout.OfBoolean C_BOOL = (ValueLayout.OfBoolean) Linker.nativeLinker().canonicalLayouts().get("bool"); - 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"); - 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)); - """); - if (TypeImpl.IS_WINDOWS) { - first.appendIndentedLines("public static final ValueLayout.OfInt C_LONG = (ValueLayout.OfInt) Linker.nativeLinker().canonicalLayouts().get(\"long\");"); - first.appendIndentedLines("public static final ValueLayout.OfDouble C_LONG_DOUBLE = (ValueLayout.OfDouble) Linker.nativeLinker().canonicalLayouts().get(\"double\");"); - } else { - first.appendIndentedLines("public static final ValueLayout.OfLong C_LONG = (ValueLayout.OfLong) Linker.nativeLinker().canonicalLayouts().get(\"long\");"); - } return first; } @@ -118,6 +111,9 @@ public List toFiles() { // add remaining builders files.addAll(otherBuilders.stream() .map(SourceFileBuilder::toFile).toList()); + //add common bindings files + files.addAll(commonUtilsBuilders.stream() + .map(SourceFileBuilder::toFile).toList()); return files; } diff --git a/test/jtreg/generator/clinitCycles/TestGlobal.java b/test/jtreg/generator/clinitCycles/TestGlobal.java index d7f8c58c..6f02406c 100644 --- a/test/jtreg/generator/clinitCycles/TestGlobal.java +++ b/test/jtreg/generator/clinitCycles/TestGlobal.java @@ -1,4 +1,4 @@ -/* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2023, 2025, 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 @@ -40,7 +40,7 @@ public class TestGlobal { @Test public void testGlobal() { - ValueLayout layout = clinit_global_h.C_INT; + ValueLayout layout = LayoutUtils.C_INT; assertNotNull(layout); assertEquals(clinit_global_h.global1(), 1); assertEquals(clinit_global_h.global2(), 2); diff --git a/test/jtreg/generator/clinitCycles/TestStruct.java b/test/jtreg/generator/clinitCycles/TestStruct.java index 931f64ca..6c250b38 100644 --- a/test/jtreg/generator/clinitCycles/TestStruct.java +++ b/test/jtreg/generator/clinitCycles/TestStruct.java @@ -1,4 +1,4 @@ -/* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2023, 2025, 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 @@ -22,6 +22,7 @@ import org.testng.annotations.Test; import test.jextract.clinit.*; +import static test.jextract.clinit.LayoutUtils.*; import java.lang.foreign.GroupLayout; import java.lang.foreign.ValueLayout; @@ -40,7 +41,7 @@ public class TestStruct { @Test public void TestStruct() { - ValueLayout layout = clinit_struct_h.C_INT; + ValueLayout layout = C_INT; assertNotNull(layout); GroupLayout pointLayout = Point.layout(); assertNotNull(pointLayout); diff --git a/test/jtreg/generator/clinitCycles/TestTypedef.java b/test/jtreg/generator/clinitCycles/TestTypedef.java index 705c629c..a4289d36 100644 --- a/test/jtreg/generator/clinitCycles/TestTypedef.java +++ b/test/jtreg/generator/clinitCycles/TestTypedef.java @@ -1,4 +1,4 @@ -/* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2023, 2025, 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 @@ -40,7 +40,7 @@ public class TestTypedef { @Test public void TestTypedef() { - ValueLayout layout = clinit_typedef_h.C_INT; + ValueLayout layout = LayoutUtils.C_INT; assertNotNull(layout); assertEquals(layout, clinit_typedef_h.one); assertEquals(layout, clinit_typedef_h.two); diff --git a/test/jtreg/generator/dedup/TestDedup.java b/test/jtreg/generator/dedup/TestDedup.java index 998c3019..6a4e093d 100644 --- a/test/jtreg/generator/dedup/TestDedup.java +++ b/test/jtreg/generator/dedup/TestDedup.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, 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 @@ -46,7 +46,7 @@ public void testMacroFields() { } void checkLayout(GroupLayout layout) { - assertEquals(layout.memberLayouts().get(0), macro_fields_h.C_INT.withName("x")); - assertEquals(layout.memberLayouts().get(1), macro_fields_h.C_INT.withName("y")); + assertEquals(layout.memberLayouts().get(0), LayoutUtils.C_INT.withName("x")); + assertEquals(layout.memberLayouts().get(1), LayoutUtils.C_INT.withName("y")); } } diff --git a/test/jtreg/generator/nestedStructTypedef/TestNestedStructTypedef.java b/test/jtreg/generator/nestedStructTypedef/TestNestedStructTypedef.java index 57ab0a92..22f274f1 100644 --- a/test/jtreg/generator/nestedStructTypedef/TestNestedStructTypedef.java +++ b/test/jtreg/generator/nestedStructTypedef/TestNestedStructTypedef.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, 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 @@ -45,6 +45,6 @@ public void testMacroFields() { } void checkLayout(GroupLayout layout) { - assertEquals(layout.memberLayouts().get(0), nestedStructTypedef_h.C_INT.withName("x")); + assertEquals(layout.memberLayouts().get(0), LayoutUtils.C_INT.withName("x")); } } diff --git a/test/jtreg/generator/nestedTypes/TestNestedTypes.java b/test/jtreg/generator/nestedTypes/TestNestedTypes.java index fa5377e8..3d19c76d 100644 --- a/test/jtreg/generator/nestedTypes/TestNestedTypes.java +++ b/test/jtreg/generator/nestedTypes/TestNestedTypes.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, 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 @@ -56,5 +56,5 @@ void checkNestedLayout(MemoryLayout layout) { assertEquals(((SequenceLayout)nestedLayout).elementLayout().withoutName(), ELEM_NESTED_LAYOUT); } - static final MemoryLayout ELEM_NESTED_LAYOUT = MemoryLayout.structLayout(nested_types_h.C_INT.withName("x")); + static final MemoryLayout ELEM_NESTED_LAYOUT = MemoryLayout.structLayout(LayoutUtils.C_INT.withName("x")); } diff --git a/test/jtreg/generator/nestedTypes/TestNestedTypesNames.java b/test/jtreg/generator/nestedTypes/TestNestedTypesNames.java index 673a41ed..ba6ff95e 100644 --- a/test/jtreg/generator/nestedTypes/TestNestedTypesNames.java +++ b/test/jtreg/generator/nestedTypes/TestNestedTypesNames.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, 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 @@ -41,11 +41,11 @@ public class TestNestedTypesNames { static MemoryLayout ARG_STRUCT = MemoryLayout.structLayout( - nested_types_names_h.C_INT.withName("y") + LayoutUtils.C_INT.withName("y") ); static MemoryLayout RET_STRUCT = MemoryLayout.structLayout( - nested_types_names_h.C_INT.withName("x") + LayoutUtils.C_INT.withName("x") ); static FunctionDescriptor FUNC_DESC = FunctionDescriptor.of( diff --git a/test/jtreg/generator/nestedTypes/TestNestedTypesUnsupported.java b/test/jtreg/generator/nestedTypes/TestNestedTypesUnsupported.java index 94e45f8b..e88ac37f 100644 --- a/test/jtreg/generator/nestedTypes/TestNestedTypesUnsupported.java +++ b/test/jtreg/generator/nestedTypes/TestNestedTypesUnsupported.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, 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 @@ -42,7 +42,7 @@ public class TestNestedTypesUnsupported { static MemoryLayout UNDEFINED_STRUCT = MemoryLayout.structLayout( - MemoryLayout.paddingLayout(nested_types_unsupported_h.C_POINTER.byteSize()) + MemoryLayout.paddingLayout(LayoutUtils.C_POINTER.byteSize()) ); @Test diff --git a/test/jtreg/generator/test8244412/LibTest8244412Test.java b/test/jtreg/generator/test8244412/LibTest8244412Test.java index 80b6485f..39d90d8a 100644 --- a/test/jtreg/generator/test8244412/LibTest8244412Test.java +++ b/test/jtreg/generator/test8244412/LibTest8244412Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, 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 @@ -31,6 +31,7 @@ import test.jextract.test8244412.*; import static test.jextract.test8244412.test8244412_h.*; +import static test.jextract.test8244412.LayoutUtils.*; /* * @test diff --git a/test/jtreg/generator/test8245003/Test8245003.java b/test/jtreg/generator/test8245003/Test8245003.java index e3b729a2..2008a90e 100644 --- a/test/jtreg/generator/test8245003/Test8245003.java +++ b/test/jtreg/generator/test8245003/Test8245003.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, 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 @@ -59,8 +59,8 @@ public void testStructAccessor() { @Test public void testArrayAccessor() { var seg = iarr(); - assertEquals(seg.byteSize(), C_INT.byteSize()*5); - int[] arr = seg.toArray(C_INT); + assertEquals(seg.byteSize(), LayoutUtils.C_INT.byteSize()*5); + int[] arr = seg.toArray(LayoutUtils.C_INT); assertEquals(arr.length, 5); assertEquals(arr[0], 2); assertEquals(arr[1], -2); @@ -72,7 +72,7 @@ public void testArrayAccessor() { assertEquals(seg.byteSize(), Foo.sizeof()); assertEquals(Foo.count(seg), 37); var greeting = Foo.greeting(seg); - byte[] barr = greeting.toArray(C_CHAR); + byte[] barr = greeting.toArray(LayoutUtils.C_CHAR); assertEquals(new String(barr), "hello"); } } diff --git a/test/jtreg/generator/test8246341/LibTest8246341Test.java b/test/jtreg/generator/test8246341/LibTest8246341Test.java index 0972d3a5..ce98f2e9 100644 --- a/test/jtreg/generator/test8246341/LibTest8246341Test.java +++ b/test/jtreg/generator/test8246341/LibTest8246341Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, 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 @@ -28,6 +28,7 @@ import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; import static test.jextract.test8246341.test8246341_h.*; +import static test.jextract.test8246341.LayoutUtils.*; /* * @test diff --git a/test/jtreg/generator/test8252121/Test8252121.java b/test/jtreg/generator/test8252121/Test8252121.java index b70cf1c4..6a112ece 100644 --- a/test/jtreg/generator/test8252121/Test8252121.java +++ b/test/jtreg/generator/test8252121/Test8252121.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, 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 @@ -46,7 +46,7 @@ public class Test8252121 { public void test() { try (var arena = Arena.ofConfined()) { int[] array = { 3, 5, 89, 34, -33 }; - MemorySegment seg = arena.allocateFrom(C_INT, array); + MemorySegment seg = arena.allocateFrom(LayoutUtils.C_INT, array); assertEquals(IntStream.of(array).sum(), sum(seg)); assertEquals(IntStream.of(array).reduce(1, (a,b) -> a*b), mul(seg)); } diff --git a/test/jtreg/generator/test8257892/LibUnsupportedTest.java b/test/jtreg/generator/test8257892/LibUnsupportedTest.java index 283a6bdd..ac33c5af 100644 --- a/test/jtreg/generator/test8257892/LibUnsupportedTest.java +++ b/test/jtreg/generator/test8257892/LibUnsupportedTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, 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 @@ -34,6 +34,8 @@ import static org.testng.Assert.*; import static test.jextract.unsupported.unsupported_h.*; +import static test.jextract.unsupported.LayoutUtils.*; +import static test.jextract.unsupported.FFMUtils.*; import test.jextract.unsupported.*; /* diff --git a/test/jtreg/generator/testPrintf/TestPrintf.java b/test/jtreg/generator/testPrintf/TestPrintf.java index c81d0911..83aa7aa5 100644 --- a/test/jtreg/generator/testPrintf/TestPrintf.java +++ b/test/jtreg/generator/testPrintf/TestPrintf.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, 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 @@ -33,6 +33,7 @@ import static org.testng.Assert.assertEquals; import static test.jextract.printf.printf_h.*; +import static test.jextract.printf.LayoutUtils.*; /* * @test diff --git a/test/jtreg/generator/testStruct/LibStructTest.java b/test/jtreg/generator/testStruct/LibStructTest.java index 4851f22c..1e4d2d4f 100644 --- a/test/jtreg/generator/testStruct/LibStructTest.java +++ b/test/jtreg/generator/testStruct/LibStructTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, 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 @@ -31,6 +31,8 @@ import static org.testng.Assert.assertEquals; import static test.jextract.struct.struct_h.*; +import static test.jextract.struct.LayoutUtils.*; +import static test.jextract.struct.FFMUtils.*; import test.jextract.struct.*; /* From fa41fe8b40dd494d752c42e42a950a0c9f5b596f Mon Sep 17 00:00:00 2001 From: Nizar Benalla Date: Tue, 4 Feb 2025 14:03:34 +0100 Subject: [PATCH 02/10] replace hardcoded strings I missed --- src/main/java/org/openjdk/jextract/impl/SourceFileBuilder.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/openjdk/jextract/impl/SourceFileBuilder.java b/src/main/java/org/openjdk/jextract/impl/SourceFileBuilder.java index bf55b8e0..f42a3249 100644 --- a/src/main/java/org/openjdk/jextract/impl/SourceFileBuilder.java +++ b/src/main/java/org/openjdk/jextract/impl/SourceFileBuilder.java @@ -65,7 +65,8 @@ public static SourceFileBuilder newSourceFile(String packageName, String classNa SourceFileBuilder sfb = new SourceFileBuilder(packageName, className); sfb.emitPackagePrefix(); sfb.emitImportSection(); - if (!className.equals("FFMUtils") && !className.equals("LayoutUtils") && !packageName.isEmpty()){ + if (!className.equals(ToplevelBuilder.FFM_UTILS) && !className.equals(ToplevelBuilder.LAYOUT_UTILS) + && !packageName.isEmpty()) { sfb.emitStaticImportSection(); } return sfb; From d4273106723871353330ad96a3ee657e50debef0 Mon Sep 17 00:00:00 2001 From: Nizar Benalla Date: Thu, 27 Mar 2025 01:36:26 +0000 Subject: [PATCH 03/10] Revert all changes - starting over from scratch --- .../jextract/impl/ClassSourceBuilder.java | 42 ++--- .../jextract/impl/CommonBindingsBuilder.java | 171 ------------------ .../impl/FunctionalInterfaceBuilder.java | 6 +- .../jextract/impl/HeaderFileBuilder.java | 130 ++++++++++--- .../jextract/impl/SourceFileBuilder.java | 23 --- .../jextract/impl/ToplevelBuilder.java | 46 ++--- .../generator/clinitCycles/TestGlobal.java | 4 +- .../generator/clinitCycles/TestStruct.java | 5 +- .../generator/clinitCycles/TestTypedef.java | 4 +- test/jtreg/generator/dedup/TestDedup.java | 6 +- .../TestNestedStructTypedef.java | 4 +- .../nestedTypes/TestNestedTypes.java | 4 +- .../nestedTypes/TestNestedTypesNames.java | 6 +- .../TestNestedTypesUnsupported.java | 4 +- .../test8244412/LibTest8244412Test.java | 3 +- .../generator/test8245003/Test8245003.java | 8 +- .../test8246341/LibTest8246341Test.java | 3 +- .../generator/test8252121/Test8252121.java | 4 +- .../test8257892/LibUnsupportedTest.java | 4 +- .../generator/testPrintf/TestPrintf.java | 3 +- .../generator/testStruct/LibStructTest.java | 4 +- 21 files changed, 178 insertions(+), 306 deletions(-) delete mode 100644 src/main/java/org/openjdk/jextract/impl/CommonBindingsBuilder.java diff --git a/src/main/java/org/openjdk/jextract/impl/ClassSourceBuilder.java b/src/main/java/org/openjdk/jextract/impl/ClassSourceBuilder.java index 8808695e..051287ac 100644 --- a/src/main/java/org/openjdk/jextract/impl/ClassSourceBuilder.java +++ b/src/main/java/org/openjdk/jextract/impl/ClassSourceBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, 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 @@ -160,15 +160,6 @@ final void emitDefaultConstructor() { """, className); } - final void emitPrivateConstructor() { - appendIndentedLines(""" - - private %1$s() { - // Should not be called directly - } - """, className); - } - final void emitDocComment(Declaration decl) { emitDocComment(decl, ""); } @@ -209,14 +200,11 @@ String layoutString(Type type, long align) { return switch (type) { case Primitive p -> primitiveLayoutString(p, 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(sb.layoutUtilsName() + "C_POINTER", 8, 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 -> layoutString(d.type(), align); - case Function _ -> alignIfNeeded(sb.layoutUtilsName() + "C_POINTER", 8, align); - case Array a -> - String.format("MemoryLayout.sequenceLayout(%1$d, %2$s)", a.elementCount().orElse(0L), layoutString(a.elementType(), 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(); }; } @@ -255,16 +243,16 @@ String indentString(int size) { private String primitiveLayoutString(Primitive primitiveType, long align) { return switch (primitiveType.kind()) { - case Bool -> sb.layoutUtilsName() + "C_BOOL"; - case Char -> sb.layoutUtilsName() + "C_CHAR"; - case Short -> alignIfNeeded(sb.layoutUtilsName() + "C_SHORT", 2, align); - case Int -> alignIfNeeded(sb.layoutUtilsName() + "C_INT", 4, align); - case Long -> alignIfNeeded(sb.layoutUtilsName() + "C_LONG", TypeImpl.IS_WINDOWS ? 4 : 8, align); - case LongLong -> alignIfNeeded(sb.layoutUtilsName() + "C_LONG_LONG", 8, align); - case Float -> alignIfNeeded(sb.layoutUtilsName() + "C_FLOAT", 4, align); - case Double -> alignIfNeeded(sb.layoutUtilsName() + "C_DOUBLE", 8, align); + case Bool -> runtimeHelperName() + ".C_BOOL"; + case Char -> runtimeHelperName() + ".C_CHAR"; + case Short -> alignIfNeeded(runtimeHelperName() + ".C_SHORT", 2, align); + case Int -> alignIfNeeded(runtimeHelperName() + ".C_INT", 4, align); + case Long -> alignIfNeeded(runtimeHelperName() + ".C_LONG", TypeImpl.IS_WINDOWS ? 4 : 8, align); + case LongLong -> alignIfNeeded(runtimeHelperName() + ".C_LONG_LONG", 8, align); + case Float -> alignIfNeeded(runtimeHelperName() + ".C_FLOAT", 4, align); + case Double -> alignIfNeeded(runtimeHelperName() + ".C_DOUBLE", 8, align); case LongDouble -> TypeImpl.IS_WINDOWS ? - alignIfNeeded(sb.layoutUtilsName() + "C_LONG_DOUBLE", 8, align) : + alignIfNeeded(runtimeHelperName() + ".C_LONG_DOUBLE", 8, align) : paddingLayoutString(8, 0); case HalfFloat, Char16, WChar -> paddingLayoutString(2, 0); // unsupported case Float128, Int128 -> paddingLayoutString(16, 0); // unsupported @@ -274,7 +262,7 @@ private String primitiveLayoutString(Primitive primitiveType, long align) { private String alignIfNeeded(String layoutPrefix, long align, long expectedAlign) { return align > expectedAlign ? - String.format("%3$salign(%1$s, %2$d)", layoutPrefix, expectedAlign, sourceFileBuilder().FFMUtilsName()) : + String.format("%1$s.align(%2$s, %3$d)", runtimeHelperName(), layoutPrefix, expectedAlign) : layoutPrefix; } diff --git a/src/main/java/org/openjdk/jextract/impl/CommonBindingsBuilder.java b/src/main/java/org/openjdk/jextract/impl/CommonBindingsBuilder.java deleted file mode 100644 index 88c6746e..00000000 --- a/src/main/java/org/openjdk/jextract/impl/CommonBindingsBuilder.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (c) 2025, 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 org.openjdk.jextract.impl; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -/** - * Moving common layouts and static methods not relevant to component areas into a centralized location - */ - -final class CommonBindingsBuilder extends ClassSourceBuilder { - private CommonBindingsBuilder(SourceFileBuilder builder, String className, String runtimeHelperName) { - super(builder, "public final", Kind.CLASS, className, null, null, runtimeHelperName); - } - - public static void generate(SourceFileBuilder builder, String className, String runtimeHelperName) { - CommonBindingsBuilder cbd = new CommonBindingsBuilder(builder, className, runtimeHelperName); - cbd.appendBlankLine(); - cbd.classBegin(); - cbd.emitPrivateConstructor(); - cbd.appendBlankLine(); - cbd.emitPrimitiveTypes(); - cbd.classEnd(); - } - - public static void generate(SourceFileBuilder builder, String className, String runtimeHelperName, List libs, Boolean useSystemLoadLibrary) { - CommonBindingsBuilder cbd = new CommonBindingsBuilder(builder, className, runtimeHelperName); - cbd.appendBlankLine(); - cbd.classBegin(); - cbd.emitPrivateConstructor(); - cbd.emitCommonFinalFields(); - cbd.emitFirstHeaderPreamble(libs, useSystemLoadLibrary); - cbd.appendBlankLine(); - cbd.emitRuntimeHelperMethods(); - cbd.classEnd(); - } - - // emit basic primitive types - private void emitPrimitiveTypes() { - appendIndentedLines(""" - public static final ValueLayout.OfBoolean C_BOOL = (ValueLayout.OfBoolean) Linker.nativeLinker().canonicalLayouts().get("bool"); - 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"); - 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)); - """); - 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\");"); - } - } - - void emitFirstHeaderPreamble(List libraries, boolean useSystemLoadLibrary) { - List lookups = new ArrayList<>(); - // if legacy library loading is selected, load libraries (if any) into current loader - if (useSystemLoadLibrary) { - appendBlankLine(); - appendIndentedLines(""" - - static { - """); - incrAlign(); - for (Options.Library lib : libraries) { - String method = lib.specKind() == Options.Library.SpecKind.PATH ? "load" : "loadLibrary"; - appendIndentedLines("System.%1$s(\"%2$s\");", method, lib.toQuotedName()); - } - decrAlign(); - appendIndentedLines(""" - } - """); - } else { - // otherwise, add a library lookup per library (if any) - libraries.stream() // add library lookups (if any) - .map(l -> l.specKind() == Options.Library.SpecKind.PATH ? - String.format("SymbolLookup.libraryLookup(\"%1$s\", LIBRARY_ARENA)", l.toQuotedName()) : - String.format("SymbolLookup.libraryLookup(System.mapLibraryName(\"%1$s\"), LIBRARY_ARENA)", l.toQuotedName())) - .collect(Collectors.toCollection(() -> lookups)); - } - - lookups.add("SymbolLookup.loaderLookup()"); // fallback to loader lookup - lookups.add("Linker.nativeLinker().defaultLookup()"); // fallback to native lookup - - // wrap all lookups (but the first) with ".or(...)" - List lookupCalls = new ArrayList<>(); - boolean isFirst = true; - for (String lookup : lookups) { - lookupCalls.add(isFirst ? lookup : String.format(".or(%1$s)", lookup)); - isFirst = false; - } - - // chain all the calls together into a combined symbol lookup - appendBlankLine(); - appendIndentedLines(lookupCalls.stream() - .collect(Collectors.joining(String.format("\n%1$s", indentString(2)), "static final SymbolLookup SYMBOL_LOOKUP = ", ";"))); - } - - private void emitCommonFinalFields() { - appendIndentedLines(""" - static final Arena LIBRARY_ARENA = Arena.ofAuto(); - static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls"); - - """); - } - private void emitRuntimeHelperMethods() { - appendIndentedLines(""" - static void traceDowncall(String name, Object... args) { - String traceArgs = Arrays.stream(args) - .map(Object::toString) - .collect(Collectors.joining(", ")); - System.out.printf("%s(%s)\\n", name, traceArgs); - } - - static MemorySegment findOrThrow(String symbol) { - return SYMBOL_LOOKUP.findOrThrow(symbol); - } - - static MethodHandle upcallHandle(Class fi, String name, FunctionDescriptor fdesc) { - try { - return MethodHandles.lookup().findVirtual(fi, name, fdesc.toMethodType()); - } catch (ReflectiveOperationException ex) { - throw new AssertionError(ex); - } - } - - static MemoryLayout align(MemoryLayout layout, long align) { - return switch (layout) { - case PaddingLayout p -> p; - case ValueLayout v -> v.withByteAlignment(align); - case GroupLayout g -> { - MemoryLayout[] alignedMembers = g.memberLayouts().stream() - .map(m -> align(m, align)).toArray(MemoryLayout[]::new); - yield g instanceof StructLayout ? - MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers); - } - case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align)); - }; - } - """); - } -} diff --git a/src/main/java/org/openjdk/jextract/impl/FunctionalInterfaceBuilder.java b/src/main/java/org/openjdk/jextract/impl/FunctionalInterfaceBuilder.java index 23896a25..8ef036c8 100644 --- a/src/main/java/org/openjdk/jextract/impl/FunctionalInterfaceBuilder.java +++ b/src/main/java/org/openjdk/jextract/impl/FunctionalInterfaceBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, 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 @@ -83,7 +83,7 @@ public interface %1$s { private void emitFunctionalFactory(String fiName) { appendIndentedLines(""" - private static final MethodHandle UP$MH = %1$supcallHandle(%2$s.%3$s.class, "apply", $DESC); + private static final MethodHandle UP$MH = %1$s.upcallHandle(%2$s.%3$s.class, "apply", $DESC); /** * Allocates a new upcall stub, whose implementation is defined by {@code fi}. @@ -92,7 +92,7 @@ private void emitFunctionalFactory(String fiName) { public static MemorySegment allocate(%2$s.%3$s fi, Arena arena) { return Linker.nativeLinker().upcallStub(UP$MH.bindTo(fi), $DESC, arena); } - """, sourceFileBuilder().FFMUtilsName(), className(), fiName); + """, runtimeHelperName(), className(), fiName); } private void emitInvoke() { diff --git a/src/main/java/org/openjdk/jextract/impl/HeaderFileBuilder.java b/src/main/java/org/openjdk/jextract/impl/HeaderFileBuilder.java index 5d640e6b..5d6f16ee 100644 --- a/src/main/java/org/openjdk/jextract/impl/HeaderFileBuilder.java +++ b/src/main/java/org/openjdk/jextract/impl/HeaderFileBuilder.java @@ -180,12 +180,12 @@ private void emitFunctionWrapper(String javaName, String nativeName, boolean nee private static class %1$s { public static final FunctionDescriptor DESC = %2$s; - public static final MemorySegment ADDR = %4$sfindOrThrow("%3$s"); + public static final MemorySegment ADDR = %3$s.findOrThrow("%4$s"); public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } """, holderClass, functionDescriptorString(1, decl.type()), - lookupName(decl), sourceFileBuilder().FFMUtilsName()); + runtimeHelperName(), lookupName(decl)); appendBlankLine(); emitDocComment(decl, "Function descriptor for:"); appendLines(""" @@ -213,8 +213,8 @@ private static class %1$s { public static %1$s %2$s(%3$s) { var mh$ = %4$s.HANDLE; try { - if (%8$sTRACE_DOWNCALLS) { - %8$straceDowncall(%5$s); + if (TRACE_DOWNCALLS) { + traceDowncall(%5$s); } %6$smh$.invokeExact(%7$s); } catch (Throwable ex$) { @@ -223,8 +223,7 @@ private static class %1$s { } """, retType, javaName, paramExprs(declType, finalParamNames, isVarArg), - holderClass, traceArgList, returnWithCast, paramList, - sourceFileBuilder().FFMUtilsName()); + holderClass, traceArgList, returnWithCast, paramList); } else { String invokerClassName = newHolderClassName(javaName); String paramExprs = paramExprs(declType, finalParamNames, isVarArg); @@ -233,19 +232,19 @@ private static class %1$s { appendLines(""" public static class %1$s { private static final FunctionDescriptor BASE_DESC = %2$s; - private static final MemorySegment ADDR = %4$sfindOrThrow("%3$s"); + private static final MemorySegment ADDR = %3$s.findOrThrow("%4$s"); private final MethodHandle handle; private final FunctionDescriptor descriptor; private final MethodHandle spreader; - private %1$s(MethodHandle handle, FunctionDescriptor descriptor, MethodHandle spreader) { + private %5$s(MethodHandle handle, FunctionDescriptor descriptor, MethodHandle spreader) { this.handle = handle; this.descriptor = descriptor; this.spreader = spreader; } """, invokerClassName, functionDescriptorString(2, decl.type()), - lookupName(decl), sourceFileBuilder().FFMUtilsName()); + runtimeHelperName(), lookupName(decl), invokerClassName); incrAlign(); appendBlankLine(); emitDocComment(decl, "Variadic invoker factory for:"); @@ -284,8 +283,8 @@ public FunctionDescriptor descriptor() { public %1$s apply(%2$s) { try { - if (%6$sTRACE_DOWNCALLS) { - %6$straceDowncall(%3$s); + if (TRACE_DOWNCALLS) { + traceDowncall(%3$s); } %4$s spreader.invokeExact(%5$s); } catch(IllegalArgumentException | ClassCastException ex$) { @@ -295,8 +294,7 @@ public FunctionDescriptor descriptor() { } } } - """, retType, paramExprs, traceArgList, - returnWithCast, paramList, sourceFileBuilder().FFMUtilsName()); + """, retType, paramExprs, traceArgList, returnWithCast, paramList); } decrAlign(); } @@ -309,6 +307,91 @@ void emitPointerTypedef(Declaration.Typedef typedefTree, String name) { emitPrimitiveTypedefLayout(name, Type.pointer(), typedefTree); } + void emitFirstHeaderPreamble(List libraries, boolean useSystemLoadLibrary) { + List lookups = new ArrayList<>(); + // if legacy library loading is selected, load libraries (if any) into current loader + if (useSystemLoadLibrary) { + appendBlankLine(); + appendIndentedLines(""" + + static { + """); + incrAlign(); + for (Options.Library lib : libraries) { + String method = lib.specKind() == Options.Library.SpecKind.PATH ? "load" : "loadLibrary"; + appendIndentedLines("System.%1$s(\"%2$s\");", method, lib.toQuotedName()); + } + decrAlign(); + appendIndentedLines(""" + } + """); + } else { + // otherwise, add a library lookup per library (if any) + libraries.stream() // add library lookups (if any) + .map(l -> l.specKind() == Options.Library.SpecKind.PATH ? + String.format("SymbolLookup.libraryLookup(\"%1$s\", LIBRARY_ARENA)", l.toQuotedName()) : + String.format("SymbolLookup.libraryLookup(System.mapLibraryName(\"%1$s\"), LIBRARY_ARENA)", l.toQuotedName())) + .collect(Collectors.toCollection(() -> lookups)); + } + + lookups.add("SymbolLookup.loaderLookup()"); // fallback to loader lookup + lookups.add("Linker.nativeLinker().defaultLookup()"); // fallback to native lookup + + // wrap all lookups (but the first) with ".or(...)" + List lookupCalls = new ArrayList<>(); + boolean isFirst = true; + for (String lookup : lookups) { + lookupCalls.add(isFirst ? lookup : String.format(".or(%1$s)", lookup)); + isFirst = false; + } + + // chain all the calls together into a combined symbol lookup + appendBlankLine(); + appendIndentedLines(lookupCalls.stream() + .collect(Collectors.joining(String.format("\n%1$s", indentString(2)), "static final SymbolLookup SYMBOL_LOOKUP = ", ";"))); + } + + void emitRuntimeHelperMethods() { + appendIndentedLines(""" + + static final Arena LIBRARY_ARENA = Arena.ofAuto(); + static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls"); + + static void traceDowncall(String name, Object... args) { + String traceArgs = Arrays.stream(args) + .map(Object::toString) + .collect(Collectors.joining(", ")); + System.out.printf("%s(%s)\\n", name, traceArgs); + } + + static MemorySegment findOrThrow(String symbol) { + return SYMBOL_LOOKUP.findOrThrow(symbol); + } + + static MethodHandle upcallHandle(Class fi, String name, FunctionDescriptor fdesc) { + try { + return MethodHandles.lookup().findVirtual(fi, name, fdesc.toMethodType()); + } catch (ReflectiveOperationException ex) { + throw new AssertionError(ex); + } + } + + static MemoryLayout align(MemoryLayout layout, long align) { + return switch (layout) { + case PaddingLayout p -> p; + case ValueLayout v -> v.withByteAlignment(align); + case GroupLayout g -> { + MemoryLayout[] alignedMembers = g.memberLayouts().stream() + .map(m -> align(m, align)).toArray(MemoryLayout[]::new); + yield g instanceof StructLayout ? + MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers); + } + case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align)); + }; + } + """); + } + private void emitGlobalGetter(String holderClass, String javaName, Declaration.Variable decl, String docHeader) { appendBlankLine(); @@ -432,20 +515,19 @@ private String emitVarHolderClass(Declaration.Variable var, String javaName) { appendIndentedLines(""" private static class %1$s { public static final %2$s LAYOUT = %3$s; - public static final MemorySegment SEGMENT = %7$sfindOrThrow("%4$s").reinterpret(LAYOUT.byteSize()); - %5$s - public static final long[] DIMS = { %6$s }; + public static final MemorySegment SEGMENT = %4$s.findOrThrow("%5$s").reinterpret(LAYOUT.byteSize()); + %6$s + public static final long[] DIMS = { %7$s }; } - """, mangledName, layoutType, layoutString(varType), lookupName(var), - accessHandle, dimsString, sourceFileBuilder().FFMUtilsName()); + """, mangledName, layoutType, layoutString(varType), runtimeHelperName(), + lookupName(var), accessHandle, dimsString); } else { appendIndentedLines(""" private static class %1$s { public static final %2$s LAYOUT = %3$s; - public static final MemorySegment SEGMENT = %5$sfindOrThrow("%4$s").reinterpret(LAYOUT.byteSize()); + public static final MemorySegment SEGMENT = %4$s.findOrThrow("%5$s").reinterpret(LAYOUT.byteSize()); } - """, mangledName, layoutType, layoutString(varType), - lookupName(var), sourceFileBuilder().FFMUtilsName()); + """, mangledName, layoutType, layoutString(varType), runtimeHelperName(), lookupName(var)); } incrAlign(); appendBlankLine(); @@ -485,15 +567,15 @@ private void emitConstant(Class javaType, String constantName, Object value, public static %1$s %2$s() { class Holder { static final %1$s %2$s - = %4$sLIBRARY_ARENA.allocateFrom("%3$s"); + = %3$s.LIBRARY_ARENA.allocateFrom("%4$s"); } return Holder.%2$s; } """, javaType.getSimpleName(), constantName, - Utils.quote(Objects.toString(value)), - sourceFileBuilder().FFMUtilsName()); + runtimeHelperName(), + Utils.quote(Objects.toString(value))); } else { appendLines(""" private static final %1$s %2$s = %3$s; diff --git a/src/main/java/org/openjdk/jextract/impl/SourceFileBuilder.java b/src/main/java/org/openjdk/jextract/impl/SourceFileBuilder.java index f42a3249..0d55f0f5 100644 --- a/src/main/java/org/openjdk/jextract/impl/SourceFileBuilder.java +++ b/src/main/java/org/openjdk/jextract/impl/SourceFileBuilder.java @@ -49,26 +49,10 @@ public String className() { return className; } - public String getPackageName(){ - return packageName; - } - - public String layoutUtilsName() { - return getPackageName().isEmpty() ? ToplevelBuilder.LAYOUT_UTILS + "." : ""; - } - - public String FFMUtilsName() { - return getPackageName().isEmpty() ? ToplevelBuilder.FFM_UTILS + "." : ""; - } - public static SourceFileBuilder newSourceFile(String packageName, String className) { SourceFileBuilder sfb = new SourceFileBuilder(packageName, className); sfb.emitPackagePrefix(); sfb.emitImportSection(); - if (!className.equals(ToplevelBuilder.FFM_UTILS) && !className.equals(ToplevelBuilder.LAYOUT_UTILS) - && !packageName.isEmpty()) { - sfb.emitStaticImportSection(); - } return sfb; } @@ -97,16 +81,9 @@ void emitImportSection() { import static java.lang.foreign.ValueLayout.*; import static java.lang.foreign.MemoryLayout.PathElement.*; - """); } - void emitStaticImportSection() { - appendLines(String.format(""" - import static %1$s.FFMUtils.*; - import static %1$s.LayoutUtils.*; - """, packageName)); - } // Internal generation helpers (used by other builders) diff --git a/src/main/java/org/openjdk/jextract/impl/ToplevelBuilder.java b/src/main/java/org/openjdk/jextract/impl/ToplevelBuilder.java index a0833ea7..7893c63b 100644 --- a/src/main/java/org/openjdk/jextract/impl/ToplevelBuilder.java +++ b/src/main/java/org/openjdk/jextract/impl/ToplevelBuilder.java @@ -41,15 +41,12 @@ */ class ToplevelBuilder implements OutputFactory.Builder { private static final int DECLS_PER_HEADER_CLASS = Integer.getInteger("jextract.decls.per.header", 1000); - private static final String PREV_SUFFIX = "#{PREV_SUFFIX}"; + public static final String PREV_SUFFIX = "#{PREV_SUFFIX}"; private static final String SUFFIX = "#{SUFFIX}"; - public static final String LAYOUT_UTILS = "LayoutUtils"; - public static final String FFM_UTILS = "FFMUtils"; private int declCount; private final List headerBuilders = new ArrayList<>(); private final List otherBuilders = new ArrayList<>(); - private final List commonUtilsBuilders = new ArrayList<>(); private HeaderFileBuilder lastHeader; private final ClassDesc headerDesc; @@ -58,25 +55,35 @@ class ToplevelBuilder implements OutputFactory.Builder { this.headerDesc = ClassDesc.of(packageName, headerClassName); SourceFileBuilder sfb = SourceFileBuilder.newSourceFile(packageName, headerClassName); headerBuilders.add(sfb); - lastHeader = createFirstHeader(sfb); - generateCommonBindings(libs, useSystemLoadLibrary); + lastHeader = createFirstHeader(sfb, libs, useSystemLoadLibrary); } - private void generateCommonBindings(List libs, boolean useSystemLoadLibrary) { - var sfb = SourceFileBuilder.newSourceFile(packageName(), LAYOUT_UTILS); - commonUtilsBuilders.add(sfb); - CommonBindingsBuilder.generate(sfb, LAYOUT_UTILS, mainHeaderClassName()); - - sfb = SourceFileBuilder.newSourceFile(packageName(), FFM_UTILS); - commonUtilsBuilders.add(sfb); - CommonBindingsBuilder.generate(sfb, FFM_UTILS, mainHeaderClassName(), libs, useSystemLoadLibrary); - } - - private static HeaderFileBuilder createFirstHeader(SourceFileBuilder sfb) { - HeaderFileBuilder first = new HeaderFileBuilder(sfb, String.format("%1$s%2$s", sfb.className(), SUFFIX), null, sfb.className()); + private static HeaderFileBuilder createFirstHeader(SourceFileBuilder sfb, List libs, boolean useSystemLoadLibrary) { + HeaderFileBuilder first = new HeaderFileBuilder(sfb, String.format("%1$s#{SUFFIX}",sfb.className()), null, sfb.className()); first.appendBlankLine(); first.classBegin(); first.emitDefaultConstructor(); + first.emitRuntimeHelperMethods(); + first.emitFirstHeaderPreamble(libs, useSystemLoadLibrary); + // emit basic primitive types + first.appendIndentedLines(""" + + public static final ValueLayout.OfBoolean C_BOOL = (ValueLayout.OfBoolean) Linker.nativeLinker().canonicalLayouts().get("bool"); + 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"); + 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)); + """); + if (TypeImpl.IS_WINDOWS) { + first.appendIndentedLines("public static final ValueLayout.OfInt C_LONG = (ValueLayout.OfInt) Linker.nativeLinker().canonicalLayouts().get(\"long\");"); + first.appendIndentedLines("public static final ValueLayout.OfDouble C_LONG_DOUBLE = (ValueLayout.OfDouble) Linker.nativeLinker().canonicalLayouts().get(\"double\");"); + } else { + first.appendIndentedLines("public static final ValueLayout.OfLong C_LONG = (ValueLayout.OfLong) Linker.nativeLinker().canonicalLayouts().get(\"long\");"); + } return first; } @@ -111,9 +118,6 @@ public List toFiles() { // add remaining builders files.addAll(otherBuilders.stream() .map(SourceFileBuilder::toFile).toList()); - //add common bindings files - files.addAll(commonUtilsBuilders.stream() - .map(SourceFileBuilder::toFile).toList()); return files; } diff --git a/test/jtreg/generator/clinitCycles/TestGlobal.java b/test/jtreg/generator/clinitCycles/TestGlobal.java index 6f02406c..d7f8c58c 100644 --- a/test/jtreg/generator/clinitCycles/TestGlobal.java +++ b/test/jtreg/generator/clinitCycles/TestGlobal.java @@ -1,4 +1,4 @@ -/* Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2023, 2024, 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 @@ -40,7 +40,7 @@ public class TestGlobal { @Test public void testGlobal() { - ValueLayout layout = LayoutUtils.C_INT; + ValueLayout layout = clinit_global_h.C_INT; assertNotNull(layout); assertEquals(clinit_global_h.global1(), 1); assertEquals(clinit_global_h.global2(), 2); diff --git a/test/jtreg/generator/clinitCycles/TestStruct.java b/test/jtreg/generator/clinitCycles/TestStruct.java index 6c250b38..931f64ca 100644 --- a/test/jtreg/generator/clinitCycles/TestStruct.java +++ b/test/jtreg/generator/clinitCycles/TestStruct.java @@ -1,4 +1,4 @@ -/* Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2023, 2024, 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 @@ -22,7 +22,6 @@ import org.testng.annotations.Test; import test.jextract.clinit.*; -import static test.jextract.clinit.LayoutUtils.*; import java.lang.foreign.GroupLayout; import java.lang.foreign.ValueLayout; @@ -41,7 +40,7 @@ public class TestStruct { @Test public void TestStruct() { - ValueLayout layout = C_INT; + ValueLayout layout = clinit_struct_h.C_INT; assertNotNull(layout); GroupLayout pointLayout = Point.layout(); assertNotNull(pointLayout); diff --git a/test/jtreg/generator/clinitCycles/TestTypedef.java b/test/jtreg/generator/clinitCycles/TestTypedef.java index a4289d36..705c629c 100644 --- a/test/jtreg/generator/clinitCycles/TestTypedef.java +++ b/test/jtreg/generator/clinitCycles/TestTypedef.java @@ -1,4 +1,4 @@ -/* Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2023, 2024, 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 @@ -40,7 +40,7 @@ public class TestTypedef { @Test public void TestTypedef() { - ValueLayout layout = LayoutUtils.C_INT; + ValueLayout layout = clinit_typedef_h.C_INT; assertNotNull(layout); assertEquals(layout, clinit_typedef_h.one); assertEquals(layout, clinit_typedef_h.two); diff --git a/test/jtreg/generator/dedup/TestDedup.java b/test/jtreg/generator/dedup/TestDedup.java index 6a4e093d..998c3019 100644 --- a/test/jtreg/generator/dedup/TestDedup.java +++ b/test/jtreg/generator/dedup/TestDedup.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2024, 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 @@ -46,7 +46,7 @@ public void testMacroFields() { } void checkLayout(GroupLayout layout) { - assertEquals(layout.memberLayouts().get(0), LayoutUtils.C_INT.withName("x")); - assertEquals(layout.memberLayouts().get(1), LayoutUtils.C_INT.withName("y")); + assertEquals(layout.memberLayouts().get(0), macro_fields_h.C_INT.withName("x")); + assertEquals(layout.memberLayouts().get(1), macro_fields_h.C_INT.withName("y")); } } diff --git a/test/jtreg/generator/nestedStructTypedef/TestNestedStructTypedef.java b/test/jtreg/generator/nestedStructTypedef/TestNestedStructTypedef.java index 22f274f1..57ab0a92 100644 --- a/test/jtreg/generator/nestedStructTypedef/TestNestedStructTypedef.java +++ b/test/jtreg/generator/nestedStructTypedef/TestNestedStructTypedef.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2024, 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 @@ -45,6 +45,6 @@ public void testMacroFields() { } void checkLayout(GroupLayout layout) { - assertEquals(layout.memberLayouts().get(0), LayoutUtils.C_INT.withName("x")); + assertEquals(layout.memberLayouts().get(0), nestedStructTypedef_h.C_INT.withName("x")); } } diff --git a/test/jtreg/generator/nestedTypes/TestNestedTypes.java b/test/jtreg/generator/nestedTypes/TestNestedTypes.java index 3d19c76d..fa5377e8 100644 --- a/test/jtreg/generator/nestedTypes/TestNestedTypes.java +++ b/test/jtreg/generator/nestedTypes/TestNestedTypes.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2024, 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 @@ -56,5 +56,5 @@ void checkNestedLayout(MemoryLayout layout) { assertEquals(((SequenceLayout)nestedLayout).elementLayout().withoutName(), ELEM_NESTED_LAYOUT); } - static final MemoryLayout ELEM_NESTED_LAYOUT = MemoryLayout.structLayout(LayoutUtils.C_INT.withName("x")); + static final MemoryLayout ELEM_NESTED_LAYOUT = MemoryLayout.structLayout(nested_types_h.C_INT.withName("x")); } diff --git a/test/jtreg/generator/nestedTypes/TestNestedTypesNames.java b/test/jtreg/generator/nestedTypes/TestNestedTypesNames.java index ba6ff95e..673a41ed 100644 --- a/test/jtreg/generator/nestedTypes/TestNestedTypesNames.java +++ b/test/jtreg/generator/nestedTypes/TestNestedTypesNames.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 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 @@ -41,11 +41,11 @@ public class TestNestedTypesNames { static MemoryLayout ARG_STRUCT = MemoryLayout.structLayout( - LayoutUtils.C_INT.withName("y") + nested_types_names_h.C_INT.withName("y") ); static MemoryLayout RET_STRUCT = MemoryLayout.structLayout( - LayoutUtils.C_INT.withName("x") + nested_types_names_h.C_INT.withName("x") ); static FunctionDescriptor FUNC_DESC = FunctionDescriptor.of( diff --git a/test/jtreg/generator/nestedTypes/TestNestedTypesUnsupported.java b/test/jtreg/generator/nestedTypes/TestNestedTypesUnsupported.java index e88ac37f..94e45f8b 100644 --- a/test/jtreg/generator/nestedTypes/TestNestedTypesUnsupported.java +++ b/test/jtreg/generator/nestedTypes/TestNestedTypesUnsupported.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 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 @@ -42,7 +42,7 @@ public class TestNestedTypesUnsupported { static MemoryLayout UNDEFINED_STRUCT = MemoryLayout.structLayout( - MemoryLayout.paddingLayout(LayoutUtils.C_POINTER.byteSize()) + MemoryLayout.paddingLayout(nested_types_unsupported_h.C_POINTER.byteSize()) ); @Test diff --git a/test/jtreg/generator/test8244412/LibTest8244412Test.java b/test/jtreg/generator/test8244412/LibTest8244412Test.java index 39d90d8a..80b6485f 100644 --- a/test/jtreg/generator/test8244412/LibTest8244412Test.java +++ b/test/jtreg/generator/test8244412/LibTest8244412Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, 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 @@ -31,7 +31,6 @@ import test.jextract.test8244412.*; import static test.jextract.test8244412.test8244412_h.*; -import static test.jextract.test8244412.LayoutUtils.*; /* * @test diff --git a/test/jtreg/generator/test8245003/Test8245003.java b/test/jtreg/generator/test8245003/Test8245003.java index 2008a90e..e3b729a2 100644 --- a/test/jtreg/generator/test8245003/Test8245003.java +++ b/test/jtreg/generator/test8245003/Test8245003.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, 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 @@ -59,8 +59,8 @@ public void testStructAccessor() { @Test public void testArrayAccessor() { var seg = iarr(); - assertEquals(seg.byteSize(), LayoutUtils.C_INT.byteSize()*5); - int[] arr = seg.toArray(LayoutUtils.C_INT); + assertEquals(seg.byteSize(), C_INT.byteSize()*5); + int[] arr = seg.toArray(C_INT); assertEquals(arr.length, 5); assertEquals(arr[0], 2); assertEquals(arr[1], -2); @@ -72,7 +72,7 @@ public void testArrayAccessor() { assertEquals(seg.byteSize(), Foo.sizeof()); assertEquals(Foo.count(seg), 37); var greeting = Foo.greeting(seg); - byte[] barr = greeting.toArray(LayoutUtils.C_CHAR); + byte[] barr = greeting.toArray(C_CHAR); assertEquals(new String(barr), "hello"); } } diff --git a/test/jtreg/generator/test8246341/LibTest8246341Test.java b/test/jtreg/generator/test8246341/LibTest8246341Test.java index ce98f2e9..0972d3a5 100644 --- a/test/jtreg/generator/test8246341/LibTest8246341Test.java +++ b/test/jtreg/generator/test8246341/LibTest8246341Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, 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 @@ -28,7 +28,6 @@ import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; import static test.jextract.test8246341.test8246341_h.*; -import static test.jextract.test8246341.LayoutUtils.*; /* * @test diff --git a/test/jtreg/generator/test8252121/Test8252121.java b/test/jtreg/generator/test8252121/Test8252121.java index 6a112ece..b70cf1c4 100644 --- a/test/jtreg/generator/test8252121/Test8252121.java +++ b/test/jtreg/generator/test8252121/Test8252121.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, 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 @@ -46,7 +46,7 @@ public class Test8252121 { public void test() { try (var arena = Arena.ofConfined()) { int[] array = { 3, 5, 89, 34, -33 }; - MemorySegment seg = arena.allocateFrom(LayoutUtils.C_INT, array); + MemorySegment seg = arena.allocateFrom(C_INT, array); assertEquals(IntStream.of(array).sum(), sum(seg)); assertEquals(IntStream.of(array).reduce(1, (a,b) -> a*b), mul(seg)); } diff --git a/test/jtreg/generator/test8257892/LibUnsupportedTest.java b/test/jtreg/generator/test8257892/LibUnsupportedTest.java index ac33c5af..283a6bdd 100644 --- a/test/jtreg/generator/test8257892/LibUnsupportedTest.java +++ b/test/jtreg/generator/test8257892/LibUnsupportedTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, 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 @@ -34,8 +34,6 @@ import static org.testng.Assert.*; import static test.jextract.unsupported.unsupported_h.*; -import static test.jextract.unsupported.LayoutUtils.*; -import static test.jextract.unsupported.FFMUtils.*; import test.jextract.unsupported.*; /* diff --git a/test/jtreg/generator/testPrintf/TestPrintf.java b/test/jtreg/generator/testPrintf/TestPrintf.java index 83aa7aa5..c81d0911 100644 --- a/test/jtreg/generator/testPrintf/TestPrintf.java +++ b/test/jtreg/generator/testPrintf/TestPrintf.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, 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 @@ -33,7 +33,6 @@ import static org.testng.Assert.assertEquals; import static test.jextract.printf.printf_h.*; -import static test.jextract.printf.LayoutUtils.*; /* * @test diff --git a/test/jtreg/generator/testStruct/LibStructTest.java b/test/jtreg/generator/testStruct/LibStructTest.java index 1e4d2d4f..4851f22c 100644 --- a/test/jtreg/generator/testStruct/LibStructTest.java +++ b/test/jtreg/generator/testStruct/LibStructTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, 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 @@ -31,8 +31,6 @@ import static org.testng.Assert.assertEquals; import static test.jextract.struct.struct_h.*; -import static test.jextract.struct.LayoutUtils.*; -import static test.jextract.struct.FFMUtils.*; import test.jextract.struct.*; /* From 736ccfbaaad151aa7fc1f3c0efd52502e88f2471 Mon Sep 17 00:00:00 2001 From: Nizar Benalla Date: Thu, 27 Mar 2025 03:04:47 +0000 Subject: [PATCH 04/10] add new option to create a sharable utility class cleanup: longer cmd option `framework` should use -- rather than - small other cleanups --- doc/GUIDE.md | 3 +- samples/opengl/compilesource.sh | 4 +- .../org/openjdk/jextract/JextractTool.java | 27 ++++--- .../jextract/impl/HeaderFileBuilder.java | 20 ++++++ .../openjdk/jextract/impl/IncludeHelper.java | 25 +++++-- .../org/openjdk/jextract/impl/Options.java | 8 ++- .../openjdk/jextract/impl/OutputFactory.java | 14 +++- .../jextract/impl/ToplevelBuilder.java | 70 ++++++++++++------- .../impl/resources/Messages.properties | 4 +- .../sharableItems/TestSharableItems.java | 55 +++++++++++++++ .../generator/sharableItems/sharableItems.h | 30 ++++++++ 11 files changed, 214 insertions(+), 46 deletions(-) create mode 100644 test/jtreg/generator/sharableItems/TestSharableItems.java create mode 100644 test/jtreg/generator/sharableItems/sharableItems.h diff --git a/doc/GUIDE.md b/doc/GUIDE.md index 2d21ab23..1b9a08bf 100644 --- a/doc/GUIDE.md +++ b/doc/GUIDE.md @@ -986,9 +986,10 @@ A complete list of all the supported command line options is given below: | `--output ` | specify where to place generated files | | `--dump-includes ` | dump included symbols into specified file (see below) | | `--include-[function,constant,struct,union,typedef,var]` | Include a symbol of the given name and kind in the generated bindings. When one of these options is specified, any symbol that is not matched by any specified filters is omitted from the generated bindings. | +| `--sharable-utils ` | generate shared utility class for common layouts and methods | | `--version` | print version information and exit | | `-F ` (macOs only) | specify the framework directory include files. Defaults to the current Mac OS X SDK dir.| -| `-framework ` (macOs only) | specify the name of the library, path will be expanded to that of the framework folder.| +| `--framework ` (macOs only) | specify the name of the library, path will be expanded to that of the framework folder.| Jextract accepts one or more header files. When multiple header files are specified, diff --git a/samples/opengl/compilesource.sh b/samples/opengl/compilesource.sh index 7419add5..055f5440 100644 --- a/samples/opengl/compilesource.sh +++ b/samples/opengl/compilesource.sh @@ -1,5 +1,5 @@ -jextract -t opengl -framework GLUT \ - -framework OpenGL \ +jextract -t opengl --framework GLUT \ + --framework OpenGL \ "" javac --source=22 -d . opengl/*.java diff --git a/src/main/java/org/openjdk/jextract/JextractTool.java b/src/main/java/org/openjdk/jextract/JextractTool.java index 59f5f17b..d7090b27 100644 --- a/src/main/java/org/openjdk/jextract/JextractTool.java +++ b/src/main/java/org/openjdk/jextract/JextractTool.java @@ -137,7 +137,8 @@ private static List generateInternal(Declaration.Scoped decl, St .findFirst().get(); return logger.hasErrors() ? List.of() : - List.of(OutputFactory.generateWrapped(transformedDecl, targetPkg, libs, useSystemLoadLibrary)); + List.of(OutputFactory.generateWrapped(transformedDecl, targetPkg, + libs, useSystemLoadLibrary, includeHelper.getSharableItems())); } /** @@ -334,7 +335,7 @@ OptionSet parse(String[] args) { // so that option lookup, value lookup will work regardless // which alias was used to check. options.put(spec.name(), values); - for (String alias : spec.aliases()) { + for (String _ : spec.aliases()) { options.put(spec.name(), values); } } else { // !isOption(arg) @@ -356,6 +357,7 @@ private int run(String[] args) { OptionParser parser = new OptionParser(); parser.accepts("-D", List.of("--define-macro"), "help.D", true); parser.accepts("--dump-includes", "help.dump-includes", true); + parser.accepts("--sharable-items", "help.sharable.items", true); for (IncludeHelper.IncludeKind includeKind : IncludeHelper.IncludeKind.values()) { parser.accepts("--" + includeKind.optionName(), "help." + includeKind.optionName(), true); } @@ -370,7 +372,7 @@ private int run(String[] args) { if (isMacOSX) { parser.accepts("-F", "help.mac.framework", true); - parser.accepts("-framework", "help.framework.library.path", true); + parser.accepts("--framework", "help.framework.library.path", true); } OptionSet optionSet; @@ -399,7 +401,7 @@ private int run(String[] args) { Path compileFlagsTxt = Paths.get(".", "compile_flags.txt"); if (Files.exists(compileFlagsTxt)) { try { - Files.lines(compileFlagsTxt).forEach(opt -> builder.addClangArg(opt)); + Files.lines(compileFlagsTxt).forEach(builder::addClangArg); } catch (IOException ioExp) { logger.fatal(ioExp, "jextract.bad.compile.flags", ioExp.getMessage()); return OPTION_ERROR; @@ -439,6 +441,10 @@ private int run(String[] args) { builder.setDumpIncludeFile(optionSet.valueOf("--dump-includes")); } + if (optionSet.has("--sharable-items")) { + builder.useSharableItems(optionSet.valueOf("--sharable-items")); + } + if (optionSet.has("--output")) { builder.setOutputDir(optionSet.valueOf("--output")); } @@ -471,7 +477,7 @@ private int run(String[] args) { builder.addClangArg("-I" + System.getProperty("user.dir")); - if (optionSet.nonOptionArguments().size() == 0) { + if (optionSet.nonOptionArguments().isEmpty()) { printOptionError(logger.format("expected.atleast.one.header")); return OPTION_ERROR; } @@ -514,7 +520,7 @@ private int run(String[] args) { } try { - if (options.includeHelper.dumpIncludesFile != null) { + if (options.includeHelper.getDumpIncludesFile() != null) { options.includeHelper.dumpIncludes(); } else { Path output = Path.of(options.outputDir); @@ -536,10 +542,13 @@ private int run(String[] args) { } private int parseLibraries(String optionString, OptionSet optionSet, boolean useSystemLoadLibrary, Options.Builder builder) { - if (optionSet.has("-" + optionString)) { - for (String lib : optionSet.valuesOf("-" + optionString)) { + String cmdOption = optionString.length() < 3 ? + "-" + optionString : + "--" + optionString; + if (optionSet.has(cmdOption)) { + for (String lib : optionSet.valuesOf(cmdOption)) { try { - String spec = optionString.equals("framework") ? + String spec = cmdOption.equals("--framework") ? resolveFrameworkPath(lib) : lib; diff --git a/src/main/java/org/openjdk/jextract/impl/HeaderFileBuilder.java b/src/main/java/org/openjdk/jextract/impl/HeaderFileBuilder.java index 5d6f16ee..3c608a48 100644 --- a/src/main/java/org/openjdk/jextract/impl/HeaderFileBuilder.java +++ b/src/main/java/org/openjdk/jextract/impl/HeaderFileBuilder.java @@ -392,6 +392,26 @@ static MemoryLayout align(MemoryLayout layout, long align) { """); } + void emitBasicPrimitiveTypes(){ + appendIndentedLines(""" + + public static final ValueLayout.OfBoolean C_BOOL = (ValueLayout.OfBoolean) Linker.nativeLinker().canonicalLayouts().get("bool"); + 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"); + 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)); + """); + 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, Declaration.Variable decl, String docHeader) { appendBlankLine(); diff --git a/src/main/java/org/openjdk/jextract/impl/IncludeHelper.java b/src/main/java/org/openjdk/jextract/impl/IncludeHelper.java index 3053b1b7..3ed9fabc 100644 --- a/src/main/java/org/openjdk/jextract/impl/IncludeHelper.java +++ b/src/main/java/org/openjdk/jextract/impl/IncludeHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2025, 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 @@ -83,10 +83,27 @@ static IncludeKind fromScoped(Declaration.Scoped scoped) { private final EnumMap> includesSymbolNamesByKind = new EnumMap<>(IncludeKind.class); private final Set usedDeclarations = new HashSet<>(); - public String dumpIncludesFile; + + private String dumpIncludesFile; + private String sharableItems; + + public String getDumpIncludesFile() { + return dumpIncludesFile; + } + + public void setDumpIncludesFile(String dumpIncludesFile) { + this.dumpIncludesFile = dumpIncludesFile; + } + + public void setSharableItems(String sharableItems) { + this.sharableItems = sharableItems; + } + public String getSharableItems() { + return sharableItems; + } public void addSymbol(IncludeKind kind, String symbolName) { - Set names = includesSymbolNamesByKind.computeIfAbsent(kind, (_unused) -> new HashSet<>()); + Set names = includesSymbolNamesByKind.computeIfAbsent(kind, _unused -> new HashSet<>()); names.add(symbolName); } @@ -122,7 +139,7 @@ private boolean isIncludedInternal(IncludeKind kind, Declaration declaration) { if (!isEnabled()) { return true; } else { - Set names = includesSymbolNamesByKind.computeIfAbsent(kind, (_unused) -> new HashSet<>()); + Set names = includesSymbolNamesByKind.computeIfAbsent(kind, _unused -> new HashSet<>()); return names.contains(declaration.name()); } } diff --git a/src/main/java/org/openjdk/jextract/impl/Options.java b/src/main/java/org/openjdk/jextract/impl/Options.java index f07a1e1c..961ba1d0 100644 --- a/src/main/java/org/openjdk/jextract/impl/Options.java +++ b/src/main/java/org/openjdk/jextract/impl/Options.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, 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 @@ -99,7 +99,11 @@ public void setTargetPackage(String pkg) { } public void setDumpIncludeFile(String dumpIncludesFile) { - includeHelper.dumpIncludesFile = dumpIncludesFile; + includeHelper.setDumpIncludesFile(dumpIncludesFile); + } + + public void useSharableItems(String name) { + includeHelper.setSharableItems(name); } public void addIncludeSymbol(IncludeHelper.IncludeKind kind, String symbolName) { diff --git a/src/main/java/org/openjdk/jextract/impl/OutputFactory.java b/src/main/java/org/openjdk/jextract/impl/OutputFactory.java index 9d0f864f..15fda0a8 100644 --- a/src/main/java/org/openjdk/jextract/impl/OutputFactory.java +++ b/src/main/java/org/openjdk/jextract/impl/OutputFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025 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 @@ -45,9 +45,17 @@ public class OutputFactory implements Declaration.Visitor { public static JavaSourceFile[] generateWrapped(Declaration.Scoped decl, String pkgName, - List libs, boolean useSystemLoadLibrary) { + List libs, + boolean useSystemLoadLibrary, + String generateShareableItems) { String clsName = JavaName.getOrThrow(decl); - ToplevelBuilder toplevelBuilder = new ToplevelBuilder(pkgName, clsName, libs, useSystemLoadLibrary); + ToplevelBuilder toplevelBuilder = new ToplevelBuilder( + pkgName, + clsName, + libs, + useSystemLoadLibrary, + generateShareableItems + ); return new OutputFactory(toplevelBuilder).generate(decl); } diff --git a/src/main/java/org/openjdk/jextract/impl/ToplevelBuilder.java b/src/main/java/org/openjdk/jextract/impl/ToplevelBuilder.java index 7893c63b..67ed23be 100644 --- a/src/main/java/org/openjdk/jextract/impl/ToplevelBuilder.java +++ b/src/main/java/org/openjdk/jextract/impl/ToplevelBuilder.java @@ -43,55 +43,77 @@ class ToplevelBuilder implements OutputFactory.Builder { private static final int DECLS_PER_HEADER_CLASS = Integer.getInteger("jextract.decls.per.header", 1000); public static final String PREV_SUFFIX = "#{PREV_SUFFIX}"; private static final String SUFFIX = "#{SUFFIX}"; + private final String jextractUTILS; private int declCount; private final List headerBuilders = new ArrayList<>(); private final List otherBuilders = new ArrayList<>(); private HeaderFileBuilder lastHeader; private final ClassDesc headerDesc; + private SourceFileBuilder jextractUtilsBuilder; ToplevelBuilder(String packageName, String headerClassName, - List libs, boolean useSystemLoadLibrary) { + List libs, + boolean useSystemLoadLibrary, + String generateShareableItems) { + jextractUTILS = generateShareableItems; this.headerDesc = ClassDesc.of(packageName, headerClassName); + + if (jextractUTILS != null) { + jextractUtilsBuilder = createJextractUtilsBuilder(packageName,libs,useSystemLoadLibrary); + } + SourceFileBuilder sfb = SourceFileBuilder.newSourceFile(packageName, headerClassName); headerBuilders.add(sfb); - lastHeader = createFirstHeader(sfb, libs, useSystemLoadLibrary); + lastHeader = createFirstHeader(sfb, libs, useSystemLoadLibrary, jextractUTILS); } - private static HeaderFileBuilder createFirstHeader(SourceFileBuilder sfb, List libs, boolean useSystemLoadLibrary) { - HeaderFileBuilder first = new HeaderFileBuilder(sfb, String.format("%1$s#{SUFFIX}",sfb.className()), null, sfb.className()); + private static HeaderFileBuilder createFirstHeader(SourceFileBuilder sfb, + List libs, + boolean useSystemLoadLibrary, + String generateShareableItems) { + HeaderFileBuilder first = new HeaderFileBuilder(sfb, + String.format("%1$s#{SUFFIX}", sfb.className()), + generateShareableItems, + sfb.className()); first.appendBlankLine(); first.classBegin(); first.emitDefaultConstructor(); - first.emitRuntimeHelperMethods(); - first.emitFirstHeaderPreamble(libs, useSystemLoadLibrary); - // emit basic primitive types - first.appendIndentedLines(""" - - public static final ValueLayout.OfBoolean C_BOOL = (ValueLayout.OfBoolean) Linker.nativeLinker().canonicalLayouts().get("bool"); - 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"); - 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)); - """); - if (TypeImpl.IS_WINDOWS) { - first.appendIndentedLines("public static final ValueLayout.OfInt C_LONG = (ValueLayout.OfInt) Linker.nativeLinker().canonicalLayouts().get(\"long\");"); - first.appendIndentedLines("public static final ValueLayout.OfDouble C_LONG_DOUBLE = (ValueLayout.OfDouble) Linker.nativeLinker().canonicalLayouts().get(\"double\");"); - } else { - first.appendIndentedLines("public static final ValueLayout.OfLong C_LONG = (ValueLayout.OfLong) Linker.nativeLinker().canonicalLayouts().get(\"long\");"); + + if (generateShareableItems == null) { + first.emitRuntimeHelperMethods(); + first.emitFirstHeaderPreamble(libs, useSystemLoadLibrary); + first.emitBasicPrimitiveTypes(); } + return first; } + private SourceFileBuilder createJextractUtilsBuilder(String packageName, List libs, boolean useSystemLoadLibrary) { + SourceFileBuilder utilsBuilder = SourceFileBuilder.newSourceFile(packageName, jextractUTILS); + HeaderFileBuilder utilsHeader = new HeaderFileBuilder(utilsBuilder, jextractUTILS, null, jextractUTILS); + + utilsHeader.appendBlankLine(); + utilsHeader.classBegin(); + utilsHeader.emitDefaultConstructor(); + + utilsHeader.emitRuntimeHelperMethods(); + utilsHeader.emitFirstHeaderPreamble(libs, useSystemLoadLibrary); + utilsHeader.emitBasicPrimitiveTypes(); + utilsHeader.classEnd(); + + return utilsBuilder; + } + public List toFiles() { lastHeader.classEnd(); List files = new ArrayList<>(); + if (jextractUTILS != null && jextractUtilsBuilder != null) { + files.add(jextractUtilsBuilder.toFile()); + } + if (headerBuilders.size() == 1) { files.add(headerBuilders.getFirst().toFile(s -> s.replace(SUFFIX, ""))); } else { diff --git a/src/main/resources/org/openjdk/jextract/impl/resources/Messages.properties b/src/main/resources/org/openjdk/jextract/impl/resources/Messages.properties index c673332e..a5cd0a19 100644 --- a/src/main/resources/org/openjdk/jextract/impl/resources/Messages.properties +++ b/src/main/resources/org/openjdk/jextract/impl/resources/Messages.properties @@ -40,6 +40,7 @@ help.include-struct=name of struct definition to include help.include-union=name of union definition to include help.D=define a C preprocessor macro help.dump-includes=dump included symbols into specified file +help.sharable.items=generate shared utility class for common layouts and methods help.h=print help help.header-class-name=name of the header class help.l=specify a library @@ -84,11 +85,12 @@ Option Description \ option is not specified, then current directory is used. \n\ -t, --target-package target package name for the generated classes. If this option\n\ \ is not specified, then unnamed package is used. \n\ +--sharable-items generate shared utility class for common layouts and methods \n\ --version print version information and exit \n\ \ \n\ macOS platform options for running jextract (available only when running on macOS): \n\ -F specify the framework directory \n\ --framework specify framework library. -framework libGL is equivalent to \n\ +--framework specify framework library. --framework libGL is equivalent to \n\ \ -l :/System/Library/Frameworks/libGL.framework/libGL jextract.version=\ diff --git a/test/jtreg/generator/sharableItems/TestSharableItems.java b/test/jtreg/generator/sharableItems/TestSharableItems.java new file mode 100644 index 00000000..047d1f12 --- /dev/null +++ b/test/jtreg/generator/sharableItems/TestSharableItems.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2025, 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 org.testng.annotations.Test; +import test.jextract.sharableItems.*; + +import java.lang.foreign.Arena; +import java.lang.foreign.GroupLayout; +import java.lang.foreign.MemorySegment; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.testng.Assert.assertEquals; +import static test.jextract.sharableItems.sharableItems_h.*; + +/* + * @test + * @bug 7903933 + * @summary This is verbatim copy of TestNestedInsideAnon except for the `@run` command, used to test the `--sharable-items` option + * @library /lib + * @build testlib.TestUtils + * @run main/othervm JtregJextract --sharable-items FFM_UTILS -t test.jextract.sharableItems sharableItems.h + * @build TestSharableItems + * @run testng/othervm --enable-native-access=ALL-UNNAMED TestSharableItems + */ +public class TestSharableItems { + @Test + public void testAnonField() { + checkLayout(S.layout()); + } + + void checkLayout(GroupLayout layout) { + assertEquals(((GroupLayout)layout.memberLayouts().get(0)).memberLayouts().get(0).withoutName(), + S.Flags.layout().withoutName()); + } +} diff --git a/test/jtreg/generator/sharableItems/sharableItems.h b/test/jtreg/generator/sharableItems/sharableItems.h new file mode 100644 index 00000000..0250bc08 --- /dev/null +++ b/test/jtreg/generator/sharableItems/sharableItems.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2025, 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. + */ + +typedef struct { + struct { + struct { + char y; + } Flags; + }; +} S; From 01c8205f7649d648a841347edd96fd539d9f3e02 Mon Sep 17 00:00:00 2001 From: Nizar Benalla Date: Tue, 15 Apr 2025 10:57:00 +0100 Subject: [PATCH 05/10] - Move shared symbols to a different class - Add option to override the name of the shared symbol class --- doc/GUIDE.md | 30 ++++----- .../org/openjdk/jextract/JextractTool.java | 12 ++-- .../openjdk/jextract/impl/IncludeHelper.java | 22 ++---- .../org/openjdk/jextract/impl/Options.java | 6 +- .../openjdk/jextract/impl/OutputFactory.java | 13 ++-- .../jextract/impl/ToplevelBuilder.java | 67 ++++++------------- .../impl/resources/Messages.properties | 6 +- .../TestSharedSymbolsHeader.java} | 40 +++++------ .../sharedSymbolsHeader.h} | 9 +-- 9 files changed, 76 insertions(+), 129 deletions(-) rename test/jtreg/generator/{sharableItems/TestSharableItems.java => sharedSymbolsHeader/TestSharedSymbolsHeader.java} (54%) rename test/jtreg/generator/{sharableItems/sharableItems.h => sharedSymbolsHeader/sharedSymbolsHeader.h} (91%) diff --git a/doc/GUIDE.md b/doc/GUIDE.md index 1b9a08bf..4e90bb66 100644 --- a/doc/GUIDE.md +++ b/doc/GUIDE.md @@ -975,21 +975,21 @@ glutSolidTeapot(0.5) A complete list of all the supported command line options is given below: -| Option | Meaning | -| :----------------------------------------------------------- | ------------------------------------------------------------ | -| `-D --define-macro =` | define `` to `` (or 1 if `` omitted) | -| `--header-class-name ` | name of the generated header class. If this option is not specified, then header class name is derived from the header file name. For example, class "foo_h" for header "foo.h". If multiple headers are specified, then this option is mandatory. | -| `-t, --target-package ` | target package name for the generated classes. If this option is not specified, then unnamed package is used. | -| `-I, --include-dir ` | append directory to the include search paths. Include search paths are searched in order. For example, if `-I foo -I bar` is specified, header files will be searched in "foo" first, then (if nothing is found) in "bar".| -| `-l, --library ` | specify a shared library that should be loaded by the generated header class. If starts with `:`, then what follows is interpreted as a library path. Otherwise, `` denotes a library name. Examples:
`-l GL`
`-l :libGL.so.1`
`-l :/usr/lib/libGL.so.1`| -| `--use-system-load-library` | libraries specified using `-l` are loaded in the loader symbol lookup (using either `System::loadLibrary`, or `System::load`). Useful if the libraries must be loaded from one of the paths in `java.library.path`.| -| `--output ` | specify where to place generated files | -| `--dump-includes ` | dump included symbols into specified file (see below) | -| `--include-[function,constant,struct,union,typedef,var]` | Include a symbol of the given name and kind in the generated bindings. When one of these options is specified, any symbol that is not matched by any specified filters is omitted from the generated bindings. | -| `--sharable-utils ` | generate shared utility class for common layouts and methods | -| `--version` | print version information and exit | -| `-F ` (macOs only) | specify the framework directory include files. Defaults to the current Mac OS X SDK dir.| -| `--framework ` (macOs only) | specify the name of the library, path will be expanded to that of the framework folder.| +| Option | Meaning | +|:-----------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `-D --define-macro =` | define `` to `` (or 1 if `` omitted) | +| `--header-class-name ` | name of the generated header class. If this option is not specified, then header class name is derived from the header file name. For example, class "foo_h" for header "foo.h". If multiple headers are specified, then this option is mandatory. | +| `-t, --target-package ` | target package name for the generated classes. If this option is not specified, then unnamed package is used. | +| `-I, --include-dir ` | append directory to the include search paths. Include search paths are searched in order. For example, if `-I foo -I bar` is specified, header files will be searched in "foo" first, then (if nothing is found) in "bar". | +| `-l, --library ` | specify a shared library that should be loaded by the generated header class. If starts with `:`, then what follows is interpreted as a library path. Otherwise, `` denotes a library name. Examples:
`-l GL`
`-l :libGL.so.1`
`-l :/usr/lib/libGL.so.1` | +| `--use-system-load-library` | libraries specified using `-l` are loaded in the loader symbol lookup (using either `System::loadLibrary`, or `System::load`). Useful if the libraries must be loaded from one of the paths in `java.library.path`. | +| `--output ` | specify where to place generated files | +| `--dump-includes ` | dump included symbols into specified file (see below) | +| `--include-[function,constant,struct,union,typedef,var]` | Include a symbol of the given name and kind in the generated bindings. When one of these options is specified, any symbol that is not matched by any specified filters is omitted from the generated bindings. | +| `--shared-symbols ` | override the name of the root header class | +| `--version` | print version information and exit | +| `-F ` (macOs only) | specify the framework directory include files. Defaults to the current Mac OS X SDK dir. | +| `--framework ` (macOs only) | specify the name of the library, path will be expanded to that of the framework folder. | Jextract accepts one or more header files. When multiple header files are specified, diff --git a/src/main/java/org/openjdk/jextract/JextractTool.java b/src/main/java/org/openjdk/jextract/JextractTool.java index d7090b27..3b44faae 100644 --- a/src/main/java/org/openjdk/jextract/JextractTool.java +++ b/src/main/java/org/openjdk/jextract/JextractTool.java @@ -137,8 +137,8 @@ private static List generateInternal(Declaration.Scoped decl, St .findFirst().get(); return logger.hasErrors() ? List.of() : - List.of(OutputFactory.generateWrapped(transformedDecl, targetPkg, - libs, useSystemLoadLibrary, includeHelper.getSharableItems())); + List.of(OutputFactory.generateWrapped(transformedDecl, targetPkg, libs, useSystemLoadLibrary, + includeHelper.getSharedSymbolsFile())); } /** @@ -357,7 +357,7 @@ private int run(String[] args) { OptionParser parser = new OptionParser(); parser.accepts("-D", List.of("--define-macro"), "help.D", true); parser.accepts("--dump-includes", "help.dump-includes", true); - parser.accepts("--sharable-items", "help.sharable.items", true); + parser.accepts("--shared-symbols", "help.shared.symbols", true); for (IncludeHelper.IncludeKind includeKind : IncludeHelper.IncludeKind.values()) { parser.accepts("--" + includeKind.optionName(), "help." + includeKind.optionName(), true); } @@ -441,8 +441,8 @@ private int run(String[] args) { builder.setDumpIncludeFile(optionSet.valueOf("--dump-includes")); } - if (optionSet.has("--sharable-items")) { - builder.useSharableItems(optionSet.valueOf("--sharable-items")); + if (optionSet.has("--shared-symbols")) { + builder.setSharedUtilsName(optionSet.valueOf("--shared-symbols")); } if (optionSet.has("--output")) { @@ -520,7 +520,7 @@ private int run(String[] args) { } try { - if (options.includeHelper.getDumpIncludesFile() != null) { + if (options.includeHelper.dumpIncludesFile != null) { options.includeHelper.dumpIncludes(); } else { Path output = Path.of(options.outputDir); diff --git a/src/main/java/org/openjdk/jextract/impl/IncludeHelper.java b/src/main/java/org/openjdk/jextract/impl/IncludeHelper.java index 3ed9fabc..7a99f880 100644 --- a/src/main/java/org/openjdk/jextract/impl/IncludeHelper.java +++ b/src/main/java/org/openjdk/jextract/impl/IncludeHelper.java @@ -83,27 +83,15 @@ static IncludeKind fromScoped(Declaration.Scoped scoped) { private final EnumMap> includesSymbolNamesByKind = new EnumMap<>(IncludeKind.class); private final Set usedDeclarations = new HashSet<>(); + public String dumpIncludesFile; + public String sharedSymbolsFile; - private String dumpIncludesFile; - private String sharableItems; - - public String getDumpIncludesFile() { - return dumpIncludesFile; - } - - public void setDumpIncludesFile(String dumpIncludesFile) { - this.dumpIncludesFile = dumpIncludesFile; - } - - public void setSharableItems(String sharableItems) { - this.sharableItems = sharableItems; - } - public String getSharableItems() { - return sharableItems; + public String getSharedSymbolsFile() { + return sharedSymbolsFile; } public void addSymbol(IncludeKind kind, String symbolName) { - Set names = includesSymbolNamesByKind.computeIfAbsent(kind, _unused -> new HashSet<>()); + Set names = includesSymbolNamesByKind.computeIfAbsent(kind, (_unused) -> new HashSet<>()); names.add(symbolName); } diff --git a/src/main/java/org/openjdk/jextract/impl/Options.java b/src/main/java/org/openjdk/jextract/impl/Options.java index 961ba1d0..063fc869 100644 --- a/src/main/java/org/openjdk/jextract/impl/Options.java +++ b/src/main/java/org/openjdk/jextract/impl/Options.java @@ -99,11 +99,11 @@ public void setTargetPackage(String pkg) { } public void setDumpIncludeFile(String dumpIncludesFile) { - includeHelper.setDumpIncludesFile(dumpIncludesFile); + includeHelper.dumpIncludesFile = dumpIncludesFile; } - public void useSharableItems(String name) { - includeHelper.setSharableItems(name); + public void setSharedUtilsName(String sharedSymbolsFile) { + includeHelper.sharedSymbolsFile = sharedSymbolsFile; } public void addIncludeSymbol(IncludeHelper.IncludeKind kind, String symbolName) { diff --git a/src/main/java/org/openjdk/jextract/impl/OutputFactory.java b/src/main/java/org/openjdk/jextract/impl/OutputFactory.java index 15fda0a8..a0514442 100644 --- a/src/main/java/org/openjdk/jextract/impl/OutputFactory.java +++ b/src/main/java/org/openjdk/jextract/impl/OutputFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, 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 @@ -47,15 +47,10 @@ public static JavaSourceFile[] generateWrapped(Declaration.Scoped decl, String pkgName, List libs, boolean useSystemLoadLibrary, - String generateShareableItems) { + String sharedSymbolsFile) { String clsName = JavaName.getOrThrow(decl); - ToplevelBuilder toplevelBuilder = new ToplevelBuilder( - pkgName, - clsName, - libs, - useSystemLoadLibrary, - generateShareableItems - ); + ToplevelBuilder toplevelBuilder = new ToplevelBuilder(pkgName, clsName, + libs, useSystemLoadLibrary, sharedSymbolsFile); return new OutputFactory(toplevelBuilder).generate(decl); } diff --git a/src/main/java/org/openjdk/jextract/impl/ToplevelBuilder.java b/src/main/java/org/openjdk/jextract/impl/ToplevelBuilder.java index 67ed23be..77045a63 100644 --- a/src/main/java/org/openjdk/jextract/impl/ToplevelBuilder.java +++ b/src/main/java/org/openjdk/jextract/impl/ToplevelBuilder.java @@ -43,77 +43,50 @@ class ToplevelBuilder implements OutputFactory.Builder { private static final int DECLS_PER_HEADER_CLASS = Integer.getInteger("jextract.decls.per.header", 1000); public static final String PREV_SUFFIX = "#{PREV_SUFFIX}"; private static final String SUFFIX = "#{SUFFIX}"; - private final String jextractUTILS; + private static String SHARED; private int declCount; private final List headerBuilders = new ArrayList<>(); private final List otherBuilders = new ArrayList<>(); private HeaderFileBuilder lastHeader; private final ClassDesc headerDesc; - private SourceFileBuilder jextractUtilsBuilder; ToplevelBuilder(String packageName, String headerClassName, - List libs, - boolean useSystemLoadLibrary, - String generateShareableItems) { - jextractUTILS = generateShareableItems; + List libs, boolean useSystemLoadLibrary, String sharedSymbolsFile) { this.headerDesc = ClassDesc.of(packageName, headerClassName); - - if (jextractUTILS != null) { - jextractUtilsBuilder = createJextractUtilsBuilder(packageName,libs,useSystemLoadLibrary); - } - + SHARED = sharedSymbolsFile != null ? sharedSymbolsFile : headerDesc.displayName() + "$shared"; + createSharedClass(); SourceFileBuilder sfb = SourceFileBuilder.newSourceFile(packageName, headerClassName); headerBuilders.add(sfb); - lastHeader = createFirstHeader(sfb, libs, useSystemLoadLibrary, jextractUTILS); + lastHeader = createFirstHeader(sfb, libs, useSystemLoadLibrary); + } + + private void createSharedClass() { + SourceFileBuilder sfb = SourceFileBuilder.newSourceFile(packageName(), SHARED); + HeaderFileBuilder sharedBuilder = new HeaderFileBuilder(sfb, SHARED, null, SHARED); + sharedBuilder.appendBlankLine(); + sharedBuilder.classBegin(); + sharedBuilder.emitDefaultConstructor(); + sharedBuilder.emitBasicPrimitiveTypes(); + sharedBuilder.classEnd(); + otherBuilders.add(sfb); } - private static HeaderFileBuilder createFirstHeader(SourceFileBuilder sfb, - List libs, - boolean useSystemLoadLibrary, - String generateShareableItems) { - HeaderFileBuilder first = new HeaderFileBuilder(sfb, - String.format("%1$s#{SUFFIX}", sfb.className()), - generateShareableItems, - sfb.className()); + private static HeaderFileBuilder createFirstHeader(SourceFileBuilder sfb, List libs, boolean useSystemLoadLibrary) { + HeaderFileBuilder first = new HeaderFileBuilder(sfb, String.format("%1$s#{SUFFIX}",sfb.className()), SHARED, sfb.className()); first.appendBlankLine(); first.classBegin(); first.emitDefaultConstructor(); - - if (generateShareableItems == null) { - first.emitRuntimeHelperMethods(); - first.emitFirstHeaderPreamble(libs, useSystemLoadLibrary); - first.emitBasicPrimitiveTypes(); - } - + first.emitRuntimeHelperMethods(); + first.emitFirstHeaderPreamble(libs, useSystemLoadLibrary); return first; } - private SourceFileBuilder createJextractUtilsBuilder(String packageName, List libs, boolean useSystemLoadLibrary) { - SourceFileBuilder utilsBuilder = SourceFileBuilder.newSourceFile(packageName, jextractUTILS); - HeaderFileBuilder utilsHeader = new HeaderFileBuilder(utilsBuilder, jextractUTILS, null, jextractUTILS); - - utilsHeader.appendBlankLine(); - utilsHeader.classBegin(); - utilsHeader.emitDefaultConstructor(); - - utilsHeader.emitRuntimeHelperMethods(); - utilsHeader.emitFirstHeaderPreamble(libs, useSystemLoadLibrary); - utilsHeader.emitBasicPrimitiveTypes(); - utilsHeader.classEnd(); - - return utilsBuilder; - } - public List toFiles() { lastHeader.classEnd(); List files = new ArrayList<>(); - if (jextractUTILS != null && jextractUtilsBuilder != null) { - files.add(jextractUtilsBuilder.toFile()); - } - if (headerBuilders.size() == 1) { files.add(headerBuilders.getFirst().toFile(s -> s.replace(SUFFIX, ""))); } else { diff --git a/src/main/resources/org/openjdk/jextract/impl/resources/Messages.properties b/src/main/resources/org/openjdk/jextract/impl/resources/Messages.properties index a5cd0a19..be1de9dc 100644 --- a/src/main/resources/org/openjdk/jextract/impl/resources/Messages.properties +++ b/src/main/resources/org/openjdk/jextract/impl/resources/Messages.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, 2025 Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2025, 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 @@ -40,7 +40,7 @@ help.include-struct=name of struct definition to include help.include-union=name of union definition to include help.D=define a C preprocessor macro help.dump-includes=dump included symbols into specified file -help.sharable.items=generate shared utility class for common layouts and methods +help.shared.symbols=override the name of the root header class help.h=print help help.header-class-name=name of the header class help.l=specify a library @@ -85,7 +85,7 @@ Option Description \ option is not specified, then current directory is used. \n\ -t, --target-package target package name for the generated classes. If this option\n\ \ is not specified, then unnamed package is used. \n\ ---sharable-items generate shared utility class for common layouts and methods \n\ +--shared-symbols override the name of the root header class \n\ --version print version information and exit \n\ \ \n\ macOS platform options for running jextract (available only when running on macOS): \n\ diff --git a/test/jtreg/generator/sharableItems/TestSharableItems.java b/test/jtreg/generator/sharedSymbolsHeader/TestSharedSymbolsHeader.java similarity index 54% rename from test/jtreg/generator/sharableItems/TestSharableItems.java rename to test/jtreg/generator/sharedSymbolsHeader/TestSharedSymbolsHeader.java index 047d1f12..f07a3c11 100644 --- a/test/jtreg/generator/sharableItems/TestSharableItems.java +++ b/test/jtreg/generator/sharedSymbolsHeader/TestSharedSymbolsHeader.java @@ -22,34 +22,30 @@ */ import org.testng.annotations.Test; -import test.jextract.sharableItems.*; - -import java.lang.foreign.Arena; -import java.lang.foreign.GroupLayout; -import java.lang.foreign.MemorySegment; -import java.util.concurrent.atomic.AtomicInteger; - -import static org.testng.Assert.assertEquals; -import static test.jextract.sharableItems.sharableItems_h.*; +import java.nio.file.Path; +import testlib.TestUtils; +import testlib.JextractToolRunner; +import test.jextract.sharedSymbolsHeader.*; +import static org.testng.Assert.*; /* * @test * @bug 7903933 - * @summary This is verbatim copy of TestNestedInsideAnon except for the `@run` command, used to test the `--sharable-items` option + * @summary check that header containg the shared symbols was created with the specified name * @library /lib - * @build testlib.TestUtils - * @run main/othervm JtregJextract --sharable-items FFM_UTILS -t test.jextract.sharableItems sharableItems.h - * @build TestSharableItems - * @run testng/othervm --enable-native-access=ALL-UNNAMED TestSharableItems + * @build testlib.JextractToolRunner testlib.TestUtils + * @run main/othervm JtregJextract -t test.jextract.sharedSymbolsHeader sharedSymbolsHeader.h */ -public class TestSharableItems { +public class TestSharedSymbolsHeader extends JextractToolRunner { @Test - public void testAnonField() { - checkLayout(S.layout()); - } - - void checkLayout(GroupLayout layout) { - assertEquals(((GroupLayout)layout.memberLayouts().get(0)).memberLayouts().get(0).withoutName(), - S.Flags.layout().withoutName()); + public void testSplit() { + Path splitOutput = getOutputFilePath("sharedSymbolsHeader"); + Path splitH = getInputFilePath("sharedSymbolsHeader.h"); + runAndCompile(splitOutput, splitH.toString()); + try(TestUtils.Loader loader = TestUtils.classLoader(splitOutput)) { + assertNotNull(loader.loadClass("sharedSymbolsHeader_h$shared")); + } finally { + TestUtils.deleteDir(splitOutput); + } } } diff --git a/test/jtreg/generator/sharableItems/sharableItems.h b/test/jtreg/generator/sharedSymbolsHeader/sharedSymbolsHeader.h similarity index 91% rename from test/jtreg/generator/sharableItems/sharableItems.h rename to test/jtreg/generator/sharedSymbolsHeader/sharedSymbolsHeader.h index 0250bc08..9da37872 100644 --- a/test/jtreg/generator/sharableItems/sharableItems.h +++ b/test/jtreg/generator/sharedSymbolsHeader/sharedSymbolsHeader.h @@ -21,10 +21,5 @@ * questions. */ -typedef struct { - struct { - struct { - char y; - } Flags; - }; -} S; +typedef int T; +typedef char* P1; From 532ee1a693f8b8b23a90f1c688e3843ae1c58430 Mon Sep 17 00:00:00 2001 From: Nizar Benalla Date: Fri, 11 Apr 2025 09:35:09 +0100 Subject: [PATCH 06/10] make utility classes final with a private constructor --- .../openjdk/jextract/impl/ClassSourceBuilder.java | 11 ++++++++++- .../jextract/impl/FunctionalInterfaceBuilder.java | 6 +++--- .../test/toolprovider/TestClassGeneration.java | 13 +++++++++++-- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/openjdk/jextract/impl/ClassSourceBuilder.java b/src/main/java/org/openjdk/jextract/impl/ClassSourceBuilder.java index 051287ac..7c1ae3a7 100644 --- a/src/main/java/org/openjdk/jextract/impl/ClassSourceBuilder.java +++ b/src/main/java/org/openjdk/jextract/impl/ClassSourceBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, 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 @@ -160,6 +160,15 @@ final void emitDefaultConstructor() { """, className); } + final void emitPrivateConstructor() { + appendIndentedLines(""" + + private %1$s() { + // Should not be called directly + } + """, className); + } + final void emitDocComment(Declaration decl) { emitDocComment(decl, ""); } diff --git a/src/main/java/org/openjdk/jextract/impl/FunctionalInterfaceBuilder.java b/src/main/java/org/openjdk/jextract/impl/FunctionalInterfaceBuilder.java index 8ef036c8..be3fe938 100644 --- a/src/main/java/org/openjdk/jextract/impl/FunctionalInterfaceBuilder.java +++ b/src/main/java/org/openjdk/jextract/impl/FunctionalInterfaceBuilder.java @@ -42,7 +42,7 @@ final class FunctionalInterfaceBuilder extends ClassSourceBuilder { private FunctionalInterfaceBuilder(SourceFileBuilder builder, String className, ClassSourceBuilder enclosing, String runtimeHelperName, Type.Function funcType, boolean isNested) { - super(builder, isNested ? "public static" : "public", Kind.CLASS, className, null, enclosing, runtimeHelperName); + super(builder, isNested ? "public final static" : "public final", Kind.CLASS, className, null, enclosing, runtimeHelperName); this.parameterNames = funcType.parameterNames().map(NameMangler::javaSafeIdentifiers); this.funcType = funcType; this.methodType = Utils.methodTypeFor(funcType); @@ -55,7 +55,7 @@ public static void generate(SourceFileBuilder builder, String className, ClassSo fib.appendBlankLine(); fib.emitDocComment(parentDecl); fib.classBegin(); - fib.emitDefaultConstructor(); + fib.emitPrivateConstructor(); String fiName = fib.emitFunctionalInterface(); fib.emitDescriptorDecl(); fib.emitFunctionalFactory(fiName); @@ -99,7 +99,7 @@ private void emitInvoke() { boolean needsAllocator = Utils.isStructOrUnion(funcType.returnType()); String allocParam = needsAllocator ? ", SegmentAllocator alloc" : ""; String allocArg = needsAllocator ? ", alloc" : ""; - String paramStr = methodType.parameterCount() != 0 ? String.format(",%1$s", paramExprs()) : ""; + String paramStr = methodType.parameterCount() != 0 ? String.format(", %1$s", paramExprs()) : ""; appendIndentedLines(""" private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); diff --git a/test/testng/org/openjdk/jextract/test/toolprovider/TestClassGeneration.java b/test/testng/org/openjdk/jextract/test/toolprovider/TestClassGeneration.java index 9a58bb20..592dab9b 100644 --- a/test/testng/org/openjdk/jextract/test/toolprovider/TestClassGeneration.java +++ b/test/testng/org/openjdk/jextract/test/toolprovider/TestClassGeneration.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, 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 @@ -192,7 +192,7 @@ public void testStructMember(String structName, MemoryLayout memberLayout, Class @Test(dataProvider = "functionalInterfaces") public void testFunctionalInterface(String name, MethodType type) { Class fpClass = loader.loadClass("com.acme." + name); - checkDefaultConstructor(fpClass); + checkPrivateConstructor(fpClass); Class fiClass = findNestedClass(fpClass, "Function"); assertNotNull(fiClass); checkMethod(fiClass, "apply", type); @@ -228,4 +228,13 @@ private void checkDefaultConstructor(Class cls) { fail("Default constructor not found!"); } } + + private void checkPrivateConstructor(Class cls) { + try { + Constructor c = cls.getDeclaredConstructor(); + assertEquals(c.getModifiers(), Modifier.PRIVATE, "Unexpected constructor modifiers"); + } catch (ReflectiveOperationException ex) { + fail("Private constructor not found!"); + } + } } From 5c8802a6abc2fbb65a1bf341be2e7e11c5936c74 Mon Sep 17 00:00:00 2001 From: Nizar Benalla Date: Fri, 2 May 2025 15:16:08 +0100 Subject: [PATCH 07/10] feedback: rename option and consolidate code --- doc/GUIDE.md | 2 +- .../org/openjdk/jextract/JextractTool.java | 6 +- .../jextract/impl/HeaderFileBuilder.java | 6 +- .../jextract/impl/ToplevelBuilder.java | 90 ++++++++++++------- .../impl/resources/Messages.properties | 2 +- 5 files changed, 67 insertions(+), 39 deletions(-) diff --git a/doc/GUIDE.md b/doc/GUIDE.md index 4e90bb66..fde681cc 100644 --- a/doc/GUIDE.md +++ b/doc/GUIDE.md @@ -986,7 +986,7 @@ A complete list of all the supported command line options is given below: | `--output ` | specify where to place generated files | | `--dump-includes ` | dump included symbols into specified file (see below) | | `--include-[function,constant,struct,union,typedef,var]` | Include a symbol of the given name and kind in the generated bindings. When one of these options is specified, any symbol that is not matched by any specified filters is omitted from the generated bindings. | -| `--shared-symbols ` | override the name of the root header class | +| `--symbols-class-name ` | override the name of the root header class | | `--version` | print version information and exit | | `-F ` (macOs only) | specify the framework directory include files. Defaults to the current Mac OS X SDK dir. | | `--framework ` (macOs only) | specify the name of the library, path will be expanded to that of the framework folder. | diff --git a/src/main/java/org/openjdk/jextract/JextractTool.java b/src/main/java/org/openjdk/jextract/JextractTool.java index 3b44faae..e244ca09 100644 --- a/src/main/java/org/openjdk/jextract/JextractTool.java +++ b/src/main/java/org/openjdk/jextract/JextractTool.java @@ -357,7 +357,7 @@ private int run(String[] args) { OptionParser parser = new OptionParser(); parser.accepts("-D", List.of("--define-macro"), "help.D", true); parser.accepts("--dump-includes", "help.dump-includes", true); - parser.accepts("--shared-symbols", "help.shared.symbols", true); + parser.accepts("--symbols-class-name", "help.shared.symbols", true); for (IncludeHelper.IncludeKind includeKind : IncludeHelper.IncludeKind.values()) { parser.accepts("--" + includeKind.optionName(), "help." + includeKind.optionName(), true); } @@ -441,8 +441,8 @@ private int run(String[] args) { builder.setDumpIncludeFile(optionSet.valueOf("--dump-includes")); } - if (optionSet.has("--shared-symbols")) { - builder.setSharedUtilsName(optionSet.valueOf("--shared-symbols")); + if (optionSet.has("--symbols-class-name")) { + builder.setSharedUtilsName(optionSet.valueOf("--symbols-class-name")); } if (optionSet.has("--output")) { diff --git a/src/main/java/org/openjdk/jextract/impl/HeaderFileBuilder.java b/src/main/java/org/openjdk/jextract/impl/HeaderFileBuilder.java index 68e631a5..e8b51103 100644 --- a/src/main/java/org/openjdk/jextract/impl/HeaderFileBuilder.java +++ b/src/main/java/org/openjdk/jextract/impl/HeaderFileBuilder.java @@ -351,10 +351,14 @@ void emitFirstHeaderPreamble(List libraries, boolean useSystemL .collect(Collectors.joining(String.format("\n%1$s", indentString(2)), "static final SymbolLookup SYMBOL_LOOKUP = ", ";"))); } + void emitLibaryArena(){appendIndentedLines(""" + + static final Arena LIBRARY_ARENA = Arena.ofAuto();"""); + } + void emitRuntimeHelperMethods() { appendIndentedLines(""" - static final Arena LIBRARY_ARENA = Arena.ofAuto(); static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls"); static void traceDowncall(String name, Object... args) { diff --git a/src/main/java/org/openjdk/jextract/impl/ToplevelBuilder.java b/src/main/java/org/openjdk/jextract/impl/ToplevelBuilder.java index 77045a63..3f8f350d 100644 --- a/src/main/java/org/openjdk/jextract/impl/ToplevelBuilder.java +++ b/src/main/java/org/openjdk/jextract/impl/ToplevelBuilder.java @@ -51,35 +51,68 @@ class ToplevelBuilder implements OutputFactory.Builder { private HeaderFileBuilder lastHeader; private final ClassDesc headerDesc; - ToplevelBuilder(String packageName, String headerClassName, - List libs, boolean useSystemLoadLibrary, String sharedSymbolsFile) { + ToplevelBuilder(String packageName, String headerClassName, List libs, + boolean useSystemLoadLibrary, String sharedSymbolsFile) { this.headerDesc = ClassDesc.of(packageName, headerClassName); - SHARED = sharedSymbolsFile != null ? sharedSymbolsFile : headerDesc.displayName() + "$shared"; - createSharedClass(); - SourceFileBuilder sfb = SourceFileBuilder.newSourceFile(packageName, headerClassName); - headerBuilders.add(sfb); - lastHeader = createFirstHeader(sfb, libs, useSystemLoadLibrary); + SHARED = sharedSymbolsFile != null ? + sharedSymbolsFile : + headerDesc.displayName() + "$shared"; + + initSharedClass(); + initFirstHeader(libs, useSystemLoadLibrary); } - private void createSharedClass() { + private void initSharedClass() { SourceFileBuilder sfb = SourceFileBuilder.newSourceFile(packageName(), SHARED); - HeaderFileBuilder sharedBuilder = new HeaderFileBuilder(sfb, SHARED, null, SHARED); - sharedBuilder.appendBlankLine(); - sharedBuilder.classBegin(); - sharedBuilder.emitDefaultConstructor(); - sharedBuilder.emitBasicPrimitiveTypes(); - sharedBuilder.classEnd(); + HeaderFileBuilder shared = initHeader(sfb, SHARED, null, null); otherBuilders.add(sfb); + shared.emitBasicPrimitiveTypes(); + shared.emitRuntimeHelperMethods(); + shared.classEnd(); } - private static HeaderFileBuilder createFirstHeader(SourceFileBuilder sfb, List libs, boolean useSystemLoadLibrary) { - HeaderFileBuilder first = new HeaderFileBuilder(sfb, String.format("%1$s#{SUFFIX}",sfb.className()), SHARED, sfb.className()); - first.appendBlankLine(); - first.classBegin(); - first.emitDefaultConstructor(); - first.emitRuntimeHelperMethods(); - first.emitFirstHeaderPreamble(libs, useSystemLoadLibrary); - return first; + private void initFirstHeader(List libs, boolean useSystemLoadLibrary) { + String base = headerDesc.displayName(); + SourceFileBuilder sfb = SourceFileBuilder.newSourceFile(packageName(), base); + headerBuilders.add(sfb); + + lastHeader = initHeader( + sfb, + base + SUFFIX, + SHARED, + base + ); + lastHeader.emitLibaryArena(); + lastHeader.emitFirstHeaderPreamble(libs, useSystemLoadLibrary); + } + + /** + * Shared boilerplate: blank line + classBegin + default constructor + */ + private HeaderFileBuilder initHeader(SourceFileBuilder sfb, String classNameWithSuffix, + String superClass, String extendsClass) { + HeaderFileBuilder hfb = new HeaderFileBuilder(sfb, classNameWithSuffix, + superClass, extendsClass); + hfb.appendBlankLine(); + hfb.classBegin(); + hfb.emitDefaultConstructor(); + return hfb; + } + + /** + * Creates a new header file chunk + */ + private HeaderFileBuilder newHeaderChunk() { + String base = headerDesc.displayName(); + SourceFileBuilder sfb = SourceFileBuilder.newSourceFile(packageName(), base); + HeaderFileBuilder hfb = initHeader( + sfb, + base + SUFFIX, + base + PREV_SUFFIX, + base + ); + headerBuilders.add(sfb); + return hfb; } public List toFiles() { @@ -173,21 +206,12 @@ public void addFunctionalInterface(Declaration parentDecl, Type.Function funcTyp private HeaderFileBuilder nextHeader() { if (declCount == DECLS_PER_HEADER_CLASS) { - SourceFileBuilder sfb = SourceFileBuilder.newSourceFile(packageName(), mainHeaderClassName()); - String className = mainHeaderClassName() + SUFFIX; - HeaderFileBuilder headerFileBuilder = new HeaderFileBuilder(sfb, className, - mainHeaderClassName() + PREV_SUFFIX, mainHeaderClassName()); lastHeader.classEnd(); - headerFileBuilder.appendBlankLine(); - headerFileBuilder.classBegin(); - headerFileBuilder.emitDefaultConstructor(); - headerBuilders.add(sfb); - lastHeader = headerFileBuilder; + lastHeader = newHeaderChunk(); declCount = 1; - return headerFileBuilder; } else { declCount++; - return lastHeader; } + return lastHeader; } } diff --git a/src/main/resources/org/openjdk/jextract/impl/resources/Messages.properties b/src/main/resources/org/openjdk/jextract/impl/resources/Messages.properties index be1de9dc..cdb8a821 100644 --- a/src/main/resources/org/openjdk/jextract/impl/resources/Messages.properties +++ b/src/main/resources/org/openjdk/jextract/impl/resources/Messages.properties @@ -85,7 +85,7 @@ Option Description \ option is not specified, then current directory is used. \n\ -t, --target-package target package name for the generated classes. If this option\n\ \ is not specified, then unnamed package is used. \n\ ---shared-symbols override the name of the root header class \n\ +--symbols-class-name override the name of the root header class \n\ --version print version information and exit \n\ \ \n\ macOS platform options for running jextract (available only when running on macOS): \n\ From e3b9ae2a21517e008a06e3e55d42a5957366d4ad Mon Sep 17 00:00:00 2001 From: Nizar Benalla Date: Mon, 5 May 2025 13:41:40 +0100 Subject: [PATCH 08/10] update test --- .../generator/sharedSymbolsHeader/TestSharedSymbolsHeader.java | 1 + 1 file changed, 1 insertion(+) diff --git a/test/jtreg/generator/sharedSymbolsHeader/TestSharedSymbolsHeader.java b/test/jtreg/generator/sharedSymbolsHeader/TestSharedSymbolsHeader.java index f07a3c11..0671f8ca 100644 --- a/test/jtreg/generator/sharedSymbolsHeader/TestSharedSymbolsHeader.java +++ b/test/jtreg/generator/sharedSymbolsHeader/TestSharedSymbolsHeader.java @@ -35,6 +35,7 @@ * @library /lib * @build testlib.JextractToolRunner testlib.TestUtils * @run main/othervm JtregJextract -t test.jextract.sharedSymbolsHeader sharedSymbolsHeader.h + * @run testng/othervm TestSharedSymbolsHeader */ public class TestSharedSymbolsHeader extends JextractToolRunner { @Test From 167423e1c23f3b3d2bf092c4cfdf6f465c6c54e1 Mon Sep 17 00:00:00 2001 From: Nizar Benalla Date: Wed, 7 May 2025 12:30:35 +0100 Subject: [PATCH 09/10] feedback from jorn --- doc/GUIDE.md | 2 +- .../org/openjdk/jextract/JextractTool.java | 29 ++++++++++++------- .../jextract/impl/HeaderFileBuilder.java | 4 ++- .../openjdk/jextract/impl/IncludeHelper.java | 6 ++-- .../org/openjdk/jextract/impl/Options.java | 14 ++++++--- .../openjdk/jextract/impl/OutputFactory.java | 4 +-- .../jextract/impl/ToplevelBuilder.java | 20 ++++++------- .../impl/resources/Messages.properties | 2 +- .../TestSharedSymbolsHeader.java | 14 +++++++-- 9 files changed, 60 insertions(+), 35 deletions(-) diff --git a/doc/GUIDE.md b/doc/GUIDE.md index fde681cc..9f866f76 100644 --- a/doc/GUIDE.md +++ b/doc/GUIDE.md @@ -986,7 +986,7 @@ A complete list of all the supported command line options is given below: | `--output ` | specify where to place generated files | | `--dump-includes ` | dump included symbols into specified file (see below) | | `--include-[function,constant,struct,union,typedef,var]` | Include a symbol of the given name and kind in the generated bindings. When one of these options is specified, any symbol that is not matched by any specified filters is omitted from the generated bindings. | -| `--symbols-class-name ` | override the name of the root header class | +| `--symbols-class-name ` | override the name of the root header class | | `--version` | print version information and exit | | `-F ` (macOs only) | specify the framework directory include files. Defaults to the current Mac OS X SDK dir. | | `--framework ` (macOs only) | specify the name of the library, path will be expanded to that of the framework folder. | diff --git a/src/main/java/org/openjdk/jextract/JextractTool.java b/src/main/java/org/openjdk/jextract/JextractTool.java index e244ca09..4f3c92a0 100644 --- a/src/main/java/org/openjdk/jextract/JextractTool.java +++ b/src/main/java/org/openjdk/jextract/JextractTool.java @@ -118,17 +118,24 @@ private static Declaration.Scoped parseInternal(Logger logger, List head public static List generate(Declaration.Scoped decl, String headerName, String targetPkg, List libs, boolean useSystemLoadLibrary) { - return generateInternal(decl, headerName, targetPkg, new IncludeHelper(), - libs, useSystemLoadLibrary, Logger.DEFAULT); + Options.Builder builder = Options.builder(); + builder.setTargetPackage(targetPkg); + builder.setUseSystemLoadLibrary(useSystemLoadLibrary); + libs.forEach(builder::addLibrary); + Options options = builder.build(); + return generateInternal(decl, headerName, targetPkg, options, Logger.DEFAULT); } - private static List generateInternal(Declaration.Scoped decl, String headerName, - String targetPkg, IncludeHelper includeHelper, - List libs, boolean useSystemLoadLibrary, + + + private static List generateInternal(Declaration.Scoped decl, + String headerName, + String targetPkg, + Options options, Logger logger) { var transformedDecl = Stream.of(decl) // process phases that add Skips first - .map(new IncludeFilter(includeHelper)::scan) + .map(new IncludeFilter(options.includeHelper)::scan) .map(new DuplicateFilter()::scan) .map(new UnsupportedFilter(logger)::scan) // then do the rest @@ -137,8 +144,8 @@ private static List generateInternal(Declaration.Scoped decl, St .findFirst().get(); return logger.hasErrors() ? List.of() : - List.of(OutputFactory.generateWrapped(transformedDecl, targetPkg, libs, useSystemLoadLibrary, - includeHelper.getSharedSymbolsFile())); + List.of(OutputFactory.generateWrapped(transformedDecl, targetPkg, options.libraries, options.useSystemLoadLibrary, + options.sharedClassName)); } /** @@ -442,7 +449,7 @@ private int run(String[] args) { } if (optionSet.has("--symbols-class-name")) { - builder.setSharedUtilsName(optionSet.valueOf("--symbols-class-name")); + builder.setSharedClassName(optionSet.valueOf("--symbols-class-name")); } if (optionSet.has("--output")) { @@ -508,8 +515,8 @@ private int run(String[] args) { System.out.println(toplevel); } files = generateInternal( - toplevel, headerName, - options.targetPackage, options.includeHelper, options.libraries, options.useSystemLoadLibrary, logger); + toplevel, headerName, targetPackage, options, logger + ); if (logger.hasClangErrors()) { return CLANG_ERROR; diff --git a/src/main/java/org/openjdk/jextract/impl/HeaderFileBuilder.java b/src/main/java/org/openjdk/jextract/impl/HeaderFileBuilder.java index e8b51103..10dc74f4 100644 --- a/src/main/java/org/openjdk/jextract/impl/HeaderFileBuilder.java +++ b/src/main/java/org/openjdk/jextract/impl/HeaderFileBuilder.java @@ -349,9 +349,11 @@ void emitFirstHeaderPreamble(List libraries, boolean useSystemL appendBlankLine(); appendIndentedLines(lookupCalls.stream() .collect(Collectors.joining(String.format("\n%1$s", indentString(2)), "static final SymbolLookup SYMBOL_LOOKUP = ", ";"))); + appendBlankLine(); } - void emitLibaryArena(){appendIndentedLines(""" + void emitLibaryArena(){ + appendIndentedLines(""" static final Arena LIBRARY_ARENA = Arena.ofAuto();"""); } diff --git a/src/main/java/org/openjdk/jextract/impl/IncludeHelper.java b/src/main/java/org/openjdk/jextract/impl/IncludeHelper.java index 7a99f880..298f609f 100644 --- a/src/main/java/org/openjdk/jextract/impl/IncludeHelper.java +++ b/src/main/java/org/openjdk/jextract/impl/IncludeHelper.java @@ -84,10 +84,10 @@ static IncludeKind fromScoped(Declaration.Scoped scoped) { private final EnumMap> includesSymbolNamesByKind = new EnumMap<>(IncludeKind.class); private final Set usedDeclarations = new HashSet<>(); public String dumpIncludesFile; - public String sharedSymbolsFile; + public String sharedClassName; - public String getSharedSymbolsFile() { - return sharedSymbolsFile; + public String getSharedClassName() { + return sharedClassName; } public void addSymbol(IncludeKind kind, String symbolName) { diff --git a/src/main/java/org/openjdk/jextract/impl/Options.java b/src/main/java/org/openjdk/jextract/impl/Options.java index 063fc869..e4f52e10 100644 --- a/src/main/java/org/openjdk/jextract/impl/Options.java +++ b/src/main/java/org/openjdk/jextract/impl/Options.java @@ -38,16 +38,20 @@ public final class Options { public final String targetPackage; // output directory public final String outputDir; + // name of the shared class + public String sharedClassName; public final IncludeHelper includeHelper; private Options(List clangArgs, List libraries, boolean useSystemLoadLibrary, - String targetPackage, String outputDir, IncludeHelper includeHelper) { + String targetPackage, String outputDir, String sharedClassName, + IncludeHelper includeHelper) { this.clangArgs = clangArgs; this.libraries = libraries; this.useSystemLoadLibrary = useSystemLoadLibrary; this.targetPackage = targetPackage; this.outputDir = outputDir; this.includeHelper = includeHelper; + this.sharedClassName = sharedClassName; } public static Builder builder() { @@ -60,6 +64,7 @@ public static class Builder { private boolean useSystemLoadLibrary; private String targetPackage; private String outputDir; + private String sharedClassName; private final IncludeHelper includeHelper = new IncludeHelper(); public Builder() { @@ -68,13 +73,14 @@ public Builder() { this.targetPackage = ""; this.outputDir = "."; this.useSystemLoadLibrary = false; + this.sharedClassName = null; } public Options build() { return new Options( Collections.unmodifiableList(clangArgs), Collections.unmodifiableList(libraries), - useSystemLoadLibrary, targetPackage, outputDir, includeHelper + useSystemLoadLibrary, targetPackage, outputDir, sharedClassName, includeHelper ); } @@ -102,8 +108,8 @@ public void setDumpIncludeFile(String dumpIncludesFile) { includeHelper.dumpIncludesFile = dumpIncludesFile; } - public void setSharedUtilsName(String sharedSymbolsFile) { - includeHelper.sharedSymbolsFile = sharedSymbolsFile; + public void setSharedClassName(String file) { + this.sharedClassName = file; } public void addIncludeSymbol(IncludeHelper.IncludeKind kind, String symbolName) { diff --git a/src/main/java/org/openjdk/jextract/impl/OutputFactory.java b/src/main/java/org/openjdk/jextract/impl/OutputFactory.java index a0514442..7fa46e92 100644 --- a/src/main/java/org/openjdk/jextract/impl/OutputFactory.java +++ b/src/main/java/org/openjdk/jextract/impl/OutputFactory.java @@ -47,10 +47,10 @@ public static JavaSourceFile[] generateWrapped(Declaration.Scoped decl, String pkgName, List libs, boolean useSystemLoadLibrary, - String sharedSymbolsFile) { + String sharedClassName) { String clsName = JavaName.getOrThrow(decl); ToplevelBuilder toplevelBuilder = new ToplevelBuilder(pkgName, clsName, - libs, useSystemLoadLibrary, sharedSymbolsFile); + libs, useSystemLoadLibrary, sharedClassName); return new OutputFactory(toplevelBuilder).generate(decl); } diff --git a/src/main/java/org/openjdk/jextract/impl/ToplevelBuilder.java b/src/main/java/org/openjdk/jextract/impl/ToplevelBuilder.java index 3f8f350d..e7c03d89 100644 --- a/src/main/java/org/openjdk/jextract/impl/ToplevelBuilder.java +++ b/src/main/java/org/openjdk/jextract/impl/ToplevelBuilder.java @@ -43,7 +43,7 @@ class ToplevelBuilder implements OutputFactory.Builder { private static final int DECLS_PER_HEADER_CLASS = Integer.getInteger("jextract.decls.per.header", 1000); public static final String PREV_SUFFIX = "#{PREV_SUFFIX}"; private static final String SUFFIX = "#{SUFFIX}"; - private static String SHARED; + private final String shared; private int declCount; private final List headerBuilders = new ArrayList<>(); @@ -52,10 +52,10 @@ class ToplevelBuilder implements OutputFactory.Builder { private final ClassDesc headerDesc; ToplevelBuilder(String packageName, String headerClassName, List libs, - boolean useSystemLoadLibrary, String sharedSymbolsFile) { + boolean useSystemLoadLibrary, String sharedClassName) { this.headerDesc = ClassDesc.of(packageName, headerClassName); - SHARED = sharedSymbolsFile != null ? - sharedSymbolsFile : + shared = sharedClassName != null ? + sharedClassName : headerDesc.displayName() + "$shared"; initSharedClass(); @@ -63,12 +63,12 @@ class ToplevelBuilder implements OutputFactory.Builder { } private void initSharedClass() { - SourceFileBuilder sfb = SourceFileBuilder.newSourceFile(packageName(), SHARED); - HeaderFileBuilder shared = initHeader(sfb, SHARED, null, null); + SourceFileBuilder sfb = SourceFileBuilder.newSourceFile(packageName(), shared); + HeaderFileBuilder sharedHeader = initHeader(sfb, shared, null, null); otherBuilders.add(sfb); - shared.emitBasicPrimitiveTypes(); - shared.emitRuntimeHelperMethods(); - shared.classEnd(); + sharedHeader.emitBasicPrimitiveTypes(); + sharedHeader.emitRuntimeHelperMethods(); + sharedHeader.classEnd(); } private void initFirstHeader(List libs, boolean useSystemLoadLibrary) { @@ -79,7 +79,7 @@ private void initFirstHeader(List libs, boolean useSystemLoadLi lastHeader = initHeader( sfb, base + SUFFIX, - SHARED, + shared, base ); lastHeader.emitLibaryArena(); diff --git a/src/main/resources/org/openjdk/jextract/impl/resources/Messages.properties b/src/main/resources/org/openjdk/jextract/impl/resources/Messages.properties index cdb8a821..520887e3 100644 --- a/src/main/resources/org/openjdk/jextract/impl/resources/Messages.properties +++ b/src/main/resources/org/openjdk/jextract/impl/resources/Messages.properties @@ -85,7 +85,7 @@ Option Description \ option is not specified, then current directory is used. \n\ -t, --target-package target package name for the generated classes. If this option\n\ \ is not specified, then unnamed package is used. \n\ ---symbols-class-name override the name of the root header class \n\ +--symbols-class-name override the name of the root header class \n\ --version print version information and exit \n\ \ \n\ macOS platform options for running jextract (available only when running on macOS): \n\ diff --git a/test/jtreg/generator/sharedSymbolsHeader/TestSharedSymbolsHeader.java b/test/jtreg/generator/sharedSymbolsHeader/TestSharedSymbolsHeader.java index 0671f8ca..eb0dbbc3 100644 --- a/test/jtreg/generator/sharedSymbolsHeader/TestSharedSymbolsHeader.java +++ b/test/jtreg/generator/sharedSymbolsHeader/TestSharedSymbolsHeader.java @@ -25,7 +25,6 @@ import java.nio.file.Path; import testlib.TestUtils; import testlib.JextractToolRunner; -import test.jextract.sharedSymbolsHeader.*; import static org.testng.Assert.*; /* @@ -34,7 +33,6 @@ * @summary check that header containg the shared symbols was created with the specified name * @library /lib * @build testlib.JextractToolRunner testlib.TestUtils - * @run main/othervm JtregJextract -t test.jextract.sharedSymbolsHeader sharedSymbolsHeader.h * @run testng/othervm TestSharedSymbolsHeader */ public class TestSharedSymbolsHeader extends JextractToolRunner { @@ -49,4 +47,16 @@ public void testSplit() { TestUtils.deleteDir(splitOutput); } } + + @Test + public void testSharedClassNameOption() { + Path splitOutput = getOutputFilePath("sharedSymbolsHeaderCustomName"); + Path splitH = getInputFilePath("sharedSymbolsHeader.h"); + runAndCompile(splitOutput, "--symbols-class-name", "CustomName", splitH.toString()); + try(TestUtils.Loader loader = TestUtils.classLoader(splitOutput)) { + assertNotNull(loader.loadClass("CustomName")); + } finally { + TestUtils.deleteDir(splitOutput); + } + } } From 5733943f65702d599da7cbbc1699b38740a531c9 Mon Sep 17 00:00:00 2001 From: Nizar Benalla Date: Thu, 8 May 2025 14:39:59 +0100 Subject: [PATCH 10/10] revert changes to IncludeHelper --- .../java/org/openjdk/jextract/impl/IncludeHelper.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/openjdk/jextract/impl/IncludeHelper.java b/src/main/java/org/openjdk/jextract/impl/IncludeHelper.java index 298f609f..3053b1b7 100644 --- a/src/main/java/org/openjdk/jextract/impl/IncludeHelper.java +++ b/src/main/java/org/openjdk/jextract/impl/IncludeHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, 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 @@ -84,11 +84,6 @@ static IncludeKind fromScoped(Declaration.Scoped scoped) { private final EnumMap> includesSymbolNamesByKind = new EnumMap<>(IncludeKind.class); private final Set usedDeclarations = new HashSet<>(); public String dumpIncludesFile; - public String sharedClassName; - - public String getSharedClassName() { - return sharedClassName; - } public void addSymbol(IncludeKind kind, String symbolName) { Set names = includesSymbolNamesByKind.computeIfAbsent(kind, (_unused) -> new HashSet<>()); @@ -127,7 +122,7 @@ private boolean isIncludedInternal(IncludeKind kind, Declaration declaration) { if (!isEnabled()) { return true; } else { - Set names = includesSymbolNamesByKind.computeIfAbsent(kind, _unused -> new HashSet<>()); + Set names = includesSymbolNamesByKind.computeIfAbsent(kind, (_unused) -> new HashSet<>()); return names.contains(declaration.name()); } }