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