Skip to content

Commit 7b1f8ec

Browse files
committedDec 1, 2024
Various cleanups and fixes. Fixes #154 but needs more work.
1 parent 6d30099 commit 7b1f8ec

36 files changed

+497
-426
lines changed
 

‎generator/src/main/java/io/github/jwharm/javagi/generators/AliasGenerator.java

+34-17
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030

3131
import java.lang.foreign.MemorySegment;
3232
import java.lang.foreign.ValueLayout;
33-
import java.util.List;
3433

3534
import static io.github.jwharm.javagi.util.Conversions.getValueLayoutPlain;
3635
import static io.github.jwharm.javagi.util.Conversions.toJavaSimpleType;
@@ -43,18 +42,18 @@ public class AliasGenerator extends RegisteredTypeGenerator {
4342
public AliasGenerator(Alias alias) {
4443
super(alias);
4544
this.alias = alias;
46-
this.target = alias.type().get();
45+
this.target = alias.lookup();
4746
}
4847

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

5352
// Alias for an alias for a primitive type
54-
if (target instanceof Alias other && other.type().isPrimitive())
53+
if (target instanceof Alias other && other.isValueWrapper())
5554
builder.superclass(other.typeName())
56-
.addMethod(valueConstructor(other.type().typeName()))
57-
.addMethod(arrayConstructor(other.type()));
55+
.addMethod(valueConstructor(other.anyType().typeName()))
56+
.addMethod(arrayConstructor(other.anyType()));
5857

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

73-
else if (alias.type().isPrimitive()
74-
|| List.of("java.lang.String", "java.lang.foreign.MemorySegment")
75-
.contains(alias.type().javaType()))
72+
else if (alias.isValueWrapper())
7673
builder.superclass(ParameterizedTypeName.get(
77-
ClassNames.ALIAS, alias.type().typeName().box()))
78-
.addMethod(valueConstructor(alias.type().typeName()))
79-
.addMethod(arrayConstructor(alias.type()));
74+
ClassNames.ALIAS, alias.anyType().typeName().box()))
75+
.addMethod(valueConstructor(alias.anyType().typeName()))
76+
.addMethod(arrayConstructor(alias.anyType()));
8077

