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