1 /* 2 * Copyright (c) 2022, 2025, 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 package jdk.internal.classfile.impl; 26 27 import java.lang.classfile.*; 28 import java.lang.classfile.attribute.CodeAttribute; 29 import jdk.internal.classfile.components.ClassPrinter; 30 import java.lang.classfile.constantpool.ClassEntry; 31 import java.lang.classfile.constantpool.ConstantPool; 32 import java.lang.classfile.constantpool.ConstantPoolBuilder; 33 import java.lang.classfile.constantpool.ModuleEntry; 34 import java.lang.classfile.constantpool.PoolEntry; 35 import java.lang.classfile.constantpool.Utf8Entry; 36 import java.lang.constant.ClassDesc; 37 import java.lang.constant.MethodTypeDesc; 38 import java.lang.constant.ModuleDesc; 39 import java.lang.reflect.AccessFlag; 40 import java.lang.reflect.ClassFileFormatVersion; 41 import java.util.AbstractList; 42 import java.util.Collection; 43 import java.util.List; 44 import java.util.function.Consumer; 45 import java.util.function.Function; 46 47 import jdk.internal.access.SharedSecrets; 48 import jdk.internal.constant.ClassOrInterfaceDescImpl; 49 import jdk.internal.vm.annotation.ForceInline; 50 import jdk.internal.vm.annotation.Stable; 51 52 import static java.lang.classfile.ClassFile.ACC_STATIC; 53 import static java.lang.constant.ConstantDescs.INIT_NAME; 54 import static jdk.internal.constant.PrimitiveClassDescImpl.CD_double; 55 import static jdk.internal.constant.PrimitiveClassDescImpl.CD_long; 56 import static jdk.internal.constant.PrimitiveClassDescImpl.CD_void; 57 58 /** 59 * Helper to create and manipulate type descriptors, where type descriptors are 60 * represented as JVM type descriptor strings and symbols are represented as 61 * name strings 62 */ 63 public final class Util { 64 65 public static final int VALUE_OBJECTS_MAJOR = ClassFile.latestMajorVersion(); 66 67 private Util() { 68 } 69 70 public static <T> Consumer<Consumer<T>> writingAll(Iterable<T> container) { 71 record ForEachConsumer<T>(Iterable<T> container) implements Consumer<Consumer<T>> { 72 @Override 73 public void accept(Consumer<T> consumer) { 74 container.forEach(consumer); 75 } 76 } 77 return new ForEachConsumer<>(container); 78 } 79 80 public static Consumer<MethodBuilder> buildingCode(Consumer<? super CodeBuilder> codeHandler) { 81 record WithCodeMethodHandler(Consumer<? super CodeBuilder> codeHandler) implements Consumer<MethodBuilder> { 82 @Override 83 public void accept(MethodBuilder builder) { 84 builder.withCode(codeHandler); 85 } 86 } 87 return new WithCodeMethodHandler(codeHandler); 88 } 89 90 public static Consumer<FieldBuilder> buildingFlags(int flags) { 91 record WithFlagFieldHandler(int flags) implements Consumer<FieldBuilder> { 92 @Override 93 public void accept(FieldBuilder builder) { 94 builder.withFlags(flags); 95 } 96 } 97 return new WithFlagFieldHandler(flags); 98 } 99 100 private static final int ATTRIBUTE_STABILITY_COUNT = AttributeMapper.AttributeStability.values().length; 101 102 public static boolean isAttributeAllowed(final Attribute<?> attr, 103 final ClassFileImpl context) { 104 return attr instanceof BoundAttribute 105 ? ATTRIBUTE_STABILITY_COUNT - attr.attributeMapper().stability().ordinal() > context.attributesProcessingOption().ordinal() 106 : true; 107 } 108 109 public static int parameterSlots(MethodTypeDesc mDesc) { 110 int count = mDesc.parameterCount(); 111 for (int i = count - 1; i >= 0; i--) { 112 if (isDoubleSlot(mDesc.parameterType(i))) { 113 count++; 114 } 115 } 116 return count; 117 } 118 119 public static int[] parseParameterSlots(int flags, MethodTypeDesc mDesc) { 120 int[] result = new int[mDesc.parameterCount()]; 121 int count = ((flags & ACC_STATIC) != 0) ? 0 : 1; 122 for (int i = 0; i < result.length; i++) { 123 result[i] = count; 124 count += paramSlotSize(mDesc.parameterType(i)); 125 } 126 return result; 127 } 128 129 public static int maxLocals(int flags, MethodTypeDesc mDesc) { 130 return parameterSlots(mDesc) + ((flags & ACC_STATIC) == 0 ? 1 : 0) ; 131 } 132 133 /** 134 * Converts a descriptor of classes or interfaces into 135 * a binary name. Rejects primitive types or arrays. 136 * This is an inverse of {@link ClassDesc#of(String)}. 137 */ 138 public static String toBinaryName(ClassDesc cd) { 139 return toInternalName(cd).replace('/', '.'); 140 } 141 142 public static String toInternalName(ClassDesc cd) { 143 if (cd instanceof ClassOrInterfaceDescImpl coi) { 144 return coi.internalName(); 145 } 146 throw new IllegalArgumentException(cd.descriptorString()); 147 } 148 149 public static ClassDesc toClassDesc(String classInternalNameOrArrayDesc) { 150 return classInternalNameOrArrayDesc.charAt(0) == '[' 151 ? ClassDesc.ofDescriptor(classInternalNameOrArrayDesc) 152 : ClassDesc.ofInternalName(classInternalNameOrArrayDesc); 153 } 154 155 /// Sanitizes an input list to make it immutable, and verify its size can 156 /// be represented with U1, throwing IAE otherwise. 157 public static <T> List<T> sanitizeU1List(List<T> input) { 158 var copy = List.copyOf(input); 159 checkU1(copy.size(), "list size"); 160 return copy; 161 } 162 163 /// Sanitizes an input list to make it immutable, and verify its size can 164 /// be represented with U2, throwing IAE otherwise. 165 public static <T> List<T> sanitizeU2List(Collection<T> input) { 166 var copy = List.copyOf(input); 167 checkU2(copy.size(), "list size"); 168 return copy; 169 } 170 171 /// Sanitizes an input nested list of parameter annotations. 172 public static List<List<Annotation>> sanitizeParameterAnnotations(List<List<Annotation>> input) { 173 var array = input.toArray().clone(); 174 checkU1(array.length, "parameter count"); 175 for (int i = 0; i < array.length; i++) { 176 array[i] = sanitizeU2List((List<?>) array[i]); 177 } 178 179 return SharedSecrets.getJavaUtilCollectionAccess().listFromTrustedArray(array); 180 } 181 182 public static<T, U> List<U> mappedList(List<? extends T> list, Function<T, U> mapper) { 183 return new AbstractList<>() { 184 @Override 185 public U get(int index) { 186 return mapper.apply(list.get(index)); 187 } 188 189 @Override 190 public int size() { 191 return list.size(); 192 } 193 }; 194 } 195 196 public static List<ClassEntry> entryList(List<? extends ClassDesc> list) { 197 var result = new Object[list.size()]; // null check 198 for (int i = 0; i < result.length; i++) { 199 result[i] = TemporaryConstantPool.INSTANCE.classEntry(list.get(i)); 200 } 201 return SharedSecrets.getJavaUtilCollectionAccess().listFromTrustedArray(result); 202 } 203 204 public static List<ModuleEntry> moduleEntryList(List<? extends ModuleDesc> list) { 205 var result = new Object[list.size()]; // null check 206 for (int i = 0; i < result.length; i++) { 207 result[i] = TemporaryConstantPool.INSTANCE.moduleEntry(TemporaryConstantPool.INSTANCE.utf8Entry(list.get(i).name())); 208 } 209 return SharedSecrets.getJavaUtilCollectionAccess().listFromTrustedArray(result); 210 } 211 212 public static void checkKind(Opcode op, Opcode.Kind k) { 213 if (op.kind() != k) 214 throw badOpcodeKindException(op, k); 215 } 216 217 public static IllegalArgumentException badOpcodeKindException(Opcode op, Opcode.Kind k) { 218 return new IllegalArgumentException( 219 String.format("Wrong opcode kind specified; found %s(%s), expected %s", op, op.kind(), k)); 220 } 221 222 /// Ensures the given value won't be truncated when written as a u1 223 public static int checkU1(int incoming, String valueName) { 224 if ((incoming & ~0xFF) != 0) { 225 throw outOfRangeException(incoming, valueName, "u1"); 226 } 227 return incoming; 228 } 229 230 /// Ensures the given value won't be truncated when written as a u2 231 public static char checkU2(int incoming, String valueName) { 232 if ((incoming & ~0xFFFF) != 0) 233 throw outOfRangeException(incoming, valueName, "u2"); 234 return (char) incoming; 235 } 236 237 public static IllegalArgumentException outOfRangeException(int value, String fieldName, String typeName) { 238 return new IllegalArgumentException( 239 String.format("%s out of range of %d: %d", fieldName, typeName, value)); 240 } 241 242 /// Ensures the given mask won't be truncated when written as an access flag 243 public static char checkFlags(int mask) { 244 return checkU2(mask, "access flags"); 245 } 246 247 public static int flagsToBits(AccessFlag.Location location, Collection<AccessFlag> flags) { 248 int i = 0; 249 for (AccessFlag f : flags) { 250 if (!f.locations().contains(location)) { 251 throw new IllegalArgumentException("unexpected flag: " + f + " use in target location: " + location); 252 } 253 i |= f.mask(); 254 } 255 return i; 256 } 257 258 public static int flagsToBits(AccessFlag.Location location, AccessFlag... flags) { 259 int i = 0; 260 for (AccessFlag f : flags) { 261 if (!f.locations().contains(location) && !f.locations(ClassFileFormatVersion.CURRENT_PREVIEW_FEATURES).contains(location)) { 262 throw new IllegalArgumentException("unexpected flag: " + f + " use in target location: " + location); 263 } 264 i |= f.mask(); 265 } 266 return i; 267 } 268 269 public static boolean has(AccessFlag.Location location, int flagsMask, AccessFlag flag) { 270 return (flag.mask() & flagsMask) == flag.mask() && (flag.locations().contains(location) 271 || flag.locations(ClassFileFormatVersion.CURRENT_PREVIEW_FEATURES).contains(location)); 272 } 273 274 public static ClassDesc fieldTypeSymbol(Utf8Entry utf8) { 275 return ((AbstractPoolEntry.Utf8EntryImpl) utf8).fieldTypeSymbol(); 276 } 277 278 public static MethodTypeDesc methodTypeSymbol(Utf8Entry utf8) { 279 return ((AbstractPoolEntry.Utf8EntryImpl) utf8).methodTypeSymbol(); 280 } 281 282 @SuppressWarnings("unchecked") 283 public static <T extends Attribute<T>> void writeAttribute(BufWriterImpl writer, Attribute<?> attr) { 284 if (attr instanceof CustomAttribute<?> ca) { 285 var mapper = (AttributeMapper<T>) ca.attributeMapper(); 286 mapper.writeAttribute(writer, (T) ca); 287 } else { 288 assert attr instanceof BoundAttribute || attr instanceof UnboundAttribute; 289 ((Writable) attr).writeTo(writer); 290 } 291 } 292 293 @ForceInline 294 public static void writeAttributes(BufWriterImpl buf, List<? extends Attribute<?>> list) { 295 int size = list.size(); 296 Util.checkU2(size, "attributes count"); 297 buf.writeU2(size); 298 for (int i = 0; i < size; i++) { 299 writeAttribute(buf, list.get(i)); 300 } 301 } 302 303 @ForceInline 304 static void writeList(BufWriterImpl buf, Writable[] array, int size) { 305 Util.checkU2(size, "member count"); 306 buf.writeU2(size); 307 for (int i = 0; i < size; i++) { 308 array[i].writeTo(buf); 309 } 310 } 311 312 public static int slotSize(ClassDesc desc) { 313 return desc == CD_void ? 0 : isDoubleSlot(desc) ? 2 : 1; 314 } 315 316 public static int paramSlotSize(ClassDesc desc) { 317 return isDoubleSlot(desc) ? 2 : 1; 318 } 319 320 public static boolean isDoubleSlot(ClassDesc desc) { 321 return desc == CD_double || desc == CD_long; 322 } 323 324 public static boolean checkConstantPoolsCompatible(ConstantPool one, ConstantPool two) { 325 if (one.equals(two)) 326 return true; 327 if (one instanceof ConstantPoolBuilder cpb && cpb.canWriteDirect(two)) 328 return true; 329 return two instanceof ConstantPoolBuilder cpb && cpb.canWriteDirect(one); 330 } 331 332 public static void dumpMethod(SplitConstantPool cp, 333 ClassDesc cls, 334 String methodName, 335 MethodTypeDesc methodDesc, 336 int acc, 337 RawBytecodeHelper.CodeRange bytecode, 338 Consumer<String> dump) { 339 340 // try to dump debug info about corrupted bytecode 341 try { 342 var cc = ClassFile.of(); 343 var clm = cc.parse(cc.build(cp.classEntry(cls), cp, clb -> 344 clb.withMethod(methodName, methodDesc, acc, mb -> 345 ((DirectMethodBuilder)mb).writeAttribute(new UnboundAttribute.AdHocAttribute<CodeAttribute>(Attributes.code()) { 346 @Override 347 public void writeBody(BufWriterImpl b) { 348 b.writeU2U2(-1, -1);//max stack & locals 349 b.writeInt(bytecode.length()); 350 b.writeBytes(bytecode.array(), 0, bytecode.length()); 351 b.writeU2U2(0, 0);//exception handlers & attributes 352 } 353 354 @Override 355 public Utf8Entry attributeName() { 356 return cp.utf8Entry(Attributes.NAME_CODE); 357 } 358 })))); 359 ClassPrinter.toYaml(clm.methods().get(0).code().get(), ClassPrinter.Verbosity.TRACE_ALL, dump); 360 } catch (Error | Exception _) { 361 // fallback to bytecode hex dump 362 dumpBytesHex(dump, bytecode.array(), bytecode.length()); 363 } 364 } 365 366 public static void dumpBytesHex(Consumer<String> dump, byte[] bytes, int length) { 367 for (int i = 0; i < length; i++) { 368 if (i % 16 == 0) { 369 dump.accept("%n%04x:".formatted(i)); 370 } 371 dump.accept(" %02x".formatted(bytes[i])); 372 } 373 } 374 375 public static boolean canSkipMethodInflation(ClassReader cr, MethodInfo method, BufWriterImpl buf) { 376 if (!buf.canWriteDirect(cr)) { 377 return false; 378 } 379 if (method.methodName().equalsString(INIT_NAME) && 380 !buf.strictFieldsMatch(((ClassReaderImpl) cr).getContainedClass())) { 381 return false; 382 } 383 return true; 384 } 385 386 public static void writeListIndices(BufWriter writer, List<? extends PoolEntry> list) { 387 writer.writeU2(list.size()); 388 for (PoolEntry info : list) { 389 writer.writeIndex(info); 390 } 391 } 392 393 public static boolean writeLocalVariable(BufWriterImpl buf, PseudoInstruction lvOrLvt) { 394 return ((WritableLocalVariable) lvOrLvt).writeLocalTo(buf); 395 } 396 397 /** 398 * A generic interface for objects to write to a 399 * buf writer. Do not implement unless necessary, 400 * as this writeTo is public, which can be troublesome. 401 */ 402 interface Writable { 403 void writeTo(BufWriterImpl writer); 404 } 405 406 interface WritableLocalVariable { 407 boolean writeLocalTo(BufWriterImpl buf); 408 } 409 410 /** 411 * Returns the hash code of a class or interface L descriptor given the internal name. 412 */ 413 public static int descriptorStringHash(int length, int hash) { 414 if (length > 0xffff) 415 throw new IllegalArgumentException("String too long: ".concat(Integer.toString(length))); 416 return 'L' * pow31(length + 1) + hash * 31 + ';'; 417 } 418 419 // k is at most 65536, length of Utf8 entry + 1 420 public static int pow31(int k) { 421 int r = 1; 422 // calculate the power contribution from index-th octal digit 423 // from least to most significant (right to left) 424 // e.g. decimal 26=octal 32, power(26)=powerOctal(2,0)*powerOctal(3,1) 425 for (int i = 0; i < SIGNIFICANT_OCTAL_DIGITS; i++) { 426 r *= powerOctal(k & 7, i); 427 k >>= 3; 428 } 429 return r; 430 } 431 432 // The inverse of 31 in Z/2^32Z* modulo group, a * INVERSE_31 * 31 = a 433 static final int INVERSE_31 = 0xbdef7bdf; 434 435 // k is at most 65536 = octal 200000, only consider 6 octal digits 436 // Note: 31 powers repeat beyond 1 << 27, only 9 octal digits matter 437 static final int SIGNIFICANT_OCTAL_DIGITS = 6; 438 439 // for base k, storage is k * log_k(N)=k/ln(k) * ln(N) 440 // k = 2 or 4 is better for space at the cost of more multiplications 441 /** 442 * The code below is as if: 443 * {@snippet lang=java : 444 * int[] powers = new int[7 * SIGNIFICANT_OCTAL_DIGITS]; 445 * 446 * for (int i = 1, k = 31; i <= 7; i++, k *= 31) { 447 * int t = powers[powersIndex(i, 0)] = k; 448 * for (int j = 1; j < SIGNIFICANT_OCTAL_DIGITS; j++) { 449 * t *= t; 450 * t *= t; 451 * t *= t; 452 * powers[powersIndex(i, j)] = t; 453 * } 454 * } 455 * } 456 * This is converted to explicit initialization to avoid bootstrap overhead. 457 * Validated in UtilTest. 458 */ 459 static final @Stable int[] powers = new int[] { 460 0x0000001f, 0x000003c1, 0x0000745f, 0x000e1781, 0x01b4d89f, 0x34e63b41, 0x67e12cdf, 461 0x94446f01, 0x50a9de01, 0x84304d01, 0x7dd7bc01, 0x8ca02b01, 0xff899a01, 0x25940901, 462 0x4dbf7801, 0xe3bef001, 0xc1fe6801, 0xe87de001, 0x573d5801, 0x0e3cd001, 0x0d7c4801, 463 0x54fbc001, 0xb9f78001, 0x2ef34001, 0xb3ef0001, 0x48eac001, 0xede68001, 0xa2e24001, 464 0x67de0001, 0xcfbc0001, 0x379a0001, 0x9f780001, 0x07560001, 0x6f340001, 0xd7120001, 465 0x3ef00001, 0x7de00001, 0xbcd00001, 0xfbc00001, 0x3ab00001, 0x79a00001, 0xb8900001, 466 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 467 }; 468 469 static int powersIndex(int digit, int index) { 470 return (digit - 1) + index * 7; 471 } 472 473 // (31 ^ digit) ^ (8 * index) = 31 ^ (digit * (8 ^ index)) 474 // digit: 0 - 7 475 // index: 0 - SIGNIFICANT_OCTAL_DIGITS - 1 476 private static int powerOctal(int digit, int index) { 477 return digit == 0 ? 1 : powers[powersIndex(digit, index) & 0x3F]; // & 0x3F eliminates bound check 478 } 479 }