8178
if (target instanceof Class || target instanceof Interface
8279
|| target instanceof Record || target instanceof Boxed
@@ -133,8 +130,11 @@ private MethodSpec fromTargetType() {
133130
.build();
134131
}
135132

136-
private MethodSpec arrayConstructor(Type primitiveType) {
137-
String layout = getValueLayoutPlain(primitiveType, false);
133+
private MethodSpec arrayConstructor(AnyType anyType) {
134+
String layout = switch (anyType) {
135+
case Array _ -> "ADDRESS";
136+
case Type type -> getValueLayoutPlain(type, false);
137+
};
138138

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

148-
if (primitiveType.isLong()) {
148+
if (anyType instanceof Type t && t.isLong()) {
149149
spec.addStatement("long byteSize = $1T.longAsInt() ? $2T.JAVA_INT.byteSize() : $2T.JAVA_LONG.byteSize()",
150150
ClassNames.INTEROP, ValueLayout.class);
151151
} else {
@@ -157,12 +157,29 @@ private MethodSpec arrayConstructor(Type primitiveType) {
157157
MemorySegment.class)
158158
.beginControlFlow("for (int i = 0; i < length; i++)");
159159

160-
if ("java.lang.String".equals(primitiveType.javaType()))
160+
// String[]
161+
if (anyType instanceof Array a
162+
&& a.anyType() instanceof Type t
163+
&& t.typeName().equals(TypeName.get(String.class)))
164+
spec.addStatement("array[i] = new $T($T.getStringArrayFrom(segment.get($T.$L, i * byteSize), free))",
165+
alias.typeName(),
166+
ClassNames.INTEROP,
167+
ValueLayout.class,
168+
layout);
169+
// String
170+
else if (anyType instanceof Type t
171+
&& t.typeName().equals(TypeName.get(String.class)))
161172
spec.addStatement("array[i] = new $T($T.getStringFrom(segment.get($T.$L, i * byteSize), free))",
162-
alias.typeName(), ClassNames.INTEROP, ValueLayout.class, layout);
173+
alias.typeName(),
174+
ClassNames.INTEROP,
175+
ValueLayout.class,
176+
layout);
177+
// Primitive value
163178
else
164179
spec.addStatement("array[i] = new $T(segment.get($T.$L, i * byteSize))",
165-
alias.typeName(), ValueLayout.class, layout);
180+
alias.typeName(),
181+
ValueLayout.class,
182+
layout);
166183

167184
return spec.endControlFlow()
168185
.addStatement("if (free) $T.free(address)", ClassNames.GLIB)

‎generator/src/main/java/io/github/jwharm/javagi/generators/BuilderGenerator.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ public TypeSpec generateBuilderClass() {
8383
.addTypeVariable(BUILDER_B)
8484
.superclass(ParameterizedTypeName.get(parentTypeName, B))
8585
.addSuperinterfaces(cls.implements_().stream()
86-
.map(TypeReference::get)
86+
.map(TypeReference::lookup)
8787
.map(Interface.class::cast)
8888
.filter(Interface::hasProperties)
8989
.map(inf -> inf.typeName().nestedClass("Builder"))

‎generator/src/main/java/io/github/jwharm/javagi/generators/CallableGenerator.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ PartialStatement marshalParameters(boolean intAsLong) {
150150
// Marshal instance parameter
151151
InstanceParameter iParam = parameters.instanceParameter();
152152
if (iParam != null) {
153-
if (iParam.type().get() instanceof FlaggedType)
153+
if (iParam.type().lookup() instanceof FlaggedType)
154154
stmt.add("getValue()"); // method in Enumeration class
155155
else
156156
stmt.add("handle()"); // method in regular TypeInstance class
@@ -204,8 +204,8 @@ else if (p.varargs())
204204
// Preprocessing statement
205205
else if (p.isOutParameter()
206206
|| (p.anyType() instanceof Type type
207-
&& type.get() instanceof Alias a
208-
&& a.type().isPrimitive()
207+
&& type.lookup() instanceof Alias a
208+
&& a.isValueWrapper()
209209
&& type.isPointer())) {
210210
stmt.add("_" + name + "Pointer");
211211
}

‎generator/src/main/java/io/github/jwharm/javagi/generators/ClassGenerator.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public TypeSpec generate() {
6666
// Add "implements" clause for all implemented interfaces.
6767
// For generic interfaces, add "<GObject>" generic type.
6868
for (var impl : cls.implements_())
69-
if (impl.get() instanceof Interface iface)
69+
if (impl.lookup() instanceof Interface iface)
7070
builder.addSuperinterface(iface.generic()
7171
? ParameterizedTypeName.get(iface.typeName(),
7272
ClassNames.GENERIC_T)

‎generator/src/main/java/io/github/jwharm/javagi/generators/ClosureGenerator.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -274,8 +274,8 @@ private PartialStatement marshalParameters(String methodToInvoke) {
274274

275275
if (p.anyType() instanceof Type t
276276
&& t.isPointer()
277-
&& t.get() instanceof Alias a
278-
&& a.type().isPrimitive()) {
277+
&& t.lookup() instanceof Alias a
278+
&& a.isValueWrapper()) {
279279
stmt.add("_" + toJavaIdentifier(p.name()) + "Alias");
280280
continue;
281281
}
@@ -302,10 +302,10 @@ private PartialStatement marshalParameters(String methodToInvoke) {
302302

303303
private void returnNull(MethodSpec.Builder upcall) {
304304
if (returnValue.anyType() instanceof Type type) {
305-
var target = type.get();
305+
var target = type.lookup();
306306
if ((type.isPrimitive()
307307
|| target instanceof FlaggedType
308-
|| (target instanceof Alias a && a.type().isPrimitive()))
308+
|| (target instanceof Alias a && a.isValueWrapper()))
309309
&& (!type.isPointer())) {
310310
upcall.addStatement("return 0");
311311
return;

‎generator/src/main/java/io/github/jwharm/javagi/generators/InterfaceGenerator.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public TypeSpec generate() {
5454
// Add "extends" clause for all prerequisite interfaces.
5555
// For generic interfaces, add "<GObject>" generic type.
5656
for (var prereq : inf.prerequisites())
57-
if (prereq.get() instanceof Interface iface)
57+
if (prereq.lookup() instanceof Interface iface)
5858
builder.addSuperinterface(iface.generic()
5959
? ParameterizedTypeName.get(iface.typeName(),
6060
ClassNames.GENERIC_T)

‎generator/src/main/java/io/github/jwharm/javagi/generators/MemoryLayoutGenerator.java

+6-6
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ public class MemoryLayoutGenerator {
3939
// Opaque structs have unknown memory layout.
4040
public boolean canGenerate(FieldContainer rt) {
4141
boolean isOpaque = switch (rt) {
42-
case Class c -> c.hasOpaqueStructFields() || c.isOpaque();
43-
case Record r -> r.hasOpaqueStructFields() || r.isOpaque();
44-
case Union u -> u.hasOpaqueStructFields() || u.isOpaque();
42+
case Class c -> c.hasOpaqueStructFields() || c.opaque();
43+
case Record r -> r.hasOpaqueStructFields() || r.opaque();
44+
case Union u -> u.hasOpaqueStructFields() || u.opaque();
4545
default -> false;
4646
};
4747
return !isOpaque;
@@ -184,11 +184,11 @@ private PartialStatement getFieldLayout(Field f, boolean longAsInt) {
184184
}
185185

186186
private PartialStatement layoutForType(Type type, boolean longAsInt) {
187-
RegisteredType target = type.get();
187+
RegisteredType target = type.lookup();
188188

189189
// Recursive lookup for aliases
190-
if (target instanceof Alias alias)
191-
return layoutForType(alias.type(), longAsInt);
190+
if (target instanceof Alias alias && alias.anyType() instanceof Type t)
191+
return layoutForType(t, longAsInt);
192192

193193
// Proxy objects with a known memory layout
194194
if (!type.isPointer()

‎generator/src/main/java/io/github/jwharm/javagi/generators/MethodGenerator.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ else if (!returnValue.anyType().isVoid()) {
240240
private void generateOwnershipTransfer() {
241241
// Prepare a statement that marshals the return value to Java
242242
RegisteredType target = returnValue.anyType() instanceof Type type
243-
? type.get() : null;
243+
? type.lookup() : null;
244244
var generator = new TypedValueGenerator(returnValue);
245245
PartialStatement stmt = PartialStatement.of("");
246246
if (generic && returnValue.anyType().typeName().equals(ClassNames.GOBJECT))

‎generator/src/main/java/io/github/jwharm/javagi/generators/NamespaceGenerator.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ private MethodSpec registerTypes() {
135135
spec.addCode(register(i.constructorName(), i.typeName()));
136136

137137
for (Alias a : ns.aliases()) {
138-
RegisteredType target = a.type().get();
138+
RegisteredType target = a.lookup();
139139
if (target instanceof Class c)
140140
spec.addCode(register(c.constructorName(), a.typeName()));
141141
if (target instanceof Interface i)

‎generator/src/main/java/io/github/jwharm/javagi/generators/PostprocessingGenerator.java

+45-52
Original file line numberDiff line numberDiff line change
@@ -48,22 +48,22 @@ private void readPointer(MethodSpec.Builder builder) {
4848
|| (type != null
4949
&& type.isPointer()
5050
&& target instanceof Alias a
51-
&& a.type().isPrimitive())) {
51+
&& a.isValueWrapper())) {
5252

5353
// Pointer to single value
5454
if (array == null) {
5555
var stmt = PartialStatement.of(null, "valueLayout", ValueLayout.class);
5656

5757
stmt.add(getName())
58-
.add((target instanceof Alias a && a.type().isPrimitive())
58+
.add((target instanceof Alias a && a.isValueWrapper())
5959
? ".setValue("
6060
: ".set(");
6161

6262
var layout = generateValueLayoutPlain(type);
6363
String identifier = "_%sPointer.get(%s, 0)"
6464
.formatted(getName(), layout.format());
6565

66-
if ((target instanceof Alias a && a.type().isPrimitive())
66+
if ((target instanceof Alias a && a.isValueWrapper())
6767
|| (type.isPrimitive() && type.isPointer())) {
6868
stmt.add(identifier);
6969
if (type.isBoolean())
@@ -80,60 +80,56 @@ private void readPointer(MethodSpec.Builder builder) {
8080
.endControlFlow();
8181
else
8282
builder.addNamedCode(stmt.format(), stmt.arguments());
83+
84+
return;
8385
}
8486

8587
// Pointer to array
86-
else {
87-
String len = array.sizeExpression(false);
88-
PartialStatement payload;
89-
Type arrayType = (Type) array.anyType();
90-
91-
if (p.isOutParameter() && len != null && p.callerAllocates()) {
92-
// Out-parameter array with known length
93-
payload = arrayType.isPrimitive()
94-
? PartialStatement.of("_$name:LPointer.toArray(",
95-
"name", getName())
96-
.add(generateValueLayoutPlain(arrayType))
97-
.add(")")
98-
: marshalNativeToJava("_%sPointer"
99-
.formatted(getName()), false);
100-
} else if (len != null
101-
&& arrayType.isPrimitive()
102-
&& !arrayType.isBoolean()) {
103-
// Arrays with primitive values and known length
104-
payload = PartialStatement.of("_$name:LPointer$Z.get($valueLayout:T.ADDRESS, 0)$Z.reinterpret(" + len + " * ",
105-
"name", getName(),
106-
"valueLayout", ValueLayout.class)
107-
.add(generateValueLayoutPlain(arrayType))
108-
.add(".byteSize(), _arena, null)$Z.toArray(")
109-
.add(generateValueLayoutPlain(arrayType))
110-
.add(")");
111-
} else {
112-
// Other arrays
113-
payload = marshalNativeToJava("_" + getName() + "Pointer", false);
114-
}
88+
String len = array.sizeExpression(false);
89+
PartialStatement payload;
90+
91+
// Out-parameter array with known length
92+
if (p.isOutParameter() && len != null && p.callerAllocates())
93+
payload = array.anyType() instanceof Type t && t.isPrimitive()
94+
? PartialStatement.of("_$name:LPointer.toArray(", "name", getName())
95+
.add(generateValueLayoutPlain(t))
96+
.add(")")
97+
: marshalNativeToJava("_%sPointer".formatted(getName()), false);
98+
99+
// Arrays with primitive values and known length
100+
else if (len != null
101+
&& array.anyType() instanceof Type t
102+
&& t.isPrimitive()
103+
&& !t.isBoolean())
104+
payload = PartialStatement.of("_$name:LPointer$Z.get($valueLayout:T.ADDRESS, 0)$Z.reinterpret(" + len + " * ",
105+
"name", getName(),
106+
"valueLayout", ValueLayout.class)
107+
.add(generateValueLayoutPlain(t))
108+
.add(".byteSize(), _arena, null)$Z.toArray(")
109+
.add(generateValueLayoutPlain(t))
110+
.add(")");
111+
112+
// Other arrays
113+
else
114+
payload = marshalNativeToJava("_" + getName() + "Pointer", false);
115115

116-
var stmt = PartialStatement.of(getName())
117-
.add(".set(")
118-
.add(payload)
119-
.add(");\n");
116+
var stmt = PartialStatement.of(getName())
117+
.add(".set(")
118+
.add(payload)
119+
.add(");\n");
120120

121-
// Null-check
122-
builder.beginControlFlow("if ($1L != null)", getName())
123-
.addNamedCode(stmt.format(), stmt.arguments())
124-
.endControlFlow();
125-
}
121+
// Null-check
122+
builder.beginControlFlow("if ($1L != null)", getName())
123+
.addNamedCode(stmt.format(), stmt.arguments())
124+
.endControlFlow();
126125
}
127126
}
128127

129128
private void writePrimitiveAliasPointer(MethodSpec.Builder builder) {
130-
if (target instanceof Alias a
131-
&& a.type().isPrimitive()
132-
&& type.isPointer()) {
129+
if (target instanceof Alias a && a.isValueWrapper() && type.isPointer()) {
133130
var stmt = PartialStatement.of("$name:LParam.set(")
134131
.add(generateValueLayoutPlain(type))
135-
.add(", 0, _$name:LAlias.getValue());\n",
136-
"name", getName());
132+
.add(", 0, _$name:LAlias.getValue());\n", "name", getName());
137133
builder.addNamedCode(stmt.format(), stmt.arguments());
138134
}
139135
}
@@ -144,20 +140,17 @@ private void writeOutParameter(MethodSpec.Builder builder) {
144140

145141
if (type != null) {
146142
PartialStatement payload;
147-
if (type.isPrimitive()
148-
|| (target instanceof Alias a && a.type().isPrimitive())) {
143+
if (type.isPrimitive() || (target instanceof Alias a && a.isValueWrapper())) {
149144
payload = PartialStatement.of("_" + getName() + "Out.get()");
150145
if (type.isBoolean())
151146
payload.add(" ? 1 : 0");
152147
}
153148
else if (target instanceof FlaggedType)
154-
payload = PartialStatement.of("_$name:LOut.get().getValue()",
155-
"name", getName());
149+
payload = PartialStatement.of("_$name:LOut.get().getValue()", "name", getName());
156150
else
157151
payload = marshalJavaToNative("_" + getName() + "Out.get()");
158152

159-
var stmt = PartialStatement.of("$name:LParam.set(",
160-
"name", getName())
153+
var stmt = PartialStatement.of("$name:LParam.set(", "name", getName())
161154
.add(generateValueLayoutPlain(type))
162155
.add(", 0, ")
163156
.add(payload)

‎generator/src/main/java/io/github/jwharm/javagi/generators/PreprocessingGenerator.java

+78-90
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,7 @@ private void nullCheck(MethodSpec.Builder builder) {
6565
|| p.isDestroyNotifyParameter()
6666
|| p.isArrayLengthParameter()
6767
|| p.varargs()
68-
|| (type != null
69-
&& type.isPrimitive()
70-
&& !type.isPointer())))) {
68+
|| (type != null && type.isPrimitive() && !type.isPointer())))) {
7169
builder.addStatement("$T.requireNonNull($L, $S)",
7270
Objects.class,
7371
getName(),
@@ -77,9 +75,7 @@ private void nullCheck(MethodSpec.Builder builder) {
7775

7876
// Allocate memory for out-parameter
7977
private void pointerAllocation(MethodSpec.Builder builder) {
80-
if (p.isOutParameter()
81-
&& array != null
82-
&& (!array.unknownSize())) {
78+
if (p.isOutParameter() && array != null && !array.unknownSize()) {
8379
/*
8480
* Out-parameter array with known size: If the array isn't null,
8581
* copy the contents into the native memory buffer. If it is null,
@@ -100,7 +96,7 @@ private void pointerAllocation(MethodSpec.Builder builder) {
10096
|| (type != null
10197
&& type.isPointer()
10298
&& target instanceof Alias a
103-
&& a.type().isPrimitive())) {
99+
&& a.isValueWrapper())) {
104100
var stmt = PartialStatement.of(
105101
"$memorySegment:T _$name:LPointer = _arena.allocate(",
106102
"memorySegment", MemorySegment.class,
@@ -116,33 +112,31 @@ private void pointerAllocation(MethodSpec.Builder builder) {
116112
* can be omitted from the Java API.
117113
*/
118114
private void arrayLength(MethodSpec.Builder builder) {
119-
if (p.isArrayLengthParameter()) {
120-
if (p.isOutParameter()) {
121-
// Set the initial value of the allocated pointer to the length
122-
// of the input array
123-
if (p.isArrayLengthParameter() && p.isOutParameter()) {
124-
var stmt = PartialStatement.of("_$name:LPointer.set(",
125-
"name", getName())
126-
.add(generateValueLayoutPlain(type))
127-
.add(", 0L,$W")
128-
.add(arrayLengthStatement())
129-
.add(");\n");
130-
builder.addNamedCode(stmt.format(), stmt.arguments());
131-
}
132-
// Declare an Out<> instance
133-
builder.addStatement("$1T $2L = new $3T<>()",
134-
getType(),
135-
getName(),
136-
ClassNames.OUT);
137-
} else {
138-
// Declare a primitive value
139-
var stmt = PartialStatement.of("$type:T $name:L =$W",
140-
"type", getType(),
141-
"name", getName())
115+
if (!p.isArrayLengthParameter())
116+
return;
117+
118+
if (p.isOutParameter()) {
119+
// Set the initial value of the allocated pointer to the length
120+
// of the input array
121+
if (p.isArrayLengthParameter() && p.isOutParameter()) {
122+
var stmt = PartialStatement.of("_$name:LPointer.set(", "name", getName())
123+
.add(generateValueLayoutPlain(type))
124+
.add(", 0L,$W")
142125
.add(arrayLengthStatement())
143-
.add(";\n");
126+
.add(");\n");
144127
builder.addNamedCode(stmt.format(), stmt.arguments());
145128
}
129+
// Declare an Out<> instance
130+
builder.addStatement("$1T $2L = new $3T<>()",
131+
getType(), getName(), ClassNames.OUT);
132+
} else {
133+
// Declare a primitive value
134+
var stmt = PartialStatement.of("$type:T $name:L =$W",
135+
"type", getType(),
136+
"name", getName())
137+
.add(arrayLengthStatement())
138+
.add(";\n");
139+
builder.addNamedCode(stmt.format(), stmt.arguments());
146140
}
147141
}
148142

@@ -162,27 +156,23 @@ private PartialStatement arrayLengthStatement() {
162156
"zero", literal(p.anyType().typeName(), "0"),
163157
"cast", List.of("byte", "short").contains(type.javaType())
164158
? "(" + type.javaType() + ") "
165-
: ""
166-
)
159+
: "")
167160
.add(arrayParam.isOutParameter()
168161
? "$arr:L.get() == null ? $zero:L : $cast:L$arr:L.get().length"
169-
: "$arr:L.length"
170-
);
162+
: "$arr:L.length");
171163
}
172164

173165
// Arena for parameters with async or notified scope
174166
private void scope(MethodSpec.Builder builder) {
175167
if (p.scope() == Scope.NOTIFIED && p.destroy() != null)
176168
builder.addStatement("final $1T _$2LScope = $1T.ofConfined()",
177-
Arena.class,
178-
getName());
179-
else if (p.scope() == Scope.ASYNC && (!p.isDestroyNotifyParameter()))
169+
Arena.class, getName());
170+
171+
if (p.scope() == Scope.ASYNC && !p.isDestroyNotifyParameter())
180172
builder.addStatement("final $1T _$2LScope = $1T.ofConfined()",
181-
Arena.class,
182-
getName())
183-
.addStatement("if ($2L != null) $1T.CLEANER.register($2L, new $1T(_$2LScope))",
184-
ClassNames.ARENA_CLOSE_ACTION,
185-
getName());
173+
Arena.class, getName())
174+
.addStatement("if ($2L != null) $1T.CLEANER.register($2L, new $1T(_$2LScope))",
175+
ClassNames.ARENA_CLOSE_ACTION, getName());
186176
}
187177

188178
// If the parameter has attribute transfer-ownership="full", we must
@@ -193,8 +183,8 @@ private void transferOwnership(MethodSpec.Builder builder) {
193183
// out parameter or a pointer)
194184
if (target != null && target.checkIsGObject()
195185
&& p.transferOwnership() != TransferOwnership.NONE
196-
&& (!p.isOutParameter())
197-
&& (type.cType() == null || (! type.cType().endsWith("**")))) {
186+
&& !p.isOutParameter()
187+
&& (type.cType() == null || !type.cType().endsWith("**"))) {
198188
builder.beginControlFlow("if ($L instanceof $T _gobject)",
199189
getName(), ClassNames.GOBJECT)
200190
.addStatement("$T.debug($S, _gobject.handle().address())",
@@ -207,12 +197,11 @@ private void transferOwnership(MethodSpec.Builder builder) {
207197
// Same, but for structs/unions: Disable the cleaner
208198
else if ((target instanceof StandardLayoutType)
209199
&& p.transferOwnership() != TransferOwnership.NONE
210-
&& (!p.isOutParameter())
211-
&& (type.cType() == null || (! type.cType().endsWith("**")))) {
200+
&& !p.isOutParameter()
201+
&& (type.cType() == null || !type.cType().endsWith("**"))) {
212202
builder.addStatement(
213-
checkNull()
214-
? "if ($1L != null) $2T.yieldOwnership($1L)"
215-
: "$2T.yieldOwnership($1L)",
203+
checkNull() ? "if ($1L != null) $2T.yieldOwnership($1L)"
204+
: "$2T.yieldOwnership($1L)",
216205
getName(),
217206
ClassNames.MEMORY_CLEANER);
218207
}
@@ -221,9 +210,7 @@ else if ((target instanceof StandardLayoutType)
221210
// Read the value from a pointer to a primitive value and store it
222211
// in a Java Alias object
223212
private void readPrimitiveAliasPointer(MethodSpec.Builder builder) {
224-
if (target instanceof Alias a
225-
&& a.type().isPrimitive()
226-
&& type.isPointer()) {
213+
if (target instanceof Alias a && a.isValueWrapper() && type.isPointer()) {
227214
var stmt = PartialStatement.of(
228215
"$memorySegment:T $name:LParam = $name:L.reinterpret(",
229216
"memorySegment", MemorySegment.class,
@@ -248,49 +235,50 @@ private void readOutParameter(MethodSpec.Builder builder) {
248235
if (!p.isOutParameter())
249236
return;
250237

251-
// Pointer to a single value
252-
if (type != null) {
253-
var stmt = PartialStatement.of(
254-
"$memorySegment:T $name:LParam = $name:L.reinterpret(",
255-
"memorySegment", MemorySegment.class,
256-
"name", getName())
257-
.add(generateValueLayoutPlain(type))
258-
.add(".byteSize(), _arena, null);\n");
238+
// Pointer to an array
239+
if (array != null) {
240+
var stmt = PartialStatement.of("$outType:T _" + getName() + "Out = new $out:T<>(",
241+
"outType", getType(),
242+
"out", ClassNames.OUT)
243+
.add(marshalNativeToJava(getName(), true))
244+
.add(");\n");
259245
builder.addNamedCode(stmt.format(), stmt.arguments());
246+
return;
247+
}
260248

261-
if (type.isPrimitive()
262-
|| target instanceof Alias a && a.type().isPrimitive()) {
263-
stmt = PartialStatement.of(
264-
"$outType:T _$name:LOut = new $out:T<>($name:LParam.get(",
265-
"outType", getType(),
266-
"name", getName(),
267-
"out", ClassNames.OUT)
268-
.add(generateValueLayoutPlain(type))
269-
.add(", 0)")
270-
.add(type.isBoolean() ? " != 0" : "")
271-
.add(");\n");
272-
builder.addNamedCode(stmt.format(), stmt.arguments());
273-
} else {
274-
String identifier = getName() + "Param";
275-
if (target instanceof FlaggedType)
276-
identifier += ".get($valueLayout:T.JAVA_INT, 0)";
249+
if (type == null)
250+
return;
277251

278-
stmt = PartialStatement.of("$outType:T _" + getName() + "Out = new $out:T<>(",
279-
"valueLayout", ValueLayout.class,
280-
"outType", getType(),
281-
"out", ClassNames.OUT)
282-
.add(marshalNativeToJava(type, identifier, true))
283-
.add(");\n");
284-
builder.addNamedCode(stmt.format(), stmt.arguments());
285-
}
286-
}
252+
// Pointer to a single value
253+
var stmt = PartialStatement.of(
254+
"$memorySegment:T $name:LParam = $name:L.reinterpret(",
255+
"memorySegment", MemorySegment.class,
256+
"name", getName())
257+
.add(generateValueLayoutPlain(type))
258+
.add(".byteSize(), _arena, null);\n");
259+
builder.addNamedCode(stmt.format(), stmt.arguments());
287260

288-
// Pointer to an array
289-
else if (array != null) {
290-
var stmt = PartialStatement.of("$outType:T _" + getName() + "Out = new $out:T<>(",
261+
if (type.isPrimitive() || target instanceof Alias a && a.isValueWrapper()) {
262+
stmt = PartialStatement.of(
263+
"$outType:T _$name:LOut = new $out:T<>($name:LParam.get(",
291264
"outType", getType(),
265+
"name", getName(),
292266
"out", ClassNames.OUT)
293-
.add(marshalNativeToJava(getName(), true))
267+
.add(generateValueLayoutPlain(type))
268+
.add(", 0)")
269+
.add(type.isBoolean() ? " != 0" : "")
270+
.add(");\n");
271+
builder.addNamedCode(stmt.format(), stmt.arguments());
272+
} else {
273+
String identifier = getName() + "Param";
274+
if (target instanceof FlaggedType)
275+
identifier += ".get($valueLayout:T.JAVA_INT, 0)";
276+
277+
stmt = PartialStatement.of("$outType:T _" + getName() + "Out = new $out:T<>(",
278+
"valueLayout", ValueLayout.class,
279+
"outType", getType(),
280+
"out", ClassNames.OUT)
281+
.add(marshalNativeToJava(type, identifier, true))
294282
.add(");\n");
295283
builder.addNamedCode(stmt.format(), stmt.arguments());
296284
}

‎generator/src/main/java/io/github/jwharm/javagi/generators/RecordGenerator.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ public TypeSpec generate() {
8080
* disguised.
8181
*/
8282
Type type = (Type) rec.fields().getFirst().anyType();
83-
Record parentRec = (Record) type.get();
83+
Record parentRec = (Record) type.lookup();
8484
RegisteredType parentClass = parentRec.isGTypeStructFor();
8585
if (parentClass != null) {
8686
String nestedClassName = toJavaSimpleType(parentRec.name(), parentRec.namespace());
@@ -154,7 +154,7 @@ private void generateField(Field f) {
154154
if (cb == null) {
155155
if (f.anyType() instanceof Type t
156156
&& (!t.isPointer())
157-
&& t.get() instanceof Record)
157+
&& t.lookup() instanceof Record)
158158
// Copy contents from nested struct
159159
builder.addMethod(generator.generateReadCopyMethod());
160160
else
@@ -191,7 +191,7 @@ else if (outerClass != null && cb.parameters() != null) {
191191

192192
if (f.anyType() instanceof Type t
193193
&& (!t.isPointer())
194-
&& t.get() instanceof Record)
194+
&& t.lookup() instanceof Record)
195195
// Generate write-method to copy contents to nested struct
196196
builder.addMethod(generator.generateWriteCopyMethod());
197197
else if (cb == null || outerClass == null)

‎generator/src/main/java/io/github/jwharm/javagi/generators/RegisteredTypeGenerator.java

+2-4
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
import java.util.List;
3434

3535
import static io.github.jwharm.javagi.util.CollectionUtils.filter;
36-
import static io.github.jwharm.javagi.util.Conversions.toJavaIdentifier;
3736
import static java.util.function.Predicate.not;
3837

3938
public class RegisteredTypeGenerator {
@@ -132,9 +131,8 @@ protected MethodSpec memoryAddressConstructor() {
132131
""", name())
133132
.addParameter(MemorySegment.class, "address");
134133

135-
if (rt instanceof Record rec && rec.isOpaque()
136-
|| rt instanceof Boxed
137-
|| rt instanceof Union union && union.isOpaque())
134+
if (rt instanceof FieldContainer fc
135+
&& (fc.opaque() || fc.hasOpaqueStructFields()))
138136
builder.addStatement("super(address)");
139137
else
140138
builder.addStatement("super($T.reinterpret(address, getMemoryLayout().byteSize()))",

‎generator/src/main/java/io/github/jwharm/javagi/generators/TypedValueGenerator.java

+138-149
Large diffs are not rendered by default.

‎generator/src/main/java/io/github/jwharm/javagi/gir/Alias.java

+26-7
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,12 @@
1919

2020
package io.github.jwharm.javagi.gir;
2121

22-
import io.github.jwharm.javagi.configuration.ClassNames;
22+
import com.squareup.javapoet.TypeName;
2323
import io.github.jwharm.javagi.util.PartialStatement;
2424

2525
import static io.github.jwharm.javagi.util.CollectionUtils.*;
2626

27+
import java.lang.foreign.MemorySegment;
2728
import java.util.List;
2829
import java.util.Map;
2930
import java.util.Objects;
@@ -43,20 +44,38 @@ public Namespace parent() {
4344

4445
@Override
4546
public String getTypeFunc() {
46-
if (type().isPrimitive())
47+
RegisteredType target = lookup();
48+
return target == null ? null : target.getTypeFunc();
49+
}
50+
51+
public boolean isValueWrapper() {
52+
return !isProxy();
53+
}
54+
55+
public boolean isProxy() {
56+
return switch (anyType()) {
57+
case Array _ -> false;
58+
case Type t when t.isProxy() -> true;
59+
case Type _ -> false;
60+
};
61+
}
62+
63+
public RegisteredType lookup() {
64+
if (anyType() instanceof Type t)
65+
return t.lookup();
66+
else
4767
return null;
48-
return type().get().getTypeFunc();
4968
}
5069

5170
@Override
5271
public PartialStatement constructorName() {
53-
RegisteredType target = type().get();
72+
RegisteredType target = lookup();
5473
return target == null ? null : target.constructorName();
5574
}
5675

5776
@Override
5877
public PartialStatement destructorName() {
59-
RegisteredType target = type().get();
78+
RegisteredType target = lookup();
6079
return target == null ? null : target.destructorName();
6180
}
6281

@@ -70,8 +89,8 @@ public Alias mergeWith(RegisteredType rt) {
7089
return this;
7190
}
7291

73-
public Type type() {
74-
return findAny(children(), Type.class);
92+
public AnyType anyType() {
93+
return findAny(children(), AnyType.class);
7594
}
7695

7796
@Override

‎generator/src/main/java/io/github/jwharm/javagi/gir/Array.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ public String sizeExpression(boolean upcall) {
8383
if (lp.anyType() instanceof Type type) {
8484
if (type.isPointer() || lp.isOutParameter())
8585
return name + ".get().intValue()";
86-
if (type.get() instanceof Alias a && a.type().isPrimitive())
86+
if (type.lookup() instanceof Alias a && a.isValueWrapper())
8787
return name + ".getValue()";
8888
}
8989
return name;

‎generator/src/main/java/io/github/jwharm/javagi/gir/Boxed.java

+12-1
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,14 @@
2323
import io.github.jwharm.javagi.util.PartialStatement;
2424

2525
import static io.github.jwharm.javagi.util.CollectionUtils.*;
26+
import static java.util.Collections.emptyList;
2627

2728
import java.lang.foreign.MemorySegment;
2829
import java.util.List;
2930
import java.util.Map;
3031
import java.util.Objects;
3132

32-
public final class Boxed extends Multiplatform implements StandardLayoutType {
33+
public final class Boxed extends Multiplatform implements StandardLayoutType, FieldContainer {
3334

3435
public Boxed(Map<String, String> attributes,
3536
List<Node> children,
@@ -66,6 +67,16 @@ tag, typeName(),
6667
"memorySegment", MemorySegment.class);
6768
}
6869

70+
@Override
71+
public List<Field> fields() {
72+
return emptyList();
73+
}
74+
75+
@Override
76+
public boolean opaque() {
77+
return true;
78+
}
79+
6980
@Override
7081
public Callable copyFunction() {
7182
// copy-function specified in annotation

‎generator/src/main/java/io/github/jwharm/javagi/gir/Class.java

+7-7
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public boolean generic() {
7878
return true;
7979

8080
for (var impl : implements_())
81-
if (impl.get().generic())
81+
if (impl.lookup().generic())
8282
return true;
8383

8484
return false;
@@ -88,12 +88,8 @@ public boolean autoCloseable() {
8888
return attrBool("java-gi-auto-closeable", false);
8989
}
9090

91-
public boolean isOpaque() {
92-
return fields().isEmpty() && unions().isEmpty();
93-
}
94-
9591
public Class parentClass() {
96-
return (Class) TypeReference.get(namespace(), attr("parent"));
92+
return (Class) TypeReference.lookup(namespace(), attr("parent"));
9793
}
9894

9995
public boolean isInstanceOf(String ns, String name) {
@@ -107,7 +103,11 @@ public boolean isInstanceOf(String ns, String name) {
107103

108104
public Record typeStruct() {
109105
String typeStruct = attr("glib:type-struct");
110-
return (Record) TypeReference.get(namespace(), typeStruct);
106+
return (Record) TypeReference.lookup(namespace(), typeStruct);
107+
}
108+
109+
public boolean opaque() {
110+
return fields().isEmpty() && records().isEmpty() && unions().isEmpty();
111111
}
112112

113113
public Method refFunc() {

‎generator/src/main/java/io/github/jwharm/javagi/gir/Field.java

+8-5
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public boolean allocatesMemory() {
4343
return TypedValue.super.allocatesMemory()
4444
|| (callback() != null)
4545
|| (anyType() instanceof Type type
46-
&& type.get() instanceof Callback);
46+
&& type.lookup() instanceof Callback);
4747
}
4848

4949
/**
@@ -69,8 +69,11 @@ private int getSize(Array array, boolean longAsInt) {
6969
}
7070

7171
private int getSize(Type type, boolean longAsInt) {
72-
if (type.get() instanceof Alias alias)
73-
return getSize(alias.type(), longAsInt);
72+
if (type.lookup() instanceof Alias alias)
73+
return switch (alias.anyType()) {
74+
case Array a -> getSize(a, longAsInt);
75+
case Type t -> getSize(t, longAsInt);
76+
};
7477

7578
var typeName = type.typeName();
7679
if (List.of(BYTE, CHAR).contains(typeName))
@@ -79,7 +82,7 @@ private int getSize(Type type, boolean longAsInt) {
7982
return 2;
8083
if (List.of(BOOLEAN, INT, FLOAT).contains(typeName))
8184
return 4;
82-
if (type.get() instanceof FlaggedType)
85+
if (type.lookup() instanceof FlaggedType)
8386
return 4;
8487
if (type.isLong() && longAsInt)
8588
return 4;
@@ -93,7 +96,7 @@ private int getSize(Type type, boolean longAsInt) {
9396
public boolean isDisguised() {
9497
// No getter/setter for a "disguised" record or private data
9598
if (anyType() instanceof Type type
96-
&& type.get() instanceof Record r
99+
&& type.lookup() instanceof Record r
97100
&& (r.disguised() || r.skipJava()))
98101
return true;
99102

‎generator/src/main/java/io/github/jwharm/javagi/gir/FieldContainer.java

+7-3
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,14 @@
2323

2424
public sealed interface FieldContainer
2525
extends RegisteredType
26-
permits Class, Interface, Record, Union {
26+
permits Class, Interface, Boxed, Record, Union {
2727

2828
List<Field> fields();
2929

30+
default boolean opaque() {
31+
return false;
32+
}
33+
3034
default Field getAtIndex(int index) {
3135
return index == -1 ? null : fields().get(index);
3236
}
@@ -42,8 +46,8 @@ default boolean hasOpaqueStructFields() {
4246
for (Field field : fields())
4347
if (field.anyType() instanceof Type type
4448
&& !type.isPointer()
45-
&& type.get() instanceof Class cls
46-
&& (cls.isOpaque() || cls.hasOpaqueStructFields()))
49+
&& type.lookup() instanceof FieldContainer fc
50+
&& (fc.opaque() || fc.hasOpaqueStructFields()))
4751
return true;
4852
return false;
4953
}

‎generator/src/main/java/io/github/jwharm/javagi/gir/Interface.java

+4-7
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,6 @@
1919

2020
package io.github.jwharm.javagi.gir;
2121

22-
import com.squareup.javapoet.ParameterizedTypeName;
23-
import com.squareup.javapoet.TypeName;
24-
import io.github.jwharm.javagi.configuration.ClassNames;
2522
import io.github.jwharm.javagi.util.PartialStatement;
2623

2724
import com.squareup.javapoet.ClassName;
@@ -82,7 +79,7 @@ public boolean generic() {
8279
return true;
8380

8481
for (var prereq : prerequisites())
85-
if (prereq.get() instanceof Interface i && i.generic())
82+
if (prereq.lookup() instanceof Interface i && i.generic())
8683
return true;
8784

8885
return false;
@@ -100,14 +97,14 @@ public ClassName helperClass() {
10097
*/
10198
public Class prerequisiteBaseClass() {
10299
for (var prerequisite : prerequisites())
103-
if (prerequisite.get() instanceof Class c)
100+
if (prerequisite.lookup() instanceof Class c)
104101
return c;
105-
return (Class) TypeReference.get(namespace(), "GObject.Object");
102+
return (Class) TypeReference.lookup(namespace(), "GObject.Object");
106103
}
107104

108105
public Record typeStruct() {
109106
String typeStruct = attr("glib:type-struct");
110-
return (Record) TypeReference.get(namespace(), typeStruct);
107+
return (Record) TypeReference.lookup(namespace(), typeStruct);
111108
}
112109

113110
public boolean hasProperties() {

‎generator/src/main/java/io/github/jwharm/javagi/gir/Parameter.java

+6-6
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ public boolean isOutParameter() {
4747
|| (type.cType()) != null
4848
&& type.cType().endsWith("gsize")))
4949
&& (!type.isProxy())
50-
&& (!(type.get() instanceof Alias a
51-
&& a.type().isPrimitive())));
50+
&& (!(type.lookup() instanceof Alias a
51+
&& a.isValueWrapper())));
5252
}
5353

5454
public boolean isUserDataParameter() {
@@ -62,7 +62,7 @@ public boolean isUserDataParameter() {
6262
&& List.of("gpointer", "gconstpointer").contains(t.cType())) {
6363
return parent().parameters().stream().anyMatch(p ->
6464
p.anyType() instanceof Type type
65-
&& type.get() instanceof Callback
65+
&& type.lookup() instanceof Callback
6666
&& p.closure() == this);
6767
}
6868
return false;
@@ -71,7 +71,7 @@ public boolean isUserDataParameter() {
7171
public boolean isUserDataParameterForDestroyNotify() {
7272
return parent().parameters().stream().anyMatch(p ->
7373
p.anyType() instanceof Type type
74-
&& type.get() instanceof Callback
74+
&& type.lookup() instanceof Callback
7575
&& p.scope() == Scope.NOTIFIED
7676
&& p.closure() == this
7777
&& p.destroy() != null);
@@ -111,13 +111,13 @@ public boolean allocatesMemory() {
111111
return true;
112112

113113
Type type = (Type) anyType();
114-
RegisteredType target = type.get();
114+
RegisteredType target = type.lookup();
115115

116116
if (target instanceof Callback)
117117
return true;
118118

119119
return type.isPointer()
120-
&& target instanceof Alias a && a.type().isPrimitive();
120+
&& target instanceof Alias a && a.isValueWrapper();
121121
}
122122

123123
public boolean isLastParameter() {

‎generator/src/main/java/io/github/jwharm/javagi/gir/Record.java

+5-7
Original file line numberDiff line numberDiff line change
@@ -105,16 +105,15 @@ public boolean generic() {
105105
return attrBool("java-gi-generic", false);
106106
}
107107

108-
public boolean isOpaque() {
109-
return fields().isEmpty() && unions().isEmpty();
110-
}
111-
112108
public boolean disguised() {
113109
return attrBool("disguised", false);
114110
}
115111

116112
public boolean opaque() {
117-
return attrBool("opaque", false);
113+
if (attributes().containsKey("opaque"))
114+
return attrBool("opaque", false);
115+
else
116+
return fields().isEmpty() && unions().isEmpty();
118117
}
119118

120119
public boolean pointer() {
@@ -126,8 +125,7 @@ public boolean foreign() {
126125
}
127126

128127
public RegisteredType isGTypeStructFor() {
129-
Namespace ns = namespace();
130-
return TypeReference.get(ns, attr("glib:is-gtype-struct-for"));
128+
return TypeReference.lookup(namespace(), attr("glib:is-gtype-struct-for"));
131129
}
132130

133131
@Override

‎generator/src/main/java/io/github/jwharm/javagi/gir/RegisteredType.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ default boolean checkIsGObject() {
6969
case Class c -> c.isInstanceOf("GObject", "Object");
7070
case Interface _ -> true; // Requires a runtime instanceof check
7171
case Alias a -> {
72-
var target = a.type().get();
72+
var target = a.lookup();
7373
yield target != null && target.checkIsGObject();
7474
}
7575
default -> false;

‎generator/src/main/java/io/github/jwharm/javagi/gir/ReturnValue.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public boolean allocatesMemory() {
4242
return switch(anyType()) {
4343
case Array _ -> true;
4444
case Type type -> List.of(Scope.CALL, Scope.ASYNC).contains(scope())
45-
&& type.get() instanceof Callback;
45+
&& type.lookup() instanceof Callback;
4646
};
4747
}
4848

‎generator/src/main/java/io/github/jwharm/javagi/gir/Type.java

+7-7
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public TypeName typeName() {
7777
}
7878

7979
private TypeName genericTypeName() {
80-
var target = get();
80+
var target = lookup();
8181
var rawType = toJavaQualifiedType(target.name(), target.namespace());
8282
var elements = new TypeName[anyTypes().size()];
8383
int i = 0;
@@ -133,25 +133,25 @@ public boolean isProxy() {
133133
if (cType() != null && cType().endsWith("**"))
134134
return false;
135135

136-
return switch(get()) {
137-
case Alias a -> a.type().isProxy();
136+
return switch(lookup()) {
137+
case Alias a -> a.isProxy();
138138
case Class _, Interface _, Record _, Union _ -> true;
139139
case null, default -> false;
140140
};
141141
}
142142

143143
public boolean checkIsGObject() {
144-
RegisteredType target = get();
144+
RegisteredType target = lookup();
145145
return target != null && target.checkIsGObject();
146146
}
147147

148148
public boolean checkIsGList() {
149-
RegisteredType target = get();
149+
RegisteredType target = lookup();
150150
return target != null && target.checkIsGList();
151151
}
152152

153153
public boolean checkIsGHashTable() {
154-
RegisteredType target = get();
154+
RegisteredType target = lookup();
155155
return target != null && target.checkIsGHashTable();
156156
}
157157

@@ -183,7 +183,7 @@ public String toTypeTag() {
183183
if ("String".equals(javaBaseType))
184184
return "string";
185185

186-
RegisteredType target = get();
186+
RegisteredType target = lookup();
187187
return target == null ? null : target.typeTag();
188188
}
189189

‎generator/src/main/java/io/github/jwharm/javagi/gir/TypeReference.java

+10-17
Original file line numberDiff line numberDiff line change
@@ -27,34 +27,37 @@
2727

2828
/**
2929
* This interface is implemented by GIR elements whose name refers to a
30-
* RegisteredType. The {@link #get()} method will retrieve the type from the
30+
* RegisteredType. The {@link #lookup()} method will retrieve the type from the
3131
* GIR model.
3232
*/
3333
public interface TypeReference {
3434
Namespace namespace();
3535
String name();
3636

37-
default RegisteredType get() {
38-
String name = name();
39-
if (name == null)
37+
static RegisteredType lookup(Namespace namespace, String name) {
38+
if (name == null || namespace == null)
4039
return null;
4140

4241
int dot = name.indexOf('.');
4342
if (dot == -1)
44-
return namespace().registeredTypes().get(name);
43+
return namespace.registeredTypes().get(name);
4544

46-
return namespace().parent()
45+
return namespace.parent()
4746
.lookupNamespace(name.substring(0, dot))
4847
.registeredTypes().get(name.substring(dot + 1));
4948
}
5049

50+
default RegisteredType lookup() {
51+
return lookup(namespace(), name());
52+
}
53+
5154
default TypeName typeName() {
5255
// Type without name: Unknown, fallback to MemorySegment
5356
if (name() == null)
5457
return TypeName.get(MemorySegment.class);
5558

5659
// Get the target type
57-
var type = get();
60+
var type = lookup();
5861

5962
// A TypeClass or TypeInterface is an inner class
6063
if (type instanceof Record rec) {
@@ -76,14 +79,4 @@ default TypeName typeName() {
7679
default String javaType() {
7780
return typeName().toString();
7881
}
79-
80-
static RegisteredType get(Namespace namespace, String name) {
81-
if (namespace == null || name == null)
82-
return null;
83-
84-
return new TypeReference() {
85-
public Namespace namespace() { return namespace; }
86-
public String name() { return name; }
87-
}.get();
88-
}
8982
}

‎generator/src/main/java/io/github/jwharm/javagi/gir/TypedValue.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,9 @@ default boolean allocatesMemory() {
4949

5050
default boolean isBitfield() {
5151
if (anyType() instanceof Type type && (!type.isPrimitive())) {
52-
RegisteredType target = type.get();
52+
RegisteredType target = type.lookup();
5353
if (target instanceof Alias alias)
54-
target = alias.type().get();
54+
target = alias.lookup();
5555
return target instanceof Bitfield;
5656
}
5757
return false;

‎generator/src/main/java/io/github/jwharm/javagi/gir/Union.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public Union mergeWith(RegisteredType rt) {
5050
return this;
5151
}
5252

53-
public boolean isOpaque() {
53+
public boolean opaque() {
5454
return fields().isEmpty() && records().isEmpty();
5555
}
5656

‎generator/src/main/java/io/github/jwharm/javagi/patches/BasePatch.java

+7
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,13 @@ && isInstanceParameter(func.parameters().parameters().getFirst(),
110110
return m.withAttribute("shadows", null);
111111
}
112112

113+
/*
114+
* Remove aliases for void. They are unusable for language bindings.
115+
*/
116+
if (element instanceof Alias a
117+
&& a.anyType() instanceof Type t && t.isVoid())
118+
return element.withAttribute("java-gi-skip", "1");
119+
113120
return element;
114121
}
115122

‎generator/src/main/java/io/github/jwharm/javagi/patches/GLibPatch.java

+20-5
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,11 @@
2424
import io.github.jwharm.javagi.util.Patch;
2525
import io.github.jwharm.javagi.util.Platform;
2626

27-
import java.util.Collections;
2827
import java.util.List;
2928
import java.util.Map;
3029

30+
import static java.util.Collections.emptyList;
31+
3132
public class GLibPatch implements Patch {
3233

3334
@Override
@@ -43,9 +44,8 @@ public GirElement patch(GirElement element, String namespace) {
4344
*/
4445
var gtype = new Alias(
4546
Map.of("name", "Type", "c:type", "GType"),
46-
List.of(new Type(Map.of("name", "gsize",
47-
"c:type", "gsize"),
48-
Collections.emptyList())),
47+
List.of(new Type(Map.of("name", "gsize", "c:type", "gsize"),
48+
emptyList())),
4949
ns.platforms());
5050
ns = add(ns, gtype);
5151

@@ -108,14 +108,29 @@ public GirElement patch(GirElement element, String namespace) {
108108
Parameter replacement = current.withChildren(
109109
current.infoElements().doc(),
110110
new Type(Map.of("name", "gpointer", "c:type", "gpointer"),
111-
Collections.emptyList()));
111+
emptyList()));
112112
return f.withChildren(
113113
f.infoElements().doc(),
114114
f.infoElements().sourcePosition(),
115115
f.returnValue(),
116116
f.parameters().withChildren(replacement));
117117
}
118118

119+
/*
120+
* GLib.Strv is an alias for a String[], but it is defined as a type
121+
* with name="utf8" (though the c-type is "gchar**"). We change it to an
122+
* array.
123+
*/
124+
if (element instanceof Alias a && "Strv".equals(a.name())) {
125+
return new Alias(a.attributes(),
126+
List.of(a.infoElements().doc(),
127+
a.infoElements().sourcePosition(),
128+
new Array(Map.of("zero-terminated", "1"),
129+
List.of(new Type(Map.of("name", "utf8"),
130+
emptyList())))),
131+
a.platforms());
132+
}
133+
119134
/*
120135
* GLib.HashTable, GLib.List and GLib.SList are not generated from the
121136
* gir data. Java-GI provides custom HashTable, List and SList classes

‎generator/src/main/java/io/github/jwharm/javagi/patches/HarfBuzzPatch.java

+9-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,15 @@ public GirElement patch(GirElement element, String namespace) {
7373
/*
7474
* This constant has type "language_t" which cannot be instantiated.
7575
*/
76-
return remove(ns, Constant.class, "name", "LANGUAGE_INVALID");
76+
ns = remove(ns, Constant.class, "name", "LANGUAGE_INVALID");
77+
78+
/*
79+
* This function returns an array of "ot_name_entry_t" entries. The
80+
* size of "ot_name_entry_t" is unknown because one of its fields
81+
* has a disguised type ("language_t"), therefore the array contents
82+
* cannot be read. Remove the function.
83+
*/
84+
return remove(ns, Function.class, "name", "ot_name_list_names");
7785
}
7886

7987
/*

‎generator/src/main/java/io/github/jwharm/javagi/util/Conversions.java

+12-11
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public static String toJavaIdentifier(String typeName) {
4747
public static ClassName toJavaQualifiedType(String typeName, Namespace ns) {
4848
if (typeName == null) return null;
4949
if (typeName.contains(".")) {
50-
var rt = TypeReference.get(ns, typeName);
50+
var rt = TypeReference.lookup(ns, typeName);
5151
return toJavaQualifiedType(rt.name(), rt.namespace());
5252
}
5353
return ClassName.get(
@@ -60,7 +60,7 @@ public static ClassName toJavaQualifiedType(String typeName, Namespace ns) {
6060
*/
6161
public static String toJavaSimpleType(String typeName, Namespace ns) {
6262
if (typeName.contains(".")) {
63-
var rt = TypeReference.get(ns, typeName);
63+
var rt = TypeReference.lookup(ns, typeName);
6464
return toJavaSimpleType(rt.name(), rt.namespace());
6565
}
6666
return prefixDigits(replaceKnownType(
@@ -258,13 +258,13 @@ public static TypeName getCarrierTypeName(AnyType t, boolean longAsInt) {
258258
if (type.isPrimitive() || "none".equals(t.cType()))
259259
return type.typeName();
260260

261-
RegisteredType target = type.get();
261+
RegisteredType target = type.lookup();
262262

263263
if (target instanceof FlaggedType)
264264
return TypeName.INT;
265265

266-
if (target instanceof Alias a && a.type().isPrimitive())
267-
return a.type().typeName();
266+
if (target instanceof Alias a && a.isValueWrapper())
267+
return a.anyType().typeName();
268268

269269
return TypeName.get(MemorySegment.class);
270270
}
@@ -287,13 +287,13 @@ public static String getCarrierTypeTag(AnyType t) {
287287
if (type.isPrimitive() || "none".equals(t.cType()))
288288
return toJavaBaseType(type.name());
289289

290-
RegisteredType target = type.get();
290+
RegisteredType target = type.lookup();
291291

292292
if (target instanceof FlaggedType)
293293
return "int";
294294

295-
if (target instanceof Alias a && a.type().isPrimitive())
296-
return getCarrierTypeTag(a.type());
295+
if (target instanceof Alias a && a.isValueWrapper())
296+
return getCarrierTypeTag(a.anyType());
297297

298298
return "memorySegment";
299299
}
@@ -319,7 +319,7 @@ public static String getValueLayoutPlain(Type t, boolean longAsInt) {
319319
if (t == null) {
320320
return "ADDRESS";
321321
}
322-
RegisteredType target = t.get();
322+
RegisteredType target = t.lookup();
323323
if (target instanceof FlaggedType || t.isBoolean()) {
324324
return "JAVA_INT";
325325
}
@@ -329,8 +329,9 @@ public static String getValueLayoutPlain(Type t, boolean longAsInt) {
329329
if (t.isPrimitive()) {
330330
return "JAVA_" + t.javaType().toUpperCase();
331331
}
332-
if (target instanceof Alias a && a.type().isPrimitive()) {
333-
return getValueLayoutPlain(a.type(), longAsInt);
332+
if (target instanceof Alias a && a.isValueWrapper()
333+
&& a.anyType() instanceof Type type) {
334+
return getValueLayoutPlain(type, longAsInt);
334335
}
335336
return "ADDRESS";
336337
}

‎generator/src/main/java/io/github/jwharm/javagi/util/Javadoc.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ private String convertConstantref(String ref) {
328328
// Replace #text with {@link text}
329329
private String convertTyperef(String ref) {
330330
String type = ref.substring(1);
331-
RegisteredType rt = TypeReference.get(doc.namespace(), type);
331+
RegisteredType rt = TypeReference.lookup(doc.namespace(), type);
332332
if (rt == null) {
333333
return "{@code " + ref.substring(1) + "}";
334334
} else {
@@ -539,7 +539,7 @@ private String checkLink(String ns, String identifier) {
539539
* "{@link" tag, otherwise, generate a "{@code" tag.
540540
*/
541541
private String checkLink(String ns, String type, String identifier) {
542-
RegisteredType rt = TypeReference.get(getNamespace(ns), type);
542+
RegisteredType rt = TypeReference.lookup(getNamespace(ns), type);
543543

544544
if (rt == null)
545545
return "{@code ";

‎modules/glib/src/main/java/io/github/jwharm/javagi/interop/Interop.java

+30
Original file line numberDiff line numberDiff line change
@@ -1107,6 +1107,36 @@ public static MemorySegment allocateNativeArray(String[] strings,
11071107
return memorySegment;
11081108
}
11091109

1110+
/**
1111+
* Allocate and initialize an (optionally {@code NULL}-terminated) array of
1112+
* arrays of strings (a Strv-array).
1113+
*
1114+
* @param strvs the array of String arrays
1115+
* @param zeroTerminated whether to add a {@code NULL} to the array. The
1116+
* embedded arrays are always
1117+
* {@code NULL}-terminated.
1118+
* @param arena the segment allocator for memory allocation
1119+
* @return the memory segment of the native array
1120+
*/
1121+
public static MemorySegment allocateNativeArray(String[][] strvs,
1122+
boolean zeroTerminated,
1123+
Arena arena) {
1124+
1125+
int length = zeroTerminated ? strvs.length + 1 : strvs.length;
1126+
var memorySegment = arena.allocate(ValueLayout.ADDRESS, length);
1127+
1128+
for (int i = 0; i < strvs.length; i++) {
1129+
var s = strvs[i] == null ? NULL
1130+
: allocateNativeArray(strvs[i], true, arena);
1131+
memorySegment.setAtIndex(ValueLayout.ADDRESS, i, s);
1132+
}
1133+
1134+
if (zeroTerminated)
1135+
memorySegment.setAtIndex(ValueLayout.ADDRESS, strvs.length, NULL);
1136+
1137+
return memorySegment;
1138+
}
1139+
11101140
/**
11111141
* Convert a boolean[] array into an int[] array, and calls
11121142
* {@link #allocateNativeArray(int[], boolean, Arena)}.

0 commit comments

Comments
 (0)
Please sign in to comment.