1 /* 2 * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. 3 * Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved. 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. Oracle designates this 9 * particular file as subject to the "Classpath" exception as provided 10 * by Oracle in the LICENSE file that accompanied this code. 11 * 12 * This code is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 * version 2 for more details (a copy is included in the LICENSE file that 16 * accompanied this code). 17 * 18 * You should have received a copy of the GNU General Public License version 19 * 2 along with this work; if not, write to the Free Software Foundation, 20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 21 * 22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 23 * or visit www.oracle.com if you need additional information or have any 24 * questions. 25 * 26 */ 27 package jdk.internal.classfile.impl; 28 29 import java.lang.classfile.Attribute; 30 import java.lang.classfile.Attributes; 31 import java.lang.classfile.Label; 32 import java.lang.classfile.attribute.StackMapTableAttribute; 33 import java.lang.classfile.constantpool.*; 34 import java.lang.constant.ClassDesc; 35 import java.lang.constant.MethodTypeDesc; 36 import java.util.ArrayList; 37 import java.util.Arrays; 38 import java.util.List; 39 import java.util.Objects; 40 import java.util.stream.Collectors; 41 42 import jdk.internal.classfile.impl.WritableField.UnsetField; 43 import jdk.internal.constant.ClassOrInterfaceDescImpl; 44 import jdk.internal.util.Preconditions; 45 46 import static java.lang.classfile.ClassFile.*; 47 import static java.lang.classfile.constantpool.PoolEntry.*; 48 import static java.lang.constant.ConstantDescs.*; 49 import static jdk.internal.classfile.impl.RawBytecodeHelper.*; 50 51 /** 52 * StackMapGenerator is responsible for stack map frames generation. 53 * <p> 54 * Stack map frames are computed from serialized bytecode similar way they are verified during class loading process. 55 * <p> 56 * The {@linkplain #generate() frames computation} consists of following steps: 57 * <ol> 58 * <li>{@linkplain #detectFrames() Detection} of mandatory stack map frames:<ul> 59 * <li>Mandatory stack map frame include all jump and switch instructions targets, 60 * offsets immediately following {@linkplain #noControlFlow(int) "no control flow"} 61 * and all exception table handlers. 62 * <li>Detection is performed in a single fast pass through the bytecode, 63 * with no auxiliary structures construction nor further instructions processing. 64 * </ul> 65 * <li>Generator loop {@linkplain #processMethod() processing bytecode instructions}:<ul> 66 * <li>Generator loop simulates sequence instructions {@linkplain #processBlock(RawBytecodeHelper) processing effect on the actual stack and locals}. 67 * <li>All mandatory {@linkplain Frame frames} detected in the step #1 are {@linkplain Frame#checkAssignableTo(Frame) retro-filled} 68 * (or {@linkplain Frame#merge(Type, Type[], int, Frame) reverse-merged} in subsequent processing) 69 * with the actual stack and locals for all matching jump, switch and exception handler targets. 70 * <li>All frames modified by reverse merges are marked as {@linkplain Frame#dirty dirty} for further processing. 71 * <li>Code blocks with not yet known entry frame content are skipped and related frames are also marked as dirty. 72 * <li>Generator loop process is repeated until all mandatory frames are cleared or until an error state is reached. 73 * <li>Generator loop always passes all instructions at least once to calculate {@linkplain #maxStack max stack} 74 * and {@linkplain #maxLocals max locals} code attributes. 75 * <li>More than one pass is usually not necessary, except for more complex bytecode sequences.<br> 76 * <i>(Note: experimental measurements showed that more than 99% of the cases required only single pass to clear all frames, 77 * less than 1% of the cases required second pass and remaining 0,01% of the cases required third pass to clear all frames.)</i>. 78 * </ul> 79 * <li>Dead code patching to pass class loading verification:<ul> 80 * <li>Dead code blocks are indicated by frames remaining without content after leaving the Generator loop. 81 * <li>Each dead code block is filled with <code>NOP</code> instructions, terminated with 82 * <code>ATHROW</code> instruction, and removed from exception handlers table. 83 * <li>Dead code block entry frame is set to <code>java.lang.Throwable</code> single stack item and no locals. 84 * </ul> 85 * </ol> 86 * <p> 87 * {@linkplain Frame#merge(Type, Type[], int, Frame) Reverse-merge} of the stack map frames 88 * may in some situations require to determine {@linkplain ClassHierarchyImpl class hierarchy} relations. 89 * <p> 90 * Reverse-merge of individual {@linkplain Type types} is performed when a target frame has already been retro-filled 91 * and it is necessary to adjust its existing stack entries and locals to also match actual stack map frame conditions. 92 * Following tables describe how new target stack entry or local type is calculated, based on the actual frame stack entry or local ("from") 93 * and actual value of the target stack entry or local ("to"). 94 * 95 * <table border="1"> 96 * <caption>Reverse-merge of general type categories</caption> 97 * <tr><th>to \ from<th>TOP<th>PRIMITIVE<th>UNINITIALIZED<th>REFERENCE 98 * <tr><th>TOP<td>TOP<td>TOP<td>TOP<td>TOP 99 * <tr><th>PRIMITIVE<td>TOP<td><a href="#primitives">Reverse-merge of primitive types</a><td>TOP<td>TOP 100 * <tr><th>UNINITIALIZED<td>TOP<td>TOP<td>Is NEW offset matching ? UNINITIALIZED : TOP<td>TOP 101 * <tr><th>REFERENCE<td>TOP<td>TOP<td>TOP<td><a href="#references">Reverse-merge of reference types</a> 102 * </table> 103 * <p> 104 * <table id="primitives" border="1"> 105 * <caption>Reverse-merge of primitive types</caption> 106 * <tr><th>to \ from<th>SHORT<th>BYTE<th>BOOLEAN<th>LONG<th>DOUBLE<th>FLOAT<th>INTEGER 107 * <tr><th>SHORT<td>SHORT<td>TOP<td>TOP<td>TOP<td>TOP<td>TOP<td>SHORT 108 * <tr><th>BYTE<td>TOP<td>BYTE<td>TOP<td>TOP<td>TOP<td>TOP<td>BYTE 109 * <tr><th>BOOLEAN<td>TOP<td>TOP<td>BOOLEAN<td>TOP<td>TOP<td>TOP<td>BOOLEAN 110 * <tr><th>LONG<td>TOP<td>TOP<td>TOP<td>LONG<td>TOP<td>TOP<td>TOP 111 * <tr><th>DOUBLE<td>TOP<td>TOP<td>TOP<td>TOP<td>DOUBLE<td>TOP<td>TOP 112 * <tr><th>FLOAT<td>TOP<td>TOP<td>TOP<td>TOP<td>TOP<td>FLOAT<td>TOP 113 * <tr><th>INTEGER<td>TOP<td>TOP<td>TOP<td>TOP<td>TOP<td>TOP<td>INTEGER 114 * </table> 115 * <p> 116 * <table id="references" border="1"> 117 * <caption>Reverse merge of reference types</caption> 118 * <tr><th>to \ from<th>NULL<th>j.l.Object<th>j.l.Cloneable<th>j.i.Serializable<th>ARRAY<th>INTERFACE*<th>OBJECT** 119 * <tr><th>NULL<td>NULL<td>j.l.Object<td>j.l.Cloneable<td>j.i.Serializable<td>ARRAY<td>INTERFACE<td>OBJECT 120 * <tr><th>j.l.Object<td>j.l.Object<td>j.l.Object<td>j.l.Object<td>j.l.Object<td>j.l.Object<td>j.l.Object<td>j.l.Object 121 * <tr><th>j.l.Cloneable<td>j.l.Cloneable<td>j.l.Cloneable<td>j.l.Cloneable<td>j.l.Cloneable<td>j.l.Object<td>j.l.Cloneable<td>j.l.Cloneable 122 * <tr><th>j.i.Serializable<td>j.i.Serializable<td>j.i.Serializable<td>j.i.Serializable<td>j.i.Serializable<td>j.l.Object<td>j.i.Serializable<td>j.i.Serializable 123 * <tr><th>ARRAY<td>ARRAY<td>j.l.Object<td>j.l.Object<td>j.l.Object<td><a href="#arrays">Reverse merge of arrays</a><td>j.l.Object<td>j.l.Object 124 * <tr><th>INTERFACE*<td>INTERFACE<td>j.l.Object<td>j.l.Object<td>j.l.Object<td>j.l.Object<td>j.l.Object<td>j.l.Object 125 * <tr><th>OBJECT**<td>OBJECT<td>j.l.Object<td>j.l.Object<td>j.l.Object<td>j.l.Object<td>j.l.Object<td>Resolved common ancestor 126 * <tr><td colspan="8">*any interface reference except for j.l.Cloneable and j.i.Serializable<br>**any object reference except for j.l.Object 127 * </table> 128 * <p id="arrays"> 129 * Array types are reverse-merged as reference to array type constructed from reverse-merged components. 130 * Reference to j.l.Object is an alternate result when construction of the array type is not possible (when reverse-merge of components returned TOP or other non-reference and non-primitive type). 131 * <p> 132 * Custom class hierarchy resolver has been implemented as a part of the library to avoid heavy class loading 133 * and to allow stack maps generation even for code with incomplete dependency classpath. 134 * However stack maps generated with {@linkplain ClassHierarchyImpl#resolve(java.lang.constant.ClassDesc) warnings of unresolved dependencies} may later fail to verify during class loading process. 135 * <p> 136 * Focus of the whole algorithm is on high performance and low memory footprint:<ul> 137 * <li>It does not produce, collect nor visit any complex intermediate structures 138 * <i>(beside {@linkplain RawBytecodeHelper traversing} the {@linkplain #bytecode bytecode in binary form}).</i> 139 * <li>It works with only minimal mandatory stack map frames. 140 * <li>It does not spend time on any non-essential verifications. 141 * </ul> 142 */ 143 144 public final class StackMapGenerator { 145 146 static StackMapGenerator of(DirectCodeBuilder dcb, BufWriterImpl buf) { 147 return new StackMapGenerator( 148 dcb, 149 buf.thisClass().asSymbol(), 150 dcb.methodInfo.methodName().stringValue(), 151 dcb.methodInfo.methodTypeSymbol(), 152 (dcb.methodInfo.methodFlags() & ACC_STATIC) != 0, 153 dcb.bytecodesBufWriter.bytecodeView(), 154 dcb.constantPool, 155 dcb.context, 156 buf.getStrictInstanceFields(), 157 dcb.handlers); 158 } 159 160 private static final String OBJECT_INITIALIZER_NAME = "<init>"; 161 private static final int FLAG_THIS_UNINIT = 0x01; 162 private static final int FRAME_DEFAULT_CAPACITY = 10; 163 private static final int T_BOOLEAN = 4, T_LONG = 11; 164 private static final Frame[] EMPTY_FRAME_ARRAY = {}; 165 166 private static final int ITEM_TOP = 0, 167 ITEM_INTEGER = 1, 168 ITEM_FLOAT = 2, 169 ITEM_DOUBLE = 3, 170 ITEM_LONG = 4, 171 ITEM_NULL = 5, 172 ITEM_UNINITIALIZED_THIS = 6, 173 ITEM_OBJECT = 7, 174 ITEM_UNINITIALIZED = 8, 175 ITEM_BOOLEAN = 9, 176 ITEM_BYTE = 10, 177 ITEM_SHORT = 11, 178 ITEM_CHAR = 12, 179 ITEM_LONG_2ND = 13, 180 ITEM_DOUBLE_2ND = 14; 181 182 private static final Type[] ARRAY_FROM_BASIC_TYPE = {null, null, null, null, 183 Type.BOOLEAN_ARRAY_TYPE, Type.CHAR_ARRAY_TYPE, Type.FLOAT_ARRAY_TYPE, Type.DOUBLE_ARRAY_TYPE, 184 Type.BYTE_ARRAY_TYPE, Type.SHORT_ARRAY_TYPE, Type.INT_ARRAY_TYPE, Type.LONG_ARRAY_TYPE}; 185 186 static record RawExceptionCatch(int start, int end, int handler, Type catchType) {} 187 188 private final Type thisType; 189 private final String methodName; 190 private final MethodTypeDesc methodDesc; 191 private final RawBytecodeHelper.CodeRange bytecode; 192 private final SplitConstantPool cp; 193 private final boolean isStatic; 194 private final LabelContext labelContext; 195 private final List<AbstractPseudoInstruction.ExceptionCatchImpl> handlers; 196 private final List<RawExceptionCatch> rawHandlers; 197 private final ClassHierarchyImpl classHierarchy; 198 private final UnsetField[] strictFieldsToPut; // exact-sized, do not modify this copy! 199 private final boolean patchDeadCode; 200 private final boolean filterDeadLabels; 201 private Frame[] frames = EMPTY_FRAME_ARRAY; 202 private int framesCount = 0; 203 private final Frame currentFrame; 204 private int maxStack, maxLocals; 205 206 /** 207 * Primary constructor of the <code>Generator</code> class. 208 * New <code>Generator</code> instance must be created for each individual class/method. 209 * Instance contains only immutable results, all the calculations are processed during instance construction. 210 * 211 * @param labelContext <code>LabelContext</code> instance used to resolve or patch <code>ExceptionHandler</code> 212 * labels to bytecode offsets (or vice versa) 213 * @param thisClass class to generate stack maps for 214 * @param methodName method name to generate stack maps for 215 * @param methodDesc method descriptor to generate stack maps for 216 * @param isStatic information whether the method is static 217 * @param bytecode R/W <code>ByteBuffer</code> wrapping method bytecode, the content is altered in case <code>Generator</code> detects and patches dead code 218 * @param cp R/W <code>ConstantPoolBuilder</code> instance used to resolve all involved CP entries and also generate new entries referenced from the generated stack maps 219 * @param handlers R/W <code>ExceptionHandler</code> list used to detect mandatory frame offsets as well as to determine stack maps in exception handlers 220 * and also to be altered when dead code is detected and must be excluded from exception handlers 221 */ 222 public StackMapGenerator(LabelContext labelContext, 223 ClassDesc thisClass, 224 String methodName, 225 MethodTypeDesc methodDesc, 226 boolean isStatic, 227 RawBytecodeHelper.CodeRange bytecode, 228 SplitConstantPool cp, 229 ClassFileImpl context, 230 UnsetField[] strictFields, 231 List<AbstractPseudoInstruction.ExceptionCatchImpl> handlers) { 232 this.thisType = Type.referenceType(thisClass); 233 this.methodName = methodName; 234 this.methodDesc = methodDesc; 235 this.isStatic = isStatic; 236 this.bytecode = bytecode; 237 this.cp = cp; 238 this.labelContext = labelContext; 239 this.handlers = handlers; 240 this.rawHandlers = new ArrayList<>(handlers.size()); 241 this.classHierarchy = new ClassHierarchyImpl(context.classHierarchyResolver()); 242 this.patchDeadCode = context.patchDeadCode(); 243 this.filterDeadLabels = context.dropDeadLabels(); 244 this.currentFrame = new Frame(classHierarchy); 245 if (OBJECT_INITIALIZER_NAME.equals(methodName)) { 246 this.strictFieldsToPut = strictFields; 247 } else { 248 this.strictFieldsToPut = UnsetField.EMPTY_ARRAY; 249 } 250 generate(); 251 } 252 253 /** 254 * Calculated maximum number of the locals required 255 * @return maximum number of the locals required 256 */ 257 public int maxLocals() { 258 return maxLocals; 259 } 260 261 /** 262 * Calculated maximum stack size required 263 * @return maximum stack size required 264 */ 265 public int maxStack() { 266 return maxStack; 267 } 268 269 private Frame getFrame(int offset) { 270 //binary search over frames ordered by offset 271 int low = 0; 272 int high = framesCount - 1; 273 while (low <= high) { 274 int mid = (low + high) >>> 1; 275 var f = frames[mid]; 276 if (f.offset < offset) 277 low = mid + 1; 278 else if (f.offset > offset) 279 high = mid - 1; 280 else 281 return f; 282 } 283 return null; 284 } 285 286 private void checkJumpTarget(Frame frame, int target) { 287 frame.checkAssignableTo(getFrame(target)); 288 } 289 290 private int exMin, exMax; 291 292 private boolean isAnyFrameDirty() { 293 for (int i = 0; i < framesCount; i++) { 294 if (frames[i].dirty) return true; 295 } 296 return false; 297 } 298 299 private void generate() { 300 exMin = bytecode.length(); 301 exMax = -1; 302 if (!handlers.isEmpty()) { 303 generateHandlers(); 304 } 305 detectFrames(); 306 do { 307 processMethod(); 308 } while (isAnyFrameDirty()); 309 maxLocals = currentFrame.frameMaxLocals; 310 maxStack = currentFrame.frameMaxStack; 311 312 //dead code patching 313 for (int i = 0; i < framesCount; i++) { 314 var frame = frames[i]; 315 if (frame.flags == -1) { 316 deadCodePatching(frame, i); 317 } 318 } 319 } 320 321 private void generateHandlers() { 322 var labelContext = this.labelContext; 323 for (int i = 0; i < handlers.size(); i++) { 324 var exhandler = handlers.get(i); 325 int start_pc = labelContext.labelToBci(exhandler.tryStart()); 326 int end_pc = labelContext.labelToBci(exhandler.tryEnd()); 327 int handler_pc = labelContext.labelToBci(exhandler.handler()); 328 if (start_pc >= 0 && end_pc >= 0 && end_pc > start_pc && handler_pc >= 0) { 329 if (start_pc < exMin) exMin = start_pc; 330 if (end_pc > exMax) exMax = end_pc; 331 var catchType = exhandler.catchType(); 332 rawHandlers.add(new RawExceptionCatch(start_pc, end_pc, handler_pc, 333 catchType.isPresent() ? cpIndexToType(catchType.get().index(), cp) 334 : Type.THROWABLE_TYPE)); 335 } 336 } 337 } 338 339 private void deadCodePatching(Frame frame, int i) { 340 if (!patchDeadCode) throw generatorError("Unable to generate stack map frame for dead code", frame.offset); 341 //patch frame 342 frame.pushStack(Type.THROWABLE_TYPE); 343 if (maxStack < 1) maxStack = 1; 344 int end = (i < framesCount - 1 ? frames[i + 1].offset : bytecode.length()) - 1; 345 //patch bytecode 346 var arr = bytecode.array(); 347 Arrays.fill(arr, frame.offset, end, (byte) NOP); 348 arr[end] = (byte) ATHROW; 349 //patch handlers 350 removeRangeFromExcTable(frame.offset, end + 1); 351 } 352 353 private void removeRangeFromExcTable(int rangeStart, int rangeEnd) { 354 var it = handlers.listIterator(); 355 while (it.hasNext()) { 356 var e = it.next(); 357 int handlerStart = labelContext.labelToBci(e.tryStart()); 358 int handlerEnd = labelContext.labelToBci(e.tryEnd()); 359 if (rangeStart >= handlerEnd || rangeEnd <= handlerStart) { 360 //out of range 361 continue; 362 } 363 if (rangeStart <= handlerStart) { 364 if (rangeEnd >= handlerEnd) { 365 //complete removal 366 it.remove(); 367 } else { 368 //cut from left 369 Label newStart = labelContext.newLabel(); 370 labelContext.setLabelTarget(newStart, rangeEnd); 371 it.set(new AbstractPseudoInstruction.ExceptionCatchImpl(e.handler(), newStart, e.tryEnd(), e.catchType())); 372 } 373 } else if (rangeEnd >= handlerEnd) { 374 //cut from right 375 Label newEnd = labelContext.newLabel(); 376 labelContext.setLabelTarget(newEnd, rangeStart); 377 it.set(new AbstractPseudoInstruction.ExceptionCatchImpl(e.handler(), e.tryStart(), newEnd, e.catchType())); 378 } else { 379 //split 380 Label newStart = labelContext.newLabel(); 381 labelContext.setLabelTarget(newStart, rangeEnd); 382 Label newEnd = labelContext.newLabel(); 383 labelContext.setLabelTarget(newEnd, rangeStart); 384 it.set(new AbstractPseudoInstruction.ExceptionCatchImpl(e.handler(), e.tryStart(), newEnd, e.catchType())); 385 it.add(new AbstractPseudoInstruction.ExceptionCatchImpl(e.handler(), newStart, e.tryEnd(), e.catchType())); 386 } 387 } 388 } 389 390 /** 391 * Getter of the generated <code>StackMapTableAttribute</code> or null if stack map is empty 392 * @return <code>StackMapTableAttribute</code> or null if stack map is empty 393 */ 394 public Attribute<? extends StackMapTableAttribute> stackMapTableAttribute() { 395 return framesCount == 0 ? null : new UnboundAttribute.AdHocAttribute<>(Attributes.stackMapTable()) { 396 @Override 397 public void writeBody(BufWriterImpl b) { 398 int countPos = b.size(); 399 if (framesCount != (char) framesCount) { 400 throw generatorError("Too many frames: " + framesCount); 401 } 402 b.writeU2(framesCount); 403 Frame prevFrame = new Frame(classHierarchy); 404 prevFrame.setLocalsFromArg(methodName, methodDesc, isStatic, thisType, strictFieldsToPut); 405 prevFrame.trimAndCompress(); 406 for (int i = 0; i < framesCount; i++) { 407 var fr = frames[i]; 408 fr.trimAndCompress(); 409 fr.writeTo(b, prevFrame, cp); 410 prevFrame = fr; 411 } 412 } 413 414 @Override 415 public Utf8Entry attributeName() { 416 return cp.utf8Entry(Attributes.NAME_STACK_MAP_TABLE); 417 } 418 }; 419 } 420 421 private static Type cpIndexToType(int index, ConstantPoolBuilder cp) { 422 return Type.referenceType(cp.entryByIndex(index, ClassEntry.class).asSymbol()); 423 } 424 425 private void processMethod() { 426 var frames = this.frames; 427 var currentFrame = this.currentFrame; 428 currentFrame.setLocalsFromArg(methodName, methodDesc, isStatic, thisType, strictFieldsToPut); 429 currentFrame.stackSize = 0; 430 currentFrame.offset = -1; 431 int stackmapIndex = 0; 432 var bcs = bytecode.start(); 433 boolean ncf = false; 434 while (bcs.next()) { 435 currentFrame.offset = bcs.bci(); 436 if (stackmapIndex < framesCount) { 437 int thisOffset = frames[stackmapIndex].offset; 438 if (ncf && thisOffset > bcs.bci()) { 439 throw generatorError("Expecting a stack map frame"); 440 } 441 if (thisOffset == bcs.bci()) { 442 Frame nextFrame = frames[stackmapIndex++]; 443 if (!ncf) { 444 currentFrame.checkAssignableTo(nextFrame); 445 } 446 while (!nextFrame.dirty) { //skip unmatched frames 447 if (stackmapIndex == framesCount) return; //skip the rest of this round 448 nextFrame = frames[stackmapIndex++]; 449 } 450 bcs.reset(nextFrame.offset); //skip code up-to the next frame 451 bcs.next(); 452 currentFrame.offset = bcs.bci(); 453 currentFrame.copyFrom(nextFrame); 454 nextFrame.dirty = false; 455 } else if (thisOffset < bcs.bci()) { 456 throw generatorError("Bad stack map offset"); 457 } 458 } else if (ncf) { 459 throw generatorError("Expecting a stack map frame"); 460 } 461 ncf = processBlock(bcs); 462 } 463 } 464 465 private boolean processBlock(RawBytecodeHelper bcs) { 466 int opcode = bcs.opcode(); 467 boolean ncf = false; 468 boolean this_uninit = false; 469 boolean verified_exc_handlers = false; 470 int bci = bcs.bci(); 471 Type type1, type2, type3, type4; 472 if (RawBytecodeHelper.isStoreIntoLocal(opcode) && bci >= exMin && bci < exMax) { 473 processExceptionHandlerTargets(bci, this_uninit); 474 verified_exc_handlers = true; 475 } 476 switch (opcode) { 477 case NOP -> {} 478 case RETURN -> { 479 ncf = true; 480 } 481 case ACONST_NULL -> 482 currentFrame.pushStack(Type.NULL_TYPE); 483 case ICONST_M1, ICONST_0, ICONST_1, ICONST_2, ICONST_3, ICONST_4, ICONST_5, SIPUSH, BIPUSH -> 484 currentFrame.pushStack(Type.INTEGER_TYPE); 485 case LCONST_0, LCONST_1 -> 486 currentFrame.pushStack(Type.LONG_TYPE, Type.LONG2_TYPE); 487 case FCONST_0, FCONST_1, FCONST_2 -> 488 currentFrame.pushStack(Type.FLOAT_TYPE); 489 case DCONST_0, DCONST_1 -> 490 currentFrame.pushStack(Type.DOUBLE_TYPE, Type.DOUBLE2_TYPE); 491 case LDC -> 492 processLdc(bcs.getIndexU1()); 493 case LDC_W, LDC2_W -> 494 processLdc(bcs.getIndexU2()); 495 case ILOAD -> 496 currentFrame.checkLocal(bcs.getIndex()).pushStack(Type.INTEGER_TYPE); 497 case ILOAD_0, ILOAD_1, ILOAD_2, ILOAD_3 -> 498 currentFrame.checkLocal(opcode - ILOAD_0).pushStack(Type.INTEGER_TYPE); 499 case LLOAD -> 500 currentFrame.checkLocal(bcs.getIndex() + 1).pushStack(Type.LONG_TYPE, Type.LONG2_TYPE); 501 case LLOAD_0, LLOAD_1, LLOAD_2, LLOAD_3 -> 502 currentFrame.checkLocal(opcode - LLOAD_0 + 1).pushStack(Type.LONG_TYPE, Type.LONG2_TYPE); 503 case FLOAD -> 504 currentFrame.checkLocal(bcs.getIndex()).pushStack(Type.FLOAT_TYPE); 505 case FLOAD_0, FLOAD_1, FLOAD_2, FLOAD_3 -> 506 currentFrame.checkLocal(opcode - FLOAD_0).pushStack(Type.FLOAT_TYPE); 507 case DLOAD -> 508 currentFrame.checkLocal(bcs.getIndex() + 1).pushStack(Type.DOUBLE_TYPE, Type.DOUBLE2_TYPE); 509 case DLOAD_0, DLOAD_1, DLOAD_2, DLOAD_3 -> 510 currentFrame.checkLocal(opcode - DLOAD_0 + 1).pushStack(Type.DOUBLE_TYPE, Type.DOUBLE2_TYPE); 511 case ALOAD -> 512 currentFrame.pushStack(currentFrame.getLocal(bcs.getIndex())); 513 case ALOAD_0, ALOAD_1, ALOAD_2, ALOAD_3 -> 514 currentFrame.pushStack(currentFrame.getLocal(opcode - ALOAD_0)); 515 case IALOAD, BALOAD, CALOAD, SALOAD -> 516 currentFrame.decStack(2).pushStack(Type.INTEGER_TYPE); 517 case LALOAD -> 518 currentFrame.decStack(2).pushStack(Type.LONG_TYPE, Type.LONG2_TYPE); 519 case FALOAD -> 520 currentFrame.decStack(2).pushStack(Type.FLOAT_TYPE); 521 case DALOAD -> 522 currentFrame.decStack(2).pushStack(Type.DOUBLE_TYPE, Type.DOUBLE2_TYPE); 523 case AALOAD -> 524 currentFrame.pushStack((type1 = currentFrame.decStack(1).popStack()) == Type.NULL_TYPE ? Type.NULL_TYPE : type1.getComponent()); 525 case ISTORE -> 526 currentFrame.decStack(1).setLocal(bcs.getIndex(), Type.INTEGER_TYPE); 527 case ISTORE_0, ISTORE_1, ISTORE_2, ISTORE_3 -> 528 currentFrame.decStack(1).setLocal(opcode - ISTORE_0, Type.INTEGER_TYPE); 529 case LSTORE -> 530 currentFrame.decStack(2).setLocal2(bcs.getIndex(), Type.LONG_TYPE, Type.LONG2_TYPE); 531 case LSTORE_0, LSTORE_1, LSTORE_2, LSTORE_3 -> 532 currentFrame.decStack(2).setLocal2(opcode - LSTORE_0, Type.LONG_TYPE, Type.LONG2_TYPE); 533 case FSTORE -> 534 currentFrame.decStack(1).setLocal(bcs.getIndex(), Type.FLOAT_TYPE); 535 case FSTORE_0, FSTORE_1, FSTORE_2, FSTORE_3 -> 536 currentFrame.decStack(1).setLocal(opcode - FSTORE_0, Type.FLOAT_TYPE); 537 case DSTORE -> 538 currentFrame.decStack(2).setLocal2(bcs.getIndex(), Type.DOUBLE_TYPE, Type.DOUBLE2_TYPE); 539 case DSTORE_0, DSTORE_1, DSTORE_2, DSTORE_3 -> 540 currentFrame.decStack(2).setLocal2(opcode - DSTORE_0, Type.DOUBLE_TYPE, Type.DOUBLE2_TYPE); 541 case ASTORE -> 542 currentFrame.setLocal(bcs.getIndex(), currentFrame.popStack()); 543 case ASTORE_0, ASTORE_1, ASTORE_2, ASTORE_3 -> 544 currentFrame.setLocal(opcode - ASTORE_0, currentFrame.popStack()); 545 case LASTORE, DASTORE -> 546 currentFrame.decStack(4); 547 case IASTORE, BASTORE, CASTORE, SASTORE, FASTORE, AASTORE -> 548 currentFrame.decStack(3); 549 case POP, MONITORENTER, MONITOREXIT -> 550 currentFrame.decStack(1); 551 case POP2 -> 552 currentFrame.decStack(2); 553 case DUP -> 554 currentFrame.pushStack(type1 = currentFrame.popStack()).pushStack(type1); 555 case DUP_X1 -> { 556 type1 = currentFrame.popStack(); 557 type2 = currentFrame.popStack(); 558 currentFrame.pushStack(type1).pushStack(type2).pushStack(type1); 559 } 560 case DUP_X2 -> { 561 type1 = currentFrame.popStack(); 562 type2 = currentFrame.popStack(); 563 type3 = currentFrame.popStack(); 564 currentFrame.pushStack(type1).pushStack(type3).pushStack(type2).pushStack(type1); 565 } 566 case DUP2 -> { 567 type1 = currentFrame.popStack(); 568 type2 = currentFrame.popStack(); 569 currentFrame.pushStack(type2).pushStack(type1).pushStack(type2).pushStack(type1); 570 } 571 case DUP2_X1 -> { 572 type1 = currentFrame.popStack(); 573 type2 = currentFrame.popStack(); 574 type3 = currentFrame.popStack(); 575 currentFrame.pushStack(type2).pushStack(type1).pushStack(type3).pushStack(type2).pushStack(type1); 576 } 577 case DUP2_X2 -> { 578 type1 = currentFrame.popStack(); 579 type2 = currentFrame.popStack(); 580 type3 = currentFrame.popStack(); 581 type4 = currentFrame.popStack(); 582 currentFrame.pushStack(type2).pushStack(type1).pushStack(type4).pushStack(type3).pushStack(type2).pushStack(type1); 583 } 584 case SWAP -> { 585 type1 = currentFrame.popStack(); 586 type2 = currentFrame.popStack(); 587 currentFrame.pushStack(type1); 588 currentFrame.pushStack(type2); 589 } 590 case IADD, ISUB, IMUL, IDIV, IREM, ISHL, ISHR, IUSHR, IOR, IXOR, IAND -> 591 currentFrame.decStack(2).pushStack(Type.INTEGER_TYPE); 592 case INEG, ARRAYLENGTH, INSTANCEOF -> 593 currentFrame.decStack(1).pushStack(Type.INTEGER_TYPE); 594 case LADD, LSUB, LMUL, LDIV, LREM, LAND, LOR, LXOR -> 595 currentFrame.decStack(4).pushStack(Type.LONG_TYPE, Type.LONG2_TYPE); 596 case LNEG -> 597 currentFrame.decStack(2).pushStack(Type.LONG_TYPE, Type.LONG2_TYPE); 598 case LSHL, LSHR, LUSHR -> 599 currentFrame.decStack(3).pushStack(Type.LONG_TYPE, Type.LONG2_TYPE); 600 case FADD, FSUB, FMUL, FDIV, FREM -> 601 currentFrame.decStack(2).pushStack(Type.FLOAT_TYPE); 602 case FNEG -> 603 currentFrame.decStack(1).pushStack(Type.FLOAT_TYPE); 604 case DADD, DSUB, DMUL, DDIV, DREM -> 605 currentFrame.decStack(4).pushStack(Type.DOUBLE_TYPE, Type.DOUBLE2_TYPE); 606 case DNEG -> 607 currentFrame.decStack(2).pushStack(Type.DOUBLE_TYPE, Type.DOUBLE2_TYPE); 608 case IINC -> 609 currentFrame.checkLocal(bcs.getIndex()); 610 case I2L -> 611 currentFrame.decStack(1).pushStack(Type.LONG_TYPE, Type.LONG2_TYPE); 612 case L2I -> 613 currentFrame.decStack(2).pushStack(Type.INTEGER_TYPE); 614 case I2F -> 615 currentFrame.decStack(1).pushStack(Type.FLOAT_TYPE); 616 case I2D -> 617 currentFrame.decStack(1).pushStack(Type.DOUBLE_TYPE, Type.DOUBLE2_TYPE); 618 case L2F -> 619 currentFrame.decStack(2).pushStack(Type.FLOAT_TYPE); 620 case L2D -> 621 currentFrame.decStack(2).pushStack(Type.DOUBLE_TYPE, Type.DOUBLE2_TYPE); 622 case F2I -> 623 currentFrame.decStack(1).pushStack(Type.INTEGER_TYPE); 624 case F2L -> 625 currentFrame.decStack(1).pushStack(Type.LONG_TYPE, Type.LONG2_TYPE); 626 case F2D -> 627 currentFrame.decStack(1).pushStack(Type.DOUBLE_TYPE, Type.DOUBLE2_TYPE); 628 case D2L -> 629 currentFrame.decStack(2).pushStack(Type.LONG_TYPE, Type.LONG2_TYPE); 630 case D2F -> 631 currentFrame.decStack(2).pushStack(Type.FLOAT_TYPE); 632 case I2B, I2C, I2S -> 633 currentFrame.decStack(1).pushStack(Type.INTEGER_TYPE); 634 case LCMP, DCMPL, DCMPG -> 635 currentFrame.decStack(4).pushStack(Type.INTEGER_TYPE); 636 case FCMPL, FCMPG, D2I -> 637 currentFrame.decStack(2).pushStack(Type.INTEGER_TYPE); 638 case IF_ICMPEQ, IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ACMPEQ, IF_ACMPNE -> 639 checkJumpTarget(currentFrame.decStack(2), bcs.dest()); 640 case IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, IFNULL, IFNONNULL -> 641 checkJumpTarget(currentFrame.decStack(1), bcs.dest()); 642 case GOTO -> { 643 checkJumpTarget(currentFrame, bcs.dest()); 644 ncf = true; 645 } 646 case GOTO_W -> { 647 checkJumpTarget(currentFrame, bcs.destW()); 648 ncf = true; 649 } 650 case TABLESWITCH, LOOKUPSWITCH -> { 651 processSwitch(bcs); 652 ncf = true; 653 } 654 case LRETURN, DRETURN -> { 655 currentFrame.decStack(2); 656 ncf = true; 657 } 658 case IRETURN, FRETURN, ARETURN, ATHROW -> { 659 currentFrame.decStack(1); 660 ncf = true; 661 } 662 case GETSTATIC, PUTSTATIC, GETFIELD, PUTFIELD -> 663 processFieldInstructions(bcs); 664 case INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC, INVOKEINTERFACE, INVOKEDYNAMIC -> 665 this_uninit = processInvokeInstructions(bcs, (bci >= exMin && bci < exMax), this_uninit); 666 case NEW -> 667 currentFrame.pushStack(Type.uninitializedType(bci)); 668 case NEWARRAY -> 669 currentFrame.decStack(1).pushStack(getNewarrayType(bcs.getIndex())); 670 case ANEWARRAY -> 671 processAnewarray(bcs.getIndexU2()); 672 case CHECKCAST -> 673 currentFrame.decStack(1).pushStack(cpIndexToType(bcs.getIndexU2(), cp)); 674 case MULTIANEWARRAY -> { 675 type1 = cpIndexToType(bcs.getIndexU2(), cp); 676 int dim = bcs.getU1Unchecked(bcs.bci() + 3); 677 for (int i = 0; i < dim; i++) { 678 currentFrame.popStack(); 679 } 680 currentFrame.pushStack(type1); 681 } 682 case JSR, JSR_W, RET -> 683 throw generatorError("Instructions jsr, jsr_w, or ret must not appear in the class file version >= 51.0"); 684 default -> 685 throw generatorError(String.format("Bad instruction: %02x", opcode)); 686 } 687 if (!verified_exc_handlers && bci >= exMin && bci < exMax) { 688 processExceptionHandlerTargets(bci, this_uninit); 689 } 690 return ncf; 691 } 692 693 private void processExceptionHandlerTargets(int bci, boolean this_uninit) { 694 for (var ex : rawHandlers) { 695 if (bci == ex.start || (currentFrame.localsChanged && bci > ex.start && bci < ex.end)) { 696 int flags = currentFrame.flags; 697 if (this_uninit) flags |= FLAG_THIS_UNINIT; 698 Frame newFrame = currentFrame.frameInExceptionHandler(flags, ex.catchType); 699 checkJumpTarget(newFrame, ex.handler); 700 } 701 } 702 currentFrame.localsChanged = false; 703 } 704 705 private void processLdc(int index) { 706 switch (cp.entryByIndex(index).tag()) { 707 case TAG_UTF8 -> 708 currentFrame.pushStack(Type.OBJECT_TYPE); 709 case TAG_STRING -> 710 currentFrame.pushStack(Type.STRING_TYPE); 711 case TAG_CLASS -> 712 currentFrame.pushStack(Type.CLASS_TYPE); 713 case TAG_INTEGER -> 714 currentFrame.pushStack(Type.INTEGER_TYPE); 715 case TAG_FLOAT -> 716 currentFrame.pushStack(Type.FLOAT_TYPE); 717 case TAG_DOUBLE -> 718 currentFrame.pushStack(Type.DOUBLE_TYPE, Type.DOUBLE2_TYPE); 719 case TAG_LONG -> 720 currentFrame.pushStack(Type.LONG_TYPE, Type.LONG2_TYPE); 721 case TAG_METHOD_HANDLE -> 722 currentFrame.pushStack(Type.METHOD_HANDLE_TYPE); 723 case TAG_METHOD_TYPE -> 724 currentFrame.pushStack(Type.METHOD_TYPE); 725 case TAG_DYNAMIC -> 726 currentFrame.pushStack(cp.entryByIndex(index, ConstantDynamicEntry.class).asSymbol().constantType()); 727 default -> 728 throw generatorError("CP entry #%d %s is not loadable constant".formatted(index, cp.entryByIndex(index).tag())); 729 } 730 } 731 732 private void processSwitch(RawBytecodeHelper bcs) { 733 int bci = bcs.bci(); 734 int alignedBci = RawBytecodeHelper.align(bci + 1); 735 int defaultOffset = bcs.getIntUnchecked(alignedBci); 736 int keys, delta; 737 currentFrame.popStack(); 738 if (bcs.opcode() == TABLESWITCH) { 739 int low = bcs.getIntUnchecked(alignedBci + 4); 740 int high = bcs.getIntUnchecked(alignedBci + 2 * 4); 741 if (low > high) { 742 throw generatorError("low must be less than or equal to high in tableswitch"); 743 } 744 keys = high - low + 1; 745 if (keys < 0) { 746 throw generatorError("too many keys in tableswitch"); 747 } 748 delta = 1; 749 } else { 750 keys = bcs.getIntUnchecked(alignedBci + 4); 751 if (keys < 0) { 752 throw generatorError("number of keys in lookupswitch less than 0"); 753 } 754 delta = 2; 755 for (int i = 0; i < (keys - 1); i++) { 756 int this_key = bcs.getIntUnchecked(alignedBci + (2 + 2 * i) * 4); 757 int next_key = bcs.getIntUnchecked(alignedBci + (2 + 2 * i + 2) * 4); 758 if (this_key >= next_key) { 759 throw generatorError("Bad lookupswitch instruction"); 760 } 761 } 762 } 763 int target = bci + defaultOffset; 764 checkJumpTarget(currentFrame, target); 765 for (int i = 0; i < keys; i++) { 766 target = bci + bcs.getIntUnchecked(alignedBci + (3 + i * delta) * 4); 767 checkJumpTarget(currentFrame, target); 768 } 769 } 770 771 private void processFieldInstructions(RawBytecodeHelper bcs) { 772 var nameAndType = cp.entryByIndex(bcs.getIndexU2(), MemberRefEntry.class).nameAndType(); 773 var desc = Util.fieldTypeSymbol(nameAndType.type()); 774 var currentFrame = this.currentFrame; 775 switch (bcs.opcode()) { 776 case GETSTATIC -> 777 currentFrame.pushStack(desc); 778 case PUTSTATIC -> { 779 currentFrame.decStack(Util.isDoubleSlot(desc) ? 2 : 1); 780 } 781 case GETFIELD -> { 782 currentFrame.decStack(1); 783 currentFrame.pushStack(desc); 784 } 785 case PUTFIELD -> { 786 if (strictFieldsToPut.length > 0) { 787 currentFrame.putStrictField(nameAndType); 788 } 789 currentFrame.decStack(Util.isDoubleSlot(desc) ? 3 : 2); 790 } 791 default -> throw new AssertionError("Should not reach here"); 792 } 793 } 794 795 private boolean processInvokeInstructions(RawBytecodeHelper bcs, boolean inTryBlock, boolean thisUninit) { 796 int index = bcs.getIndexU2(); 797 int opcode = bcs.opcode(); 798 var nameAndType = opcode == INVOKEDYNAMIC 799 ? cp.entryByIndex(index, InvokeDynamicEntry.class).nameAndType() 800 : cp.entryByIndex(index, MemberRefEntry.class).nameAndType(); 801 var mDesc = Util.methodTypeSymbol(nameAndType.type()); 802 int bci = bcs.bci(); 803 var currentFrame = this.currentFrame; 804 currentFrame.decStack(Util.parameterSlots(mDesc)); 805 if (opcode != INVOKESTATIC && opcode != INVOKEDYNAMIC) { 806 if (nameAndType.name().equalsString(OBJECT_INITIALIZER_NAME)) { 807 Type type = currentFrame.popStack(); 808 if (type == Type.UNITIALIZED_THIS_TYPE) { 809 if (inTryBlock) { 810 processExceptionHandlerTargets(bci, true); 811 } 812 var owner = cp.entryByIndex(index, MemberRefEntry.class).owner(); 813 if (!owner.name().equalsString(((ClassOrInterfaceDescImpl) thisType.sym).internalName()) 814 && currentFrame.unsetFieldsSize != 0) { 815 throw generatorError("Unset fields mismatch"); 816 } 817 currentFrame.initializeObject(type, thisType); 818 currentFrame.unsetFieldsSize = 0; 819 currentFrame.unsetFields = UnsetField.EMPTY_ARRAY; 820 thisUninit = true; 821 } else if (type.tag == ITEM_UNINITIALIZED) { 822 Type new_class_type = cpIndexToType(bcs.getU2(type.bci + 1), cp); 823 if (inTryBlock) { 824 processExceptionHandlerTargets(bci, thisUninit); 825 } 826 currentFrame.initializeObject(type, new_class_type); 827 } else { 828 throw generatorError("Bad operand type when invoking <init>"); 829 } 830 } else { 831 currentFrame.decStack(1); 832 } 833 } 834 currentFrame.pushStack(mDesc.returnType()); 835 return thisUninit; 836 } 837 838 private Type getNewarrayType(int index) { 839 if (index < T_BOOLEAN || index > T_LONG) throw generatorError("Illegal newarray instruction type %d".formatted(index)); 840 return ARRAY_FROM_BASIC_TYPE[index]; 841 } 842 843 private void processAnewarray(int index) { 844 currentFrame.popStack(); 845 currentFrame.pushStack(cpIndexToType(index, cp).toArray()); 846 } 847 848 /** 849 * {@return the generator error with attached details} 850 * @param msg error message 851 */ 852 private IllegalArgumentException generatorError(String msg) { 853 return generatorError(msg, currentFrame.offset); 854 } 855 856 /** 857 * {@return the generator error with attached details} 858 * @param msg error message 859 * @param offset bytecode offset where the error occurred 860 */ 861 private IllegalArgumentException generatorError(String msg, int offset) { 862 var sb = new StringBuilder("%s at bytecode offset %d of method %s(%s)".formatted( 863 msg, 864 offset, 865 methodName, 866 methodDesc.parameterList().stream().map(ClassDesc::displayName).collect(Collectors.joining(",")))); 867 Util.dumpMethod(cp, thisType.sym(), methodName, methodDesc, isStatic ? ACC_STATIC : 0, bytecode, sb::append); 868 return new IllegalArgumentException(sb.toString()); 869 } 870 871 /** 872 * Performs detection of mandatory stack map frames in a single bytecode traversing pass 873 * @return detected frames 874 */ 875 private void detectFrames() { 876 var bcs = bytecode.start(); 877 boolean no_control_flow = false; 878 int opcode, bci = 0; 879 while (bcs.next()) try { 880 opcode = bcs.opcode(); 881 bci = bcs.bci(); 882 if (no_control_flow) { 883 addFrame(bci); 884 } 885 no_control_flow = switch (opcode) { 886 case GOTO -> { 887 addFrame(bcs.dest()); 888 yield true; 889 } 890 case GOTO_W -> { 891 addFrame(bcs.destW()); 892 yield true; 893 } 894 case IF_ICMPEQ, IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, 895 IF_ICMPGT, IF_ICMPLE, IFEQ, IFNE, 896 IFLT, IFGE, IFGT, IFLE, IF_ACMPEQ, 897 IF_ACMPNE , IFNULL , IFNONNULL -> { 898 addFrame(bcs.dest()); 899 yield false; 900 } 901 case TABLESWITCH, LOOKUPSWITCH -> { 902 int aligned_bci = RawBytecodeHelper.align(bci + 1); 903 int default_ofset = bcs.getIntUnchecked(aligned_bci); 904 int keys, delta; 905 if (bcs.opcode() == TABLESWITCH) { 906 int low = bcs.getIntUnchecked(aligned_bci + 4); 907 int high = bcs.getIntUnchecked(aligned_bci + 2 * 4); 908 keys = high - low + 1; 909 delta = 1; 910 } else { 911 keys = bcs.getIntUnchecked(aligned_bci + 4); 912 delta = 2; 913 } 914 addFrame(bci + default_ofset); 915 for (int i = 0; i < keys; i++) { 916 addFrame(bci + bcs.getIntUnchecked(aligned_bci + (3 + i * delta) * 4)); 917 } 918 yield true; 919 } 920 case IRETURN, LRETURN, FRETURN, DRETURN, 921 ARETURN, RETURN, ATHROW -> true; 922 default -> false; 923 }; 924 } catch (IllegalArgumentException iae) { 925 throw generatorError("Detected branch target out of bytecode range", bci); 926 } 927 for (int i = 0; i < rawHandlers.size(); i++) try { 928 addFrame(rawHandlers.get(i).handler()); 929 } catch (IllegalArgumentException iae) { 930 if (!filterDeadLabels) 931 throw generatorError("Detected exception handler out of bytecode range"); 932 } 933 } 934 935 private void addFrame(int offset) { 936 Preconditions.checkIndex(offset, bytecode.length(), RawBytecodeHelper.IAE_FORMATTER); 937 var frames = this.frames; 938 int i = 0, framesCount = this.framesCount; 939 for (; i < framesCount; i++) { 940 var frameOffset = frames[i].offset; 941 if (frameOffset == offset) { 942 return; 943 } 944 if (frameOffset > offset) { 945 break; 946 } 947 } 948 if (framesCount >= frames.length) { 949 int newCapacity = framesCount + 8; 950 this.frames = frames = framesCount == 0 ? new Frame[newCapacity] : Arrays.copyOf(frames, newCapacity); 951 } 952 if (i != framesCount) { 953 System.arraycopy(frames, i, frames, i + 1, framesCount - i); 954 } 955 frames[i] = new Frame(offset, classHierarchy); 956 this.framesCount = framesCount + 1; 957 } 958 959 private final class Frame { 960 961 int offset; 962 int localsSize, stackSize, unsetFieldsSize; 963 int flags; 964 int frameMaxStack = 0, frameMaxLocals = 0; 965 boolean dirty = false; 966 boolean localsChanged = false; 967 968 private final ClassHierarchyImpl classHierarchy; 969 970 private Type[] locals, stack; 971 private UnsetField[] unsetFields = UnsetField.EMPTY_ARRAY; // sorted, modifiable oversized array 972 973 Frame(ClassHierarchyImpl classHierarchy) { 974 this(-1, 0, 0, 0, null, null, classHierarchy); 975 } 976 977 Frame(int offset, ClassHierarchyImpl classHierarchy) { 978 this(offset, -1, 0, 0, null, null, classHierarchy); 979 } 980 981 Frame(int offset, int flags, int locals_size, int stack_size, Type[] locals, Type[] stack, ClassHierarchyImpl classHierarchy) { 982 this.offset = offset; 983 this.localsSize = locals_size; 984 this.stackSize = stack_size; 985 this.flags = flags; 986 this.locals = locals; 987 this.stack = stack; 988 this.classHierarchy = classHierarchy; 989 } 990 991 @Override 992 public String toString() { 993 return (dirty ? "frame* @" : "frame @") + offset + 994 " with locals " + (locals == null ? "[]" : Arrays.asList(locals).subList(0, localsSize)) + 995 " and stack " + (stack == null ? "[]" : Arrays.asList(stack).subList(0, stackSize)) + 996 " and unset fields " + (unsetFields == null ? "[]" : Arrays.asList(unsetFields).subList(0, unsetFieldsSize)); 997 } 998 999 Frame pushStack(ClassDesc desc) { 1000 if (desc == CD_long) return pushStack(Type.LONG_TYPE, Type.LONG2_TYPE); 1001 if (desc == CD_double) return pushStack(Type.DOUBLE_TYPE, Type.DOUBLE2_TYPE); 1002 return desc == CD_void ? this 1003 : pushStack( 1004 desc.isPrimitive() 1005 ? (desc == CD_float ? Type.FLOAT_TYPE : Type.INTEGER_TYPE) 1006 : Type.referenceType(desc)); 1007 } 1008 1009 Frame pushStack(Type type) { 1010 checkStack(stackSize); 1011 stack[stackSize++] = type; 1012 return this; 1013 } 1014 1015 Frame pushStack(Type type1, Type type2) { 1016 checkStack(stackSize + 1); 1017 stack[stackSize++] = type1; 1018 stack[stackSize++] = type2; 1019 return this; 1020 } 1021 1022 Type popStack() { 1023 if (stackSize < 1) throw generatorError("Operand stack underflow"); 1024 return stack[--stackSize]; 1025 } 1026 1027 Frame decStack(int size) { 1028 stackSize -= size; 1029 if (stackSize < 0) throw generatorError("Operand stack underflow"); 1030 return this; 1031 } 1032 1033 Frame frameInExceptionHandler(int flags, Type excType) { 1034 return new Frame(offset, flags, localsSize, 1, locals, new Type[] {excType}, classHierarchy); 1035 } 1036 1037 void initializeObject(Type old_object, Type new_object) { 1038 int i; 1039 for (i = 0; i < localsSize; i++) { 1040 if (locals[i].equals(old_object)) { 1041 locals[i] = new_object; 1042 localsChanged = true; 1043 } 1044 } 1045 for (i = 0; i < stackSize; i++) { 1046 if (stack[i].equals(old_object)) { 1047 stack[i] = new_object; 1048 } 1049 } 1050 if (old_object == Type.UNITIALIZED_THIS_TYPE) { 1051 flags &= ~FLAG_THIS_UNINIT; 1052 assert flags == 0 : flags; 1053 } 1054 } 1055 1056 Frame checkLocal(int index) { 1057 if (index >= frameMaxLocals) frameMaxLocals = index + 1; 1058 if (locals == null) { 1059 locals = new Type[index + FRAME_DEFAULT_CAPACITY]; 1060 Arrays.fill(locals, Type.TOP_TYPE); 1061 } else if (index >= locals.length) { 1062 int current = locals.length; 1063 locals = Arrays.copyOf(locals, index + FRAME_DEFAULT_CAPACITY); 1064 Arrays.fill(locals, current, locals.length, Type.TOP_TYPE); 1065 } 1066 return this; 1067 } 1068 1069 void putStrictField(NameAndTypeEntry nat) { 1070 int shift = 0; 1071 var array = unsetFields; 1072 for (int i = 0; i < unsetFieldsSize; i++) { 1073 var f = array[i]; 1074 if (f.name().equals(nat.name()) && f.type().equals(nat.type())) { 1075 shift++; 1076 } else if (shift != 0) { 1077 array[i - shift] = array[i]; 1078 array[i] = null; 1079 } 1080 } 1081 if (shift > 1) { 1082 throw generatorError(nat + "; discovered " + shift); 1083 } 1084 unsetFieldsSize -= shift; 1085 } 1086 1087 private void checkStack(int index) { 1088 if (index >= frameMaxStack) frameMaxStack = index + 1; 1089 if (stack == null) { 1090 stack = new Type[index + FRAME_DEFAULT_CAPACITY]; 1091 Arrays.fill(stack, Type.TOP_TYPE); 1092 } else if (index >= stack.length) { 1093 int current = stack.length; 1094 stack = Arrays.copyOf(stack, index + FRAME_DEFAULT_CAPACITY); 1095 Arrays.fill(stack, current, stack.length, Type.TOP_TYPE); 1096 } 1097 } 1098 1099 private void setLocalRawInternal(int index, Type type) { 1100 checkLocal(index); 1101 localsChanged |= !type.equals(locals[index]); 1102 locals[index] = type; 1103 } 1104 1105 void setLocalsFromArg(String name, MethodTypeDesc methodDesc, boolean isStatic, Type thisKlass, UnsetField[] strictFieldsToPut) { 1106 int localsSize = 0; 1107 // Pre-emptively create a locals array that encompass all parameter slots 1108 checkLocal(Util.parameterSlots(methodDesc) + (isStatic ? -1 : 0)); 1109 Type type; 1110 Type[] locals = this.locals; 1111 if (!isStatic) { 1112 if (OBJECT_INITIALIZER_NAME.equals(name) && !CD_Object.equals(thisKlass.sym)) { 1113 int strictFieldCount = strictFieldsToPut.length; 1114 this.unsetFields = UnsetField.copyArray(strictFieldsToPut, strictFieldCount); 1115 this.unsetFieldsSize = strictFieldCount; 1116 type = Type.UNITIALIZED_THIS_TYPE; 1117 this.flags = FLAG_THIS_UNINIT; 1118 } else { 1119 this.unsetFields = UnsetField.EMPTY_ARRAY; 1120 this.unsetFieldsSize = 0; 1121 type = thisKlass; 1122 this.flags = 0; 1123 } 1124 locals[localsSize++] = type; 1125 } 1126 for (int i = 0; i < methodDesc.parameterCount(); i++) { 1127 var desc = methodDesc.parameterType(i); 1128 if (desc == CD_long) { 1129 locals[localsSize ] = Type.LONG_TYPE; 1130 locals[localsSize + 1] = Type.LONG2_TYPE; 1131 localsSize += 2; 1132 } else if (desc == CD_double) { 1133 locals[localsSize ] = Type.DOUBLE_TYPE; 1134 locals[localsSize + 1] = Type.DOUBLE2_TYPE; 1135 localsSize += 2; 1136 } else { 1137 if (!desc.isPrimitive()) { 1138 type = Type.referenceType(desc); 1139 } else if (desc == CD_float) { 1140 type = Type.FLOAT_TYPE; 1141 } else { 1142 type = Type.INTEGER_TYPE; 1143 } 1144 locals[localsSize++] = type; 1145 } 1146 } 1147 this.localsSize = localsSize; 1148 } 1149 1150 void copyFrom(Frame src) { 1151 if (locals != null && src.localsSize < locals.length) Arrays.fill(locals, src.localsSize, locals.length, Type.TOP_TYPE); 1152 localsSize = src.localsSize; 1153 checkLocal(src.localsSize - 1); 1154 if (src.localsSize > 0) System.arraycopy(src.locals, 0, locals, 0, src.localsSize); 1155 if (stack != null && src.stackSize < stack.length) Arrays.fill(stack, src.stackSize, stack.length, Type.TOP_TYPE); 1156 stackSize = src.stackSize; 1157 checkStack(src.stackSize - 1); 1158 if (src.stackSize > 0) System.arraycopy(src.stack, 0, stack, 0, src.stackSize); 1159 unsetFieldsSize = src.unsetFieldsSize; 1160 unsetFields = UnsetField.copyArray(src.unsetFields, src.unsetFieldsSize); 1161 flags = src.flags; 1162 localsChanged = true; 1163 } 1164 1165 void checkAssignableTo(Frame target) { 1166 int localsSize = this.localsSize; 1167 int stackSize = this.stackSize; 1168 int myUnsetFieldsSize = this.unsetFieldsSize; 1169 if (target.flags == -1) { 1170 target.locals = locals == null ? null : locals.clone(); 1171 target.localsSize = localsSize; 1172 if (stackSize > 0) { 1173 target.stack = stack.clone(); 1174 target.stackSize = stackSize; 1175 } 1176 target.unsetFields = UnsetField.copyArray(this.unsetFields, myUnsetFieldsSize); 1177 target.unsetFieldsSize = myUnsetFieldsSize; 1178 target.flags = flags; 1179 target.dirty = true; 1180 } else { 1181 if (target.localsSize > localsSize) { 1182 target.localsSize = localsSize; 1183 target.dirty = true; 1184 } 1185 for (int i = 0; i < target.localsSize; i++) { 1186 merge(locals[i], target.locals, i, target); 1187 } 1188 if (stackSize != target.stackSize) { 1189 throw generatorError("Stack size mismatch"); 1190 } 1191 for (int i = 0; i < target.stackSize; i++) { 1192 if (merge(stack[i], target.stack, i, target) == Type.TOP_TYPE) { 1193 throw generatorError("Stack content mismatch"); 1194 } 1195 } 1196 if (myUnsetFieldsSize != 0) { 1197 mergeUnsetFields(target); 1198 } 1199 } 1200 } 1201 1202 private Type getLocalRawInternal(int index) { 1203 checkLocal(index); 1204 return locals[index]; 1205 } 1206 1207 Type getLocal(int index) { 1208 Type ret = getLocalRawInternal(index); 1209 if (index >= localsSize) { 1210 localsSize = index + 1; 1211 } 1212 return ret; 1213 } 1214 1215 void setLocal(int index, Type type) { 1216 Type old = getLocalRawInternal(index); 1217 if (old == Type.DOUBLE_TYPE || old == Type.LONG_TYPE) { 1218 setLocalRawInternal(index + 1, Type.TOP_TYPE); 1219 } 1220 if (old == Type.DOUBLE2_TYPE || old == Type.LONG2_TYPE) { 1221 setLocalRawInternal(index - 1, Type.TOP_TYPE); 1222 } 1223 setLocalRawInternal(index, type); 1224 if (index >= localsSize) { 1225 localsSize = index + 1; 1226 } 1227 } 1228 1229 void setLocal2(int index, Type type1, Type type2) { 1230 Type old = getLocalRawInternal(index + 1); 1231 if (old == Type.DOUBLE_TYPE || old == Type.LONG_TYPE) { 1232 setLocalRawInternal(index + 2, Type.TOP_TYPE); 1233 } 1234 old = getLocalRawInternal(index); 1235 if (old == Type.DOUBLE2_TYPE || old == Type.LONG2_TYPE) { 1236 setLocalRawInternal(index - 1, Type.TOP_TYPE); 1237 } 1238 setLocalRawInternal(index, type1); 1239 setLocalRawInternal(index + 1, type2); 1240 if (index >= localsSize - 1) { 1241 localsSize = index + 2; 1242 } 1243 } 1244 1245 private Type merge(Type me, Type[] toTypes, int i, Frame target) { 1246 var to = toTypes[i]; 1247 var newTo = to.mergeFrom(me, classHierarchy); 1248 if (to != newTo && !to.equals(newTo)) { 1249 toTypes[i] = newTo; 1250 target.dirty = true; 1251 } 1252 return newTo; 1253 } 1254 1255 // Merge this frame's unset fields into the target frame 1256 private void mergeUnsetFields(Frame target) { 1257 int myUnsetSize = unsetFieldsSize; 1258 int targetUnsetSize = target.unsetFieldsSize; 1259 var myUnsets = unsetFields; 1260 var targetUnsets = target.unsetFields; 1261 if (UnsetField.matches(myUnsets, myUnsetSize, targetUnsets, targetUnsetSize)) { 1262 return; // no merge 1263 } 1264 // merge sort 1265 var merged = new UnsetField[StackMapGenerator.this.strictFieldsToPut.length]; 1266 int mergedSize = 0; 1267 int i = 0; 1268 int j = 0; 1269 while (i < myUnsetSize && j < targetUnsetSize) { 1270 var myCandidate = myUnsets[i]; 1271 var targetCandidate = targetUnsets[j]; 1272 var cmp = myCandidate.compareTo(targetCandidate); 1273 if (cmp == 0) { 1274 merged[mergedSize++] = myCandidate; 1275 i++; 1276 j++; 1277 } else if (cmp < 0) { 1278 merged[mergedSize++] = myCandidate; 1279 i++; 1280 } else { 1281 merged[mergedSize++] = targetCandidate; 1282 j++; 1283 } 1284 } 1285 if (i < myUnsetSize) { 1286 int len = myUnsetSize - i; 1287 System.arraycopy(myUnsets, i, merged, mergedSize, len); 1288 mergedSize += len; 1289 } else if (j < targetUnsetSize) { 1290 int len = targetUnsetSize - j; 1291 System.arraycopy(targetUnsets, j, merged, mergedSize, len); 1292 mergedSize += len; 1293 } 1294 1295 target.unsetFieldsSize = mergedSize; 1296 target.unsetFields = merged; 1297 target.dirty = true; 1298 } 1299 1300 private static int trimAndCompress(Type[] types, int count) { 1301 while (count > 0 && types[count - 1] == Type.TOP_TYPE) count--; 1302 int compressed = 0; 1303 for (int i = 0; i < count; i++) { 1304 if (!types[i].isCategory2_2nd()) { 1305 if (compressed != i) { 1306 types[compressed] = types[i]; 1307 } 1308 compressed++; 1309 } 1310 } 1311 return compressed; 1312 } 1313 1314 void trimAndCompress() { 1315 localsSize = trimAndCompress(locals, localsSize); 1316 stackSize = trimAndCompress(stack, stackSize); 1317 } 1318 1319 boolean hasUninitializedThis() { 1320 int size = this.localsSize; 1321 var localVars = this.locals; 1322 for (int i = 0; i < size; i++) { 1323 if (localVars[i] == Type.UNITIALIZED_THIS_TYPE) 1324 return true; 1325 } 1326 return false; 1327 } 1328 1329 private static boolean equals(Type[] l1, Type[] l2, int commonSize) { 1330 if (l1 == null || l2 == null) return commonSize == 0; 1331 return Arrays.equals(l1, 0, commonSize, l2, 0, commonSize); 1332 } 1333 1334 // In sync with StackMapDecoder::needsLarvalFrameForTransition 1335 private boolean needsLarvalFrame(Frame prevFrame) { 1336 if (UnsetField.matches(unsetFields, unsetFieldsSize, prevFrame.unsetFields, prevFrame.unsetFieldsSize)) 1337 return false; 1338 if (!hasUninitializedThis()) { 1339 assert unsetFieldsSize == 0 : this; // Should have been handled by processInvokeInstructions 1340 return false; 1341 } 1342 return true; 1343 } 1344 1345 void writeTo(BufWriterImpl out, Frame prevFrame, ConstantPoolBuilder cp) { 1346 // enclosing frames 1347 if (needsLarvalFrame(prevFrame)) { 1348 out.writeU1U2(StackMapDecoder.EARLY_LARVAL, unsetFieldsSize); 1349 for (int i = 0; i < unsetFieldsSize; i++) { 1350 var f = unsetFields[i]; 1351 out.writeIndex(cp.nameAndTypeEntry(f.name(), f.type())); 1352 } 1353 } 1354 // base frame 1355 int localsSize = this.localsSize; 1356 int stackSize = this.stackSize; 1357 int offsetDelta = offset - prevFrame.offset - 1; 1358 if (stackSize == 0) { 1359 int commonLocalsSize = localsSize > prevFrame.localsSize ? prevFrame.localsSize : localsSize; 1360 int diffLocalsSize = localsSize - prevFrame.localsSize; 1361 if (-3 <= diffLocalsSize && diffLocalsSize <= 3 && equals(locals, prevFrame.locals, commonLocalsSize)) { 1362 if (diffLocalsSize == 0 && offsetDelta < 64) { //same frame 1363 out.writeU1(offsetDelta); 1364 } else { //chop, same extended or append frame 1365 out.writeU1U2(251 + diffLocalsSize, offsetDelta); 1366 for (int i=commonLocalsSize; i<localsSize; i++) locals[i].writeTo(out, cp); 1367 } 1368 return; 1369 } 1370 } else if (stackSize == 1 && localsSize == prevFrame.localsSize && equals(locals, prevFrame.locals, localsSize)) { 1371 if (offsetDelta < 64) { //same locals 1 stack item frame 1372 out.writeU1(64 + offsetDelta); 1373 } else { //same locals 1 stack item extended frame 1374 out.writeU1U2(247, offsetDelta); 1375 } 1376 stack[0].writeTo(out, cp); 1377 return; 1378 } 1379 //full frame 1380 out.writeU1U2U2(255, offsetDelta, localsSize); 1381 for (int i=0; i<localsSize; i++) locals[i].writeTo(out, cp); 1382 out.writeU2(stackSize); 1383 for (int i=0; i<stackSize; i++) stack[i].writeTo(out, cp); 1384 } 1385 } 1386 1387 private static record Type(int tag, ClassDesc sym, int bci) { 1388 1389 //singleton types 1390 static final Type TOP_TYPE = simpleType(ITEM_TOP), 1391 NULL_TYPE = simpleType(ITEM_NULL), 1392 INTEGER_TYPE = simpleType(ITEM_INTEGER), 1393 FLOAT_TYPE = simpleType(ITEM_FLOAT), 1394 LONG_TYPE = simpleType(ITEM_LONG), 1395 LONG2_TYPE = simpleType(ITEM_LONG_2ND), 1396 DOUBLE_TYPE = simpleType(ITEM_DOUBLE), 1397 BOOLEAN_TYPE = simpleType(ITEM_BOOLEAN), 1398 BYTE_TYPE = simpleType(ITEM_BYTE), 1399 CHAR_TYPE = simpleType(ITEM_CHAR), 1400 SHORT_TYPE = simpleType(ITEM_SHORT), 1401 DOUBLE2_TYPE = simpleType(ITEM_DOUBLE_2ND), 1402 UNITIALIZED_THIS_TYPE = simpleType(ITEM_UNINITIALIZED_THIS); 1403 1404 //frequently used types to reduce footprint 1405 static final Type OBJECT_TYPE = referenceType(CD_Object), 1406 THROWABLE_TYPE = referenceType(CD_Throwable), 1407 INT_ARRAY_TYPE = referenceType(CD_int.arrayType()), 1408 BOOLEAN_ARRAY_TYPE = referenceType(CD_boolean.arrayType()), 1409 BYTE_ARRAY_TYPE = referenceType(CD_byte.arrayType()), 1410 CHAR_ARRAY_TYPE = referenceType(CD_char.arrayType()), 1411 SHORT_ARRAY_TYPE = referenceType(CD_short.arrayType()), 1412 LONG_ARRAY_TYPE = referenceType(CD_long.arrayType()), 1413 DOUBLE_ARRAY_TYPE = referenceType(CD_double.arrayType()), 1414 FLOAT_ARRAY_TYPE = referenceType(CD_float.arrayType()), 1415 STRING_TYPE = referenceType(CD_String), 1416 CLASS_TYPE = referenceType(CD_Class), 1417 METHOD_HANDLE_TYPE = referenceType(CD_MethodHandle), 1418 METHOD_TYPE = referenceType(CD_MethodType); 1419 1420 private static Type simpleType(int tag) { 1421 return new Type(tag, null, 0); 1422 } 1423 1424 static Type referenceType(ClassDesc desc) { 1425 return new Type(ITEM_OBJECT, desc, 0); 1426 } 1427 1428 static Type uninitializedType(int bci) { 1429 return new Type(ITEM_UNINITIALIZED, null, bci); 1430 } 1431 1432 @Override //mandatory override to avoid use of method reference during JDK bootstrap 1433 public boolean equals(Object o) { 1434 return (o instanceof Type t) && t.tag == tag && t.bci == bci && Objects.equals(sym, t.sym); 1435 } 1436 1437 boolean isCategory2_2nd() { 1438 return this == DOUBLE2_TYPE || this == LONG2_TYPE; 1439 } 1440 1441 boolean isReference() { 1442 return tag == ITEM_OBJECT || this == NULL_TYPE; 1443 } 1444 1445 boolean isObject() { 1446 return tag == ITEM_OBJECT && sym.isClassOrInterface(); 1447 } 1448 1449 boolean isArray() { 1450 return tag == ITEM_OBJECT && sym.isArray(); 1451 } 1452 1453 Type mergeFrom(Type from, ClassHierarchyImpl context) { 1454 if (this == TOP_TYPE || this == from || equals(from)) { 1455 return this; 1456 } else { 1457 return switch (tag) { 1458 case ITEM_BOOLEAN, ITEM_BYTE, ITEM_CHAR, ITEM_SHORT -> 1459 from == INTEGER_TYPE ? this : TOP_TYPE; 1460 default -> 1461 isReference() && from.isReference() ? mergeReferenceFrom(from, context) : TOP_TYPE; 1462 }; 1463 } 1464 } 1465 1466 Type mergeComponentFrom(Type from, ClassHierarchyImpl context) { 1467 if (this == TOP_TYPE || this == from || equals(from)) { 1468 return this; 1469 } else { 1470 return switch (tag) { 1471 case ITEM_BOOLEAN, ITEM_BYTE, ITEM_CHAR, ITEM_SHORT -> 1472 TOP_TYPE; 1473 default -> 1474 isReference() && from.isReference() ? mergeReferenceFrom(from, context) : TOP_TYPE; 1475 }; 1476 } 1477 } 1478 1479 private static final ClassDesc CD_Cloneable = ClassOrInterfaceDescImpl.ofValidated("Ljava/lang/Cloneable;"); 1480 private static final ClassDesc CD_Serializable = ClassOrInterfaceDescImpl.ofValidated("Ljava/io/Serializable;"); 1481 1482 private Type mergeReferenceFrom(Type from, ClassHierarchyImpl context) { 1483 if (from == NULL_TYPE) { 1484 return this; 1485 } else if (this == NULL_TYPE) { 1486 return from; 1487 } else if (sym.equals(from.sym)) { 1488 return this; 1489 } else if (isObject()) { 1490 if (CD_Object.equals(sym)) { 1491 return this; 1492 } 1493 if (context.isInterface(sym)) { 1494 if (!from.isArray() || CD_Cloneable.equals(sym) || CD_Serializable.equals(sym)) { 1495 return this; 1496 } 1497 } else if (from.isObject()) { 1498 var anc = context.commonAncestor(sym, from.sym); 1499 return anc == null ? this : Type.referenceType(anc); 1500 } 1501 } else if (isArray() && from.isArray()) { 1502 Type compThis = getComponent(); 1503 Type compFrom = from.getComponent(); 1504 if (compThis != TOP_TYPE && compFrom != TOP_TYPE) { 1505 return compThis.mergeComponentFrom(compFrom, context).toArray(); 1506 } 1507 } 1508 return OBJECT_TYPE; 1509 } 1510 1511 Type toArray() { 1512 return switch (tag) { 1513 case ITEM_BOOLEAN -> BOOLEAN_ARRAY_TYPE; 1514 case ITEM_BYTE -> BYTE_ARRAY_TYPE; 1515 case ITEM_CHAR -> CHAR_ARRAY_TYPE; 1516 case ITEM_SHORT -> SHORT_ARRAY_TYPE; 1517 case ITEM_INTEGER -> INT_ARRAY_TYPE; 1518 case ITEM_LONG -> LONG_ARRAY_TYPE; 1519 case ITEM_FLOAT -> FLOAT_ARRAY_TYPE; 1520 case ITEM_DOUBLE -> DOUBLE_ARRAY_TYPE; 1521 case ITEM_OBJECT -> Type.referenceType(sym.arrayType()); 1522 default -> OBJECT_TYPE; 1523 }; 1524 } 1525 1526 Type getComponent() { 1527 if (isArray()) { 1528 var comp = sym.componentType(); 1529 if (comp.isPrimitive()) { 1530 return switch (comp.descriptorString().charAt(0)) { 1531 case 'Z' -> Type.BOOLEAN_TYPE; 1532 case 'B' -> Type.BYTE_TYPE; 1533 case 'C' -> Type.CHAR_TYPE; 1534 case 'S' -> Type.SHORT_TYPE; 1535 case 'I' -> Type.INTEGER_TYPE; 1536 case 'J' -> Type.LONG_TYPE; 1537 case 'F' -> Type.FLOAT_TYPE; 1538 case 'D' -> Type.DOUBLE_TYPE; 1539 default -> Type.TOP_TYPE; 1540 }; 1541 } 1542 return Type.referenceType(comp); 1543 } 1544 return Type.TOP_TYPE; 1545 } 1546 1547 void writeTo(BufWriterImpl bw, ConstantPoolBuilder cp) { 1548 switch (tag) { 1549 case ITEM_OBJECT -> 1550 bw.writeU1U2(tag, cp.classEntry(sym).index()); 1551 case ITEM_UNINITIALIZED -> 1552 bw.writeU1U2(tag, bci); 1553 default -> 1554 bw.writeU1(tag); 1555 } 1556 } 1557 } 1558 }