From f3c7c85fa6a9385dd0e8f353bdb4b38f5c231454 Mon Sep 17 00:00:00 2001 From: Maurizio Cimadamore Date: Thu, 30 Mar 2023 14:49:43 +0000 Subject: [PATCH] 7903449: Jextract generates structs that cannot be compiled Reviewed-by: jvernee --- src/main/java/org/openjdk/jextract/Type.java | 12 +- .../jextract/impl/ClassSourceBuilder.java | 31 +- .../jextract/impl/ConstantBuilder.java | 415 ------------- .../org/openjdk/jextract/impl/Constants.java | 547 ++++++++++++++++++ .../impl/FunctionalInterfaceBuilder.java | 163 +++--- .../jextract/impl/HeaderFileBuilder.java | 111 +--- .../jextract/impl/JavaSourceBuilder.java | 7 +- .../openjdk/jextract/impl/StructBuilder.java | 33 +- .../jextract/impl/ToplevelBuilder.java | 152 +---- .../resources/RuntimeHelper.java.template | 4 + 10 files changed, 702 insertions(+), 773 deletions(-) delete mode 100644 src/main/java/org/openjdk/jextract/impl/ConstantBuilder.java create mode 100644 src/main/java/org/openjdk/jextract/impl/Constants.java diff --git a/src/main/java/org/openjdk/jextract/Type.java b/src/main/java/org/openjdk/jextract/Type.java index 9f0a97f2..7f3071ce 100644 --- a/src/main/java/org/openjdk/jextract/Type.java +++ b/src/main/java/org/openjdk/jextract/Type.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -107,21 +107,21 @@ enum Kind { /** * {@code short} type. */ - Short("short", ValueLayout.JAVA_SHORT.withBitAlignment(16)), + Short("short", ValueLayout.JAVA_SHORT), /** * {@code int} type. */ - Int("int", ValueLayout.JAVA_INT.withBitAlignment(32)), + Int("int", ValueLayout.JAVA_INT), /** * {@code long} type. */ Long("long", TypeImpl.IS_WINDOWS ? - ValueLayout.JAVA_INT.withBitAlignment(32) : - ValueLayout.JAVA_LONG.withBitAlignment(64)), + ValueLayout.JAVA_INT : + ValueLayout.JAVA_LONG), /** * {@code long long} type. */ - LongLong("long long", ValueLayout.JAVA_LONG.withBitAlignment(64)), + LongLong("long long", ValueLayout.JAVA_LONG), /** * {@code int128} type. */ diff --git a/src/main/java/org/openjdk/jextract/impl/ClassSourceBuilder.java b/src/main/java/org/openjdk/jextract/impl/ClassSourceBuilder.java index 759b82fc..ffd4ec5e 100644 --- a/src/main/java/org/openjdk/jextract/impl/ClassSourceBuilder.java +++ b/src/main/java/org/openjdk/jextract/impl/ClassSourceBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,9 +27,10 @@ import javax.tools.JavaFileObject; import java.lang.constant.ClassDesc; import java.util.List; -import java.util.function.Consumer; + import org.openjdk.jextract.Declaration; import org.openjdk.jextract.Type; +import org.openjdk.jextract.impl.Constants.Constant; /** * Superclass for .java source generator classes. @@ -159,6 +160,10 @@ public List toFiles() { // Internal generation helpers (used by other builders) + void append(Object o) { + sb.append(o); + } + void append(String s) { sb.append(s); } @@ -264,17 +269,17 @@ protected void emitImportSection() { } } - protected void emitGetter(String mods, Class type, String name, String access, boolean nullCheck, String symbolName) { + void emitConstantGetter(String mods, String getterName, boolean nullCheck, String symbolName, Constant constant) { incrAlign(); indent(); - append(mods + " " + type.getSimpleName() + " " +name + "() {\n"); + append(mods + " " + constant.type().getSimpleName() + " " + getterName + "() {\n"); incrAlign(); indent(); append("return "); if (nullCheck) { append("RuntimeHelper.requireNonNull("); } - append(access); + append(constant.accessExpression()); if (nullCheck) { append(",\""); append(symbolName); @@ -287,20 +292,8 @@ protected void emitGetter(String mods, Class type, String name, String access decrAlign(); } - protected void emitGetter(String mods, Class type, String name, String access) { - emitGetter(mods, type, name, access, false, null); - } - - ToplevelBuilder toplevel() { - JavaSourceBuilder encl = enclosing; - while (encl instanceof ClassSourceBuilder classSourceBuilder) { - encl = classSourceBuilder.enclosing; - } - return (ToplevelBuilder)encl; - } - @Override - protected void emitWithConstantClass(Consumer constantConsumer) { - enclosing.emitWithConstantClass(constantConsumer); + protected Constants constants() { + return enclosing.constants(); } } diff --git a/src/main/java/org/openjdk/jextract/impl/ConstantBuilder.java b/src/main/java/org/openjdk/jextract/impl/ConstantBuilder.java deleted file mode 100644 index b427ea6f..00000000 --- a/src/main/java/org/openjdk/jextract/impl/ConstantBuilder.java +++ /dev/null @@ -1,415 +0,0 @@ -/* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package org.openjdk.jextract.impl; - -import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.GroupLayout; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemoryLayout; -import java.lang.foreign.SequenceLayout; -import java.lang.foreign.StructLayout; -import java.lang.foreign.ValueLayout; - -import java.lang.invoke.MethodHandle; -import java.lang.invoke.VarHandle; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -public class ConstantBuilder extends ClassSourceBuilder { - - // set of names generates already - private final Map namesGenerated = new HashMap<>(); - - public ConstantBuilder(JavaSourceBuilder enclosing, String className) { - super(enclosing, Kind.CLASS, className); - } - - String memberMods() { - return kind == ClassSourceBuilder.Kind.CLASS ? - "static final " : ""; - } - - // public API - - public Constant addLayout(String javaName, MemoryLayout layout) { - return emitIfAbsent(javaName, Constant.Kind.LAYOUT, - () -> emitLayoutField(javaName, layout)); - } - - public Constant addFieldVarHandle(String javaName, String nativeName, ValueLayout valueLayout, - String rootJavaName, List prefixElementNames) { - return addVarHandle(javaName, nativeName, valueLayout, rootJavaName, prefixElementNames); - } - - public Constant addGlobalVarHandle(String javaName, String nativeName, ValueLayout valueLayout) { - return addVarHandle(javaName, nativeName, valueLayout, null, List.of()); - } - - private Constant addVarHandle(String javaName, String nativeName, ValueLayout valueLayout, - String rootLayoutName, List prefixElementNames) { - return emitIfAbsent(javaName, Constant.Kind.VAR_HANDLE, - () -> emitVarHandleField(javaName, nativeName, valueLayout, rootLayoutName, prefixElementNames)); - } - - public Constant addDowncallMethodHandle(String javaName, String nativeName, FunctionDescriptor descriptor, boolean isVarargs, boolean virtual) { - return emitIfAbsent(javaName, Constant.Kind.METHOD_HANDLE, - () -> emitDowncallMethodHandleField(javaName, nativeName, descriptor, isVarargs, virtual)); - } - - public Constant addLookupMethodHandle(String javaName, String className, String name, FunctionDescriptor descriptor) { - return emitIfAbsent(javaName, Constant.Kind.METHOD_HANDLE, - () -> emitUpcallMethodHandleField(javaName, className, name, descriptor)); - } - - public Constant addSegment(String javaName, String nativeName, MemoryLayout layout) { - return emitIfAbsent(javaName, Constant.Kind.SEGMENT, - () -> emitSegmentField(javaName, nativeName, layout)); - } - - public Constant addFunctionDesc(String javaName, FunctionDescriptor desc) { - return emitIfAbsent(javaName, Constant.Kind.FUNCTION_DESCRIPTOR, - () -> emitFunctionDescField(javaName, desc)); - } - - public Constant addConstantDesc(String javaName, Class type, Object value) { - if (value instanceof String) { - return emitIfAbsent(javaName, Constant.Kind.SEGMENT, - () -> emitConstantSegment(javaName, value)); - } else if (type == MemorySegment.class) { - return emitIfAbsent(javaName, Constant.Kind.ADDRESS, - () -> emitConstantAddress(javaName, value)); - } else { - throw new UnsupportedOperationException(); - } - } - - static final class Constant { - - enum Kind { - LAYOUT(MemoryLayout.class, "$LAYOUT"), - METHOD_HANDLE(MethodHandle.class, "$MH"), - VAR_HANDLE(VarHandle.class, "$VH"), - FUNCTION_DESCRIPTOR(FunctionDescriptor.class, "$FUNC"), - ADDRESS(MemorySegment.class, "$ADDR"), - SEGMENT(MemorySegment.class, "$SEGMENT"); - - final Class type; - final String nameSuffix; - - Kind(Class type, String nameSuffix) { - this.type = type; - this.nameSuffix = nameSuffix; - } - - String fieldName(String javaName) { - return javaName + nameSuffix; - } - } - - private final String className; - private final String javaName; - private final Kind kind; - - Constant(String className, String javaName, Kind kind) { - this.className = className; - this.javaName = javaName; - this.kind = kind; - } - - String className() { - return className; - } - - String javaName() { - return javaName; - } - - Kind kind() { - return kind; - } - - List getterNameParts() { - return List.of(className, javaName, kind.nameSuffix); - } - - String accessExpression() { - return className + "." + kind.fieldName(javaName); - } - - Constant emitGetter(ClassSourceBuilder builder, String mods, Function, String> getterNameFunc) { - builder.emitGetter(mods, kind.type, getterNameFunc.apply(getterNameParts()), accessExpression()); - return this; - } - - Constant emitGetter(ClassSourceBuilder builder, String mods, Function, String> getterNameFunc, String symbolName) { - builder.emitGetter(mods, kind.type, getterNameFunc.apply(getterNameParts()), accessExpression(), true, symbolName); - return this; - } - - static final Function, String> QUALIFIED_NAME = - l -> l.stream().skip(1).collect(Collectors.joining()); - - static final Function, String> JAVA_NAME = - l -> l.get(1); - - static final Function, String> SUFFIX_ONLY = - l -> l.get(2); - } - - // private generators - - public Constant emitIfAbsent(String name, Constant.Kind kind, Supplier constantFactory) { - String lookupName = kind.fieldName(name); - Constant constant = namesGenerated.get(lookupName); - if (constant == null) { - constant = constantFactory.get(); - if (constant.kind != kind) { - throw new AssertionError("Factory return wrong kind of constant; expected: " - + kind + "; found: " + constant.kind); - } - namesGenerated.put(lookupName, constant); - } - return constant; - } - - private Constant emitDowncallMethodHandleField(String javaName, String nativeName, FunctionDescriptor descriptor, boolean isVarargs, boolean virtual) { - Constant functionDesc = addFunctionDesc(javaName, descriptor); - incrAlign(); - String fieldName = Constant.Kind.METHOD_HANDLE.fieldName(javaName); - indent(); - append(memberMods() + "MethodHandle "); - append(fieldName + " = RuntimeHelper."); - if (isVarargs) { - append("downcallHandleVariadic"); - } else { - append("downcallHandle"); - } - append("(\n"); - incrAlign(); - indent(); - if (!virtual) { - append("\"" + nativeName + "\""); - append(",\n"); - indent(); - } - append(functionDesc.accessExpression()); - append("\n"); - decrAlign(); - indent(); - append(");\n"); - decrAlign(); - return new Constant(className(), javaName, Constant.Kind.METHOD_HANDLE); - } - - private Constant emitUpcallMethodHandleField(String javaName, String className, String methodName, FunctionDescriptor descriptor) { - Constant functionDesc = addFunctionDesc(javaName, descriptor); - incrAlign(); - String fieldName = Constant.Kind.METHOD_HANDLE.fieldName(javaName); - indent(); - append(memberMods() + "MethodHandle "); - append(fieldName + " = RuntimeHelper.upcallHandle("); - append(className + ".class, "); - append("\"" + methodName + "\", "); - append(functionDesc.accessExpression()); - append(");\n"); - decrAlign(); - return new Constant(className(), javaName, Constant.Kind.METHOD_HANDLE); - } - - private Constant emitVarHandleField(String javaName, String nativeName, ValueLayout valueLayout, - String rootLayoutName, List prefixElementNames) { - String layoutAccess = rootLayoutName != null ? - Constant.Kind.LAYOUT.fieldName(rootLayoutName) : - addLayout(javaName, valueLayout).accessExpression(); - incrAlign(); - indent(); - String fieldName = Constant.Kind.VAR_HANDLE.fieldName(javaName); - append(memberMods() + "VarHandle " + fieldName + " = "); - append(layoutAccess); - append(".varHandle("); - String prefix = ""; - if (rootLayoutName != null) { - for (String prefixElementName : prefixElementNames) { - append(prefix + "MemoryLayout.PathElement.groupElement(\"" + prefixElementName + "\")"); - prefix = ", "; - } - append(prefix + "MemoryLayout.PathElement.groupElement(\"" + nativeName + "\")"); - } - append(")"); - append(";\n"); - decrAlign(); - return new Constant(className(), javaName, Constant.Kind.VAR_HANDLE); - } - - private Constant emitLayoutField(String javaName, MemoryLayout layout) { - String fieldName = Constant.Kind.LAYOUT.fieldName(javaName); - incrAlign(); - indent(); - String layoutClassName = Utils.layoutDeclarationType(layout).getSimpleName(); - append(memberMods() + layoutClassName + " " + fieldName + " = "); - emitLayoutString(layout); - append(";\n"); - decrAlign(); - return new Constant(className(), javaName, Constant.Kind.LAYOUT); - } - - protected String primitiveLayoutString(ValueLayout layout) { - return toplevel().rootConstants().resolvePrimitiveLayout(layout).accessExpression(); - } - - private void emitLayoutString(MemoryLayout l) { - if (l instanceof ValueLayout val) { - append(primitiveLayoutString(val)); - if (l.bitAlignment() != l.bitSize()) { - append(".withBitAlignment("); - append(l.bitAlignment()); - append(")"); - } - } else if (l instanceof SequenceLayout seq) { - append("MemoryLayout.sequenceLayout("); - append(seq.elementCount() + ", "); - emitLayoutString(seq.elementLayout()); - append(")"); - } else if (l instanceof GroupLayout group) { - if (group instanceof StructLayout) { - append("MemoryLayout.structLayout(\n"); - } else { - append("MemoryLayout.unionLayout(\n"); - } - incrAlign(); - String delim = ""; - for (MemoryLayout e : group.memberLayouts()) { - append(delim); - indent(); - emitLayoutString(e); - delim = ",\n"; - } - append("\n"); - decrAlign(); - indent(); - append(")"); - } else { - // padding (or unsupported) - append("MemoryLayout.paddingLayout(" + l.bitSize() + ")"); - } - if (l.name().isPresent()) { - append(".withName(\"" + l.name().get() + "\")"); - } - } - - private Constant emitFunctionDescField(String javaName, FunctionDescriptor desc) { - incrAlign(); - indent(); - String fieldName = Constant.Kind.FUNCTION_DESCRIPTOR.fieldName(javaName); - final boolean noArgs = desc.argumentLayouts().isEmpty(); - append(memberMods()); - append("FunctionDescriptor "); - append(fieldName); - append(" = "); - if (desc.returnLayout().isPresent()) { - append("FunctionDescriptor.of("); - emitLayoutString(desc.returnLayout().get()); - if (!noArgs) { - append(","); - } - } else { - append("FunctionDescriptor.ofVoid("); - } - if (!noArgs) { - append("\n"); - incrAlign(); - String delim = ""; - for (MemoryLayout e : desc.argumentLayouts()) { - append(delim); - indent(); - emitLayoutString(e); - delim = ",\n"; - } - append("\n"); - decrAlign(); - indent(); - } - append(");\n"); - decrAlign(); - return new Constant(className(), javaName, Constant.Kind.FUNCTION_DESCRIPTOR); - } - - private Constant emitConstantSegment(String javaName, Object value) { - incrAlign(); - indent(); - String fieldName = Constant.Kind.SEGMENT.fieldName(javaName); - append(memberMods()); - append("MemorySegment "); - append(fieldName); - append(" = RuntimeHelper.CONSTANT_ALLOCATOR.allocateUtf8String(\""); - append(Utils.quote(Objects.toString(value))); - append("\");\n"); - decrAlign(); - return new Constant(className(), javaName, Constant.Kind.SEGMENT); - } - - private Constant emitConstantAddress(String javaName, Object value) { - incrAlign(); - indent(); - String fieldName = Constant.Kind.ADDRESS.fieldName(javaName); - append(memberMods()); - append("MemorySegment "); - append(fieldName); - append(" = MemorySegment.ofAddress("); - append(((Number)value).longValue()); - append("L);\n"); - decrAlign(); - return new Constant(className(), javaName, Constant.Kind.ADDRESS); - } - - private Constant emitSegmentField(String javaName, String nativeName, MemoryLayout layout) { - Constant layoutConstant = addLayout(javaName, layout); - incrAlign(); - indent(); - String fieldName = Constant.Kind.SEGMENT.fieldName(javaName); - append(memberMods()); - append("MemorySegment "); - append(fieldName); - append(" = "); - append("RuntimeHelper.lookupGlobalVariable("); - append("\"" + nativeName + "\", "); - append(layoutConstant.accessExpression()); - append(");\n"); - decrAlign(); - return new Constant(className(), javaName, Constant.Kind.SEGMENT); - } - - @Override - protected void emitWithConstantClass(Consumer constantConsumer) { - constantConsumer.accept(this); - } -} diff --git a/src/main/java/org/openjdk/jextract/impl/Constants.java b/src/main/java/org/openjdk/jextract/impl/Constants.java new file mode 100644 index 00000000..72455c08 --- /dev/null +++ b/src/main/java/org/openjdk/jextract/impl/Constants.java @@ -0,0 +1,547 @@ +/* + * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.openjdk.jextract.impl; + +import org.openjdk.jextract.Type; + +import javax.tools.JavaFileObject; +import java.lang.foreign.AddressLayout; +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.GroupLayout; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.SequenceLayout; +import java.lang.foreign.StructLayout; +import java.lang.foreign.ValueLayout; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.VarHandle; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; + +public class Constants { + + private final Map cache = new HashMap<>(); + + List constantBuilders = new ArrayList<>(); + Builder currentBuilder; + + public Constants(JavaSourceBuilder enclosing) { + currentBuilder = new Builder(enclosing, 0); + constantBuilders.add(currentBuilder); + currentBuilder.classBegin(); + // prime the cache with basic primitive/pointer (immediate) layouts + for (Type.Primitive.Kind kind : Type.Primitive.Kind.values()) { + kind.layout().ifPresent(layout -> { + if (layout instanceof ValueLayout valueLayout) { + cache.put(valueLayout, ImmediateConstant.ofPrimitiveLayout(valueLayout)); + } + }); + } + AddressLayout pointerLayout = ValueLayout.ADDRESS.withTargetLayout( + MemoryLayout.sequenceLayout(ValueLayout.JAVA_BYTE)); + cache.put(pointerLayout, ImmediateConstant.ofPrimitiveLayout(pointerLayout)); + } + + static final int CONSTANTS_PER_CLASS = Integer.getInteger("jextract.constants.per.class", 5); + + private Builder builder() { + if (currentBuilder.constantIndex > CONSTANTS_PER_CLASS || currentBuilder == null) { + if (currentBuilder != null) { + currentBuilder.classEnd(); + } + currentBuilder = new Builder(currentBuilder.enclosing, constantBuilders.size()); + constantBuilders.add(currentBuilder); + currentBuilder.classBegin(); + } + return currentBuilder; + } + + static sealed abstract class Constant permits Builder.NamedConstant, ImmediateConstant { + + final Class type; + + public Constant(Class type) { + this.type = type; + } + + Class type() { + return type; + } + + String getterName(String javaName) { + return javaName + nameSuffix(); + } + + Constant emitGetter(ClassSourceBuilder builder, String mods, String javaName) { + return emitGetter(builder, mods, c -> c.getterName(javaName)); + } + + Constant emitGetter(ClassSourceBuilder builder, String mods, String javaName, String symbolName) { + return emitGetter(builder, mods, symbolName, c -> c.getterName(javaName)); + } + + Constant emitGetter(ClassSourceBuilder builder, String mods, Function getterNameFunc) { + builder.emitConstantGetter(mods, getterNameFunc.apply(this), false, null, this); + return this; + } + + Constant emitGetter(ClassSourceBuilder builder, String mods, String symbolName, Function getterNameFunc) { + builder.emitConstantGetter(mods, getterNameFunc.apply(this), true, symbolName, this); + return this; + } + + String nameSuffix() { + if (type.equals(MemorySegment.class)) { + return "$SEGMENT"; + } else if (type.equals(MemoryLayout.class)) { + return "$LAYOUT"; + } else if (type.equals(MethodHandle.class)) { + return "$MH"; + } else if (type.equals(VarHandle.class)) { + return "$VH"; + } else if (type.equals(FunctionDescriptor.class)) { + return "$DESC"; + } else { + return ""; + } + } + + abstract String accessExpression(); + } + + final static class ImmediateConstant extends Constant { + final String value; + + ImmediateConstant(Class type, String value) { + super(type); + this.value = value; + } + + @Override + String accessExpression() { + return value; + } + + static ImmediateConstant ofPrimitiveLayout(ValueLayout vl) { + final String layoutStr; + if (vl.carrier() == boolean.class) { + layoutStr = "JAVA_BOOLEAN"; + } else if (vl.carrier() == char.class) { + layoutStr = "JAVA_CHAR"; + } else if (vl.carrier() == byte.class) { + layoutStr = "JAVA_BYTE"; + } else if (vl.carrier() == short.class) { + layoutStr = "JAVA_SHORT"; + } else if (vl.carrier() == int.class) { + layoutStr = "JAVA_INT"; + } else if (vl.carrier() == float.class) { + layoutStr = "JAVA_FLOAT"; + } else if (vl.carrier() == long.class) { + layoutStr = "JAVA_LONG"; + } else if (vl.carrier() == double.class) { + layoutStr = "JAVA_DOUBLE"; + } else if (vl.carrier() == MemorySegment.class) { + layoutStr = "RuntimeHelper.POINTER"; + } else { + throw new UnsupportedOperationException("Unsupported layout: " + vl); + } + return new ImmediateConstant(MemoryLayout.class, layoutStr); + } + + static Constant ofLiteral(Class type, Object value) { + StringBuilder buf = new StringBuilder(); + if (type == float.class) { + float f = ((Number)value).floatValue(); + if (Float.isFinite(f)) { + buf.append(value); + buf.append("f"); + } else { + buf.append("Float.valueOf(\""); + buf.append(value); + buf.append("\")"); + } + } else if (type == long.class) { + buf.append(value.toString()); + buf.append("L"); + } else if (type == double.class) { + double d = ((Number)value).doubleValue(); + if (Double.isFinite(d)) { + buf.append(value); + buf.append("d"); + } else { + buf.append("Double.valueOf(\""); + buf.append(value); + buf.append("\")"); + } + } else if (type == boolean.class) { + boolean booleanValue = ((Number)value).byteValue() != 0; + buf.append(booleanValue); + } else { + buf.append("(" + type.getName() + ")"); + buf.append(value + "L"); + } + return new ImmediateConstant(type, buf.toString()); + } + } + + public List toFiles() { + currentBuilder.classEnd(); + List files = new ArrayList<>(); + files.addAll(constantBuilders.stream() + .flatMap(b -> b.toFiles().stream()).toList()); + return files; + } + + class Builder extends ClassSourceBuilder { + + Builder(JavaSourceBuilder encl, int id) { + super(encl, Kind.CLASS, "constants$" + id); + } + + String memberMods() { + return kind == ClassSourceBuilder.Kind.CLASS ? + "static final " : ""; + } + + @Override + String mods() { + return "final "; // constants package-private! + } + + int constantIndex = 0; + + final class NamedConstant extends Constant { + final String constantName; + + NamedConstant(Class type) { + super(type); + this.constantName = newConstantName(); + } + + String constantName() { + return constantName; + } + + @Override + String accessExpression() { + return className() + "." + constantName; + } + } + + private Constant emitDowncallMethodHandleField(String nativeName, FunctionDescriptor descriptor, boolean isVarargs, boolean virtual) { + Constant functionDesc = addFunctionDesc(descriptor); + incrAlign(); + NamedConstant mhConst = new NamedConstant(MethodHandle.class); + indent(); + append(memberMods() + "MethodHandle "); + append(mhConst.constantName + " = RuntimeHelper."); + if (isVarargs) { + append("downcallHandleVariadic"); + } else { + append("downcallHandle"); + } + append("(\n"); + incrAlign(); + indent(); + if (!virtual) { + append("\"" + nativeName + "\""); + append(",\n"); + indent(); + } + append(functionDesc.accessExpression()); + append("\n"); + decrAlign(); + indent(); + append(");\n"); + decrAlign(); + return mhConst; + } + + private Constant emitUpcallMethodHandleField(String className, String methodName, FunctionDescriptor descriptor) { + Constant functionDesc = addFunctionDesc(descriptor); + incrAlign(); + NamedConstant mhConst = new NamedConstant(MethodHandle.class); + indent(); + append(memberMods() + "MethodHandle "); + append(mhConst.constantName + " = RuntimeHelper.upcallHandle("); + append(className + ".class, "); + append("\"" + methodName + "\", "); + append(functionDesc.accessExpression()); + append(");\n"); + decrAlign(); + return mhConst; + } + + private Constant emitVarHandle(ValueLayout valueLayout) { + Constant layoutConstant = addLayout(valueLayout); + incrAlign(); + indent(); + NamedConstant vhConst = new NamedConstant(VarHandle.class); + append(memberMods() + "VarHandle " + vhConst.constantName + " = "); + append(layoutConstant.accessExpression()); + append(".varHandle();\n"); + decrAlign(); + return vhConst; + } + + private Constant emitFieldVarHandle(String nativeName, GroupLayout parentLayout, List prefixElementNames) { + Constant layoutConstant = addLayout(parentLayout); + incrAlign(); + indent(); + NamedConstant vhConst = new NamedConstant(VarHandle.class); + append(memberMods() + "VarHandle " + vhConst.constantName + " = "); + append(layoutConstant.accessExpression()); + append(".varHandle("); + String prefix = ""; + for (String prefixElementName : prefixElementNames) { + append(prefix + "MemoryLayout.PathElement.groupElement(\"" + prefixElementName + "\")"); + prefix = ", "; + } + append(prefix + "MemoryLayout.PathElement.groupElement(\"" + nativeName + "\")"); + append(")"); + append(";\n"); + decrAlign(); + return vhConst; + } + + private Constant emitLayoutField(MemoryLayout layout) { + NamedConstant layoutConst = new NamedConstant(MemoryLayout.class); + incrAlign(); + indent(); + String layoutClassName = Utils.layoutDeclarationType(layout).getSimpleName(); + append(memberMods() + layoutClassName + " " + layoutConst.constantName + " = "); + emitLayoutString(layout); + append(";\n"); + decrAlign(); + return layoutConst; + } + + private void emitLayoutString(MemoryLayout l) { + if (l instanceof ValueLayout val) { + append(ImmediateConstant.ofPrimitiveLayout(val).accessExpression()); + if (l.bitAlignment() != l.bitSize()) { + append(".withBitAlignment("); + append(l.bitAlignment()); + append(")"); + } + } else if (l instanceof SequenceLayout seq) { + append("MemoryLayout.sequenceLayout("); + append(seq.elementCount() + ", "); + emitLayoutString(seq.elementLayout()); + append(")"); + } else if (l instanceof GroupLayout group) { + if (group instanceof StructLayout) { + append("MemoryLayout.structLayout(\n"); + } else { + append("MemoryLayout.unionLayout(\n"); + } + incrAlign(); + String delim = ""; + for (MemoryLayout e : group.memberLayouts()) { + append(delim); + indent(); + emitLayoutString(e); + delim = ",\n"; + } + append("\n"); + decrAlign(); + indent(); + append(")"); + } else { + // padding (or unsupported) + append("MemoryLayout.paddingLayout(" + l.bitSize() + ")"); + } + if (l.name().isPresent()) { + append(".withName(\"" + l.name().get() + "\")"); + } + } + + private Constant emitFunctionDescField(FunctionDescriptor desc) { + incrAlign(); + indent(); + final boolean noArgs = desc.argumentLayouts().isEmpty(); + append(memberMods()); + append("FunctionDescriptor "); + NamedConstant descConstant = new NamedConstant(FunctionDescriptor.class); + append(descConstant.constantName); + append(" = "); + if (desc.returnLayout().isPresent()) { + append("FunctionDescriptor.of("); + emitLayoutString(desc.returnLayout().get()); + if (!noArgs) { + append(","); + } + } else { + append("FunctionDescriptor.ofVoid("); + } + if (!noArgs) { + append("\n"); + incrAlign(); + String delim = ""; + for (MemoryLayout e : desc.argumentLayouts()) { + append(delim); + indent(); + emitLayoutString(e); + delim = ",\n"; + } + append("\n"); + decrAlign(); + indent(); + } + append(");\n"); + decrAlign(); + return descConstant; + } + + private Constant emitConstantString(Object value) { + incrAlign(); + indent(); + append(memberMods()); + append("MemorySegment "); + NamedConstant segConstant = new NamedConstant(MemorySegment.class); + append(segConstant.constantName); + append(" = RuntimeHelper.CONSTANT_ALLOCATOR.allocateUtf8String(\""); + append(Utils.quote(Objects.toString(value))); + append("\");\n"); + decrAlign(); + return segConstant; + } + + private Constant emitConstantAddress(Object value) { + incrAlign(); + indent(); + append(memberMods()); + append("MemorySegment "); + NamedConstant segConstant = new NamedConstant(MemorySegment.class); + append(segConstant.constantName); + append(" = MemorySegment.ofAddress("); + append(((Number)value).longValue()); + append("L);\n"); + decrAlign(); + return segConstant; + } + + private Constant emitSegmentField(String nativeName, MemoryLayout layout) { + Constant layoutConstant = addLayout(layout); + incrAlign(); + indent(); + append(memberMods()); + append("MemorySegment "); + NamedConstant segConstant = new NamedConstant(MemorySegment.class); + append(segConstant.constantName); + append(" = "); + append("RuntimeHelper.lookupGlobalVariable("); + append("\"" + nativeName + "\", "); + append(layoutConstant.accessExpression()); + append(");\n"); + decrAlign(); + return segConstant; + } + + String newConstantName() { + return "const$" + constantIndex++; + } + } + + // public API + + public Constant addLayout(MemoryLayout layout) { + Constant constant = cache.get(layout); + if (constant == null) { + constant = builder().emitLayoutField(layout); + cache.put(layout, constant); + } + return constant; + } + + public Constant addFieldVarHandle(String nativeName, GroupLayout parentLayout, List prefixElementNames) { + return builder().emitFieldVarHandle(nativeName, parentLayout, prefixElementNames); + } + + public Constant addGlobalVarHandle(ValueLayout valueLayout) { + record VarHandleKey(ValueLayout valueLayout) { } + VarHandleKey key = new VarHandleKey(valueLayout.withoutName()); + Constant constant = cache.get(key); + if (constant == null) { + constant = builder().emitVarHandle(valueLayout); + cache.put(key, constant); + } + return constant; + } + + public Constant addDowncallMethodHandle(String nativeName, FunctionDescriptor descriptor, boolean isVarargs) { + return builder().emitDowncallMethodHandleField(nativeName, descriptor, isVarargs, false); + } + + public Constant addVirtualDowncallMethodHandle(FunctionDescriptor descriptor) { + record DowncallKey(FunctionDescriptor desc) { } + DowncallKey downcallKey = new DowncallKey(descriptor); + Constant constant = cache.get(downcallKey); + if (constant == null) { + constant = builder().emitDowncallMethodHandleField(null, descriptor, false, true); + cache.put(downcallKey, constant); + } + return constant; + } + + public Constant addUpcallMethodHandle(String className, String name, FunctionDescriptor descriptor) { + return builder().emitUpcallMethodHandleField(className, name, descriptor); + } + + public Constant addSegment(String nativeName, MemoryLayout layout) { + return builder().emitSegmentField(nativeName, layout); + } + + public Constant addFunctionDesc(FunctionDescriptor desc) { + Constant constant = cache.get(desc); + if (constant == null) { + constant = builder().emitFunctionDescField(desc); + cache.put(desc, constant); + } + return constant; + } + + public Constant addConstantDesc(Class type, Object value) { + record ConstantKey(Class type, Object value) { } + var key = new ConstantKey(type, value); + Constant constant = cache.get(key); + if (constant == null) { + if (value instanceof String) { + constant = builder().emitConstantString(value); + } else if (type == MemorySegment.class) { + constant = builder().emitConstantAddress(value); + } else { + constant = ImmediateConstant.ofLiteral(type, value); + } + cache.put(key, constant); + } + return constant; + } +} diff --git a/src/main/java/org/openjdk/jextract/impl/FunctionalInterfaceBuilder.java b/src/main/java/org/openjdk/jextract/impl/FunctionalInterfaceBuilder.java index 67ac41df..459ac689 100644 --- a/src/main/java/org/openjdk/jextract/impl/FunctionalInterfaceBuilder.java +++ b/src/main/java/org/openjdk/jextract/impl/FunctionalInterfaceBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,13 +27,12 @@ import java.lang.foreign.*; -import org.openjdk.jextract.impl.ConstantBuilder.Constant; +import org.openjdk.jextract.impl.Constants.Constant; import org.openjdk.jextract.Type; import java.lang.invoke.MethodType; import java.util.List; import java.util.Optional; -import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -95,93 +94,83 @@ private void emitFunctionalInterfaceMethod() { } private void emitFunctionalFactories() { - emitWithConstantClass(constantBuilder -> { - Constant functionDesc = constantBuilder.addFunctionDesc(className(), fiDesc); - Constant upcallHandle = constantBuilder.addLookupMethodHandle(className() + "_UP", className(), "apply", fiDesc); - incrAlign(); - indent(); - append(MEMBER_MODS + " MemorySegment allocate(" + className() + " fi, Arena scope) {\n"); - incrAlign(); - indent(); - append("return RuntimeHelper.upcallStub(" + - upcallHandle.accessExpression() + ", fi, " + functionDesc.accessExpression() + ", scope);\n"); - decrAlign(); - indent(); - append("}\n"); - decrAlign(); - }); + Constant functionDesc = constants().addFunctionDesc(fiDesc); + Constant upcallHandle = constants().addUpcallMethodHandle(fullName(), "apply", fiDesc); + incrAlign(); + indent(); + append(MEMBER_MODS + " MemorySegment allocate(" + className() + " fi, Arena scope) {\n"); + incrAlign(); + indent(); + append("return RuntimeHelper.upcallStub(" + + upcallHandle.accessExpression() + ", fi, " + functionDesc.accessExpression() + ", scope);\n"); + decrAlign(); + indent(); + append("}\n"); + decrAlign(); } private void emitFunctionalFactoryForPointer() { - emitWithConstantClass(constantBuilder -> { - Constant mhConstant = constantBuilder.addDowncallMethodHandle(className() + "_DOWN", className(), - fiDesc, false, true); - incrAlign(); - indent(); - append(MEMBER_MODS + " " + className() + " ofAddress(MemorySegment addr, Arena arena) {\n"); - incrAlign(); - indent(); - append("MemorySegment symbol = addr.reinterpret("); - append("arena, null);\n"); - indent(); - append("return ("); - String delim = ""; - for (int i = 0 ; i < fiType.parameterCount(); i++) { - append(delim + fiType.parameterType(i).getName()); - append(" "); - append("_" + parameterName(i)); - delim = ", "; - } - append(") -> {\n"); - incrAlign(); - indent(); - append("try {\n"); - incrAlign(); - indent(); - if (!fiType.returnType().equals(void.class)) { - append("return (" + fiType.returnType().getName() + ")"); - if (fiType.returnType() != downcallType.returnType()) { - // add cast for invokeExact - append("(" + downcallType.returnType().getName() + ")"); - } - } - append(mhConstant.accessExpression() + ".invokeExact(symbol"); - if (fiType.parameterCount() > 0) { - String params = IntStream.range(0, fiType.parameterCount()) - .mapToObj(i -> { - String paramExpr = "_" + parameterName(i); - if (fiType.parameterType(i) != downcallType.parameterType(i)) { - // add cast for invokeExact - return "(" + downcallType.parameterType(i).getName() + ")" + paramExpr; - } else { - return paramExpr; - } - }) - .collect(Collectors.joining(", ")); - append(", " + params); + Constant mhConstant = constants().addVirtualDowncallMethodHandle(fiDesc); + incrAlign(); + indent(); + append(MEMBER_MODS + " " + className() + " ofAddress(MemorySegment addr, Arena arena) {\n"); + incrAlign(); + indent(); + append("MemorySegment symbol = addr.reinterpret("); + append("arena, null);\n"); + indent(); + append("return ("); + String delim = ""; + for (int i = 0 ; i < fiType.parameterCount(); i++) { + append(delim + fiType.parameterType(i).getName()); + append(" "); + append("_" + parameterName(i)); + delim = ", "; + } + append(") -> {\n"); + incrAlign(); + indent(); + append("try {\n"); + incrAlign(); + indent(); + if (!fiType.returnType().equals(void.class)) { + append("return (" + fiType.returnType().getName() + ")"); + if (fiType.returnType() != downcallType.returnType()) { + // add cast for invokeExact + append("(" + downcallType.returnType().getName() + ")"); } - append(");\n"); - decrAlign(); - indent(); - append("} catch (Throwable ex$) {\n"); - incrAlign(); - indent(); - append("throw new AssertionError(\"should not reach here\", ex$);\n"); - decrAlign(); - indent(); - append("}\n"); - decrAlign(); - indent(); - append("};\n"); - decrAlign(); - indent(); - append("}\n"); - decrAlign(); - }); - } - - @Override - protected void emitWithConstantClass(Consumer constantConsumer) { - enclosing.emitWithConstantClass(constantConsumer); + } + append(mhConstant.accessExpression() + ".invokeExact(symbol"); + if (fiType.parameterCount() > 0) { + String params = IntStream.range(0, fiType.parameterCount()) + .mapToObj(i -> { + String paramExpr = "_" + parameterName(i); + if (fiType.parameterType(i) != downcallType.parameterType(i)) { + // add cast for invokeExact + return "(" + downcallType.parameterType(i).getName() + ")" + paramExpr; + } else { + return paramExpr; + } + }) + .collect(Collectors.joining(", ")); + append(", " + params); + } + append(");\n"); + decrAlign(); + indent(); + append("} catch (Throwable ex$) {\n"); + incrAlign(); + indent(); + append("throw new AssertionError(\"should not reach here\", ex$);\n"); + decrAlign(); + indent(); + append("}\n"); + decrAlign(); + indent(); + append("};\n"); + decrAlign(); + indent(); + append("}\n"); + decrAlign(); } } diff --git a/src/main/java/org/openjdk/jextract/impl/HeaderFileBuilder.java b/src/main/java/org/openjdk/jextract/impl/HeaderFileBuilder.java index 9237f830..d1cc34d0 100644 --- a/src/main/java/org/openjdk/jextract/impl/HeaderFileBuilder.java +++ b/src/main/java/org/openjdk/jextract/impl/HeaderFileBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,7 +34,7 @@ import org.openjdk.jextract.Declaration; import org.openjdk.jextract.Type; -import org.openjdk.jextract.impl.ConstantBuilder.Constant; +import org.openjdk.jextract.impl.Constants.Constant; import java.lang.invoke.MethodType; import java.util.ArrayList; @@ -74,30 +74,26 @@ public void addVar(Declaration.Variable varTree, String javaName, MemoryLayout layout, Optional fiName) { String nativeName = varTree.name(); if (layout instanceof SequenceLayout || layout instanceof GroupLayout) { - emitWithConstantClass(constantBuilder -> { - if (layout.byteSize() > 0) { - emitDocComment(varTree); - constantBuilder.addSegment(javaName, nativeName, layout) - .emitGetter(this, MEMBER_MODS, Constant.QUALIFIED_NAME, nativeName); - } - }); + if (layout.byteSize() > 0) { + emitDocComment(varTree); + constants().addSegment(nativeName, layout) + .emitGetter(this, MEMBER_MODS, javaName, nativeName); + }; } else if (layout instanceof ValueLayout valueLayout) { - emitWithConstantClass(constantBuilder -> { - constantBuilder.addLayout(javaName, valueLayout) - .emitGetter(this, MEMBER_MODS, Constant.QUALIFIED_NAME); - Constant vhConstant = constantBuilder.addGlobalVarHandle(javaName, nativeName, valueLayout) - .emitGetter(this, MEMBER_MODS, Constant.QUALIFIED_NAME); - Constant segmentConstant = constantBuilder.addSegment(javaName, nativeName, valueLayout) - .emitGetter(this, MEMBER_MODS, Constant.QUALIFIED_NAME, nativeName); - emitDocComment(varTree, "Getter for variable:"); - emitGlobalGetter(segmentConstant, vhConstant, javaName, nativeName, valueLayout.carrier()); - emitDocComment(varTree, "Setter for variable:"); - emitGlobalSetter(segmentConstant, vhConstant, javaName, nativeName, valueLayout.carrier()); + constants().addLayout(valueLayout) + .emitGetter(this, MEMBER_MODS, javaName); + Constant vhConstant = constants().addGlobalVarHandle(valueLayout) + .emitGetter(this, MEMBER_MODS, javaName); + Constant segmentConstant = constants().addSegment(nativeName, valueLayout) + .emitGetter(this, MEMBER_MODS, javaName, nativeName); + emitDocComment(varTree, "Getter for variable:"); + emitGlobalGetter(segmentConstant, vhConstant, javaName, nativeName, valueLayout.carrier()); + emitDocComment(varTree, "Setter for variable:"); + emitGlobalSetter(segmentConstant, vhConstant, javaName, nativeName, valueLayout.carrier()); - if (fiName.isPresent()) { - emitFunctionalInterfaceGetter(fiName.get(), javaName); - } - }); + if (fiName.isPresent()) { + emitFunctionalInterfaceGetter(fiName.get(), javaName); + } } } @@ -107,29 +103,21 @@ public void addFunction(Declaration.Function funcTree, FunctionDescriptor descri String nativeName = funcTree.name(); boolean isVarargs = funcTree.type().varargs(); - emitWithConstantClass(constantBuilder -> { - Constant mhConstant = constantBuilder.addDowncallMethodHandle(javaName, nativeName, descriptor, isVarargs, false) - .emitGetter(this, MEMBER_MODS, Constant.QUALIFIED_NAME, nativeName); - MethodType downcallType = descriptor.toMethodType(); - boolean needsAllocator = descriptor.returnLayout().isPresent() && - descriptor.returnLayout().get() instanceof GroupLayout; - emitDocComment(funcTree); - emitFunctionWrapper(mhConstant, javaName, nativeName, downcallType, needsAllocator, isVarargs, parameterNames); - }); + Constant mhConstant = constants().addDowncallMethodHandle(nativeName, descriptor, isVarargs) + .emitGetter(this, MEMBER_MODS, javaName, nativeName); + MethodType downcallType = descriptor.toMethodType(); + boolean needsAllocator = descriptor.returnLayout().isPresent() && + descriptor.returnLayout().get() instanceof GroupLayout; + emitDocComment(funcTree); + emitFunctionWrapper(mhConstant, javaName, nativeName, downcallType, needsAllocator, isVarargs, parameterNames); } @Override public void addConstant(Declaration.Constant constantTree, String javaName, Class javaType) { Object value = constantTree.value(); emitDocComment(constantTree); - if (javaType.equals(MemorySegment.class)) { - emitWithConstantClass(constantBuilder -> { - constantBuilder.addConstantDesc(javaName, javaType, value) - .emitGetter(this, MEMBER_MODS, Constant.JAVA_NAME); - }); - } else { - emitGetter(MEMBER_MODS, javaType, javaName, getConstantString(javaType, value)); - } + constants().addConstantDesc(javaType, value) + .emitGetter(this, MEMBER_MODS, c -> javaName); } // private generation @@ -150,7 +138,7 @@ private void emitFunctionWrapper(Constant mhConstant, String javaName, String na incrAlign(); indent(); append("var mh$ = "); - append(mhConstant.kind().fieldName(javaName)); + append(mhConstant.getterName(javaName)); append("();\n"); indent(); append("try {\n"); @@ -230,7 +218,7 @@ void emitPrimitiveTypedef(Declaration.Typedef typedefTree, Type.Primitive primTy append(" " + Utils.layoutDeclarationType(primType.kind().layout().orElseThrow()).getSimpleName()); append(" " + name); append(" = "); - append(toplevel().rootConstants().resolvePrimitiveLayout((ValueLayout)kind.layout().get()).accessExpression()); + append(constants().addLayout(kind.layout().get()).accessExpression()); append(";\n"); decrAlign(); } @@ -251,7 +239,7 @@ void emitPointerTypedef(Declaration.Typedef typedefTree, String name) { append(" AddressLayout "); append(name); append(" = "); - append(toplevel().rootConstants().resolvePrimitiveLayout(TypeImpl.PointerImpl.POINTER_LAYOUT).accessExpression()); + append(constants().addLayout(TypeImpl.PointerImpl.POINTER_LAYOUT).accessExpression()); append(";\n"); decrAlign(); } @@ -263,41 +251,6 @@ private boolean primitiveKindSupported(Type.Primitive.Kind kind) { }; } - private String getConstantString(Class type, Object value) { - StringBuilder buf = new StringBuilder(); - if (type == float.class) { - float f = ((Number)value).floatValue(); - if (Float.isFinite(f)) { - buf.append(value); - buf.append("f"); - } else { - buf.append("Float.valueOf(\""); - buf.append(value); - buf.append("\")"); - } - } else if (type == long.class) { - buf.append(value); - buf.append("L"); - } else if (type == double.class) { - double d = ((Number)value).doubleValue(); - if (Double.isFinite(d)) { - buf.append(value); - buf.append("d"); - } else { - buf.append("Double.valueOf(\""); - buf.append(value); - buf.append("\")"); - } - } else if (type == boolean.class) { - boolean booleanValue = ((Number)value).byteValue() != 0; - buf.append(booleanValue); - } else { - buf.append("(" + type.getName() + ")"); - buf.append(value + "L"); - } - return buf.toString(); - } - private void emitGlobalGetter(Constant segmentConstant, Constant vhConstant, String javaName, String nativeName, Class type) { incrAlign(); indent(); diff --git a/src/main/java/org/openjdk/jextract/impl/JavaSourceBuilder.java b/src/main/java/org/openjdk/jextract/impl/JavaSourceBuilder.java index ff14eac2..d205a2db 100644 --- a/src/main/java/org/openjdk/jextract/impl/JavaSourceBuilder.java +++ b/src/main/java/org/openjdk/jextract/impl/JavaSourceBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,11 +32,8 @@ import org.openjdk.jextract.Type; import javax.tools.JavaFileObject; -import java.util.HashSet; import java.util.List; import java.util.Optional; -import java.util.Set; -import java.util.function.Consumer; public abstract class JavaSourceBuilder { @@ -79,5 +76,5 @@ public void addFunctionalInterface(Type.Function funcType, String javaName, abstract boolean isEnclosedBySameName(String name); - abstract protected void emitWithConstantClass(Consumer constantConsumer); + abstract protected Constants constants(); } diff --git a/src/main/java/org/openjdk/jextract/impl/StructBuilder.java b/src/main/java/org/openjdk/jextract/impl/StructBuilder.java index c935aa78..9047accb 100644 --- a/src/main/java/org/openjdk/jextract/impl/StructBuilder.java +++ b/src/main/java/org/openjdk/jextract/impl/StructBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,10 +29,10 @@ import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; import java.lang.foreign.SequenceLayout; -import java.lang.foreign.UnionLayout; import java.lang.foreign.ValueLayout; import org.openjdk.jextract.Declaration; import org.openjdk.jextract.Type; +import org.openjdk.jextract.impl.Constants.Constant; import java.util.ArrayDeque; import java.util.ArrayList; @@ -44,7 +44,7 @@ /** * This class generates static utilities class for C structs, unions. */ -class StructBuilder extends ConstantBuilder { +class StructBuilder extends ClassSourceBuilder { private static final String MEMBER_MODS = "public static"; @@ -52,10 +52,11 @@ class StructBuilder extends ConstantBuilder { private final GroupLayout structLayout; private final Type structType; private final Deque prefixElementNames; + private Constant layoutConstant; StructBuilder(JavaSourceBuilder enclosing, Declaration.Scoped structTree, String name, GroupLayout structLayout) { - super(enclosing, name); + super(enclosing, Kind.CLASS, name); this.structTree = structTree; this.structLayout = structLayout; this.structType = Type.declared(structTree); @@ -84,8 +85,8 @@ private List prefixNamesList() { void classBegin() { if (!inAnonymousNested()) { super.classBegin(); - addLayout(layoutField(), ((Type.Declared) structType).tree().layout().orElseThrow()) - .emitGetter(this, MEMBER_MODS, Constant.SUFFIX_ONLY); + layoutConstant = constants().addLayout(((Type.Declared) structType).tree().layout().orElseThrow()); + layoutConstant.emitGetter(this, MEMBER_MODS, Constant::nameSuffix); } } @@ -152,8 +153,8 @@ public void addVar(Declaration.Variable varTree, String javaName, emitSegmentGetter(javaName, nativeName, layout); } } else if (layout instanceof ValueLayout valueLayout) { - Constant vhConstant = addFieldVarHandle(javaName, nativeName, valueLayout, layoutField(), prefixNamesList()) - .emitGetter(this, MEMBER_MODS, Constant.QUALIFIED_NAME); + Constant vhConstant = constants().addFieldVarHandle(nativeName, structLayout, prefixNamesList()) + .emitGetter(this, MEMBER_MODS, javaName); emitFieldDocComment(varTree, "Getter for field:"); emitFieldGetter(vhConstant, javaName, valueLayout.carrier()); emitFieldDocComment(varTree, "Setter for field:"); @@ -330,20 +331,4 @@ private void emitIndexedFieldSetter(Constant vhConstant, String javaName, Class< append("}\n"); decrAlign(); } - - private String qualifiedName(ClassSourceBuilder builder) { - if (builder.isNested()) { - String prefix = qualifiedName((ClassSourceBuilder)builder.enclosing); - return prefix.isEmpty() ? - builder.className() : - prefix + "$" + builder.className(); - } else { - return ""; - } - } - - private String layoutField() { - String suffix = (structLayout instanceof UnionLayout) ? "union" : "struct"; - return qualifiedName(this) + "$" + suffix; - } } diff --git a/src/main/java/org/openjdk/jextract/impl/ToplevelBuilder.java b/src/main/java/org/openjdk/jextract/impl/ToplevelBuilder.java index 85fefed3..34086223 100644 --- a/src/main/java/org/openjdk/jextract/impl/ToplevelBuilder.java +++ b/src/main/java/org/openjdk/jextract/impl/ToplevelBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,19 +26,15 @@ import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.GroupLayout; -import java.lang.foreign.MemorySegment; import java.lang.foreign.MemoryLayout; -import java.lang.foreign.ValueLayout; import org.openjdk.jextract.Declaration; import org.openjdk.jextract.Type; -import org.openjdk.jextract.Type.Primitive; -import org.openjdk.jextract.Type.Primitive.Kind; import javax.tools.JavaFileObject; import java.lang.constant.ClassDesc; -import java.util.*; -import java.util.function.Consumer; -import java.util.stream.Collectors; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; /** * A helper class to generate header interface class in source form. @@ -50,34 +46,27 @@ class ToplevelBuilder extends JavaSourceBuilder { private int declCount; private final List builders = new ArrayList<>(); private SplitHeader lastHeader; - private final RootConstants rootConstants; private int headersCount; private final ClassDesc headerDesc; + Constants constants; + static final int DECLS_PER_HEADER_CLASS = Integer.getInteger("jextract.decls.per.header", 1000); ToplevelBuilder(String packageName, String headerClassName) { this.headerDesc = ClassDesc.of(packageName, headerClassName); SplitHeader first = lastHeader = new FirstHeader(headerClassName); - rootConstants = new RootConstants(); - first.classBegin(); builders.add(first); - } - - public RootConstants rootConstants() { - return rootConstants; + constants = new Constants(this); + first.classBegin(); } public List toFiles() { - if (constantBuilder != null) { - constantBuilder.classEnd(); - } lastHeader.classEnd(); - builders.addAll(constantBuilders); - builders.add(rootConstants); List files = new ArrayList<>(); files.addAll(builders.stream() .flatMap(b -> b.toFiles().stream()).toList()); + files.addAll(constants.toFiles()); return files; } @@ -95,6 +84,11 @@ public String packageName() { return headerDesc.packageName(); } + @Override + protected Constants constants() { + return constants; + } + @Override public void addVar(Declaration.Variable varTree, String javaName, MemoryLayout layout, Optional fiName) { @@ -217,122 +211,4 @@ String build() { last != this ? "extends " + last.className() : ""); } } - - // constant support - - class RootConstants extends ConstantBuilder { - - private final Map primitiveLayouts = new HashMap<>(); - - public RootConstants() { - super(ToplevelBuilder.this, "Constants$root"); - classBegin(); - addPrimitiveLayout("C_BOOL", Type.Primitive.Kind.Bool); - addPrimitiveLayout("C_CHAR", Type.Primitive.Kind.Char); - addPrimitiveLayout("C_SHORT", Type.Primitive.Kind.Short); - addPrimitiveLayout("C_INT", Type.Primitive.Kind.Int); - addPrimitiveLayout("C_LONG", Type.Primitive.Kind.Long); - addPrimitiveLayout("C_LONG_LONG", Type.Primitive.Kind.LongLong); - addPrimitiveLayout("C_FLOAT", Type.Primitive.Kind.Float); - addPrimitiveLayout("C_DOUBLE", Type.Primitive.Kind.Double); - addPrimitiveLayout("C_POINTER", TypeImpl.PointerImpl.POINTER_LAYOUT); - classEnd(); - } - - @Override - String mods() { - return "final "; // Constants$root package-private! - } - - @Override - protected String primitiveLayoutString(ValueLayout vl) { - if (vl.carrier() == boolean.class) { - return "JAVA_BOOLEAN"; - } else if (vl.carrier() == char.class) { - return "JAVA_CHAR" + withBitAlignmentIfNeeded(ValueLayout.JAVA_CHAR, vl); - } else if (vl.carrier() == byte.class) { - return "JAVA_BYTE"; - } else if (vl.carrier() == short.class) { - return "JAVA_SHORT" + withBitAlignmentIfNeeded(ValueLayout.JAVA_SHORT, vl); - } else if (vl.carrier() == int.class) { - return "JAVA_INT" + withBitAlignmentIfNeeded(ValueLayout.JAVA_INT, vl); - } else if (vl.carrier() == float.class) { - return "JAVA_FLOAT" + withBitAlignmentIfNeeded(ValueLayout.JAVA_FLOAT, vl); - } else if (vl.carrier() == long.class) { - return "JAVA_LONG" + withBitAlignmentIfNeeded(ValueLayout.JAVA_LONG, vl); - } else if (vl.carrier() == double.class) { - return "JAVA_DOUBLE" + withBitAlignmentIfNeeded(ValueLayout.JAVA_DOUBLE, vl); - } else if (vl.carrier() == MemorySegment.class) { - return "ADDRESS.withBitAlignment(" + vl.bitAlignment() + ")" + - ".withTargetLayout(MemoryLayout.sequenceLayout(" + - resolvePrimitiveLayout((ValueLayout)Primitive.Kind.Char.layout().get()).accessExpression() + "))"; - } else { - return "MemoryLayout.paddingLayout(" + vl.bitSize() + ")"; - } - } - - String withBitAlignmentIfNeeded(ValueLayout original, ValueLayout actual) { - if (original.bitAlignment() == actual.bitAlignment()) { - return ""; - } - return ".withBitAlignment(" + actual.bitAlignment() + ")"; - } - - private Constant addPrimitiveLayout(String javaName, ValueLayout layout) { - ValueLayout layoutNoName = normalize(layout); - Constant layoutConstant = super.addLayout(javaName, layoutNoName); - primitiveLayouts.put(layoutNoName, layoutConstant); - return layoutConstant; - } - - private Constant addPrimitiveLayout(String javaName, Type.Primitive.Kind kind) { - return addPrimitiveLayout(javaName, (ValueLayout)kind.layout().orElseThrow()); - } - - public Constant resolvePrimitiveLayout(ValueLayout layout) { - return primitiveLayouts.get(normalize(layout)); - } - - public ValueLayout normalize(ValueLayout valueLayout) { - return valueLayout - .withBitAlignment(valueLayout.bitSize()) // use natural alignment - .withoutName(); // drop name - } - } - - // other constants - - int constant_counter = 0; - int constant_class_index = 0; - List constantBuilders = new ArrayList<>(); - - static final int CONSTANTS_PER_CLASS = Integer.getInteger("jextract.constants.per.class", 5); - ConstantBuilder constantBuilder; - - protected void emitWithConstantClass(Consumer constantConsumer) { - if (constant_counter > CONSTANTS_PER_CLASS || constantBuilder == null) { - if (constantBuilder != null) { - constantBuilder.classEnd(); - } - constant_counter = 0; - constantBuilder = new ConstantsSequelBuilder(this, "constants$" + constant_class_index++); - constantBuilders.add(constantBuilder); - constantBuilder.classBegin(); - } - constantConsumer.accept(constantBuilder); - constant_counter++; - } - - static final class ConstantsSequelBuilder extends ConstantBuilder { - - ConstantsSequelBuilder(JavaSourceBuilder enclosing, String className) { - super(enclosing, className); - } - - @Override - String mods() { - return "final "; // constants package-private! - } - } - } diff --git a/src/main/resources/org/openjdk/jextract/impl/resources/RuntimeHelper.java.template b/src/main/resources/org/openjdk/jextract/impl/resources/RuntimeHelper.java.template index a3492fb5..a10e0170 100644 --- a/src/main/resources/org/openjdk/jextract/impl/resources/RuntimeHelper.java.template +++ b/src/main/resources/org/openjdk/jextract/impl/resources/RuntimeHelper.java.template @@ -19,6 +19,9 @@ import java.util.Arrays; import java.util.Optional; import java.util.stream.Stream; +import java.lang.foreign.AddressLayout; +import java.lang.foreign.MemoryLayout; + import static java.lang.foreign.Linker.*; import static java.lang.foreign.ValueLayout.*; @@ -29,6 +32,7 @@ final class RuntimeHelper { private static final MethodHandles.Lookup MH_LOOKUP = MethodHandles.lookup(); private static final SymbolLookup SYMBOL_LOOKUP; private static final SegmentAllocator THROWING_ALLOCATOR = (x, y) -> { throw new AssertionError("should not reach here"); }; + static final AddressLayout POINTER = ValueLayout.ADDRESS.withTargetLayout(MemoryLayout.sequenceLayout(JAVA_BYTE)); final static SegmentAllocator CONSTANT_ALLOCATOR = (size, align) -> Arena.ofAuto().allocate(size, align);