Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Register interfaces and enums #163

Merged
merged 24 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
143ebaf
Register interfaces and enumerations: Initial check-in
jwharm Nov 17, 2024
6216dac
Fixed bugs in the MemoryCleaner class
jwharm Nov 18, 2024
9fb6902
Moved GLib testcases
jwharm Nov 18, 2024
de51d19
Fix (well, ignore) warning about generic type in ArrayTest testcase
jwharm Nov 18, 2024
a6f00a4
Log and ignore missing flag definitions, and add placeholders for int…
jwharm Nov 25, 2024
59b1a88
Ignore records with a "_" prefix
jwharm Nov 29, 2024
efb0242
Rename methods that override final methods from java.lang.Object
jwharm Nov 29, 2024
a0be22e
Avoid dependency of generated code to an unreleased java-gi version
jwharm Nov 29, 2024
aadb122
Fix error in GdkModifierType workaround
jwharm Nov 29, 2024
d8f5f4d
Various cleanups and fixes. Fixes #154 but needs more work.
jwharm Dec 1, 2024
fa55547
Added custom ByteArray class
jwharm Dec 1, 2024
c9ec32c
Renamed constants for consistency
jwharm Dec 1, 2024
f789060
Use custom ByteArray class for GValues
jwharm Dec 1, 2024
62b4fa2
Bump org.gradle.toolchains.foojay-resolver-convention
dependabot[bot] Dec 2, 2024
b29a64c
Improve StrvArray testcase (fixes #145)
jwharm Dec 2, 2024
f34e381
Fix NullPointerException and improve console error output (fixes #159)
jwharm Dec 4, 2024
523bb93
Fix double-free issue on ownership transfer of array parameters
jwharm Dec 4, 2024
a8ae098
Changed JavaBuilderScope to not make any assumptions about method sig…
jwharm Dec 7, 2024
93fa71a
Fix bugs with registering an interface
jwharm Dec 9, 2024
5138879
Fix testcase
jwharm Dec 9, 2024
486f372
Fix generating null check
jwharm Dec 9, 2024
0f6b7d6
Register interface properties
jwharm Dec 10, 2024
de4d7cb
Fix array size calculation bug
jwharm Dec 12, 2024
7ba8e35
Fix registration of enums, add registration of flags types, add testcase
jwharm Dec 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion generator/settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
rootProject.name = "generator"

plugins {
id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"
id("org.gradle.toolchains.foojay-resolver-convention") version "0.9.0"
}

dependencyResolutionManagement {
Expand Down
13 changes: 7 additions & 6 deletions generator/src/main/java/io/github/jwharm/javagi/JavaGI.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,7 @@
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
Expand Down Expand Up @@ -134,8 +131,12 @@ public static void main(String[] args) {
private static int handleException(Exception ex,
CommandLine cmd,
CommandLine.ParseResult parseResult) {
String message = Objects.requireNonNullElse(
ex.getMessage(),
ex.getClass().getSimpleName());

// bold red error message
cmd.getErr().println(cmd.getColorScheme().errorText(ex.getMessage()));
cmd.getErr().println(cmd.getColorScheme().errorText(message));

return cmd.getExitCodeExceptionMapper() != null
? cmd.getExitCodeExceptionMapper().getExitCode(ex)
Expand Down Expand Up @@ -353,7 +354,7 @@ private String generateDependencyLine(String name) {
}
else if (ModuleInfo.INCLUDED_MODULES.containsKey(name)) {
String version = System.getProperty("app.version");
return " api(\"io.github.jwharm.javagi:" + name + ":" + version + "\")";
return " api(\"io.github.jwharm.javagi:" + name + ":0.11.0\")";
} else {
return " api(project(\":" + name + "\"))";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,18 +68,19 @@ public final class ClassNames {
public static final ClassName TYPES = get(PKG_GOBJECT_TYPES, "Types");

// Some frequently used class names
public final static ClassName GERROR = get("org.gnome.glib", "GError");
public final static ClassName GLIB = get("org.gnome.glib", "GLib");
public final static ClassName GTYPE = get("org.gnome.glib", "Type");
public final static ClassName LOG_LEVEL_FLAGS = get("org.gnome.glib", "LogLevelFlags");
public final static ClassName G_BYTE_ARRAY = get("org.gnome.glib", "ByteArray");
public final static ClassName G_ERROR = get("org.gnome.glib", "GError");
public final static ClassName G_LIB = get("org.gnome.glib", "GLib");
public final static ClassName G_LOG_LEVEL_FLAGS = get("org.gnome.glib", "LogLevelFlags");
public final static ClassName G_TYPE = get("org.gnome.glib", "Type");

public final static ClassName GOBJECT = get("org.gnome.gobject", "GObject");
public final static ClassName GOBJECTS = get("org.gnome.gobject", "GObjects");
public final static ClassName GVALUE = get("org.gnome.gobject", "Value");
public final static ClassName TYPE_CLASS = get("org.gnome.gobject", "TypeClass");
public final static ClassName TYPE_INTERFACE = get("org.gnome.gobject", "TypeInterface");
public final static ClassName TYPE_INSTANCE = get("org.gnome.gobject", "TypeInstance");
public final static ClassName G_OBJECT = get("org.gnome.gobject", "GObject");
public final static ClassName G_OBJECTS = get("org.gnome.gobject", "GObjects");
public final static ClassName G_VALUE = get("org.gnome.gobject", "Value");
public final static ClassName G_TYPE_CLASS = get("org.gnome.gobject", "TypeClass");
public final static ClassName G_TYPE_INTERFACE = get("org.gnome.gobject", "TypeInterface");
public final static ClassName G_TYPE_INSTANCE = get("org.gnome.gobject", "TypeInstance");

// The type variable used for <T extends GObject>
public final static TypeVariableName GENERIC_T = TypeVariableName.get("T", GOBJECT);
public final static TypeVariableName GENERIC_T = TypeVariableName.get("T", G_OBJECT);
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public class Patches {
new GstAudioPatch(),
new GstVideoPatch(),
new GstBasePatch(),
new GdkPatch(),
new GtkPatch(),
new HarfBuzzPatch(),
new PangoPatch(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@

import java.lang.foreign.MemorySegment;
import java.lang.foreign.ValueLayout;
import java.util.List;

import static io.github.jwharm.javagi.util.Conversions.getValueLayoutPlain;
import static io.github.jwharm.javagi.util.Conversions.toJavaSimpleType;
Expand All @@ -43,18 +42,18 @@ public class AliasGenerator extends RegisteredTypeGenerator {
public AliasGenerator(Alias alias) {
super(alias);
this.alias = alias;
this.target = alias.type().get();
this.target = alias.lookup();
}

public TypeSpec generate() {
TypeSpec.Builder builder = TypeSpec.classBuilder(alias.typeName());
builder.addAnnotation(GeneratedAnnotationBuilder.generate());

// Alias for an alias for a primitive type
if (target instanceof Alias other && other.type().isPrimitive())
if (target instanceof Alias other && other.isValueWrapper())
builder.superclass(other.typeName())
.addMethod(valueConstructor(other.type().typeName()))
.addMethod(arrayConstructor(other.type()));
.addMethod(valueConstructor(other.anyType().typeName()))
.addMethod(arrayConstructor(other.anyType()));

// Alias for an alias
else if (target instanceof Alias other)
Expand All @@ -70,13 +69,11 @@ else if (target instanceof Interface || target instanceof Callback)
builder = TypeSpec.interfaceBuilder(alias.typeName())
.addSuperinterface(target.typeName());

else if (alias.type().isPrimitive()
|| List.of("java.lang.String", "java.lang.foreign.MemorySegment")
.contains(alias.type().javaType()))
else if (alias.isValueWrapper())
builder.superclass(ParameterizedTypeName.get(
ClassNames.ALIAS, alias.type().typeName().box()))
.addMethod(valueConstructor(alias.type().typeName()))
.addMethod(arrayConstructor(alias.type()));
ClassNames.ALIAS, alias.anyType().typeName().box()))
.addMethod(valueConstructor(alias.anyType().typeName()))
.addMethod(arrayConstructor(alias.anyType()));

if (target instanceof Class || target instanceof Interface
|| target instanceof Record || target instanceof Boxed
Expand Down Expand Up @@ -133,8 +130,11 @@ private MethodSpec fromTargetType() {
.build();
}

private MethodSpec arrayConstructor(Type primitiveType) {
String layout = getValueLayoutPlain(primitiveType, false);
private MethodSpec arrayConstructor(AnyType anyType) {
String layout = switch (anyType) {
case Array _ -> "ADDRESS";
case Type type -> getValueLayoutPlain(type, false);
};

var spec = MethodSpec.methodBuilder("fromNativeArray")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
Expand All @@ -145,7 +145,7 @@ private MethodSpec arrayConstructor(Type primitiveType) {
.addStatement("$T array = new $T[(int) length]",
ArrayTypeName.of(alias.typeName()), alias.typeName());

if (primitiveType.isLong()) {
if (anyType instanceof Type t && t.isLong()) {
spec.addStatement("long byteSize = $1T.longAsInt() ? $2T.JAVA_INT.byteSize() : $2T.JAVA_LONG.byteSize()",
ClassNames.INTEROP, ValueLayout.class);
} else {
Expand All @@ -157,15 +157,32 @@ private MethodSpec arrayConstructor(Type primitiveType) {
MemorySegment.class)
.beginControlFlow("for (int i = 0; i < length; i++)");

if ("java.lang.String".equals(primitiveType.javaType()))
// String[]
if (anyType instanceof Array a
&& a.anyType() instanceof Type t
&& t.typeName().equals(TypeName.get(String.class)))
spec.addStatement("array[i] = new $T($T.getStringArrayFrom(segment.get($T.$L, i * byteSize), free))",
alias.typeName(),
ClassNames.INTEROP,
ValueLayout.class,
layout);
// String
else if (anyType instanceof Type t
&& t.typeName().equals(TypeName.get(String.class)))
spec.addStatement("array[i] = new $T($T.getStringFrom(segment.get($T.$L, i * byteSize), free))",
alias.typeName(), ClassNames.INTEROP, ValueLayout.class, layout);
alias.typeName(),
ClassNames.INTEROP,
ValueLayout.class,
layout);
// Primitive value
else
spec.addStatement("array[i] = new $T(segment.get($T.$L, i * byteSize))",
alias.typeName(), ValueLayout.class, layout);
alias.typeName(),
ValueLayout.class,
layout);

return spec.endControlFlow()
.addStatement("if (free) $T.free(address)", ClassNames.GLIB)
.addStatement("if (free) $T.free(address)", ClassNames.G_LIB)
.addStatement("return array")
.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public TypeSpec generateBuilderClass() {
.addTypeVariable(BUILDER_B)
.superclass(ParameterizedTypeName.get(parentTypeName, B))
.addSuperinterfaces(cls.implements_().stream()
.map(TypeReference::get)
.map(TypeReference::lookup)
.map(Interface.class::cast)
.filter(Interface::hasProperties)
.map(inf -> inf.typeName().nestedClass("Builder"))
Expand Down Expand Up @@ -155,7 +155,7 @@ private MethodSpec setter(Property prp) {
PartialStatement valueSetter = generator.getValueSetter(gtype, generator.getName())
.add(";\n");
return builder.addStatement("$T _arena = getArena()", Arena.class)
.addStatement("$1T _value = new $1T(_arena)", ClassNames.GVALUE)
.addStatement("$1T _value = new $1T(_arena)", ClassNames.G_VALUE)
.addNamedCode("_value.init(" + gtype.format() + ");\n", gtype.arguments())
.addNamedCode(valueSetter.format(), valueSetter.arguments())
.addStatement("addBuilderProperty($S, _value)", prp.name())
Expand Down Expand Up @@ -257,7 +257,7 @@ private MethodSpec buildMethod() {

@return a new instance of {@code $1L} with the properties
that were set in the Builder object.
""", rt.typeName().simpleName(), ClassNames.GOBJECT);
""", rt.typeName().simpleName(), ClassNames.G_OBJECT);
if (rt instanceof Multiplatform mp && mp.doPlatformCheck())
builder.addException(ClassNames.UNSUPPORTED_PLATFORM_EXCEPTION)
.addJavadoc("@throws $T when run on an unsupported platform",
Expand All @@ -272,12 +272,12 @@ private MethodSpec buildMethod() {

return builder.addStatement("var _instance = ($1T) $2T.withProperties($1T.getType(), getNames(), getValues())",
rt.typeName(),
ClassNames.GOBJECT)
ClassNames.G_OBJECT)
.addStatement("connectSignals(_instance.handle())")
.addStatement("return _instance")
.nextControlFlow("finally")
.addStatement("for ($T _value : getValues()) _value.unset()",
ClassNames.GVALUE)
ClassNames.G_VALUE)
.addStatement("getArena().close()")
.endControlFlow()
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ void generateMethodParameters(MethodSpec.Builder builder,
builder.varargs(true);
}

if (generic && type.equals(ClassNames.GOBJECT))
if (generic && type.equals(ClassNames.G_OBJECT))
type = ClassNames.GENERIC_T;

var spec = ParameterSpec.builder(type, generator.getName());
Expand All @@ -150,7 +150,7 @@ PartialStatement marshalParameters(boolean intAsLong) {
// Marshal instance parameter
InstanceParameter iParam = parameters.instanceParameter();
if (iParam != null) {
if (iParam.type().get() instanceof FlaggedType)
if (iParam.type().lookup() instanceof FlaggedType)
stmt.add("getValue()"); // method in Enumeration class
else
stmt.add("handle()"); // method in regular TypeInstance class
Expand Down Expand Up @@ -204,8 +204,8 @@ else if (p.varargs())
// Preprocessing statement
else if (p.isOutParameter()
|| (p.anyType() instanceof Type type
&& type.get() instanceof Alias a
&& a.type().isPrimitive()
&& type.lookup() instanceof Alias a
&& a.isValueWrapper()
&& type.isPointer())) {
stmt.add("_" + name + "Pointer");
}
Expand Down Expand Up @@ -273,7 +273,7 @@ else if (callable.parent() instanceof Interface)

// Return type
var returnValue = callable.returnValue();
if (generic && returnValue.anyType().typeName().equals(ClassNames.GOBJECT))
if (generic && returnValue.anyType().typeName().equals(ClassNames.G_OBJECT))
builder.returns(ClassNames.GENERIC_T);
else if ((!ctor) || namedCtor)
builder.returns(new TypedValueGenerator(returnValue).getType());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,12 @@ public TypeSpec generate() {
ClassNames.GENERIC_T)
: parentClass.typeName());
else
builder.superclass(ClassNames.TYPE_INSTANCE);
builder.superclass(ClassNames.G_TYPE_INSTANCE);

// Add "implements" clause for all implemented interfaces.
// For generic interfaces, add "<GObject>" generic type.
for (var impl : cls.implements_())
if (impl.get() instanceof Interface iface)
if (impl.lookup() instanceof Interface iface)
builder.addSuperinterface(iface.generic()
? ParameterizedTypeName.get(iface.typeName(),
ClassNames.GENERIC_T)
Expand Down Expand Up @@ -194,7 +194,7 @@ protected MethodSpec paramSpecGetTypeMethod() {
@return always {@link $T#PARAM}
""", cls.cType(), ClassNames.TYPES)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(ClassNames.GTYPE)
.returns(ClassNames.G_TYPE)
.addStatement("return $T.PARAM", ClassNames.TYPES)
.build();
}
Expand All @@ -208,12 +208,12 @@ private MethodSpec gobjectConstructor() {
@return the newly created GObject instance
""")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addTypeVariable(TypeVariableName.get("T", ClassNames.GOBJECT))
.addTypeVariable(TypeVariableName.get("T", ClassNames.G_OBJECT))
.returns(TypeVariableName.get("T"))
.addParameter(ClassNames.GTYPE, "objectType")
.addParameter(ClassNames.G_TYPE, "objectType")
.addStatement("var _result = constructNew(objectType, null)")
.addStatement("T _object = (T) $T.getForType(_result, $T::new, true)",
ClassNames.INSTANCE_CACHE, ClassNames.GOBJECT)
ClassNames.INSTANCE_CACHE, ClassNames.G_OBJECT)
.addStatement("return _object")
.build();
}
Expand All @@ -231,9 +231,9 @@ private MethodSpec gobjectConstructorVarargs() {
@throws IllegalArgumentException invalid property name
""")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addTypeVariable(TypeVariableName.get("T", ClassNames.GOBJECT))
.addTypeVariable(TypeVariableName.get("T", ClassNames.G_OBJECT))
.returns(TypeVariableName.get("T"))
.addParameter(ClassNames.GTYPE, "objectType")
.addParameter(ClassNames.G_TYPE, "objectType")
.addParameter(Object[].class, "propertyNamesAndValues")
.varargs(true)
.addStatement("return $T.newGObjectWithProperties(objectType, propertyNamesAndValues)",
Expand Down Expand Up @@ -323,7 +323,7 @@ private MethodSpec gobjectBindProperty() {
.addTypeVariable(T)
.returns(ParameterizedTypeName.get(ClassNames.BINDING_BUILDER, S, T))
.addParameter(String.class, "sourceProperty")
.addParameter(ClassNames.GOBJECT, "target")
.addParameter(ClassNames.G_OBJECT, "target")
.addParameter(String.class, "targetProperty")
.addStatement("return new $T<S, T>(this, sourceProperty, target, targetProperty)",
ClassNames.BINDING_BUILDER)
Expand Down Expand Up @@ -371,7 +371,7 @@ private MethodSpec gobjectConnectAfter() {
.addStatement("$1T closure = new $1T(callback)",
ClassNames.JAVA_CLOSURE)
.addStatement("int handlerId = $T.signalConnectClosure(this, detailedSignal, closure, after)",
ClassNames.GOBJECTS)
ClassNames.G_OBJECTS)
.addStatement("return new $T(handle(), handlerId, closure)",
ClassNames.SIGNAL_CONNECTION)
.build();
Expand Down
Loading