Skip to content

Commit

Permalink
Marshal native char*** to Java String[][]
Browse files Browse the repository at this point in the history
  • Loading branch information
jwharm committed May 15, 2024
1 parent 7d69ab4 commit f2acb5b
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,14 @@ boolean checkNull() {
|| target instanceof FlaggedType));
}

String doFree() {
return switch(v) {
case Parameter p when p.transferOwnership() == TransferOwnership.FULL -> "true";
case ReturnValue rv when rv.transferOwnership() == TransferOwnership.FULL -> "true";
default -> "false";
};
}

TypeName getType() {
return getType(true);
}
Expand Down Expand Up @@ -261,8 +269,12 @@ PartialStatement marshalNativeToJava(String identifier, boolean upcall) {
return marshalNativeToJava(type, identifier, upcall);
}

if (array != null && array.anyType() instanceof Array)
return PartialStatement.of("null /* unsupported */");
if (array != null && array.anyType() instanceof Array inner
&& inner.anyType() instanceof Type t
&& t.isString())
return PartialStatement.of(
"$interop:T.getStrvArrayFrom(" + identifier + ", " + doFree() + ")",
"interop", ClassNames.INTEROP);

if (array != null && array.anyType() instanceof Type arrayType)
return marshalNativeToJavaArray(
Expand All @@ -277,12 +289,7 @@ PartialStatement marshalNativeToJava(String identifier, boolean upcall) {
PartialStatement marshalNativeToJava(Type type,
String identifier,
boolean upcall) {
String free = switch(v) {
case Parameter p when p.transferOwnership() == TransferOwnership.FULL -> "true";
case ReturnValue rv when rv.transferOwnership() == TransferOwnership.FULL -> "true";
default -> "false";
};

String free = doFree();
String targetTypeTag = target == null ? null : type.toTypeTag();

boolean isTypeClass = target instanceof Record
Expand Down Expand Up @@ -376,12 +383,7 @@ PartialStatement marshalNativeToJava(Type type,
private PartialStatement marshalNativeToJavaArray(Type type,
String size,
String identifier) {
String free = switch(v) {
case Parameter p when p.transferOwnership() == TransferOwnership.FULL -> "true";
case ReturnValue rv when rv.transferOwnership() == TransferOwnership.FULL -> "true";
default -> "false";
};

String free = doFree();
RegisteredType target = type.get();
String targetTypeTag = target != null ? type.toTypeTag() : null;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package io.github.jwharm.javagi.test.gio;

import org.gnome.gio.DesktopAppInfo;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

/**
* Get a String[][] array from a Gio function, and check that it contains
* usable data.
*/
public class StrvArrayTest {

@Test
public void testStrvArrayToJava() {
String[][] array = DesktopAppInfo.search("gnome");
assertNotNull(array);
String result = "";
for (String[] inner : array) {
assertNotNull(inner);
for (String str : inner) {
if (str.contains("org.gnome"))
result = str;
}
}
assertNotEquals("", result);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,16 @@

import io.github.jwharm.javagi.base.*;
import org.gnome.glib.Type;
import org.jetbrains.annotations.Nullable;

/**
* The Interop class contains functionality for interoperability with native
* code.
*/
public class Interop {

private final static int UNBOUNDED = Integer.MAX_VALUE;
private final static int INT_UNBOUNDED = Integer.MAX_VALUE;
private final static long LONG_UNBOUNDED = Long.MAX_VALUE;
private final static Linker LINKER = Linker.nativeLinker();
public static SymbolLookup symbolLookup = SymbolLookup.loaderLookup()
.or(Linker.nativeLinker().defaultLookup());
Expand Down Expand Up @@ -240,7 +242,7 @@ public static String getStringFrom(MemorySegment address, boolean free) {
return null;

try {
return address.reinterpret(Long.MAX_VALUE).getString(0);
return address.reinterpret(LONG_UNBOUNDED).getString(0);
} finally {
if (free)
GLib.free(address);
Expand Down Expand Up @@ -275,7 +277,7 @@ public static String[] getStringArrayFrom(MemorySegment address,
}

if (free)
GLib.free(array);
GLib.strfreev(array);

return result;
}
Expand All @@ -294,26 +296,60 @@ public static String[] getStringArrayFrom(MemorySegment address,
return null;

MemorySegment array = address;
if (array.byteSize() == 0) {
array = address.reinterpret(Long.MAX_VALUE);
}
if (array.byteSize() == 0)
array = address.reinterpret(LONG_UNBOUNDED);

ArrayList<String> result = new ArrayList<>();
long offset = 0;
while (true) {
MemorySegment ptr = array.get(ValueLayout.ADDRESS, offset);
MemorySegment ptr = array.get(ValueLayout.ADDRESS, offset)
.reinterpret(LONG_UNBOUNDED);
if (MemorySegment.NULL.equals(ptr))
break;
result.add(ptr.getString(0));
offset += ValueLayout.ADDRESS.byteSize();
}

if (free)
GLib.free(address);
GLib.strfreev(array);

return result.toArray(new String[0]);
}

/**
* Read {@code NULL}-terminated arrays of Strings from a
* {@code NULL}-terminated array in native memory.
*
* @param address address of the memory segment
* @param free if the strings and the array must be freed
* @return two-dimensional array of Strings
*/
public static String[][] getStrvArrayFrom(MemorySegment address,
boolean free) {
if (address == null || MemorySegment.NULL.equals(address))
return null;

MemorySegment array = address;
if (array.byteSize() == 0)
array = address.reinterpret(LONG_UNBOUNDED);

ArrayList<String[]> result = new ArrayList<>();

long offset = 0;
while (true) {
MemorySegment ptr = array.get(ValueLayout.ADDRESS, offset);
if (MemorySegment.NULL.equals(ptr))
break;
result.add(getStringArrayFrom(ptr, free));
offset += ValueLayout.ADDRESS.byteSize();
}

if (free)
GLib.free(address);

return result.toArray(new String[0][0]);
}

/**
* Read an array of pointers with the requested length from native memory.
*
Expand Down Expand Up @@ -363,7 +399,7 @@ public static MemorySegment[] getAddressArrayFrom(MemorySegment address,

MemorySegment array = address;
if (array.byteSize() == 0)
array = address.reinterpret(Long.MAX_VALUE);
array = address.reinterpret(LONG_UNBOUNDED);

ArrayList<MemorySegment> result = new ArrayList<>();
long offset = 0;
Expand Down Expand Up @@ -441,7 +477,7 @@ public static byte[] getByteArrayFrom(MemorySegment address,
Arena arena,
boolean free) {
// Find the null byte
MemorySegment array = address.reinterpret(Long.MAX_VALUE, arena, null);
MemorySegment array = address.reinterpret(LONG_UNBOUNDED, arena, null);
long idx = 0;
while (array.get(ValueLayout.JAVA_BYTE, idx) != 0) {
idx++;
Expand Down Expand Up @@ -555,7 +591,7 @@ public static int[] getIntegerArrayFrom(MemorySegment address,
boolean free) {

// Find the null byte
MemorySegment array = address.reinterpret(UNBOUNDED, arena, null);
MemorySegment array = address.reinterpret(INT_UNBOUNDED, arena, null);
long idx = 0;
while (array.get(ValueLayout.JAVA_INT, idx) != 0) {
idx++;
Expand Down Expand Up @@ -631,7 +667,7 @@ public static short[] getShortArrayFrom(MemorySegment address,

MemorySegment array = address;
if (array.byteSize() == 0)
array = address.reinterpret(Long.MAX_VALUE);
array = address.reinterpret(LONG_UNBOUNDED);

long offset = 0;
while (!MemorySegment.NULL.equals(
Expand Down

0 comments on commit f2acb5b

Please sign in to comment.