From 06c3191642cdaae3811eacd473e18129f72c4fbe Mon Sep 17 00:00:00 2001 From: Jan-Willem Harmannij Date: Tue, 7 Nov 2023 20:33:48 +0100 Subject: [PATCH 1/4] Update cairo dependency version to 1.18.1 --- modules/gdk/build.gradle | 2 +- modules/harfbuzz/build.gradle | 2 +- modules/pango/build.gradle | 2 +- modules/pangocairo/build.gradle | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/gdk/build.gradle b/modules/gdk/build.gradle index 614de378..492b72eb 100644 --- a/modules/gdk/build.gradle +++ b/modules/gdk/build.gradle @@ -7,7 +7,7 @@ dependencies { api project(':gio') api project(':pango') api project(':pangocairo') - api 'io.github.jwharm.cairobindings:cairo:1.18.0' + api 'io.github.jwharm.cairobindings:cairo:1.18.1' } tasks.named('generateSources') { diff --git a/modules/harfbuzz/build.gradle b/modules/harfbuzz/build.gradle index 2a21df78..a831970b 100644 --- a/modules/harfbuzz/build.gradle +++ b/modules/harfbuzz/build.gradle @@ -6,7 +6,7 @@ plugins { dependencies { api project(':gobject') - api 'io.github.jwharm.cairobindings:cairo:1.18.0' + api 'io.github.jwharm.cairobindings:cairo:1.18.1' } tasks.named('generateSources') { diff --git a/modules/pango/build.gradle b/modules/pango/build.gradle index c2c7efc0..b289234e 100644 --- a/modules/pango/build.gradle +++ b/modules/pango/build.gradle @@ -8,7 +8,7 @@ dependencies { api project(':gobject') api project(':gio') api project(':harfbuzz') - api 'io.github.jwharm.cairobindings:cairo:1.18.0' + api 'io.github.jwharm.cairobindings:cairo:1.18.1' } tasks.named('generateSources') { diff --git a/modules/pangocairo/build.gradle b/modules/pangocairo/build.gradle index 1c3b2bdc..d84d3916 100644 --- a/modules/pangocairo/build.gradle +++ b/modules/pangocairo/build.gradle @@ -5,7 +5,7 @@ plugins { dependencies { api project(':gobject') api project(':pango') - api 'io.github.jwharm.cairobindings:cairo:1.18.0' + api 'io.github.jwharm.cairobindings:cairo:1.18.1' } tasks.named('generateSources') { From 21d70b30bdd195e4963a2ef692aa978ab62a5554 Mon Sep 17 00:00:00 2001 From: Jan-Willem Harmannij Date: Tue, 7 Nov 2023 20:34:36 +0100 Subject: [PATCH 2/4] Virtual methods with protected visibility (WIP) --- .../github/jwharm/javagi/generator/Merge.java | 24 +++++------ .../github/jwharm/javagi/generator/Patch.java | 41 +++++++++++++------ .../jwharm/javagi/model/Constructor.java | 2 + .../github/jwharm/javagi/model/Function.java | 1 + .../io/github/jwharm/javagi/model/Method.java | 27 ++++++------ .../io/github/jwharm/javagi/model/Module.java | 31 +++++++++++++- .../jwharm/javagi/model/RegisteredType.java | 23 ++++------- .../jwharm/javagi/model/VirtualMethod.java | 18 ++++---- .../jwharm/javagi/patches/GioPatch.java | 13 ++++++ .../jwharm/javagi/patches/GstBasePatch.java | 14 +++++++ .../jwharm/javagi/patches/GtkPatch.java | 8 ++++ .../jwharm/javagi/patches/PangoPatch.java | 4 +- modules/gstbase/build.gradle | 3 ++ .../github/jwharm/javagi/gtk/types/Types.java | 19 +++++++-- .../javagi/gtk/util/BuilderJavaScope.java | 4 +- 15 files changed, 160 insertions(+), 72 deletions(-) create mode 100644 buildSrc/src/main/java/io/github/jwharm/javagi/patches/GstBasePatch.java diff --git a/buildSrc/src/main/java/io/github/jwharm/javagi/generator/Merge.java b/buildSrc/src/main/java/io/github/jwharm/javagi/generator/Merge.java index 72ef7c0b..d48bc86f 100644 --- a/buildSrc/src/main/java/io/github/jwharm/javagi/generator/Merge.java +++ b/buildSrc/src/main/java/io/github/jwharm/javagi/generator/Merge.java @@ -212,10 +212,10 @@ private void mergeMethods(GirElement multi, List registere * @param methods the list of methods to add */ private void mergeMethods(GirElement parent, List multi, List methods) { - Set signatures = new HashSet<>(multi.stream().map(Method::getMethodSpecification).toList()); + Set signatures = new HashSet<>(multi.stream().map(Method::getTypeSignature).toList()); Set cIdentifiers = new HashSet<>(multi.stream().map(method -> method.cIdentifier).toList()); for (T method : methods) { - var signature = method.getMethodSpecification(); + var signature = method.getTypeSignature(); var cIdentifier = method.cIdentifier; if (cIdentifier != null && cIdentifiers.contains(cIdentifier)) { continue; @@ -237,21 +237,21 @@ private void mergeMethods(GirElement parent, List multi, L */ private void setMethodPlatforms(List methods, List registeredTypes) { for (Method method : methods) { - String signature = method.getMethodSpecification(); + String signature = method.getTypeSignature(); for (var rt : registeredTypes) { if (rt == null) { continue; } - if (rt.methodList.stream().map(Method::getMethodSpecification).anyMatch(signature::equals)) { + if (rt.methodList.stream().map(Method::getTypeSignature).anyMatch(signature::equals)) { method.platforms.add(rt.module().platform); } - if (rt.virtualMethodList.stream().map(Method::getMethodSpecification).anyMatch(signature::equals)) { + if (rt.virtualMethodList.stream().map(Method::getTypeSignature).anyMatch(signature::equals)) { method.platforms.add(rt.module().platform); } - if (rt.functionList.stream().map(Method::getMethodSpecification).anyMatch(signature::equals)) { + if (rt.functionList.stream().map(Method::getTypeSignature).anyMatch(signature::equals)) { method.platforms.add(rt.module().platform); } - if (rt.constructorList.stream().map(Method::getMethodSpecification).anyMatch(signature::equals)) { + if (rt.constructorList.stream().map(Method::getTypeSignature).anyMatch(signature::equals)) { method.platforms.add(rt.module().platform); } } @@ -265,32 +265,32 @@ private void setMethodPlatforms(List methods, List methods, List registeredTypes) { for (Method method : methods) { - String signature = method.getMethodSpecification(); + String signature = method.getTypeSignature(); for (var rt : registeredTypes) { if (rt == null) { continue; } // Check if this method exists on Windows - if (rt.methodList.stream().map(Method::getMethodSpecification).anyMatch(signature::equals)) { + if (rt.methodList.stream().map(Method::getTypeSignature).anyMatch(signature::equals)) { if (rt.module().platform == Platform.WINDOWS) { // Convert glong/gulong parameters to gint/guint overrideLongValues(method); } } // Same for virtual methods - if (rt.virtualMethodList.stream().map(Method::getMethodSpecification).anyMatch(signature::equals)) { + if (rt.virtualMethodList.stream().map(Method::getTypeSignature).anyMatch(signature::equals)) { if (rt.module().platform == Platform.WINDOWS) { overrideLongValues(method); } } // Same for functions (static methods) - if (rt.functionList.stream().map(Method::getMethodSpecification).anyMatch(signature::equals)) { + if (rt.functionList.stream().map(Method::getTypeSignature).anyMatch(signature::equals)) { if (rt.module().platform == Platform.WINDOWS) { overrideLongValues(method); } } // Same for constructors - if (rt.constructorList.stream().map(Method::getMethodSpecification).anyMatch(signature::equals)) { + if (rt.constructorList.stream().map(Method::getTypeSignature).anyMatch(signature::equals)) { if (rt.module().platform == Platform.WINDOWS) { overrideLongValues(method); } diff --git a/buildSrc/src/main/java/io/github/jwharm/javagi/generator/Patch.java b/buildSrc/src/main/java/io/github/jwharm/javagi/generator/Patch.java index 97c184e8..06ceea54 100644 --- a/buildSrc/src/main/java/io/github/jwharm/javagi/generator/Patch.java +++ b/buildSrc/src/main/java/io/github/jwharm/javagi/generator/Patch.java @@ -71,19 +71,21 @@ default void removeType(Repository repo, String type) { } default void setReturnType(Repository repo, String type, String name, String typeName, String typeCType, String defaultReturnValue, String doc) { - Method m = findVirtualMethod(repo, type, name); - if (m == null) - m = findMethod(repo, type, name); - if (m != null) { - ReturnValue rv = m.returnValue; - rv.type = new Type(rv, typeName, typeCType); - rv.overrideReturnValue = defaultReturnValue; - if (doc != null) { - rv.doc = new Doc(rv, "1"); - rv.doc.contents = doc; - } - } else - System.out.println("Did not change return type of " + type + "." + name + ": Not found"); + setReturnType(findVirtualMethod(repo, type, name), typeName, typeCType, defaultReturnValue, doc); + setReturnType(findMethod(repo, type, name), typeName, typeCType, defaultReturnValue, doc); + } + + private void setReturnType(Method m, String typeName, String typeCType, String defaultReturnValue, String doc) { + if (m == null) { + return; + } + ReturnValue rv = m.returnValue; + rv.type = new Type(rv, typeName, typeCType); + rv.overrideReturnValue = defaultReturnValue; + if (doc != null) { + rv.doc = new Doc(rv, "1"); + rv.doc.contents = doc; + } } default void setReturnFloating(CallableType ct) { @@ -177,6 +179,19 @@ default void removeEnumMember(Repository repo, String type, String member) { else e.memberList.remove(found); } + default void addInstanceMethod(Repository repo, String type, String virtualMethod) { + var vm = findVirtualMethod(repo, type, virtualMethod); + if (vm == null) { + return; + } + var method = new Method(vm.parent, vm.name, vm.cIdentifier, vm.deprecated, vm.throws_, vm.shadowedBy, vm.shadows, vm.movedTo); + method.doc = vm.doc; + method.docDeprecated = vm.docDeprecated; + method.parameters = vm.parameters; + method.returnValue = vm.returnValue; + vm.parent.methodList.add(method); + } + default void makeGeneric(Repository repo, String type) { RegisteredType inst = repo.namespace.registeredTypeMap.get(type); if (inst != null) { diff --git a/buildSrc/src/main/java/io/github/jwharm/javagi/model/Constructor.java b/buildSrc/src/main/java/io/github/jwharm/javagi/model/Constructor.java index 2b283123..a19ed240 100644 --- a/buildSrc/src/main/java/io/github/jwharm/javagi/model/Constructor.java +++ b/buildSrc/src/main/java/io/github/jwharm/javagi/model/Constructor.java @@ -28,6 +28,8 @@ public class Constructor extends Method { public Constructor(GirElement parent, String name, String cIdentifier, String deprecated, String throws_) { super(parent, name, cIdentifier, deprecated, throws_, null, null, null); + // constructor helper method has private visibility + visibility = "private"; } public void generate(SourceWriter writer) throws IOException { diff --git a/buildSrc/src/main/java/io/github/jwharm/javagi/model/Function.java b/buildSrc/src/main/java/io/github/jwharm/javagi/model/Function.java index 53312bd8..a8866e2e 100644 --- a/buildSrc/src/main/java/io/github/jwharm/javagi/model/Function.java +++ b/buildSrc/src/main/java/io/github/jwharm/javagi/model/Function.java @@ -23,5 +23,6 @@ public class Function extends Method { public Function(GirElement parent, String name, String cIdentifier, String deprecated, String throws_, String movedTo) { super(parent, name, cIdentifier, deprecated, throws_, null, null, movedTo); + visibility = "public"; } } diff --git a/buildSrc/src/main/java/io/github/jwharm/javagi/model/Method.java b/buildSrc/src/main/java/io/github/jwharm/javagi/model/Method.java index f7a6dd89..0340bd01 100644 --- a/buildSrc/src/main/java/io/github/jwharm/javagi/model/Method.java +++ b/buildSrc/src/main/java/io/github/jwharm/javagi/model/Method.java @@ -37,22 +37,26 @@ public class Method extends GirElement implements CallableType { public ReturnValue returnValue; public Parameters parameters; - public String visibility = "public"; + public String visibility; public boolean skip = false; + public VirtualMethod linkedVirtualMethod = null; + private String methodSpecification = null; public Method(GirElement parent, String name, String cIdentifier, String deprecated, String throws_, String shadowedBy, String shadows, String movedTo) { super(parent); - this.name = shadows == null ? name : shadows; // Language bindings are expected to rename a function to the shadowed function + // Language bindings are expected to rename a function to the shadowed function + this.name = shadows == null ? name : shadows; this.cIdentifier = cIdentifier; this.deprecated = deprecated; this.throws_ = throws_; this.shadowedBy = shadowedBy; this.shadows = shadows; this.movedTo = movedTo; - + this.visibility = (parent instanceof Interface) ? "default" : "public"; + // Generated under another name if (shadowedBy != null) { this.skip = true; @@ -73,15 +77,8 @@ public String getMethodDeclaration() { SourceWriter writer = new SourceWriter(new StringWriter()); try { - if ((parent instanceof Interface) && (! (this instanceof Function))) { - // Default interface methods - writer.write("default "); - } else if (this instanceof Constructor) { - writer.write("private "); - } else { - // Visibility - writer.write(visibility + " "); - } + // Visibility + writer.write(visibility + " "); // Static methods (functions and constructor helpers) if (this instanceof Function || this instanceof Constructor) { @@ -126,7 +123,11 @@ public String getMethodDeclaration() { return writer.toString(); } - public String getMethodSpecification() { + /** + * Generates a String that describes the type signature of this method. + * The result is cached for performance reasons. + */ + public String getTypeSignature() { if (this.methodSpecification != null) { return this.methodSpecification; } diff --git a/buildSrc/src/main/java/io/github/jwharm/javagi/model/Module.java b/buildSrc/src/main/java/io/github/jwharm/javagi/model/Module.java index 706fe74e..12ee9a57 100644 --- a/buildSrc/src/main/java/io/github/jwharm/javagi/model/Module.java +++ b/buildSrc/src/main/java/io/github/jwharm/javagi/model/Module.java @@ -45,6 +45,9 @@ public class Module { */ public final Map superLookupTable = new HashMap<>(); + /** + * The OS platform of this module + */ public final Platform platform; public Module(Platform platform) { @@ -111,6 +114,9 @@ public void link() { } } + // Link virtual methods to the accompanying methods + linkVirtualMethods(); + // Create lookup tables createIdLookupTable(); createCTypeLookupTable(); @@ -152,10 +158,31 @@ private void flagVaListFunction(Method method) { } } + /** + * Find classes that define methods and virtual methods with the same name and type signature. + * When found, add cross-references between them. + */ + private void linkVirtualMethods() { + for (Repository repository : repositories.values()) { + for (RegisteredType rt : repository.namespace.registeredTypeMap.values()) { + for (Method method : rt.methodList) { + for (VirtualMethod vm : rt.virtualMethodList) { + if (method.name.equals(vm.name) + && method.getTypeSignature().equals(vm.getTypeSignature())) { + method.linkedVirtualMethod = vm; + vm.linkedMethod = method; + break; + } + } + } + } + } + } + /** * Update {@code cIdentifierLookupTable} with current {@code repositoriesLookupTable} */ - public void createIdLookupTable() { + private void createIdLookupTable() { cIdentifierLookupTable.clear(); for (Repository repository : repositories.values()) { GirElement element = repository; @@ -173,7 +200,7 @@ public void createIdLookupTable() { /** * Update {@code cTypeLookupTable} with current {@code repositoriesLookupTable} */ - public void createCTypeLookupTable() { + private void createCTypeLookupTable() { cTypeLookupTable.clear(); for (Repository gir : repositories.values()) { for (RegisteredType rt : gir.namespace.registeredTypeMap.values()) { diff --git a/buildSrc/src/main/java/io/github/jwharm/javagi/model/RegisteredType.java b/buildSrc/src/main/java/io/github/jwharm/javagi/model/RegisteredType.java index 1e4895f3..2abc3cd7 100644 --- a/buildSrc/src/main/java/io/github/jwharm/javagi/model/RegisteredType.java +++ b/buildSrc/src/main/java/io/github/jwharm/javagi/model/RegisteredType.java @@ -293,27 +293,20 @@ public void generateSetFreeFunc(SourceWriter writer, String identifier, String c } protected void generateMethodsAndSignals(SourceWriter writer) throws IOException { - HashSet generatedMethods = new HashSet<>(); - - // First, generate all virtual methods - for (VirtualMethod vm : virtualMethodList) { - if (vm.hasVaListParameter()) { // va_list parameters are not supported + // Generate instance methods + for (Method m : methodList) { + if (m.hasVaListParameter()) { // va_list parameters are not supported continue; } - vm.generate(writer); - generatedMethods.add(vm.name + " " + vm.getMethodSpecification()); + m.generate(writer); } - // Next, generate the non-virtual instance methods - for (Method m : methodList) { - if (m.hasVaListParameter()) { - continue; - } - if (generatedMethods.contains(m.name + " " + m.getMethodSpecification())) { + // Generate virtual methods + for (VirtualMethod vm : virtualMethodList) { + if (vm.hasVaListParameter()) { continue; } - m.generate(writer); - generatedMethods.add(m.name + " " + m.getMethodSpecification()); + vm.generate(writer); } // Generate all functions as static methods diff --git a/buildSrc/src/main/java/io/github/jwharm/javagi/model/VirtualMethod.java b/buildSrc/src/main/java/io/github/jwharm/javagi/model/VirtualMethod.java index 62734fed..68b2e8c3 100644 --- a/buildSrc/src/main/java/io/github/jwharm/javagi/model/VirtualMethod.java +++ b/buildSrc/src/main/java/io/github/jwharm/javagi/model/VirtualMethod.java @@ -26,11 +26,18 @@ public class VirtualMethod extends Method { + public Method linkedMethod = null; + public VirtualMethod(GirElement parent, String name, String deprecated, String throws_) { super(parent, name, null, deprecated, throws_, null, null, null); + visibility = "protected"; } public void generate(SourceWriter writer) throws IOException { + if (parent instanceof Interface || linkedMethod != null) { + return; + } + writer.write("\n"); // Documentation @@ -87,20 +94,11 @@ public void generate(SourceWriter writer) throws IOException { if (classStruct == null) { throw new IOException("Cannot find typestruct for " + parent.name); } - writer.write("MemorySegment _func = ((org.gnome.gobject.TypeInstance) this).callParent()\n"); - writer.increaseIndent(); - writer.write("? Overrides.lookupVirtualMethodParent(handle(), " + classStruct.javaName + ".getMemoryLayout(), \"" + name + "\""); - if (parent instanceof Interface) { - writer.write(", " + className + ".getType()"); - } - writer.write(")\n"); - writer.write(": Overrides.lookupVirtualMethod(handle(), " + classStruct.javaName + ".getMemoryLayout(), \"" + name + "\""); + writer.write("MemorySegment _func = Overrides.lookupVirtualMethodParent(handle(), " + classStruct.javaName + ".getMemoryLayout(), \"" + name + "\""); if (parent instanceof Interface) { writer.write(", " + className + ".getType()"); } writer.write(");\n"); - writer.decreaseIndent(); - writer.write("if (MemorySegment.NULL.equals(_func)) {\n"); writer.write(" throw new NullPointerException();\n"); writer.write("}\n"); diff --git a/buildSrc/src/main/java/io/github/jwharm/javagi/patches/GioPatch.java b/buildSrc/src/main/java/io/github/jwharm/javagi/patches/GioPatch.java index 879e82ce..f2c800a0 100644 --- a/buildSrc/src/main/java/io/github/jwharm/javagi/patches/GioPatch.java +++ b/buildSrc/src/main/java/io/github/jwharm/javagi/patches/GioPatch.java @@ -1,7 +1,12 @@ package io.github.jwharm.javagi.patches; import io.github.jwharm.javagi.generator.Patch; +import io.github.jwharm.javagi.model.Method; +import io.github.jwharm.javagi.model.RegisteredType; import io.github.jwharm.javagi.model.Repository; +import io.github.jwharm.javagi.model.VirtualMethod; + +import java.util.List; public class GioPatch implements Patch { @@ -22,6 +27,14 @@ public void patch(Repository repo) { // The current solution is to remove the method from the interface. It is still available in the implementing classes. removeMethod(repo, "AsyncInitable", "new_finish"); + // Add virtual methods as instance methods + for (String type : List.of("FileInputStream", "FileOutputStream", "FileIOStream")) { + addInstanceMethod(repo, type, "tell"); + addInstanceMethod(repo, type, "seek"); + addInstanceMethod(repo, type, "can_truncate"); + addInstanceMethod(repo, type, "can_seek"); + } + // Let these classes implement the AutoCloseable interface, so they can be used in try-with-resources blocks. makeAutoCloseable(repo, "IOStream"); makeAutoCloseable(repo, "InputStream"); diff --git a/buildSrc/src/main/java/io/github/jwharm/javagi/patches/GstBasePatch.java b/buildSrc/src/main/java/io/github/jwharm/javagi/patches/GstBasePatch.java new file mode 100644 index 00000000..eb9e5af1 --- /dev/null +++ b/buildSrc/src/main/java/io/github/jwharm/javagi/patches/GstBasePatch.java @@ -0,0 +1,14 @@ +package io.github.jwharm.javagi.patches; + +import io.github.jwharm.javagi.generator.Patch; +import io.github.jwharm.javagi.model.Repository; + +public class GstBasePatch implements Patch { + + @Override + public void patch(Repository repo) { + // Add virtual methods as instance methods + addInstanceMethod(repo, "BaseSink", "query"); + addInstanceMethod(repo, "BaseSrc", "query"); + } +} diff --git a/buildSrc/src/main/java/io/github/jwharm/javagi/patches/GtkPatch.java b/buildSrc/src/main/java/io/github/jwharm/javagi/patches/GtkPatch.java index 25c42574..aea72c66 100644 --- a/buildSrc/src/main/java/io/github/jwharm/javagi/patches/GtkPatch.java +++ b/buildSrc/src/main/java/io/github/jwharm/javagi/patches/GtkPatch.java @@ -21,5 +21,13 @@ public void patch(Repository repo) { setReturnFloating(findMethod(repo, "PageSetup", "to_gvariant")); setReturnFloating(findMethod(repo, "PaperSize", "to_gvariant")); setReturnFloating(findMethod(repo, "PrintSettings", "to_gvariant")); + + // Add virtual methods as instance methods + addInstanceMethod(repo, "BuilderScope", "get_type_from_name"); + addInstanceMethod(repo, "BuilderScope", "get_type_from_function"); + addInstanceMethod(repo, "BuilderScope", "create_closure"); + addInstanceMethod(repo, "Window", "activate_default"); + addInstanceMethod(repo, "Dialog", "close"); + addInstanceMethod(repo, "Popover", "activate_default"); } } diff --git a/buildSrc/src/main/java/io/github/jwharm/javagi/patches/PangoPatch.java b/buildSrc/src/main/java/io/github/jwharm/javagi/patches/PangoPatch.java index fd0039f5..0ddb3601 100644 --- a/buildSrc/src/main/java/io/github/jwharm/javagi/patches/PangoPatch.java +++ b/buildSrc/src/main/java/io/github/jwharm/javagi/patches/PangoPatch.java @@ -9,6 +9,8 @@ public class PangoPatch implements Patch { public void patch(Repository repo) { // Return type defined as "Language" but should be "Language*" removeMethod(repo, "Font", "get_languages"); - } + // Deprecated method, causes java-gi compile warnings + removeMethod(repo, "Coverage", "ref"); + } } diff --git a/modules/gstbase/build.gradle b/modules/gstbase/build.gradle index 57a5c141..94fe96f2 100644 --- a/modules/gstbase/build.gradle +++ b/modules/gstbase/build.gradle @@ -1,3 +1,5 @@ +import io.github.jwharm.javagi.patches.GstBasePatch; + plugins { id 'java-gi.library-conventions' } @@ -11,4 +13,5 @@ dependencies { tasks.named('generateSources') { girFile = 'GstBase-1.0.gir' + patch = new GstBasePatch() } diff --git a/modules/gtk/src/main/java/io/github/jwharm/javagi/gtk/types/Types.java b/modules/gtk/src/main/java/io/github/jwharm/javagi/gtk/types/Types.java index be3d16c4..f9dc12a3 100644 --- a/modules/gtk/src/main/java/io/github/jwharm/javagi/gtk/types/Types.java +++ b/modules/gtk/src/main/java/io/github/jwharm/javagi/gtk/types/Types.java @@ -27,6 +27,7 @@ import io.github.jwharm.javagi.gobject.types.Properties; import io.github.jwharm.javagi.gobject.types.Signals; +import io.github.jwharm.javagi.interop.Interop; import org.gnome.glib.GLib; import org.gnome.glib.LogLevelFlags; import org.gnome.glib.Type; @@ -159,14 +160,24 @@ private static Consumer getTemplateClass // The ui parameter must refer to a registered GResource widgetClass.setTemplateFromResource(ui); + // Override GObject.dispose() to dispose the template widgetClass.overrideDispose(Arena.global(), (object) -> { - Widget widget = (Widget) object; - widget.disposeTemplate(typeClass.readGType()); - widget.asParent().dispose(); // This will call the parent class dispose + ((Widget) object).disposeTemplate(typeClass.readGType()); + + // Chain up to the parent (GObject) dispose function. + // The Java binding is a protected method, so we call the C function directly. + try { + var parent = GObject.ObjectClass.getMemoryLayout(); + var func = Overrides.lookupVirtualMethodParent(object.handle(), parent, "dispose"); + var desc = FunctionDescriptor.ofVoid(ValueLayout.ADDRESS); + Interop.downcallHandle(func, desc).invokeExact(object.handle()); + } catch (Throwable _err) { + throw new AssertionError("Unexpected exception occurred: ", _err); + } }); // Install BuilderJavaScope to call Java signal handler methods - widgetClass.setTemplateScope(new BuilderJavaScope()); + widgetClass.setTemplateScope(BuilderJavaScope.newInstance()); for (Field field : cls.getDeclaredFields()) { if (field.isAnnotationPresent(GtkChild.class)) { diff --git a/modules/gtk/src/main/java/io/github/jwharm/javagi/gtk/util/BuilderJavaScope.java b/modules/gtk/src/main/java/io/github/jwharm/javagi/gtk/util/BuilderJavaScope.java index 09a4db50..2030da7d 100644 --- a/modules/gtk/src/main/java/io/github/jwharm/javagi/gtk/util/BuilderJavaScope.java +++ b/modules/gtk/src/main/java/io/github/jwharm/javagi/gtk/util/BuilderJavaScope.java @@ -73,8 +73,8 @@ public static Type getType() { /** * Instantiates a new {@link BuilderScope} */ - public BuilderJavaScope() { - super(gtype, null); + public static BuilderJavaScope newInstance() { + return GObject.newInstance(gtype); } /** From cb6b8bb2f60c0f48bc178d5b61e725935183f286 Mon Sep 17 00:00:00 2001 From: Jan-Willem Harmannij Date: Tue, 7 Nov 2023 22:23:05 +0100 Subject: [PATCH 3/4] Virtual methods with protected visibility (WIP) --- .../github/jwharm/javagi/generator/Merge.java | 24 ++--- .../io/github/jwharm/javagi/model/Class.java | 2 +- .../io/github/jwharm/javagi/model/Method.java | 40 ++++++-- .../io/github/jwharm/javagi/model/Module.java | 3 +- .../jwharm/javagi/model/VirtualMethod.java | 93 ++++++++++--------- .../jwharm/javagi/patches/GstBasePatch.java | 7 ++ .../jwharm/javagi/patches/GstVideoPatch.java | 12 +++ .../jwharm/javagi/patches/GtkPatch.java | 2 + .../jwharm/javagi/patches/SoupPatch.java | 12 +++ modules/gstvideo/build.gradle | 3 + .../javagi/gtk/util/BuilderJavaScope.java | 10 +- modules/soup/build.gradle | 3 + 12 files changed, 138 insertions(+), 73 deletions(-) create mode 100644 buildSrc/src/main/java/io/github/jwharm/javagi/patches/GstVideoPatch.java create mode 100644 buildSrc/src/main/java/io/github/jwharm/javagi/patches/SoupPatch.java diff --git a/buildSrc/src/main/java/io/github/jwharm/javagi/generator/Merge.java b/buildSrc/src/main/java/io/github/jwharm/javagi/generator/Merge.java index d48bc86f..96c6054c 100644 --- a/buildSrc/src/main/java/io/github/jwharm/javagi/generator/Merge.java +++ b/buildSrc/src/main/java/io/github/jwharm/javagi/generator/Merge.java @@ -212,10 +212,10 @@ private void mergeMethods(GirElement multi, List registere * @param methods the list of methods to add */ private void mergeMethods(GirElement parent, List multi, List methods) { - Set signatures = new HashSet<>(multi.stream().map(Method::getTypeSignature).toList()); + Set signatures = new HashSet<>(multi.stream().map(Method::getNameAndSignature).toList()); Set cIdentifiers = new HashSet<>(multi.stream().map(method -> method.cIdentifier).toList()); for (T method : methods) { - var signature = method.getTypeSignature(); + var signature = method.getNameAndSignature(); var cIdentifier = method.cIdentifier; if (cIdentifier != null && cIdentifiers.contains(cIdentifier)) { continue; @@ -237,21 +237,21 @@ private void mergeMethods(GirElement parent, List multi, L */ private void setMethodPlatforms(List methods, List registeredTypes) { for (Method method : methods) { - String signature = method.getTypeSignature(); + String signature = method.getNameAndSignature(); for (var rt : registeredTypes) { if (rt == null) { continue; } - if (rt.methodList.stream().map(Method::getTypeSignature).anyMatch(signature::equals)) { + if (rt.methodList.stream().map(Method::getNameAndSignature).anyMatch(signature::equals)) { method.platforms.add(rt.module().platform); } - if (rt.virtualMethodList.stream().map(Method::getTypeSignature).anyMatch(signature::equals)) { + if (rt.virtualMethodList.stream().map(Method::getNameAndSignature).anyMatch(signature::equals)) { method.platforms.add(rt.module().platform); } - if (rt.functionList.stream().map(Method::getTypeSignature).anyMatch(signature::equals)) { + if (rt.functionList.stream().map(Method::getNameAndSignature).anyMatch(signature::equals)) { method.platforms.add(rt.module().platform); } - if (rt.constructorList.stream().map(Method::getTypeSignature).anyMatch(signature::equals)) { + if (rt.constructorList.stream().map(Method::getNameAndSignature).anyMatch(signature::equals)) { method.platforms.add(rt.module().platform); } } @@ -265,32 +265,32 @@ private void setMethodPlatforms(List methods, List methods, List registeredTypes) { for (Method method : methods) { - String signature = method.getTypeSignature(); + String signature = method.getNameAndSignature(); for (var rt : registeredTypes) { if (rt == null) { continue; } // Check if this method exists on Windows - if (rt.methodList.stream().map(Method::getTypeSignature).anyMatch(signature::equals)) { + if (rt.methodList.stream().map(Method::getNameAndSignature).anyMatch(signature::equals)) { if (rt.module().platform == Platform.WINDOWS) { // Convert glong/gulong parameters to gint/guint overrideLongValues(method); } } // Same for virtual methods - if (rt.virtualMethodList.stream().map(Method::getTypeSignature).anyMatch(signature::equals)) { + if (rt.virtualMethodList.stream().map(Method::getNameAndSignature).anyMatch(signature::equals)) { if (rt.module().platform == Platform.WINDOWS) { overrideLongValues(method); } } // Same for functions (static methods) - if (rt.functionList.stream().map(Method::getTypeSignature).anyMatch(signature::equals)) { + if (rt.functionList.stream().map(Method::getNameAndSignature).anyMatch(signature::equals)) { if (rt.module().platform == Platform.WINDOWS) { overrideLongValues(method); } } // Same for constructors - if (rt.constructorList.stream().map(Method::getTypeSignature).anyMatch(signature::equals)) { + if (rt.constructorList.stream().map(Method::getNameAndSignature).anyMatch(signature::equals)) { if (rt.module().platform == Platform.WINDOWS) { overrideLongValues(method); } diff --git a/buildSrc/src/main/java/io/github/jwharm/javagi/model/Class.java b/buildSrc/src/main/java/io/github/jwharm/javagi/model/Class.java index e115fa1f..e608c647 100644 --- a/buildSrc/src/main/java/io/github/jwharm/javagi/model/Class.java +++ b/buildSrc/src/main/java/io/github/jwharm/javagi/model/Class.java @@ -141,7 +141,7 @@ protected void generateParentAccessor(SourceWriter writer) throws IOException { writer.write(" * again. To chain up, call {@code asParent().methodName()}. This will call the native function\n"); writer.write(" * pointer of this virtual method in the typeclass of the parent type.\n"); writer.write(" */\n"); - writer.write("public " + qualifiedName + " asParent() {\n"); + writer.write("protected " + qualifiedName + " asParent() {\n"); writer.increaseIndent(); if ("1".equals(abstract_)) { writer.write(qualifiedName + " _parent = new " + qualifiedName + "." + javaName + "Impl(handle());\n"); diff --git a/buildSrc/src/main/java/io/github/jwharm/javagi/model/Method.java b/buildSrc/src/main/java/io/github/jwharm/javagi/model/Method.java index 0340bd01..530445ed 100644 --- a/buildSrc/src/main/java/io/github/jwharm/javagi/model/Method.java +++ b/buildSrc/src/main/java/io/github/jwharm/javagi/model/Method.java @@ -42,7 +42,7 @@ public class Method extends GirElement implements CallableType { public VirtualMethod linkedVirtualMethod = null; - private String methodSpecification = null; + private String signature = null; public Method(GirElement parent, String name, String cIdentifier, String deprecated, String throws_, String shadowedBy, String shadows, String movedTo) { @@ -127,9 +127,9 @@ public String getMethodDeclaration() { * Generates a String that describes the type signature of this method. * The result is cached for performance reasons. */ - public String getTypeSignature() { - if (this.methodSpecification != null) { - return this.methodSpecification; + public String getNameAndSignature() { + if (this.signature != null) { + return this.signature; } SourceWriter writer = new SourceWriter(new StringWriter()); @@ -140,7 +140,7 @@ public String getTypeSignature() { if (parent instanceof Interface) { // Overriding toString() in a default method is not allowed. methodName = Conversions.replaceJavaObjectMethodNames(methodName); } - writer.write(methodName + "("); + writer.write(" " + methodName + "("); // Parameters if (getParameters() != null) { @@ -157,8 +157,8 @@ public String getTypeSignature() { } // Cache the result for future invocations - this.methodSpecification = writer.toString(); - return this.methodSpecification; + this.signature = writer.toString(); + return this.signature; } public void generate(SourceWriter writer) throws IOException { @@ -171,7 +171,7 @@ public void generate(SourceWriter writer) throws IOException { // Documentation if (this instanceof Constructor) { writer.write("/**\n"); - writer.write(" * Helper function for the (@code " + name + "} constructor\n"); + writer.write(" * Helper function for the {@code " + name + "} constructor\n"); writer.write(" */\n"); } else if (doc != null) { doc.generate(writer, false); @@ -218,11 +218,25 @@ public void generate(SourceWriter writer) throws IOException { if (! (returnValue.type != null && returnValue.type.isVoid())) { writer.write(carrierType + " _result;\n"); } - + // The method call is wrapped in a try-catch block writer.write("try {\n"); writer.increaseIndent(); + // If this method is also a virtual method, and callParent() is set, call the parent virtual method + if (linkedVirtualMethod != null) { + writer.write("if ("); + if (parent instanceof Interface) { + writer.write("((org.gnome.gobject.TypeInstance) this)."); + } + writer.write("callParent()) {\n"); + writer.increaseIndent(); + linkedVirtualMethod.generateInvocation(writer); + writer.decreaseIndent(); + writer.write("} else {\n"); + writer.increaseIndent(); + } + // Log the method call log(cIdentifier, writer); @@ -241,7 +255,13 @@ public void generate(SourceWriter writer) throws IOException { parameters.marshalJavaToNative(writer, throws_); } writer.write(");\n"); - + + // End of if/else block for virtual/named method call + if (linkedVirtualMethod != null) { + writer.decreaseIndent(); + writer.write("}\n"); + } + // If something goes wrong in the invokeExact() call writer.decreaseIndent(); writer.write("} catch (Throwable _err) {\n"); diff --git a/buildSrc/src/main/java/io/github/jwharm/javagi/model/Module.java b/buildSrc/src/main/java/io/github/jwharm/javagi/model/Module.java index 12ee9a57..1e478f86 100644 --- a/buildSrc/src/main/java/io/github/jwharm/javagi/model/Module.java +++ b/buildSrc/src/main/java/io/github/jwharm/javagi/model/Module.java @@ -167,8 +167,7 @@ private void linkVirtualMethods() { for (RegisteredType rt : repository.namespace.registeredTypeMap.values()) { for (Method method : rt.methodList) { for (VirtualMethod vm : rt.virtualMethodList) { - if (method.name.equals(vm.name) - && method.getTypeSignature().equals(vm.getTypeSignature())) { + if (method.getNameAndSignature().equals(vm.getNameAndSignature())) { method.linkedVirtualMethod = vm; vm.linkedMethod = method; break; diff --git a/buildSrc/src/main/java/io/github/jwharm/javagi/model/VirtualMethod.java b/buildSrc/src/main/java/io/github/jwharm/javagi/model/VirtualMethod.java index 68b2e8c3..e02a3bf4 100644 --- a/buildSrc/src/main/java/io/github/jwharm/javagi/model/VirtualMethod.java +++ b/buildSrc/src/main/java/io/github/jwharm/javagi/model/VirtualMethod.java @@ -39,7 +39,7 @@ public void generate(SourceWriter writer) throws IOException { } writer.write("\n"); - + // Documentation if (doc != null) { doc.generate(writer, false); @@ -52,7 +52,7 @@ public void generate(SourceWriter writer) throws IOException { // Declaration writer.write(getMethodDeclaration()); - + writer.write(" {\n"); writer.increaseIndent(); @@ -75,13 +75,56 @@ public void generate(SourceWriter writer) throws IOException { if (throws_ != null) { writer.write("MemorySegment _gerror = _arena.allocate(ValueLayout.ADDRESS);\n"); } - + + // Function descriptor + writer.write("FunctionDescriptor _fdesc = "); + generateFunctionDescriptor(writer); + writer.write(";\n"); + // Variable declaration for return value String carrierType = Conversions.getCarrierType(getReturnValue().type); if (! (returnValue.type != null && returnValue.type.isVoid())) { writer.write(carrierType + " _result;\n"); } - + + // The method call is wrapped in a try-catch block + writer.write("try {\n"); + writer.increaseIndent(); + + generateInvocation(writer); + + // If something goes wrong in the invokeExact() call + writer.decreaseIndent(); + writer.write("} catch (Throwable _err) {\n"); + writer.write(" throw new AssertionError(\"Unexpected exception occurred: \", _err);\n"); + writer.write("}\n"); + + // Throw GErrorException + if (throws_ != null) { + writer.write("if (GErrorException.isErrorSet(_gerror)) {\n"); + writer.write(" throw new GErrorException(_gerror);\n"); + writer.write("}\n"); + } + + // Generate post-processing actions for parameters + if (parameters != null) { + parameters.generatePostprocessing(writer); + } + + // Generate code to process and return the result value + returnValue.generate(writer); + + // End of memory allocation scope + if (hasScope) { + writer.decreaseIndent(); + writer.write("}\n"); + } + + writer.decreaseIndent(); + writer.write("}\n"); + } + + public void generateInvocation(SourceWriter writer) throws IOException { Record classStruct = null; String className = null; if (parent instanceof Class c) { @@ -99,23 +142,17 @@ public void generate(SourceWriter writer) throws IOException { writer.write(", " + className + ".getType()"); } writer.write(");\n"); + + // Check if the virtual method points to a null address. writer.write("if (MemorySegment.NULL.equals(_func)) {\n"); writer.write(" throw new NullPointerException();\n"); writer.write("}\n"); - // Check if the virtual method points to a null address. - writer.write("FunctionDescriptor _fdesc = "); - generateFunctionDescriptor(writer); - writer.write(";\n"); - - // The method call is wrapped in a try-catch block - writer.write("try {\n"); - writer.increaseIndent(); - // Log the method call log(classStruct.javaName + "." + name, writer); // Generate the return type + String carrierType = Conversions.getCarrierType(getReturnValue().type); if (! (returnValue.type != null && returnValue.type.isVoid())) { writer.write("_result = ("); writer.write(carrierType); @@ -130,35 +167,5 @@ public void generate(SourceWriter writer) throws IOException { parameters.marshalJavaToNative(writer, throws_); } writer.write(");\n"); - - // If something goes wrong in the invokeExact() call - writer.decreaseIndent(); - writer.write("} catch (Throwable _err) {\n"); - writer.write(" throw new AssertionError(\"Unexpected exception occurred: \", _err);\n"); - writer.write("}\n"); - - // Throw GErrorException - if (throws_ != null) { - writer.write("if (GErrorException.isErrorSet(_gerror)) {\n"); - writer.write(" throw new GErrorException(_gerror);\n"); - writer.write("}\n"); - } - - // Generate post-processing actions for parameters - if (parameters != null) { - parameters.generatePostprocessing(writer); - } - - // Generate code to process and return the result value - returnValue.generate(writer); - - // End of memory allocation scope - if (hasScope) { - writer.decreaseIndent(); - writer.write("}\n"); - } - - writer.decreaseIndent(); - writer.write("}\n"); } } diff --git a/buildSrc/src/main/java/io/github/jwharm/javagi/patches/GstBasePatch.java b/buildSrc/src/main/java/io/github/jwharm/javagi/patches/GstBasePatch.java index eb9e5af1..456b2987 100644 --- a/buildSrc/src/main/java/io/github/jwharm/javagi/patches/GstBasePatch.java +++ b/buildSrc/src/main/java/io/github/jwharm/javagi/patches/GstBasePatch.java @@ -10,5 +10,12 @@ public void patch(Repository repo) { // Add virtual methods as instance methods addInstanceMethod(repo, "BaseSink", "query"); addInstanceMethod(repo, "BaseSrc", "query"); + + // Change virtual method parameter name to the instance method parameter name + findVirtualMethod(repo, "Aggregator", "peek_next_sample") + .parameters + .parameterList + .get(1) + .name = "pad"; } } diff --git a/buildSrc/src/main/java/io/github/jwharm/javagi/patches/GstVideoPatch.java b/buildSrc/src/main/java/io/github/jwharm/javagi/patches/GstVideoPatch.java new file mode 100644 index 00000000..b5bccc98 --- /dev/null +++ b/buildSrc/src/main/java/io/github/jwharm/javagi/patches/GstVideoPatch.java @@ -0,0 +1,12 @@ +package io.github.jwharm.javagi.patches; + +import io.github.jwharm.javagi.generator.Patch; +import io.github.jwharm.javagi.model.Repository; + +public class GstVideoPatch implements Patch { + + @Override + public void patch(Repository repo) { + setReturnType(repo, "VideoOverlay", "set_render_rectangle", "none", "void", null, null); + } +} diff --git a/buildSrc/src/main/java/io/github/jwharm/javagi/patches/GtkPatch.java b/buildSrc/src/main/java/io/github/jwharm/javagi/patches/GtkPatch.java index aea72c66..854e9771 100644 --- a/buildSrc/src/main/java/io/github/jwharm/javagi/patches/GtkPatch.java +++ b/buildSrc/src/main/java/io/github/jwharm/javagi/patches/GtkPatch.java @@ -16,6 +16,8 @@ public void patch(Repository repo) { renameMethod(repo, "PrintUnixDialog", "get_settings", "get_print_settings"); renameMethod(repo, "Widget", "activate", "activate_widget"); + setReturnType(repo, "MediaStream", "play", "none", "void", null, null); + // These calls return floating references setReturnFloating(findMethod(repo, "FileFilter", "to_gvariant")); setReturnFloating(findMethod(repo, "PageSetup", "to_gvariant")); diff --git a/buildSrc/src/main/java/io/github/jwharm/javagi/patches/SoupPatch.java b/buildSrc/src/main/java/io/github/jwharm/javagi/patches/SoupPatch.java new file mode 100644 index 00000000..741d90d2 --- /dev/null +++ b/buildSrc/src/main/java/io/github/jwharm/javagi/patches/SoupPatch.java @@ -0,0 +1,12 @@ +package io.github.jwharm.javagi.patches; + +import io.github.jwharm.javagi.generator.Patch; +import io.github.jwharm.javagi.model.Repository; + +public class SoupPatch implements Patch { + + @Override + public void patch(Repository repo) { + setReturnType(repo, "AuthDomain", "challenge", "none", "void", null, null); + } +} diff --git a/modules/gstvideo/build.gradle b/modules/gstvideo/build.gradle index 5f84a96d..2d26845f 100644 --- a/modules/gstvideo/build.gradle +++ b/modules/gstvideo/build.gradle @@ -1,3 +1,5 @@ +import io.github.jwharm.javagi.patches.GstVideoPatch; + plugins { id 'java-gi.library-conventions' } @@ -9,4 +11,5 @@ dependencies { tasks.named('generateSources') { girFile = 'GstVideo-1.0.gir' + patch = new GstVideoPatch() } diff --git a/modules/gtk/src/main/java/io/github/jwharm/javagi/gtk/util/BuilderJavaScope.java b/modules/gtk/src/main/java/io/github/jwharm/javagi/gtk/util/BuilderJavaScope.java index 2030da7d..b817923e 100644 --- a/modules/gtk/src/main/java/io/github/jwharm/javagi/gtk/util/BuilderJavaScope.java +++ b/modules/gtk/src/main/java/io/github/jwharm/javagi/gtk/util/BuilderJavaScope.java @@ -46,7 +46,7 @@ * the Java instance method {@code okButtonClicked()} will be called on * the widget that is being built with the {@link GtkBuilder}. */ -public final class BuilderJavaScope extends GObject implements BuilderScope { +public final class BuilderJavaScope extends BuilderCScope implements BuilderScope { private static final Type gtype = Types.register(BuilderJavaScope.class); @@ -97,7 +97,7 @@ public Closure createClosure(GtkBuilder builder, String functionName, BuilderClo if (currentObject == null) { GLib.log(LOG_DOMAIN, LogLevelFlags.LEVEL_CRITICAL, "Cannot create closure for handler %s: Current object not set\n", functionName); - return new BuilderCScope().createClosure(builder, functionName, flags, object); + return asParent().createClosure(builder, functionName, flags, object); } try { @@ -132,7 +132,7 @@ public Closure createClosure(GtkBuilder builder, String functionName, BuilderClo GLib.log(LOG_DOMAIN, LogLevelFlags.LEVEL_CRITICAL, "Cannot find method %s in class %s\n", functionName, currentObject.getClass().getName()); - return new BuilderCScope().createClosure(builder, functionName, flags, object); + return asParent().createClosure(builder, functionName, flags, object); } } @@ -165,7 +165,7 @@ private Method getMethodForName(Class cls, String functionName) throws NoSuch */ @Override public Type getTypeFromFunction(GtkBuilder builder, String functionName) { - return new BuilderCScope().getTypeFromFunction(builder, functionName); + return asParent().getTypeFromFunction(builder, functionName); } /** @@ -176,6 +176,6 @@ public Type getTypeFromFunction(GtkBuilder builder, String functionName) { */ @Override public Type getTypeFromName(GtkBuilder builder, String typeName) { - return new BuilderCScope().getTypeFromName(builder, typeName); + return asParent().getTypeFromName(builder, typeName); } } diff --git a/modules/soup/build.gradle b/modules/soup/build.gradle index c897db58..6dcd6141 100644 --- a/modules/soup/build.gradle +++ b/modules/soup/build.gradle @@ -1,3 +1,5 @@ +import io.github.jwharm.javagi.patches.SoupPatch; + plugins { id 'java-gi.library-conventions' } @@ -9,4 +11,5 @@ dependencies { tasks.named('generateSources') { girFile = 'Soup-3.0.gir' urlPrefix = 'https://libsoup.org/libsoup-3.0/' + patch = new SoupPatch() } From 2766e4969b8e3bacd52d89833a30a7525f0aef95 Mon Sep 17 00:00:00 2001 From: Jan-Willem Harmannij Date: Wed, 8 Nov 2023 20:19:24 +0100 Subject: [PATCH 4/4] Virtual methods with protected visibility --- .../github/jwharm/javagi/GenerateSources.java | 12 +-- .../github/jwharm/javagi/generator/Patch.java | 3 + .../jwharm/javagi/model/CallableType.java | 2 + .../github/jwharm/javagi/model/Callback.java | 5 ++ .../github/jwharm/javagi/model/Closure.java | 8 +- .../jwharm/javagi/model/Constructor.java | 8 ++ .../github/jwharm/javagi/model/Function.java | 2 +- .../jwharm/javagi/model/FunctionMacro.java | 5 ++ .../io/github/jwharm/javagi/model/Method.java | 9 +- .../io/github/jwharm/javagi/model/Module.java | 13 ++- .../jwharm/javagi/model/RegisteredType.java | 43 +++------ .../io/github/jwharm/javagi/model/Signal.java | 4 + .../jwharm/javagi/model/VirtualMethod.java | 4 +- .../jwharm/javagi/patches/GtkPatch.java | 7 +- .../javagi/gobject/types/Overrides.java | 90 ++++++++++++------- 15 files changed, 135 insertions(+), 80 deletions(-) diff --git a/buildSrc/src/main/java/io/github/jwharm/javagi/GenerateSources.java b/buildSrc/src/main/java/io/github/jwharm/javagi/GenerateSources.java index 5b9bef57..e00c5f3b 100644 --- a/buildSrc/src/main/java/io/github/jwharm/javagi/GenerateSources.java +++ b/buildSrc/src/main/java/io/github/jwharm/javagi/GenerateSources.java @@ -61,11 +61,11 @@ public abstract class GenerateSources extends DefaultTask { void execute() { try { Module linux = parse(Platform.LINUX, getInputDirectory().get(), getGirFile().get(), - getUrlPrefix().getOrElse(null), getPatch().getOrElse(null)); + getUrlPrefix().getOrNull(), getPatch().getOrNull()); Module windows = parse(Platform.WINDOWS, getInputDirectory().get(), getGirFile().get(), - getUrlPrefix().getOrElse(null), getPatch().getOrElse(null)); + getUrlPrefix().getOrNull(), getPatch().getOrNull()); Module macos = parse(Platform.MACOS, getInputDirectory().get(), getGirFile().get(), - getUrlPrefix().getOrElse(null), getPatch().getOrElse(null)); + getUrlPrefix().getOrNull(), getPatch().getOrNull()); Module module = new Merge().merge(linux, windows, macos); @@ -109,6 +109,9 @@ private static Module parse(Platform platform, Directory sourceDirectory, String // Flag unsupported va_list methods so they will not be generated module.flagVaListFunctions(); + // Link the type references to the GIR type definition across the GI repositories + module.link(); + // Apply patch if (patch != null) { patch.patch(r); @@ -118,9 +121,6 @@ private static Module parse(Platform platform, Directory sourceDirectory, String // Gir file not found for this platform: This will generate code with UnsupportedPlatformExceptions } - // Link the type references to the GIR type definition across the GI repositories - module.link(); - return module; } diff --git a/buildSrc/src/main/java/io/github/jwharm/javagi/generator/Patch.java b/buildSrc/src/main/java/io/github/jwharm/javagi/generator/Patch.java index 06ceea54..df8f3493 100644 --- a/buildSrc/src/main/java/io/github/jwharm/javagi/generator/Patch.java +++ b/buildSrc/src/main/java/io/github/jwharm/javagi/generator/Patch.java @@ -190,6 +190,7 @@ default void addInstanceMethod(Repository repo, String type, String virtualMetho method.parameters = vm.parameters; method.returnValue = vm.returnValue; vm.parent.methodList.add(method); + vm.skip = true; } default void makeGeneric(Repository repo, String type) { @@ -201,6 +202,7 @@ default void makeGeneric(Repository repo, String type) { for (Parameter p : m.parameters.parameterList) { if (p.type != null && "org.gnome.gobject.GObject".equals(p.type.qualifiedJavaType)) { p.type.isGeneric = true; + p.type.init(p.type.name); } } } @@ -208,6 +210,7 @@ default void makeGeneric(Repository repo, String type) { Type returnType = m.returnValue.type; if (returnType != null && "org.gnome.gobject.GObject".equals(returnType.qualifiedJavaType)) { returnType.isGeneric = true; + returnType.init(returnType.name); } } } diff --git a/buildSrc/src/main/java/io/github/jwharm/javagi/model/CallableType.java b/buildSrc/src/main/java/io/github/jwharm/javagi/model/CallableType.java index 51a1ded8..21177027 100644 --- a/buildSrc/src/main/java/io/github/jwharm/javagi/model/CallableType.java +++ b/buildSrc/src/main/java/io/github/jwharm/javagi/model/CallableType.java @@ -26,6 +26,8 @@ public interface CallableType { + GirElement getParent(); + Parameters getParameters(); void setParameters(Parameters ps); diff --git a/buildSrc/src/main/java/io/github/jwharm/javagi/model/Callback.java b/buildSrc/src/main/java/io/github/jwharm/javagi/model/Callback.java index a1693911..f7e39a27 100644 --- a/buildSrc/src/main/java/io/github/jwharm/javagi/model/Callback.java +++ b/buildSrc/src/main/java/io/github/jwharm/javagi/model/Callback.java @@ -52,6 +52,11 @@ public String getInteropString(String paramName, boolean isPointer, Scope scope) return "%s.toCallback(%s)".formatted(paramName, arena); } + @Override + public GirElement getParent() { + return parent; + } + @Override public Parameters getParameters() { return parameters; diff --git a/buildSrc/src/main/java/io/github/jwharm/javagi/model/Closure.java b/buildSrc/src/main/java/io/github/jwharm/javagi/model/Closure.java index d3119a74..66171ba1 100644 --- a/buildSrc/src/main/java/io/github/jwharm/javagi/model/Closure.java +++ b/buildSrc/src/main/java/io/github/jwharm/javagi/model/Closure.java @@ -44,14 +44,18 @@ default void generateFunctionalInterface(SourceWriter writer, String javaName) t writer.write(" */\n"); } writer.write("@FunctionalInterface\n"); - writer.write("public interface " + javaName + " {\n"); + if (! (getParent() instanceof Interface)) { + writer.write("public "); + } + writer.write("interface " + javaName + " {\n"); writer.write("\n"); writer.increaseIndent(); // Generate javadoc for run(...) Doc doc = getDoc(); - if (doc != null) + if (doc != null) { doc.generate(writer, false); + } // Deprecation if ("1".equals(((GirElement) this).deprecated)) { diff --git a/buildSrc/src/main/java/io/github/jwharm/javagi/model/Constructor.java b/buildSrc/src/main/java/io/github/jwharm/javagi/model/Constructor.java index a19ed240..0b03a3c0 100644 --- a/buildSrc/src/main/java/io/github/jwharm/javagi/model/Constructor.java +++ b/buildSrc/src/main/java/io/github/jwharm/javagi/model/Constructor.java @@ -33,6 +33,10 @@ public Constructor(GirElement parent, String name, String cIdentifier, String de } public void generate(SourceWriter writer) throws IOException { + if (skip) { + return; + } + String privateMethodName = "construct" + Conversions.toCamelCase(name, true); writer.write("\n"); @@ -85,6 +89,10 @@ public void generate(SourceWriter writer) throws IOException { } public void generateNamed(SourceWriter writer) throws IOException { + if (skip) { + return; + } + String privateMethodName = "construct" + Conversions.toCamelCase(name, true); RegisteredType constructed = (RegisteredType) parent; diff --git a/buildSrc/src/main/java/io/github/jwharm/javagi/model/Function.java b/buildSrc/src/main/java/io/github/jwharm/javagi/model/Function.java index a8866e2e..0db9407c 100644 --- a/buildSrc/src/main/java/io/github/jwharm/javagi/model/Function.java +++ b/buildSrc/src/main/java/io/github/jwharm/javagi/model/Function.java @@ -23,6 +23,6 @@ public class Function extends Method { public Function(GirElement parent, String name, String cIdentifier, String deprecated, String throws_, String movedTo) { super(parent, name, cIdentifier, deprecated, throws_, null, null, movedTo); - visibility = "public"; + visibility = parent instanceof Interface ? "" : "public"; } } diff --git a/buildSrc/src/main/java/io/github/jwharm/javagi/model/FunctionMacro.java b/buildSrc/src/main/java/io/github/jwharm/javagi/model/FunctionMacro.java index f7141983..27d8e246 100644 --- a/buildSrc/src/main/java/io/github/jwharm/javagi/model/FunctionMacro.java +++ b/buildSrc/src/main/java/io/github/jwharm/javagi/model/FunctionMacro.java @@ -38,6 +38,11 @@ public FunctionMacro(GirElement parent, String name, String cIdentifier, String this.throws_ = throws_; } + @Override + public GirElement getParent() { + return parent; + } + @Override public Parameters getParameters() { return parameters; diff --git a/buildSrc/src/main/java/io/github/jwharm/javagi/model/Method.java b/buildSrc/src/main/java/io/github/jwharm/javagi/model/Method.java index 530445ed..82e31c24 100644 --- a/buildSrc/src/main/java/io/github/jwharm/javagi/model/Method.java +++ b/buildSrc/src/main/java/io/github/jwharm/javagi/model/Method.java @@ -78,7 +78,9 @@ public String getMethodDeclaration() { try { // Visibility - writer.write(visibility + " "); + if (! visibility.isEmpty()) { + writer.write(visibility + " "); + } // Static methods (functions and constructor helpers) if (this instanceof Function || this instanceof Constructor) { @@ -307,6 +309,11 @@ public boolean hasVaListParameter() { var lastParam = parameters.parameterList.get(parameters.parameterList.size() - 1); return lastParam.type != null && "VaList".equals(lastParam.type.simpleJavaType); } + + @Override + public GirElement getParent() { + return parent; + } @Override public Parameters getParameters() { diff --git a/buildSrc/src/main/java/io/github/jwharm/javagi/model/Module.java b/buildSrc/src/main/java/io/github/jwharm/javagi/model/Module.java index 1e478f86..52a8569c 100644 --- a/buildSrc/src/main/java/io/github/jwharm/javagi/model/Module.java +++ b/buildSrc/src/main/java/io/github/jwharm/javagi/model/Module.java @@ -130,6 +130,9 @@ public void flagVaListFunctions() { for (Repository repository : repositories.values()) { // Methods, virtual methods and functions for (RegisteredType rt : repository.namespace.registeredTypeMap.values()) { + for (Constructor method : rt.constructorList) { + flagVaListFunction(method); + } for (Method method : rt.methodList) { flagVaListFunction(method); } @@ -150,7 +153,8 @@ public void flagVaListFunctions() { private void flagVaListFunction(Method method) { if (method.parameters != null) { for (Parameter parameter : method.parameters.parameterList) { - if (parameter.type != null && "va_list".equals(parameter.type.cType)) { + if (parameter.type != null + && ("va_list".equals(parameter.type.cType) || "va_list*".equals(parameter.type.cType))) { method.skip = true; break; } @@ -170,10 +174,17 @@ private void linkVirtualMethods() { if (method.getNameAndSignature().equals(vm.getNameAndSignature())) { method.linkedVirtualMethod = vm; vm.linkedMethod = method; + vm.skip = true; break; } } } + // Flag virtual methods in interfaces to not be generated + if (rt instanceof Interface) { + for (VirtualMethod vm : rt.virtualMethodList) { + vm.skip = true; + } + } } } } diff --git a/buildSrc/src/main/java/io/github/jwharm/javagi/model/RegisteredType.java b/buildSrc/src/main/java/io/github/jwharm/javagi/model/RegisteredType.java index 2abc3cd7..2327d448 100644 --- a/buildSrc/src/main/java/io/github/jwharm/javagi/model/RegisteredType.java +++ b/buildSrc/src/main/java/io/github/jwharm/javagi/model/RegisteredType.java @@ -20,7 +20,6 @@ package io.github.jwharm.javagi.model; import java.io.IOException; -import java.util.HashSet; import java.util.List; import java.util.Objects; @@ -70,7 +69,7 @@ public RegisteredType(GirElement parent, String name, String parentClass, String } } - // Find out if this tyjpe is a subclass of the provided classname + // Find out if this type is a subclass of the provided classname protected boolean isInstanceOf(String classname) { if (this.qualifiedName.equals(classname)) { return true; @@ -86,7 +85,7 @@ protected boolean isInstanceOf(String classname) { } public boolean isFloating() { - // GObject has a ref_sink function but we don't want to treat all GObjects as floating references. + // GObject has a ref_sink function, but we don't want to treat all GObjects as floating references. if ("GObject".equals(javaName) && "org.gnome.gobject".equals(getNamespace().packageName)) { return false; } @@ -136,7 +135,10 @@ protected void generateGType(SourceWriter writer) throws IOException { writer.write(" * Get the GType of the " + cType + " " + (this instanceof Interface ? "interface" : "class") + ".\n"); writer.write(" * @return the GType\n"); writer.write(" */\n"); - writer.write("public static org.gnome.glib.Type getType() {\n"); + if (! (this instanceof Interface)) { + writer.write("public "); + } + writer.write("static org.gnome.glib.Type getType() {\n"); writer.write(" return Interop.getType(\"" + getType + "\");\n"); writer.write("}\n"); } @@ -186,7 +188,7 @@ protected void generateMemoryLayout(SourceWriter writer) throws IOException { writer.increaseIndent(); // Check if this type is either defined as a union, or has a union element - boolean isUnion = this instanceof Union || (! unionList.isEmpty()); + boolean isUnion = this instanceof Union || !unionList.isEmpty(); writer.write("return MemoryLayout."); if (isUnion) { @@ -196,8 +198,9 @@ protected void generateMemoryLayout(SourceWriter writer) throws IOException { } List fieldList = this.fieldList; - if (fieldList.isEmpty() && unionList.size() > 0 && unionList.get(0).fieldList.size() > 0) + if (fieldList.isEmpty() && !unionList.isEmpty() && !unionList.get(0).fieldList.isEmpty()) { fieldList = unionList.get(0).fieldList; + } // How many bytes have we generated thus far int size = 0; @@ -295,48 +298,25 @@ public void generateSetFreeFunc(SourceWriter writer, String identifier, String c protected void generateMethodsAndSignals(SourceWriter writer) throws IOException { // Generate instance methods for (Method m : methodList) { - if (m.hasVaListParameter()) { // va_list parameters are not supported - continue; - } m.generate(writer); } // Generate virtual methods for (VirtualMethod vm : virtualMethodList) { - if (vm.hasVaListParameter()) { - continue; - } vm.generate(writer); } - // Generate all functions as static methods + // Generate functions as static methods for (Function f : functionList) { - if (f.hasVaListParameter()) { - continue; - } f.generate(writer); } // Generate signals: functional interface, onSignal method and emitSignal method for (Signal s : signalList) { - if (s.hasVaListParameter()) { - continue; - } s.generate(writer); } } - protected void generateIsAvailable(SourceWriter writer) throws IOException { - writer.write("\n"); - writer.write("/**\n"); - writer.write(" * Check whether the type is available on the runtime platform.\n"); - writer.write(" * @return {@code true} when the type is available on the runtime platform\n"); - writer.write(" */\n"); - writer.write("public static boolean isAvailable() {\n"); - writer.write(" return Interop.isAvailable(\"" + getType + "\", FunctionDescriptor.of(ValueLayout.JAVA_LONG), false);\n"); - writer.write("}\n"); - } - protected void generateEnsureInitialized(SourceWriter writer) throws IOException { writer.write("\n"); writer.write("static {\n"); @@ -352,9 +332,6 @@ protected void generateEnsureInitialized(SourceWriter writer) throws IOException */ protected void generateConstructors(SourceWriter writer) throws IOException { for (Constructor c : constructorList) { - if (c.hasVaListParameter()) { // va_list parameters are not supported - continue; - } if (c.name.equals("new")) { c.generate(writer); } else { diff --git a/buildSrc/src/main/java/io/github/jwharm/javagi/model/Signal.java b/buildSrc/src/main/java/io/github/jwharm/javagi/model/Signal.java index b7fdf71b..95a9957c 100644 --- a/buildSrc/src/main/java/io/github/jwharm/javagi/model/Signal.java +++ b/buildSrc/src/main/java/io/github/jwharm/javagi/model/Signal.java @@ -43,6 +43,10 @@ public Signal(GirElement parent, String name, String when, String detailed, Stri } public void generate(SourceWriter writer) throws IOException { + if (skip) { + return; + } + writer.write("\n"); generateFunctionalInterface(writer, signalName); writer.write("\n"); diff --git a/buildSrc/src/main/java/io/github/jwharm/javagi/model/VirtualMethod.java b/buildSrc/src/main/java/io/github/jwharm/javagi/model/VirtualMethod.java index e02a3bf4..47285137 100644 --- a/buildSrc/src/main/java/io/github/jwharm/javagi/model/VirtualMethod.java +++ b/buildSrc/src/main/java/io/github/jwharm/javagi/model/VirtualMethod.java @@ -30,11 +30,11 @@ public class VirtualMethod extends Method { public VirtualMethod(GirElement parent, String name, String deprecated, String throws_) { super(parent, name, null, deprecated, throws_, null, null, null); - visibility = "protected"; + visibility = parent instanceof Interface ? "default" : "protected"; } public void generate(SourceWriter writer) throws IOException { - if (parent instanceof Interface || linkedMethod != null) { + if (skip) { return; } diff --git a/buildSrc/src/main/java/io/github/jwharm/javagi/patches/GtkPatch.java b/buildSrc/src/main/java/io/github/jwharm/javagi/patches/GtkPatch.java index 854e9771..1b2ad2a1 100644 --- a/buildSrc/src/main/java/io/github/jwharm/javagi/patches/GtkPatch.java +++ b/buildSrc/src/main/java/io/github/jwharm/javagi/patches/GtkPatch.java @@ -24,10 +24,11 @@ public void patch(Repository repo) { setReturnFloating(findMethod(repo, "PaperSize", "to_gvariant")); setReturnFloating(findMethod(repo, "PrintSettings", "to_gvariant")); + findVirtualMethod(repo, "BuilderScope", "get_type_from_name").skip = false; + findVirtualMethod(repo, "BuilderScope", "get_type_from_function").skip = false; + findVirtualMethod(repo, "BuilderScope", "create_closure").skip = false; + // Add virtual methods as instance methods - addInstanceMethod(repo, "BuilderScope", "get_type_from_name"); - addInstanceMethod(repo, "BuilderScope", "get_type_from_function"); - addInstanceMethod(repo, "BuilderScope", "create_closure"); addInstanceMethod(repo, "Window", "activate_default"); addInstanceMethod(repo, "Dialog", "close"); addInstanceMethod(repo, "Popover", "activate_default"); diff --git a/modules/gobject/src/main/java/io/github/jwharm/javagi/gobject/types/Overrides.java b/modules/gobject/src/main/java/io/github/jwharm/javagi/gobject/types/Overrides.java index 2221e6bf..efdcb6ac 100644 --- a/modules/gobject/src/main/java/io/github/jwharm/javagi/gobject/types/Overrides.java +++ b/modules/gobject/src/main/java/io/github/jwharm/javagi/gobject/types/Overrides.java @@ -60,12 +60,6 @@ public class Overrides { false ); - private static final MethodHandle g_type_interface_peek_parent = Interop.downcallHandle( - "g_type_interface_peek_parent", - FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.ADDRESS, ValueLayout.JAVA_LONG), - false - ); - private static final MethodHandle g_type_class_peek_parent = Interop.downcallHandle( "g_type_class_peek_parent", FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.ADDRESS), @@ -198,9 +192,10 @@ static Consumer overrideInterf /** * Returns a function pointer to the specified virtual method. The pointer is retrieved from the TypeClass. - * @param address the memory address of the object instance + * + * @param address the memory address of the object instance * @param classLayout the memory layout of the object's TypeClass - * @param name the name of the virtual method (as defined in the TypeClass) + * @param name the name of the virtual method (as defined in the TypeClass) * @return a function pointer to the requested virtual method */ public static MemorySegment lookupVirtualMethod(MemorySegment address, MemoryLayout classLayout, String name) { @@ -209,50 +204,83 @@ public static MemorySegment lookupVirtualMethod(MemorySegment address, MemoryLay .get(ValueLayout.ADDRESS, classLayout.byteOffset(MemoryLayout.PathElement.groupElement(name))); } + /** + * Returns a function pointer to the specified virtual method. The pointer is retrieved from the TypeClass of the + * parent class of the instance. + * + * @param address the memory address of the object instance + * @param classLayout the memory layout of the object's TypeClass + * @param name the name of the virtual method (as defined in the TypeClass) + * @return a function pointer to the requested virtual method + */ public static MemorySegment lookupVirtualMethodParent(MemorySegment address, MemoryLayout classLayout, String name) { - MemorySegment gclass = address.get(ValueLayout.ADDRESS, 0); - MemorySegment parent; try { - parent = (MemorySegment) g_type_class_peek_parent.invoke(gclass); + // Get the TypeClass + var myClass = address.get(ValueLayout.ADDRESS, 0); + + // Get the parent TypeClass + var parentClass = (MemorySegment) g_type_class_peek_parent.invoke(myClass); + + // Return a pointer to the requested virtual method address in the dispatch table + return parentClass.reinterpret(classLayout.byteSize()) + .get(ValueLayout.ADDRESS, classLayout.byteOffset(MemoryLayout.PathElement.groupElement(name))); } catch (Throwable t) { throw new InteropException(t); } - return parent.reinterpret(classLayout.byteSize()) - .get(ValueLayout.ADDRESS, classLayout.byteOffset(MemoryLayout.PathElement.groupElement(name))); } /** - * Returns a function pointer to the specified virtual method. The pointer is retrieved from the TypeInterface - * with the specified GType. - * @param address the memory address of the object instance + * Returns a function pointer to the specified virtual method. The pointer is retrieved from the TypeInterface with + * the specified GType. + * + * @param address the memory address of the object instance * @param classLayout the memory layout of the object's TypeClass - * @param name the name of the virtual method (as defined in the TypeInterface) - * @param ifaceType the GType of the interface that declares the virtual method + * @param name the name of the virtual method (as defined in the TypeInterface) + * @param ifaceType the GType of the interface that declares the virtual method * @return a function pointer to the requested virtual method */ public static MemorySegment lookupVirtualMethod(MemorySegment address, MemoryLayout classLayout, String name, Type ifaceType) { - MemorySegment struct = address.get(ValueLayout.ADDRESS, 0); - try { - struct = (MemorySegment) g_type_interface_peek.invokeExact(struct, ifaceType.getValue().longValue()); + // Get the TypeClass + MemorySegment myClass = address.get(ValueLayout.ADDRESS, 0); + + // Get the TypeInterface implemented by the TypeClass + MemorySegment iface = (MemorySegment) g_type_interface_peek.invokeExact(myClass, ifaceType.getValue().longValue()); + + // Return a pointer to the requested virtual method address in the dispatch table + return iface.reinterpret(classLayout.byteSize()) + .get(ValueLayout.ADDRESS, classLayout.byteOffset(MemoryLayout.PathElement.groupElement(name))); } catch (Throwable t) { throw new InteropException(t); } - - return struct.reinterpret(classLayout.byteSize()) - .get(ValueLayout.ADDRESS, classLayout.byteOffset(MemoryLayout.PathElement.groupElement(name))); } - public static MemorySegment lookupVirtualMethodParent(MemorySegment address, MemoryLayout classLayout, String name, Type ifaceType) { - MemorySegment struct = address.get(ValueLayout.ADDRESS, 0); - + /** + * Returns a function pointer to the specified virtual method. The pointer is retrieved from the TypeInterface with + * the specified GType, implemented by the parent class of the instance. + * + * @param address the memory address of the object instance + * @param parentClassLayout the memory layout of the parent object's TypeClass + * @param name the name of the virtual method (as defined in the TypeInterface) + * @param ifaceType the GType of the interface that declares the virtual method + * @return a function pointer to the requested virtual method + */ + public static MemorySegment lookupVirtualMethodParent(MemorySegment address, MemoryLayout parentClassLayout, String name, Type ifaceType) { try { - struct = (MemorySegment) g_type_interface_peek_parent.invokeExact(struct, ifaceType.getValue().longValue()); + // Get the TypeClass + var myClass = address.get(ValueLayout.ADDRESS, 0); + + // Get the parent TypeClass + var parentClass = (MemorySegment) g_type_class_peek_parent.invoke(myClass); + + // Get the TypeInterface implemented by the parent TypeClass + var parentIface = (MemorySegment) g_type_interface_peek.invokeExact(parentClass, ifaceType.getValue().longValue()); + + // Return a pointer to the requested virtual method address in the dispatch table + return parentIface.reinterpret(parentClassLayout.byteSize()) + .get(ValueLayout.ADDRESS, parentClassLayout.byteOffset(MemoryLayout.PathElement.groupElement(name))); } catch (Throwable t) { throw new InteropException(t); } - - return struct.reinterpret(classLayout.byteSize()) - .get(ValueLayout.ADDRESS, classLayout.byteOffset(MemoryLayout.PathElement.groupElement(name))); } }