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