1 /* 2 * Copyright (c) 2024, 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 hat.ifacemapper; 27 28 import hat.ifacemapper.accessor.AccessorInfo; 29 import hat.ifacemapper.accessor.ArrayInfo; 30 import hat.ifacemapper.accessor.ScalarInfo; 31 //import jdk.internal.ValueBased; 32 //import jdk.internal.ValueBased; 33 34 35 import java.lang.classfile.Annotation; 36 import java.lang.classfile.ClassBuilder; 37 import java.lang.classfile.CodeBuilder; 38 import java.lang.classfile.Label; 39 import java.lang.classfile.attribute.RuntimeVisibleAnnotationsAttribute; 40 import java.lang.constant.ClassDesc; 41 import java.lang.constant.DirectMethodHandleDesc; 42 import java.lang.constant.DynamicCallSiteDesc; 43 import java.lang.constant.DynamicConstantDesc; 44 import java.lang.constant.MethodTypeDesc; 45 import java.lang.foreign.GroupLayout; 46 import java.lang.foreign.MemorySegment; 47 import java.lang.foreign.ValueLayout; 48 import java.lang.invoke.MethodHandle; 49 import java.lang.invoke.StringConcatFactory; 50 import java.util.ArrayList; 51 import java.util.Collections; 52 import java.util.List; 53 import java.util.Objects; 54 import java.util.stream.Collectors; 55 56 import static hat.ifacemapper.MapperUtil.SECRET_BOUND_SCHEMA_METHOD_NAME; 57 import static hat.ifacemapper.MapperUtil.SECRET_LAYOUT_METHOD_NAME; 58 import static hat.ifacemapper.MapperUtil.SECRET_OFFSET_METHOD_NAME; 59 import static hat.ifacemapper.MapperUtil.SECRET_SEGMENT_METHOD_NAME; 60 import static hat.ifacemapper.MapperUtil.desc; 61 import static java.lang.classfile.ClassFile.ACC_FINAL; 62 import static java.lang.classfile.ClassFile.ACC_PRIVATE; 63 import static java.lang.classfile.ClassFile.ACC_PUBLIC; 64 import static java.lang.classfile.ClassFile.ACC_SUPER; 65 import static java.lang.constant.ConstantDescs.BSM_CLASS_DATA_AT; 66 import static java.lang.constant.ConstantDescs.CD_CallSite; 67 import static java.lang.constant.ConstantDescs.CD_MethodHandle; 68 import static java.lang.constant.ConstantDescs.CD_Object; 69 import static java.lang.constant.ConstantDescs.CD_String; 70 import static java.lang.constant.ConstantDescs.CD_boolean; 71 import static java.lang.constant.ConstantDescs.CD_int; 72 import static java.lang.constant.ConstantDescs.CD_long; 73 import static java.lang.constant.ConstantDescs.CD_void; 74 import static java.lang.constant.ConstantDescs.INIT_NAME; 75 import static java.lang.constant.ConstantDescs.MTD_void; 76 import static java.lang.constant.ConstantDescs.ofCallsiteBootstrap; 77 78 //@ValueBased 79 final class ByteCodeGenerator { 80 81 static final String SEGMENT_FIELD_NAME = "segment"; 82 static final String LAYOUT_FIELD_NAME = "layout"; 83 static final String OFFSET_FIELD_NAME = "offset"; 84 static final String BOUND_SCHEMA_FIELD_NAME = "boundSchema"; 85 86 private static final ClassDesc VALUE_LAYOUTS_CLASS_DESC = desc(ValueLayout.class); 87 private static final ClassDesc MEMORY_SEGMENT_CLASS_DESC = desc(MemorySegment.class); 88 private static final ClassDesc LAYOUT_CLASS_DESC = desc(GroupLayout.class); 89 private static final ClassDesc BOUND_SCHEMA_CLASS_DESC = desc(BoundSchema.class); 90 91 92 private final Class<?> type; 93 private final ClassBuilder cb; 94 private final ClassDesc classDesc; 95 private final ClassDesc interfaceClassDesc; 96 97 private ByteCodeGenerator(Class<?> type, ClassDesc classDesc, ClassBuilder cb) { 98 this.type = type; 99 this.cb = cb; 100 this.classDesc = classDesc; 101 this.interfaceClassDesc = desc(type); 102 } 103 104 void classDefinition() { 105 // @ValueBased 106 // Annotation valueBased = Annotation.of(desc(ValueBased.class)); 107 //cb.with(RuntimeVisibleAnnotationsAttribute.of(valueBased)); 108 // public final 109 cb.withFlags(ACC_PUBLIC | ACC_FINAL | ACC_SUPER); 110 // extends Object 111 cb.withSuperclass(CD_Object); 112 List<ClassDesc> interfaces = new ArrayList<>(); 113 // implements "type" 114 interfaces.add(interfaceClassDesc); 115 if (SegmentMapper.Discoverable.class.isAssignableFrom(type)) { 116 // implements SegmentMapper.Discoverable 117 interfaces.add(desc(SegmentMapper.Discoverable.class)); 118 } 119 cb.withInterfaceSymbols(interfaces); 120 // private final MemorySegment segment; 121 cb.withField(SEGMENT_FIELD_NAME, MEMORY_SEGMENT_CLASS_DESC, ACC_PRIVATE | ACC_FINAL); 122 // private final GroupLayout layout; 123 cb.withField(LAYOUT_FIELD_NAME, LAYOUT_CLASS_DESC, ACC_PRIVATE | ACC_FINAL); 124 // private final GroupLayout layout; 125 cb.withField(BOUND_SCHEMA_FIELD_NAME, BOUND_SCHEMA_CLASS_DESC, ACC_PRIVATE | ACC_FINAL); 126 // private final long offset; 127 cb.withField(OFFSET_FIELD_NAME, CD_long, ACC_PRIVATE | ACC_FINAL); 128 } 129 130 void constructor(long layoutByteSize) { 131 final int THIS_VAR_SLOT = 0; 132 final int SEGMENT_VAR_SLOT = 1; 133 final int LAYOUT_VAR_SLOT = 2; 134 final int BOUND_SCHEMA_VAR_SLOT = 3; 135 final int OFFSET_VAR_SLOT = 4; 136 cb.withMethodBody(INIT_NAME, MethodTypeDesc.of(CD_void, MEMORY_SEGMENT_CLASS_DESC, LAYOUT_CLASS_DESC, BOUND_SCHEMA_CLASS_DESC, CD_long), ACC_PUBLIC, 137 cob -> cob 138 .aload(THIS_VAR_SLOT) 139 .invokespecial(CD_Object, INIT_NAME, MTD_void, false) // Call Object's constructor 140 141 .aload(THIS_VAR_SLOT) 142 .aload(SEGMENT_VAR_SLOT) 143 .checkcast(MEMORY_SEGMENT_CLASS_DESC) 144 .putfield(classDesc, SEGMENT_FIELD_NAME, MEMORY_SEGMENT_CLASS_DESC) // this.segment = segment 145 146 .aload(THIS_VAR_SLOT) 147 .aload(LAYOUT_VAR_SLOT) 148 .checkcast(LAYOUT_CLASS_DESC) 149 .putfield(classDesc, LAYOUT_FIELD_NAME, LAYOUT_CLASS_DESC) // this.layout = layout 150 151 .aload(THIS_VAR_SLOT) 152 .aload(BOUND_SCHEMA_VAR_SLOT) 153 .checkcast(BOUND_SCHEMA_CLASS_DESC) 154 .putfield(classDesc, BOUND_SCHEMA_FIELD_NAME, BOUND_SCHEMA_CLASS_DESC) // this.boundSchema = boundSchema 155 156 .aload(THIS_VAR_SLOT) 157 .lload(OFFSET_VAR_SLOT) // offset 158 .ldc(layoutByteSize) // size 159 .aload(SEGMENT_VAR_SLOT) // segment 160 .invokeinterface(desc(MemorySegment.class), "byteSize", MethodTypeDesc.of(CD_long)) 161 162 .invokestatic(desc(Objects.class), "checkFromIndexSize", MethodTypeDesc.of(CD_long, CD_long, CD_long, CD_long)) 163 164 .putfield(classDesc, OFFSET_FIELD_NAME, CD_long) // this.offset = offset 165 .return_() 166 ); 167 168 } 169 170 void obscuredSegment() { 171 cb.withMethodBody(SECRET_SEGMENT_METHOD_NAME, MethodTypeDesc.of(MEMORY_SEGMENT_CLASS_DESC), ACC_PUBLIC, cob -> 172 cob.aload(0) 173 .getfield(classDesc, SEGMENT_FIELD_NAME, MEMORY_SEGMENT_CLASS_DESC) 174 .areturn() 175 ); 176 } 177 178 void obscuredLayout() { 179 cb.withMethodBody(SECRET_LAYOUT_METHOD_NAME, MethodTypeDesc.of(LAYOUT_CLASS_DESC), ACC_PUBLIC, cob -> 180 cob.aload(0) 181 .getfield(classDesc, LAYOUT_FIELD_NAME, LAYOUT_CLASS_DESC) 182 .areturn() 183 ); 184 } 185 186 187 void obscuredBoundSchema() { 188 cb.withMethodBody(SECRET_BOUND_SCHEMA_METHOD_NAME, MethodTypeDesc.of(BOUND_SCHEMA_CLASS_DESC), ACC_PUBLIC, cob -> 189 cob.aload(0) 190 .getfield(classDesc, BOUND_SCHEMA_FIELD_NAME, BOUND_SCHEMA_CLASS_DESC) 191 .areturn() 192 ); 193 } 194 195 void obscuredOffset() { 196 cb.withMethodBody(SECRET_OFFSET_METHOD_NAME, MethodTypeDesc.of(CD_long), ACC_PUBLIC, cob -> 197 cob.aload(0) 198 .getfield(classDesc, OFFSET_FIELD_NAME, CD_long) 199 .lreturn() 200 ); 201 } 202 203 void valueGetter(AccessorInfo info) { 204 String name = info.method().getName(); 205 ClassDesc returnDesc = desc(info.type()); 206 ScalarInfo scalarInfo = info.layoutInfo().scalarInfo().orElseThrow(); 207 208 var getDesc = MethodTypeDesc.of(returnDesc, desc(scalarInfo.interfaceType()), CD_long); 209 List<ClassDesc> parameterDesc = parameterDesc(info); 210 211 cb.withMethodBody(name, MethodTypeDesc.of(returnDesc, parameterDesc), ACC_PUBLIC, cob -> { 212 cob.aload(0) 213 .getfield(classDesc, SEGMENT_FIELD_NAME, MEMORY_SEGMENT_CLASS_DESC) 214 .getstatic(VALUE_LAYOUTS_CLASS_DESC, scalarInfo.memberName(), desc(scalarInfo.interfaceType())); 215 offsetBlock(cob, info, classDesc) 216 .invokeinterface(MEMORY_SEGMENT_CLASS_DESC, "get", getDesc); 217 // ireturn(), dreturn() etc. 218 info.layoutInfo().returnOp().accept(cob); 219 } 220 ); 221 } 222 223 void valueSetter(AccessorInfo info) { 224 String name = info.method().getName(); 225 List<ClassDesc> parameterDesc = parameterDesc(info); 226 227 ScalarInfo scalarInfo = info.layoutInfo().scalarInfo().orElseThrow(); 228 229 var setDesc = MethodTypeDesc.of(CD_void, desc(scalarInfo.interfaceType()), CD_long, desc(info.type())); 230 231 cb.withMethodBody(name, MethodTypeDesc.of(CD_void, parameterDesc), ACC_PUBLIC, cob -> { 232 cob.aload(0) 233 .getfield(classDesc, SEGMENT_FIELD_NAME, MEMORY_SEGMENT_CLASS_DESC) 234 .getstatic(VALUE_LAYOUTS_CLASS_DESC, scalarInfo.memberName(), desc(scalarInfo.interfaceType())); 235 offsetBlock(cob, info, classDesc); 236 // iload, dload, etc. 237 info.layoutInfo().paramOp().accept(cob, valueSlotNumber(parameterDesc.size())); 238 cob.invokeinterface(MEMORY_SEGMENT_CLASS_DESC, "set", setDesc) 239 .return_(); 240 } 241 ); 242 } 243 244 void invokeVirtualGetter(AccessorInfo info, 245 int boostrapIndex) { 246 247 var name = info.method().getName(); 248 var returnDesc = desc(info.type()); 249 250 DynamicConstantDesc<MethodHandle> desc = DynamicConstantDesc.of( 251 BSM_CLASS_DATA_AT, 252 boostrapIndex 253 ); 254 255 List<ClassDesc> parameterDesc = parameterDesc(info); 256 257 /* 258 Was 259 public ArgList.Arg args(long var1) { 260 return (ArgList.Arg)((MethodHandle)"_").invokeExact(this.segment, this.offset + 8L + Objects.checkIndex(var1, 4L) * 4L); 261 } 262 Now 263 public ArgList.Arg args(long var1) { 264 MethodHandle var10000 = (MethodHandle)"_"; 265 return (ArgList.Arg)this.segment.invokeExact((MethodHandle)"_", this.layout, this.offset + 8L + Objects.checkIndex(var1, 4L) * 4L); 266 } 267 268 */ 269 270 cb.withMethodBody(name, MethodTypeDesc.of(returnDesc, parameterDesc), ACC_PUBLIC, cob -> { 271 cob 272 .ldc(desc) 273 .checkcast(desc(MethodHandle.class)) // MethodHandle 274 .aload(0) 275 .getfield(classDesc, SEGMENT_FIELD_NAME, MEMORY_SEGMENT_CLASS_DESC) // MemorySegment 276 .aload(0) 277 .getfield(classDesc, LAYOUT_FIELD_NAME, LAYOUT_CLASS_DESC) // Layout 278 .aload(0) 279 .getfield(classDesc, BOUND_SCHEMA_FIELD_NAME, BOUND_SCHEMA_CLASS_DESC); // Layout 280 281 282 offsetBlock(cob, info, classDesc) 283 .invokevirtual(CD_MethodHandle, "invokeExact", MethodTypeDesc.of(CD_Object, MEMORY_SEGMENT_CLASS_DESC, 284 LAYOUT_CLASS_DESC, BOUND_SCHEMA_CLASS_DESC, CD_long)) 285 .checkcast(returnDesc) 286 .areturn(); 287 } 288 ); 289 } 290 291 void invokeVirtualSetter(AccessorInfo info, 292 int boostrapIndex) { 293 294 var name = info.method().getName(); 295 List<ClassDesc> parameterDesc = parameterDesc(info); 296 297 DynamicConstantDesc<MethodHandle> desc = DynamicConstantDesc.of( 298 BSM_CLASS_DATA_AT, 299 boostrapIndex 300 ); 301 302 cb.withMethodBody(name, MethodTypeDesc.of(CD_void, parameterDesc), ACC_PUBLIC, cob -> { 303 cob.ldc(desc) 304 .checkcast(desc(MethodHandle.class)) // MethodHandle 305 .aload(0) 306 .getfield(classDesc, SEGMENT_FIELD_NAME, MEMORY_SEGMENT_CLASS_DESC); // MemorySegment 307 offsetBlock(cob, info, classDesc) 308 .aload(valueSlotNumber(parameterDesc.size())) // Record 309 .checkcast(desc(Record.class)) 310 .invokevirtual(CD_MethodHandle, "invokeExact", MethodTypeDesc.of(CD_void, MEMORY_SEGMENT_CLASS_DESC, CD_long, CD_Object)) 311 .return_(); 312 } 313 ); 314 } 315 316 void hashCode_() { 317 cb.withMethodBody("hashCode", MethodTypeDesc.of(CD_int), ACC_PUBLIC | ACC_FINAL, cob -> 318 cob.aload(0) 319 .invokestatic(desc(System.class), "identityHashCode", MethodTypeDesc.of(CD_int, CD_Object)) 320 .ireturn() 321 ); 322 } 323 324 void equals_() { 325 cb.withMethodBody("equals", MethodTypeDesc.of(CD_boolean, CD_Object), ACC_PUBLIC | ACC_FINAL, cob -> { 326 Label l0 = cob.newLabel(); 327 Label l1 = cob.newLabel(); 328 cob.aload(0) 329 .aload(1) 330 .if_acmpne(l0) 331 .iconst_1() 332 .goto_(l1) 333 .labelBinding(l0) 334 .iconst_0() 335 .labelBinding(l1) 336 .ireturn() 337 ; 338 } 339 ); 340 } 341 342 void toString_(List<AccessorInfo> getters) { 343 344 // Foo[g0()=\u0001, g1()=\u0001, ...] 345 var recipe = getters.stream() 346 .map(m -> m.layoutInfo().arrayInfo() 347 .map(ai -> String.format("%s()=%s%s", m.method().getName(), m.type().getSimpleName(), ai.dimensions())) 348 .orElse(String.format("%s()=\u0001", m.method().getName())) 349 ) 350 .collect(Collectors.joining(", ", type.getSimpleName() + "[", "]")); 351 352 List<AccessorInfo> nonArrayGetters = getters.stream() 353 .filter(i -> i.layoutInfo().arrayInfo().isEmpty()) 354 .toList(); 355 356 DirectMethodHandleDesc bootstrap = ofCallsiteBootstrap( 357 desc(StringConcatFactory.class), 358 "makeConcatWithConstants", 359 CD_CallSite, 360 CD_String, CD_Object.arrayType() 361 ); 362 363 List<ClassDesc> getDescriptions = nonArrayGetters.stream() 364 .map(AccessorInfo::type) 365 .map(MapperUtil::desc) 366 .toList(); 367 368 DynamicCallSiteDesc desc = DynamicCallSiteDesc.of( 369 bootstrap, 370 "toString", 371 MethodTypeDesc.of(CD_String, getDescriptions), // String, g0, g1, ... 372 recipe 373 ); 374 375 cb.withMethodBody("toString", 376 MethodTypeDesc.of(CD_String), 377 ACC_PUBLIC | ACC_FINAL, 378 cob -> { 379 for (int i = 0; i < nonArrayGetters.size(); i++) { 380 var name = nonArrayGetters.get(i).method().getName(); 381 cob.aload(0); 382 // Method gi:()? 383 cob.invokevirtual(classDesc, name, MethodTypeDesc.of(getDescriptions.get(i))); 384 } 385 cob.invokedynamic(desc); 386 cob.areturn(); 387 }); 388 } 389 390 // Generate code that calculates: 391 // long indexOffset = f(dimensions, c1, c2, ..., long cN); // If an array, otherwise 0 392 // "this.offset + layoutOffset + indexOffset" 393 private static CodeBuilder offsetBlock(CodeBuilder cob, 394 AccessorInfo info, 395 ClassDesc classDesc) { 396 cob.aload(0) 397 .getfield(classDesc, OFFSET_FIELD_NAME, CD_long); // long 398 399 if (info.offset() != 0) { 400 cob.ldc(info.offset()) 401 .ladd(); 402 } 403 404 // If this is an array accessor, we need to 405 // compute the adjusted indices offset 406 info.layoutInfo().arrayInfo().ifPresent( 407 ai -> reduceArrayIndexes(cob, ai) 408 ); 409 410 return cob; 411 } 412 413 // Example: 414 // The dimensions are [3, 4] and the element byte size is 8 bytes 415 // reduce(2, 3) -> 2 * 4 * 8 + 3 * 8 = 88 416 // public static long reduce(long i1, long i2) { 417 // long offset = Objects.checkIndex(i1, 3) * (8 * 4) + 418 // Objects.checkIndex(i2, 4) * 8; 419 // return offset; 420 // } 421 private static void reduceArrayIndexes(CodeBuilder cob, 422 ArrayInfo arrayInfo) { 423 long elementByteSize = arrayInfo.elementLayout().byteSize(); 424 // Check parameters and push scaled offsets on the stack 425 for (int i = 0; i < arrayInfo.dimensions().size(); i++) { 426 long dimension = arrayInfo.dimensions().get(i); 427 long factor = arrayInfo.dimensions().stream() 428 .skip(i + 1) 429 .reduce(elementByteSize, Math::multiplyExact); 430 431 cob.lload(1 + i * 2) 432 .ldc(dimension) 433 .invokestatic(desc(Objects.class), "checkIndex", MethodTypeDesc.of(CD_long, CD_long, CD_long)) 434 .ldc(factor) 435 .lmul(); 436 } 437 // Sum their values (including the value that existed *before* this method was invoked) 438 for (int i = 0; i < arrayInfo.dimensions().size(); i++) { 439 cob.ladd(); 440 } 441 } 442 443 private static int valueSlotNumber(int parameters) { 444 return (parameters - 1) * 2 + 1; 445 } 446 447 private static List<ClassDesc> parameterDesc(AccessorInfo info) { 448 // If it is an array, there is a number of long parameters 449 List<ClassDesc> desc = info.layoutInfo().arrayInfo() 450 .map(ai -> ai.dimensions().stream().map(_ -> CD_long).toList()) 451 .orElse(Collections.emptyList()); 452 453 if (info.key().accessorType() == AccessorInfo.AccessorType.SETTER) { 454 // Add the trailing setter type 455 desc = new ArrayList<>(desc); 456 desc.add(desc(info.type())); 457 } 458 return List.copyOf(desc); 459 } 460 461 // Factory 462 static ByteCodeGenerator of(Class<?> type, ClassDesc classDesc, ClassBuilder cb) { 463 return new ByteCodeGenerator(type, classDesc, cb); 464 } 465 466 }