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