Skip to content

Instantly share code, notes, and snippets.

@mcimadamore
Created May 7, 2020 15:47
Show Gist options
  • Save mcimadamore/5f002d8177e05780f068dd7874a5020b to your computer and use it in GitHub Desktop.
Save mcimadamore/5f002d8177e05780f068dd7874a5020b to your computer and use it in GitHub Desktop.
A simple jextract API plugin to generate all struct layouts in a given header file.
import jdk.incubator.foreign.GroupLayout;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.SequenceLayout;
import jdk.incubator.foreign.ValueLayout;
import jdk.incubator.jextract.Declaration;
import jdk.incubator.jextract.JextractTask;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
/**
* Simple jextract API plugin to generate all struct layouts in a given header file. For simplicity,
* all layouts are considered to be little endian. Use like this:
* <blockquote><pre>{@code
* $ <PANAMA_JDK>/bin/java --add-modules jdk.incubator.jextract src/JStruct.java <header-file>
* }</pre></blockquote>
*/
public class JStruct {
public static void main(String[] args) {
if (args.length == 0) {
System.err.println("Expected header file");
System.exit(2);
}
Path header = Paths.get(args[0]);
if (!Files.isReadable(header)) {
System.err.println("Cannot read header file: " + args[0]);
System.exit(2);
}
var task = JextractTask.newTask(false, header);
Path builtinInc = Paths.get(System.getProperty("java.home"), "conf", "jextract");
var decl = task.parse("-I" + builtinInc);
decl.accept(new StructPrinter(), null);
}
static class StructPrinter implements Declaration.Visitor<Void, Void> {
static String ALIGN_STR = " ";
int align = 0;
void indent() {
print(ALIGN_STR.substring(0, align));
}
void incrAlign() {
align += 4;
}
void decrAlign() {
align -= 4;
}
void print(String s) {
System.out.print(s);
}
@Override
public Void visitScoped(Declaration.Scoped d, Void aVoid) {
if (d.layout().isPresent()) {
printLayout(d.name(), d.layout().get());
} else {
d.members().stream()
.filter(m -> m instanceof Declaration.Scoped)
.forEach(m -> m.accept(this, null));
}
return null;
}
protected void printLayout(String elementName, MemoryLayout layout) {
print("MemoryLayout " + elementName + "$LAYOUT = ");
printLayout(layout);
print(";\n\n");
}
private void printLayout(MemoryLayout l) {
if (l instanceof ValueLayout) {
String valueLayout = switch ((int)l.byteSize()) {
case 1 -> "MemoryLayouts.BITS_8_LE";
case 2 -> "MemoryLayouts.BITS_16_LE";
case 4 -> "MemoryLayouts.BITS_32_LE";
case 8 -> "MemoryLayouts.BITS_64_LE";
default -> "MemoryLayout.ofValueBits(" + l.bitSize() + ", ByteOrder.LITTLE_ENDIAN)";
};
print(valueLayout);
} else if (l instanceof SequenceLayout) {
print("MemoryLayout.ofSequence(");
if (((SequenceLayout) l).elementCount().isPresent()) {
print(((SequenceLayout) l).elementCount().getAsLong() + ", ");
}
printLayout(((SequenceLayout) l).elementLayout());
print(")");
} else if (l instanceof GroupLayout) {
if (((GroupLayout) l).isStruct()) {
print("MemoryLayout.ofStruct(\n");
} else {
print("MemoryLayout.ofUnion(\n");
}
incrAlign();
String delim = "";
for (MemoryLayout e : ((GroupLayout) l).memberLayouts()) {
print(delim);
indent();
printLayout(e);
delim = ",\n";
}
print("\n");
decrAlign();
indent();
print(")");
} else {
//padding
print("MemoryLayout.ofPaddingBits(" + l.bitSize() + ")");
}
if (l.name().isPresent()) {
print(".withName(\"" + l.name().get() + "\")");
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment