-
Couldn't load subscription status.
- Fork 84
7903947: Access to function pointers in structs could be streamlined #287
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -52,13 +52,18 @@ final class StructBuilder extends ClassSourceBuilder implements OutputFactory.Bu | |
| private final Declaration.Scoped structTree; | ||
| private final Type structType; | ||
| private final Deque<Declaration> nestedAnonDeclarations; | ||
| private final IncludeHelper includeHelper; | ||
| private final boolean functionalDispatch; | ||
|
|
||
| StructBuilder(SourceFileBuilder builder, String modifiers, String className, | ||
| ClassSourceBuilder enclosing, String runtimeHelperName, Declaration.Scoped structTree) { | ||
| ClassSourceBuilder enclosing, String runtimeHelperName, Declaration.Scoped structTree, | ||
| IncludeHelper includeHelper) { | ||
| super(builder, modifiers, Kind.CLASS, className, null, enclosing, runtimeHelperName); | ||
| this.structTree = structTree; | ||
| this.structType = Type.declared(structTree); | ||
| this.nestedAnonDeclarations = new ArrayDeque<>(); | ||
| this.functionalDispatch = includeHelper.isFunctionalDispatch(builder.className()); | ||
| this.includeHelper = includeHelper; | ||
| } | ||
|
|
||
| private String safeParameterName(String paramName) { | ||
|
|
@@ -116,7 +121,7 @@ public StructBuilder addStruct(Declaration.Scoped tree) { | |
| return this; | ||
| } else { | ||
| StructBuilder builder = new StructBuilder(sourceFileBuilder(), "public static", | ||
| JavaName.getOrThrow(tree), this, runtimeHelperName(), tree); | ||
| JavaName.getOrThrow(tree), this, runtimeHelperName(), tree, this.includeHelper); | ||
| builder.begin(); | ||
| return builder; | ||
| } | ||
|
|
@@ -137,25 +142,102 @@ public void addVar(Declaration.Variable varTree) { | |
| String layoutField = emitLayoutFieldDecl(varTree, javaName); | ||
| appendBlankLine(); | ||
| String offsetField = emitOffsetFieldDecl(varTree, javaName); | ||
| if (Utils.isArray(varTree.type()) || Utils.isStructOrUnion(varTree.type())) { | ||
| Type type = varTree.type(); | ||
| if (Utils.isArray(type) || Utils.isStructOrUnion(type)) { | ||
| emitSegmentGetter(javaName, varTree, offsetField, layoutField); | ||
| emitSegmentSetter(javaName, varTree, offsetField, layoutField); | ||
| int dims = Utils.dimensions(varTree.type()).size(); | ||
| int dims = Utils.dimensions(type).size(); | ||
| if (dims > 0) { | ||
| emitDimensionsFieldDecl(varTree, javaName); | ||
| String arrayHandle = emitArrayElementHandle(javaName, varTree, layoutField, dims); | ||
| IndexList indexList = IndexList.of(dims); | ||
| emitFieldArrayGetter(javaName, varTree, arrayHandle, indexList); | ||
| emitFieldArraySetter(javaName, varTree, arrayHandle, indexList); | ||
| } | ||
| } else if (Utils.isPointer(varTree.type()) || Utils.isPrimitive(varTree.type())) { | ||
| emitFieldGetter(javaName, varTree, layoutField, offsetField); | ||
| emitFieldSetter(javaName, varTree, layoutField, offsetField); | ||
| } else if (Utils.isPointer(type) || Utils.isPrimitive(type)) { | ||
| boolean isFuncPtr = functionalDispatch && Utils.isFunctionPointer(type); | ||
| if (isFuncPtr) { | ||
| emitFieldSetter(javaName, varTree, layoutField, offsetField); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure we even want the setter -- the fields in these structs are typically populated by a library |
||
| emitFunctionalConvenience( | ||
| javaName, | ||
| (Type.Function)((Type.Delegated) type).type(), | ||
| varTree, | ||
| layoutField, | ||
| offsetField | ||
| ); | ||
| } else { | ||
| emitFieldGetter(javaName, varTree, layoutField, offsetField); | ||
| emitFieldSetter(javaName, varTree, layoutField, offsetField); | ||
| } | ||
| } else { | ||
| throw new IllegalArgumentException(String.format("Type not supported: %1$s", varTree.type())); | ||
| throw new IllegalArgumentException("Type not supported: " + type); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Generates exactly one invoker, inlining the struct.get(...) to avoid name collision. | ||
| */ | ||
| private void emitFunctionalConvenience(String invokerName, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd call this |
||
| Type.Function funcType, | ||
| Declaration.Variable varTree, | ||
| String layoutField, | ||
| String offsetField) { | ||
| String returnType = Utils.carrierFor(funcType.returnType()).getSimpleName(); | ||
| List<Type> params = funcType.argumentTypes(); | ||
|
|
||
| String sig = IntStream.range(0, params.size()) | ||
| .mapToObj(i -> Utils.carrierFor(params.get(i)).getSimpleName() + " _x" + i) | ||
| .collect(Collectors.joining(", ")); | ||
| String args = IntStream.range(0, params.size()) | ||
| .mapToObj(i -> "_x" + i) | ||
| .collect(Collectors.joining(", ")); | ||
|
|
||
| boolean structRet = !Utils.isPrimitive(funcType.returnType()) | ||
| && !Utils.isPointer(funcType.returnType()); | ||
| String retKw = returnType.equals("void") ? "" : "return "; | ||
|
|
||
| appendBlankLine(); | ||
| incrAlign(); | ||
| emitDocComment(varTree, "Invoker for the pointer inside the Struct:"); | ||
| decrAlign(); | ||
|
|
||
| if (structRet) { | ||
| appendIndentedLines( | ||
| "public static %1$s %2$s(MemorySegment struct, SegmentAllocator alloc%3$s) {", | ||
| returnType, invokerName, sig.isEmpty() ? "" : ", " + sig | ||
| ); | ||
| incrAlign(); | ||
| appendIndentedLines( | ||
| "MemorySegment fp = struct.get(%1$s, %2$s);", | ||
| layoutField, offsetField | ||
| ); | ||
| appendIndentedLines( | ||
| "%1$s%2$s.invoke(fp, alloc%3$s);", | ||
| retKw, | ||
| JavaFunctionalInterfaceName.getOrThrow(varTree), | ||
| args.isEmpty() ? "" : ", " + args | ||
| ); | ||
| } else { | ||
| appendIndentedLines( | ||
| "public static %1$s %2$s(MemorySegment struct%3$s) {", | ||
| returnType, invokerName, sig.isEmpty() ? "" : ", " + sig | ||
| ); | ||
| incrAlign(); | ||
| appendIndentedLines( | ||
| "MemorySegment fp = struct.get(%1$s, %2$s);", | ||
| layoutField, offsetField | ||
| ); | ||
| appendIndentedLines( | ||
| "%1$s%2$s.invoke(fp%3$s);", | ||
| retKw, | ||
| JavaFunctionalInterfaceName.getOrThrow(varTree), | ||
| args.isEmpty() ? "" : ", " + args | ||
| ); | ||
| } | ||
| decrAlign(); | ||
| appendIndentedLines("}"); | ||
| } | ||
|
|
||
| private List<String> prefixNamesList() { | ||
| return nestedAnonDeclarations.stream() | ||
| .map(d -> AnonymousStruct.anonName((Declaration.Scoped)d)) | ||
|
|
@@ -490,7 +572,7 @@ private String structOrUnionLayoutString(long base, Declaration.Scoped scoped, i | |
| offset += fieldSize; | ||
| size += fieldSize; | ||
| } else { | ||
| size = Math.max(size, ClangSizeOf.getOrThrow(member)); | ||
| size = Math.max(size, fieldSize); | ||
| } | ||
| } | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure I get this logic -- it seems like we're always setting a property P for a typedef with name N whenever we see it set for a struct N? That seems odd.
But in example as the one you added in the test are a bit ambiguous, as the same declaration induces both an anonymous struct decl and a typedef, so it is less clear which is which. If I write:
It seems to be that jextract only retains a "struct" tree, and throws away the typedef. In fact I was surprised that your test worked at all, as in the above case
dump-includeswould include something like this:And not:
So adding a property on the typedef should have no effect?