Skip to content

Commit

Permalink
Cleanup: Moved generation of parameter pre- and post-processsing stat…
Browse files Browse the repository at this point in the history
…ements to Parameter class
  • Loading branch information
jwharm committed Nov 13, 2022
1 parent 892e2af commit d03d4ed
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 104 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -112,37 +112,54 @@ public void generateNamed(Writer writer, boolean isInterface) throws IOException
// prepares a GError memorysegment if necessary, calls the C API and throws the GErrorException
// (if necessary). The "real" constructor just calls super(private_method());
private String generateConstructorHelper(Writer writer) throws IOException {

// Method name
String methodName = "construct" + Conversions.toCamelCase(name, true);
writer.write(" \n");
writer.write(" private static Addressable " + methodName);

// Parameters
if (parameters != null) {
writer.write("(");
parameters.generateJavaParameters(writer, false);
writer.write(")");
} else {
writer.write("()");
}

// Exceptions
if (throws_ != null) {
writer.write(" throws GErrorException");
}
writer.write(" {\n");

// Currently unsupported constructor method: throw an exception
if (! isSafeToBind()) {
writer.write(" throw new UnsupportedOperationException(\"Operation not supported yet\");\n");
writer.write(" }\n");
return methodName;
}

// Generate checks for null parameters
generateNullParameterChecks(writer);

// Generate preprocessing statements for all parameters
if (parameters != null) {
parameters.generatePreprocessing(writer);
}

// Allocate GError pointer
if (throws_ != null) {
writer.write(" MemorySegment GERROR = Interop.getAllocator().allocate(ValueLayout.ADDRESS);\n");
}

// Generate the return type
writer.write(" Addressable RESULT;\n");

// The method call is wrapped in a try-catch block
writer.write(" try {\n");

// Invoke to the method handle
writer.write(" RESULT = (MemoryAddress) DowncallHandles." + cIdentifier + ".invokeExact");

// Marshall the parameters to the native types
if (parameters != null) {
writer.write("(");
parameters.generateCParameters(writer, throws_);
Expand All @@ -151,27 +168,24 @@ private String generateConstructorHelper(Writer writer) throws IOException {
writer.write("()");
}
writer.write(";\n");

// If something goes wrong in the invokeExact() call
writer.write(" } catch (Throwable ERR) {\n");
writer.write(" throw new AssertionError(\"Unexpected exception occured: \", ERR);\n");
writer.write(" }\n");
// If the parameter has attribute transfer-ownership="full", we don't need to unref it anymore.
if (parameters != null) {
for (Parameter p : parameters.parameterList) {
// Only for proxy objects where ownership is fully transferred away,
// unless it's an out parameter or a pointer
if (p.isProxy()
&& "full".equals(p.transferOwnership)
&& (! p.isOutParameter())
&& (p.type.cType == null || (! p.type.cType.endsWith("**")))) {
writer.write(" " + (p.isInstanceParameter() ? "this" : p.name) + ".yieldOwnership();\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);
}

writer.write(" return RESULT;\n");
writer.write(" }\n");
return methodName;
Expand Down
93 changes: 5 additions & 88 deletions generator/src/main/java/io/github/jwharm/javagi/model/Method.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,21 +60,6 @@ public void generateMethodHandle(Writer writer, boolean isInterface) throws IOEx
writer.write(" );\n");
}

protected void generateNullParameterChecks(Writer writer) throws IOException {
if (parameters != null) {
for (Parameter p : parameters.parameterList) {
// Don't null-check parameters that are hidden from the Java API, or primitive values
if (! (p.isInstanceParameter() || p.isErrorParameter() || p.isUserDataParameter() || p.isDestroyNotify() || p.varargs
|| (p.type != null && p.type.isPrimitive && (! p.type.isPointer())))) {
if (! p.nullable) {
writer.write(" java.util.Objects.requireNonNull(" + p.name
+ ", \"" + "Parameter '" + p.name + "' must not be null\");\n");
}
}
}
}
}

public void generate(Writer writer, boolean isInterface, boolean isStatic) throws IOException {
writer.write(" \n");

Expand Down Expand Up @@ -140,27 +125,16 @@ public void generate(Writer writer, boolean isInterface, boolean isStatic) throw
return;
}

// Generate checks for null parameters
generateNullParameterChecks(writer);
// Generate preprocessing statements for all parameters
if (parameters != null) {
parameters.generatePreprocessing(writer);
}

// Allocate GError pointer
if (throws_ != null) {
writer.write(" MemorySegment GERROR = Interop.getAllocator().allocate(ValueLayout.ADDRESS);\n");
}

// MemorySegment declarations for pointer parameters
if (parameters != null) {
for (Parameter p : parameters.parameterList) {
if (p.isOutParameter()) {
writer.write(" MemorySegment " + p.name + "POINTER = Interop.getAllocator().allocate(" + Conversions.getValueLayout(p.type) + ");\n");
} else if (p.type != null && p.type.isAliasForPrimitive() && p.type.isPointer()) {
String typeStr = p.type.girElementInstance.type.simpleJavaType;
typeStr = Conversions.primitiveClassName(typeStr);
writer.write(" Pointer" + typeStr + " " + p.name + "POINTER = new Pointer" + typeStr + "(" + p.name + ".getValue());\n");
}
}
}

// Variable declaration for return value
String panamaReturnType = Conversions.toPanamaJavaType(getReturnValue().type);
if (! (returnValue.type != null && returnValue.type.isVoid())) {
Expand Down Expand Up @@ -205,64 +179,7 @@ public void generate(Writer writer, boolean isInterface, boolean isStatic) throw

// Generate post-processing actions for parameters
if (parameters != null) {

// First the regular (non-array) out-parameters. These could include an out-parameter with
// the length of an array out-parameter, so we have to process these first.
for (Parameter p : parameters.parameterList) {
if (p.isOutParameter()) {
if (p.array == null) {
writer.write(" ");
if (p.checkNull()) {
writer.write("if (" + p.name + " != null) ");
}
writer.write(p.name + ".set(");
String identifier = p.name + "POINTER.get(" + Conversions.getValueLayout(p.type) + ", 0)";
if (p.type.isPrimitive && p.type.isPointer()) {
writer.write(identifier);
if (p.type.isBoolean()) writer.write(" != 0");
writer.write(");\n");
} else {
writer.write(p.getNewInstanceString(p.type, identifier, false) + ");\n");
}
}
} else if (p.type != null && p.type.isAliasForPrimitive() && p.type.isPointer()) {
writer.write(" " + p.name + ".setValue(" + p.name + "POINTER.get());\n");
}
}

// Secondly, process the array out parameters
for (Parameter p : parameters.parameterList) {
if (p.isOutParameter() && p.array != null) {
String len = p.array.size();
String valuelayout = Conversions.getValueLayout(p.array.type);
if (p.array.type.isPrimitive && (! p.array.type.isBoolean())) {
// Array of primitive values
writer.write(" " + p.name + ".set(");
writer.write("MemorySegment.ofAddress(" + p.name + "POINTER.get(ValueLayout.ADDRESS, 0), " + len + " * " + valuelayout + ".byteSize(), Interop.getScope()).toArray(" + valuelayout + "));\n");
} else {
// Array of proxy objects
writer.write(" " + p.array.type.qualifiedJavaType + "[] " + p.name + "ARRAY = new " + p.array.type.qualifiedJavaType + "[" + len + "];\n");
writer.write(" for (int I = 0; I < " + len + "; I++) {\n");
writer.write(" var OBJ = " + p.name + "POINTER.get(" + valuelayout + ", I);\n");
writer.write(" " + p.name + "ARRAY[I] = ");
writer.write(p.getNewInstanceString(p.array.type, "OBJ", false) + ";\n");
writer.write(" }\n");
writer.write(" " + p.name + ".set(" + p.name + "ARRAY);\n");
}
}
}

// If the parameter has attribute transfer-ownership="full", we don't need to unref it anymore.
for (Parameter p : parameters.parameterList) {
// Only for proxy objects where ownership is fully transferred away,
// unless it's an out parameter or a pointer
if (p.isProxy()
&& "full".equals(p.transferOwnership)
&& (! p.isOutParameter())
&& (p.type.cType == null || (! p.type.cType.endsWith("**")))) {
writer.write(" " + (p.isInstanceParameter() ? "this" : p.name) + ".yieldOwnership();\n");
}
}
parameters.generatePostprocessing(writer);
}

// If the return value is an array, try to convert it to a Java array
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package io.github.jwharm.javagi.model;

import java.io.IOException;
import java.io.Writer;

import io.github.jwharm.javagi.generator.Conversions;

public class Parameter extends Variable {
Expand Down Expand Up @@ -112,4 +115,89 @@ public boolean checkNull() {
&& (! (isInstanceParameter() || isErrorParameter() || isUserDataParameter() || isDestroyNotify()
|| (type != null && type.isPrimitive && (! type.isPointer()))));
}

/**
* Generate code to do pre-processing of the parameter before the function call. This will
* generate a null check for NotNull parameters, and generate pointer allocation logic for
* pointer parameters.
* @param writer The source code file writer
* @throws IOException Thrown when an error occurs while writing to the file
*/
public void generatePreprocessing(Writer writer) throws IOException {

// Generate null-check
// Don't null-check parameters that are hidden from the Java API, or primitive values
if (! (isInstanceParameter() || isErrorParameter() || isUserDataParameter() || isDestroyNotify() || varargs
|| (type != null && type.isPrimitive && (! type.isPointer())))) {
if (! nullable) {
writer.write(" java.util.Objects.requireNonNull(" + name
+ ", \"" + "Parameter '" + name + "' must not be null\");\n");
}
}

// Generate pointer allocation
if (isOutParameter()) {
writer.write(" MemorySegment " + name + "POINTER = Interop.getAllocator().allocate(" + Conversions.getValueLayout(type) + ");\n");
} else if (type != null && type.isAliasForPrimitive() && type.isPointer()) {
String typeStr = type.girElementInstance.type.simpleJavaType;
typeStr = Conversions.primitiveClassName(typeStr);
writer.write(" Pointer" + typeStr + " " + name + "POINTER = new Pointer" + typeStr + "(" + name + ".getValue());\n");
}
}

/**
* Generate code to do post-processing of the parameter after the function call.
* @param writer The source code file writer
* @throws IOException Thrown when an error occurs while writing to the file
*/
public void generatePostprocessing(Writer writer) throws IOException {
if (isOutParameter()) {
if (array == null) {
// First the regular (non-array) out-parameters. These could include an out-parameter with
// the length of an array out-parameter, so we have to process these first.
writer.write(" ");
if (checkNull()) {
writer.write("if (" + name + " != null) ");
}
writer.write(name + ".set(");
String identifier = name + "POINTER.get(" + Conversions.getValueLayout(type) + ", 0)";
if (type.isPrimitive && type.isPointer()) {
writer.write(identifier);
if (type.isBoolean()) writer.write(" != 0");
writer.write(");\n");
} else {
writer.write(getNewInstanceString(type, identifier, false) + ");\n");
}
} else {
// Secondly, process the array out parameters
String len = array.size();
String valuelayout = Conversions.getValueLayout(array.type);
if (array.type.isPrimitive && (! array.type.isBoolean())) {
// Array of primitive values
writer.write(" " + name + ".set(");
writer.write("MemorySegment.ofAddress(" + name + "POINTER.get(ValueLayout.ADDRESS, 0), " + len + " * " + valuelayout + ".byteSize(), Interop.getScope()).toArray(" + valuelayout + "));\n");
} else {
// Array of proxy objects
writer.write(" " + array.type.qualifiedJavaType + "[] " + name + "ARRAY = new " + array.type.qualifiedJavaType + "[" + len + "];\n");
writer.write(" for (int I = 0; I < " + len + "; I++) {\n");
writer.write(" var OBJ = " + name + "POINTER.get(" + valuelayout + ", I);\n");
writer.write(" " + name + "ARRAY[I] = ");
writer.write(getNewInstanceString(array.type, "OBJ", false) + ";\n");
writer.write(" }\n");
writer.write(" " + name + ".set(" + name + "ARRAY);\n");
}
}
} else if (type != null && type.isAliasForPrimitive() && type.isPointer()) {
writer.write(" " + name + ".setValue(" + name + "POINTER.get());\n");
}

// If the parameter has attribute transfer-ownership="full", we don't need to unref it anymore.
// Only for proxy objects where ownership is fully transferred away, unless it's an out parameter or a pointer.
if (isProxy()
&& "full".equals(transferOwnership)
&& (! isOutParameter())
&& (type.cType == null || (! type.cType.endsWith("**")))) {
writer.write(" " + (isInstanceParameter() ? "this" : name) + ".yieldOwnership();\n");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,36 @@ public void generateCParameters(Writer writer, String throws_) throws IOExceptio
writer.write(",\n (Addressable) GERROR");
}
}

/**
* Generate preprocessing statements for all parameters
* @param writer The source code file writer
* @throws IOException Thrown when an error occurs while writing
*/
public void generatePreprocessing(Writer writer) throws IOException {
for (Parameter p : parameterList) {
p.generatePreprocessing(writer);
}
}

/**
* Generate postprocessing statements for all parameters
* @param writer The source code file writer
* @throws IOException Thrown when an error occurs while writing
*/
public void generatePostprocessing(Writer writer) throws IOException {
// First the regular (non-array) out-parameters. These could include an out-parameter with
// the length of an array out-parameter, so we have to process these first.
for (Parameter p : parameterList) {
if (p.array == null) {
p.generatePostprocessing(writer);
}
}
// Secondly, process the array out parameters
for (Parameter p : parameterList) {
if (p.array != null) {
p.generatePostprocessing(writer);
}
}
}
}

0 comments on commit d03d4ed

Please sign in to comment.