1 /* 2 * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.internal.jextract.impl; 27 28 import jdk.incubator.foreign.FunctionDescriptor; 29 import jdk.incubator.foreign.GroupLayout; 30 import jdk.incubator.foreign.MemoryAddress; 31 import jdk.incubator.foreign.MemoryLayout; 32 import jdk.incubator.foreign.MemorySegment; 33 import jdk.incubator.foreign.SequenceLayout; 34 import jdk.incubator.foreign.ValueLayout; 35 36 import java.lang.invoke.MethodHandle; 37 import java.lang.invoke.VarHandle; 38 import java.util.HashMap; 39 import java.util.List; 40 import java.util.Map; 41 import java.util.Objects; 42 import java.util.function.Consumer; 43 import java.util.function.Function; 44 import java.util.function.Supplier; 45 import java.util.stream.Collectors; 46 47 public class ConstantBuilder extends ClassSourceBuilder { 48 49 // set of names generates already 50 private final Map<String, Constant> namesGenerated = new HashMap<>(); 51 52 public ConstantBuilder(JavaSourceBuilder enclosing, String className) { 53 super(enclosing, Kind.CLASS, className); 54 } 55 56 String memberMods() { 57 return kind == ClassSourceBuilder.Kind.CLASS ? 58 "static final " : ""; 59 } 60 61 // public API 62 63 public Constant addLayout(String javaName, MemoryLayout layout) { 64 return emitIfAbsent(javaName, Constant.Kind.LAYOUT, 65 () -> emitLayoutField(javaName, layout)); 66 } 67 68 public Constant addFieldVarHandle(String javaName, String nativeName, VarInfo varInfo, 69 String rootJavaName, List<String> prefixElementNames) { 70 return addVarHandle(javaName, nativeName, varInfo, rootJavaName, prefixElementNames); 71 } 72 73 public Constant addGlobalVarHandle(String javaName, String nativeName, VarInfo varInfo) { 74 return addVarHandle(javaName, nativeName, varInfo, null, List.of()); 75 } 76 77 private Constant addVarHandle(String javaName, String nativeName, VarInfo varInfo, 78 String rootLayoutName, List<String> prefixElementNames) { 79 return emitIfAbsent(javaName, Constant.Kind.VAR_HANDLE, 80 () -> emitVarHandleField(javaName, nativeName, varInfo, rootLayoutName, prefixElementNames)); 81 } 82 83 public Constant addMethodHandle(String javaName, String nativeName, FunctionInfo functionInfo, boolean virtual) { 84 return emitIfAbsent(javaName, Constant.Kind.METHOD_HANDLE, 85 () -> emitMethodHandleField(javaName, nativeName, functionInfo, virtual)); 86 } 87 88 public Constant addSegment(String javaName, String nativeName, MemoryLayout layout) { 89 return emitIfAbsent(javaName, Constant.Kind.SEGMENT, 90 () -> emitSegmentField(javaName, nativeName, layout)); 91 } 92 93 public Constant addFunctionDesc(String javaName, FunctionDescriptor desc) { 94 return emitIfAbsent(javaName, Constant.Kind.FUNCTION_DESCRIPTOR, 95 () -> emitFunctionDescField(javaName, desc)); 96 } 97 98 public Constant addConstantDesc(String javaName, Class<?> type, Object value) { 99 if (type == MemorySegment.class) { 100 return emitIfAbsent(javaName, Constant.Kind.SEGMENT, 101 () -> emitConstantSegment(javaName, value)); 102 } else if (type == MemoryAddress.class) { 103 return emitIfAbsent(javaName, Constant.Kind.ADDRESS, 104 () -> emitConstantAddress(javaName, value)); 105 } else { 106 throw new UnsupportedOperationException(); 107 } 108 } 109 110 static class Constant { 111 112 enum Kind { 113 LAYOUT(MemoryLayout.class, "$LAYOUT"), 114 METHOD_HANDLE(MethodHandle.class, "$MH"), 115 VAR_HANDLE(VarHandle.class, "$VH"), 116 FUNCTION_DESCRIPTOR(FunctionDescriptor.class, "$FUNC"), 117 ADDRESS(MemoryAddress.class, "$ADDR"), 118 SEGMENT(MemorySegment.class, "$SEGMENT"); 119 120 final Class<?> type; 121 final String nameSuffix; 122 123 Kind(Class<?> type, String nameSuffix) { 124 this.type = type; 125 this.nameSuffix = nameSuffix; 126 } 127 128 String fieldName(String javaName) { 129 return javaName + nameSuffix; 130 } 131 } 132 133 private final String className; 134 private final String javaName; 135 private final Kind kind; 136 137 Constant(String className, String javaName, Kind kind) { 138 this.className = className; 139 this.javaName = javaName; 140 this.kind = kind; 141 } 142 143 List<String> getterNameParts() { 144 return List.of(className, javaName, kind.nameSuffix); 145 } 146 147 String accessExpression() { 148 return className + "." + kind.fieldName(javaName); 149 } 150 151 Constant emitGetter(ClassSourceBuilder builder, String mods, Function<List<String>, String> getterNameFunc) { 152 builder.emitGetter(mods, kind.type, getterNameFunc.apply(getterNameParts()), accessExpression()); 153 return this; 154 } 155 156 Constant emitGetter(ClassSourceBuilder builder, String mods, Function<List<String>, String> getterNameFunc, String symbolName) { 157 builder.emitGetter(mods, kind.type, getterNameFunc.apply(getterNameParts()), accessExpression(), true, symbolName); 158 return this; 159 } 160 161 static final Function<List<String>, String> QUALIFIED_NAME = 162 l -> l.stream().skip(1).collect(Collectors.joining()); 163 164 static final Function<List<String>, String> JAVA_NAME = 165 l -> l.get(1); 166 167 static final Function<List<String>, String> SUFFIX_ONLY = 168 l -> l.get(2); 169 } 170 171 // private generators 172 173 public Constant emitIfAbsent(String name, Constant.Kind kind, Supplier<Constant> constantFactory) { 174 String lookupName = kind.fieldName(name); 175 Constant constant = namesGenerated.get(lookupName); 176 if (constant == null) { 177 constant = constantFactory.get(); 178 if (constant.kind != kind) { 179 throw new AssertionError("Factory return wrong kind of constant; expected: " 180 + kind + "; found: " + constant.kind); 181 } 182 namesGenerated.put(lookupName, constant); 183 } 184 return constant; 185 } 186 187 private Constant emitMethodHandleField(String javaName, String nativeName, FunctionInfo functionInfo, boolean virtual) { 188 Constant functionDesc = addFunctionDesc(javaName, functionInfo.descriptor()); 189 incrAlign(); 190 String fieldName = Constant.Kind.METHOD_HANDLE.fieldName(javaName); 191 indent(); 192 append(memberMods() + "MethodHandle "); 193 append(fieldName + " = RuntimeHelper.downcallHandle(\n"); 194 incrAlign(); 195 indent(); 196 if (!virtual) { 197 append("\"" + nativeName + "\""); 198 append(",\n"); 199 indent(); 200 } 201 append(functionDesc.accessExpression()); 202 append(", "); 203 // isVariadic 204 append(functionInfo.isVarargs()); 205 append("\n"); 206 decrAlign(); 207 indent(); 208 append(");\n"); 209 decrAlign(); 210 return new Constant(className(), javaName, Constant.Kind.METHOD_HANDLE); 211 } 212 213 private Constant emitVarHandleField(String javaName, String nativeName, VarInfo varInfo, 214 String rootLayoutName, List<String> prefixElementNames) { 215 String layoutAccess = rootLayoutName != null ? 216 Constant.Kind.LAYOUT.fieldName(rootLayoutName) : 217 addLayout(javaName, varInfo.layout()).accessExpression(); 218 incrAlign(); 219 String typeName = varInfo.carrier().getName(); 220 indent(); 221 String fieldName = Constant.Kind.VAR_HANDLE.fieldName(javaName); 222 append(memberMods() + "VarHandle " + fieldName + " = "); 223 append(layoutAccess); 224 append(".varHandle("); 225 String prefix = ""; 226 if (rootLayoutName != null) { 227 for (String prefixElementName : prefixElementNames) { 228 append(prefix + "MemoryLayout.PathElement.groupElement(\"" + prefixElementName + "\")"); 229 prefix = ", "; 230 } 231 append(prefix + "MemoryLayout.PathElement.groupElement(\"" + nativeName + "\")"); 232 } 233 append(")"); 234 append(";\n"); 235 decrAlign(); 236 return new Constant(className(), javaName, Constant.Kind.VAR_HANDLE); 237 } 238 239 private Constant emitLayoutField(String javaName, MemoryLayout layout) { 240 String fieldName = Constant.Kind.LAYOUT.fieldName(javaName); 241 incrAlign(); 242 indent(); 243 String layoutClassName = layout.getClass().getSimpleName(); 244 append(memberMods() + " " + layoutClassName + " " + fieldName + " = "); 245 emitLayoutString(layout); 246 append(";\n"); 247 decrAlign(); 248 return new Constant(className(), javaName, Constant.Kind.LAYOUT); 249 } 250 251 protected String primitiveLayoutString(ValueLayout layout) { 252 return toplevel().rootConstants().resolvePrimitiveLayout(layout).accessExpression(); 253 } 254 255 private void emitLayoutString(MemoryLayout l) { 256 if (l instanceof ValueLayout val) { 257 append(primitiveLayoutString(val)); 258 } else if (l instanceof SequenceLayout seq) { 259 append("MemoryLayout.sequenceLayout("); 260 if (seq.elementCount().isPresent()) { 261 append(seq.elementCount().getAsLong() + ", "); 262 } 263 emitLayoutString(seq.elementLayout()); 264 append(")"); 265 } else if (l instanceof GroupLayout group) { 266 if (group.isStruct()) { 267 append("MemoryLayout.structLayout(\n"); 268 } else { 269 append("MemoryLayout.unionLayout(\n"); 270 } 271 incrAlign(); 272 String delim = ""; 273 for (MemoryLayout e : group.memberLayouts()) { 274 append(delim); 275 indent(); 276 emitLayoutString(e); 277 delim = ",\n"; 278 } 279 append("\n"); 280 decrAlign(); 281 indent(); 282 append(")"); 283 } else { 284 // padding (or unsupported) 285 append("MemoryLayout.paddingLayout(" + l.bitSize() + ")"); 286 } 287 if (l.name().isPresent()) { 288 append(".withName(\"" + l.name().get() + "\")"); 289 } 290 } 291 292 private Constant emitFunctionDescField(String javaName, FunctionDescriptor desc) { 293 incrAlign(); 294 indent(); 295 String fieldName = Constant.Kind.FUNCTION_DESCRIPTOR.fieldName(javaName); 296 final boolean noArgs = desc.argumentLayouts().isEmpty(); 297 append(memberMods()); 298 append("FunctionDescriptor "); 299 append(fieldName); 300 append(" = "); 301 if (desc.returnLayout().isPresent()) { 302 append("FunctionDescriptor.of("); 303 emitLayoutString(desc.returnLayout().get()); 304 if (!noArgs) { 305 append(","); 306 } 307 } else { 308 append("FunctionDescriptor.ofVoid("); 309 } 310 if (!noArgs) { 311 append("\n"); 312 incrAlign(); 313 String delim = ""; 314 for (MemoryLayout e : desc.argumentLayouts()) { 315 append(delim); 316 indent(); 317 emitLayoutString(e); 318 delim = ",\n"; 319 } 320 append("\n"); 321 decrAlign(); 322 indent(); 323 } 324 append(");\n"); 325 decrAlign(); 326 return new Constant(className(), javaName, Constant.Kind.FUNCTION_DESCRIPTOR); 327 } 328 329 private Constant emitConstantSegment(String javaName, Object value) { 330 incrAlign(); 331 indent(); 332 String fieldName = Constant.Kind.SEGMENT.fieldName(javaName); 333 append(memberMods()); 334 append("MemorySegment "); 335 append(fieldName); 336 append(" = RuntimeHelper.CONSTANT_ALLOCATOR.allocateUtf8String(\""); 337 append(Utils.quote(Objects.toString(value))); 338 append("\");\n"); 339 decrAlign(); 340 return new Constant(className(), javaName, Constant.Kind.SEGMENT); 341 } 342 343 private Constant emitConstantAddress(String javaName, Object value) { 344 incrAlign(); 345 indent(); 346 String fieldName = Constant.Kind.ADDRESS.fieldName(javaName); 347 append(memberMods()); 348 append("MemoryAddress "); 349 append(fieldName); 350 append(" = MemoryAddress.ofLong("); 351 append(((Number)value).longValue()); 352 append("L);\n"); 353 decrAlign(); 354 return new Constant(className(), javaName, Constant.Kind.ADDRESS); 355 } 356 357 private Constant emitSegmentField(String javaName, String nativeName, MemoryLayout layout) { 358 Constant layoutConstant = addLayout(javaName, layout); 359 incrAlign(); 360 indent(); 361 String fieldName = Constant.Kind.SEGMENT.fieldName(javaName); 362 append(memberMods()); 363 append("MemorySegment "); 364 append(fieldName); 365 append(" = "); 366 append("RuntimeHelper.lookupGlobalVariable("); 367 append("\"" + nativeName + "\", "); 368 append(layoutConstant.accessExpression()); 369 append(");\n"); 370 decrAlign(); 371 return new Constant(className(), javaName, Constant.Kind.SEGMENT); 372 } 373 374 @Override 375 protected void emitWithConstantClass(Consumer<ConstantBuilder> constantConsumer) { 376 constantConsumer.accept(this); 377 } 378 }