1 /*
  2  * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 package jdk.internal.classfile.impl;
 26 
 27 import java.lang.classfile.*;
 28 import java.lang.classfile.attribute.CodeAttribute;
 29 import java.lang.classfile.attribute.RuntimeInvisibleTypeAnnotationsAttribute;
 30 import java.lang.classfile.attribute.RuntimeVisibleTypeAnnotationsAttribute;
 31 import java.lang.classfile.attribute.StackMapTableAttribute;
 32 import java.lang.classfile.attribute.UnknownAttribute;
 33 import java.lang.classfile.constantpool.ClassEntry;
 34 import java.lang.classfile.instruction.*;
 35 import java.util.ArrayList;
 36 import java.util.Collections;
 37 import java.util.List;
 38 import java.util.Objects;
 39 import java.util.Optional;
 40 import java.util.function.Consumer;
 41 
 42 import static jdk.internal.classfile.impl.StackMapGenerator.*;
 43 import static jdk.internal.classfile.impl.RawBytecodeHelper.*;
 44 
 45 public final class CodeImpl
 46         extends BoundAttribute.BoundCodeAttribute
 47         implements LabelContext {
 48 
 49     static final Instruction[] SINGLETON_INSTRUCTIONS = new Instruction[256];
 50 
 51     static {
 52         for (var o : Opcode.values()) {
 53             if (o.sizeIfFixed() == 1) {
 54                 SINGLETON_INSTRUCTIONS[o.bytecode()] = switch (o.kind()) {
 55                     case ARRAY_LOAD -> ArrayLoadInstruction.of(o);
 56                     case ARRAY_STORE -> ArrayStoreInstruction.of(o);
 57                     case CONSTANT -> ConstantInstruction.ofIntrinsic(o);
 58                     case CONVERT -> ConvertInstruction.of(o);
 59                     case LOAD -> new AbstractInstruction.UnboundLoadInstruction(o, BytecodeHelpers.intrinsicLoadSlot(o));
 60                     case MONITOR -> MonitorInstruction.of(o);
 61                     case NOP -> NopInstruction.of();
 62                     case OPERATOR -> OperatorInstruction.of(o);
 63                     case RETURN -> ReturnInstruction.of(o);
 64                     case STACK -> StackInstruction.of(o);
 65                     case STORE -> new AbstractInstruction.UnboundStoreInstruction(o, BytecodeHelpers.intrinsicStoreSlot(o));
 66                     case THROW_EXCEPTION -> ThrowInstruction.of();
 67                     default -> throw new AssertionError("invalid opcode: " + o);
 68                 };
 69             }
 70         }
 71     }
 72 
 73     List<ExceptionCatch> exceptionTable;
 74     List<Attribute<?>> attributes;
 75 
 76     // Inflated for iteration
 77     LabelImpl[] labels;
 78     int[] lineNumbers;
 79     boolean inflated;
 80 
 81     public CodeImpl(AttributedElement enclosing,
 82                     ClassReader reader,
 83                     AttributeMapper<CodeAttribute> mapper,
 84                     int payloadStart) {
 85         super(enclosing, reader, mapper, payloadStart);
 86     }
 87 
 88     // LabelContext
 89 
 90     @Override
 91     public Label newLabel() {
 92         throw new UnsupportedOperationException("CodeAttribute only supports fixed labels");
 93     }
 94 
 95     @Override
 96     public void setLabelTarget(Label label, int bci) {
 97         throw new UnsupportedOperationException("CodeAttribute only supports fixed labels");
 98     }
 99 
100     @Override
101     public Label getLabel(int bci) {
102         if (bci < 0 || bci > codeLength)
103             throw new IllegalArgumentException(String.format("Bytecode offset out of range; bci=%d, codeLength=%d",
104                                                              bci, codeLength));
105         if (labels == null)
106             labels = new LabelImpl[codeLength + 1];
107         LabelImpl l = labels[bci];
108         if (l == null)
109             l = labels[bci] = new LabelImpl(this, bci);
110         return l;
111     }
112 
113     @Override
114     public int labelToBci(Label label) {
115         LabelImpl lab = (LabelImpl) label;
116         if (lab.labelContext() != this)
117             throw new IllegalArgumentException(String.format("Illegal label reuse; context=%s, label=%s",
118                                                              this, lab.labelContext()));
119         return lab.getBCI();
120     }
121 
122     private void inflateMetadata() {
123         if (!inflated) {
124             if (labels == null)
125                 labels = new LabelImpl[codeLength + 1];
126             if (classReader.context().passLineNumbers())
127                 inflateLineNumbers();
128             inflateJumpTargets();
129             inflateTypeAnnotations();
130             inflated = true;
131         }
132     }
133 
134     // CodeAttribute
135 
136     @Override
137     public List<Attribute<?>> attributes() {
138         if (attributes == null) {
139             attributes = BoundAttribute.readAttributes(this, classReader, attributePos, classReader.customAttributes());
140         }
141         return attributes;
142     }
143 
144     @Override
145     public void writeTo(BufWriterImpl buf) {
146         var methodInfo = (MethodInfo) enclosingMethod;
147         if (Util.canSkipMethodInflation(classReader, methodInfo, buf)) {
148             super.writeTo(buf);
149         }
150         else {
151             DirectCodeBuilder.build(methodInfo,
152                                     Util.writingAll(this),
153                                     (SplitConstantPool)buf.constantPool(),
154                                     buf.context(),
155                                     null).writeTo(buf);
156         }
157     }
158 
159     // CodeModel
160 
161     @Override
162     public Optional<MethodModel> parent() {
163         return Optional.of(enclosingMethod);
164     }
165 
166     @Override
167     public void forEach(Consumer<? super CodeElement> consumer) {
168         Objects.requireNonNull(consumer);
169         inflateMetadata();
170         boolean doLineNumbers = (lineNumbers != null);
171         generateCatchTargets(consumer);
172         if (classReader.context().passDebugElements())
173             generateDebugElements(consumer);
174         generateUserAttributes(consumer);
175         for (int pos=codeStart; pos<codeEnd; ) {
176             if (labels[pos - codeStart] != null)
177                 consumer.accept(labels[pos - codeStart]);
178             if (doLineNumbers && lineNumbers[pos - codeStart] != 0)
179                 consumer.accept(LineNumberImpl.of(lineNumbers[pos - codeStart]));
180             int bc = classReader.readU1(pos);
181             Instruction instr = bcToInstruction(bc, pos);
182             consumer.accept(instr);
183             pos += instr.sizeInBytes();
184         }
185         // There might be labels pointing to the bci at codeEnd
186         if (labels[codeEnd-codeStart] != null)
187             consumer.accept(labels[codeEnd - codeStart]);
188         if (doLineNumbers && lineNumbers[codeEnd - codeStart] != 0)
189             consumer.accept(LineNumberImpl.of(lineNumbers[codeEnd - codeStart]));
190     }
191 
192     @Override
193     public List<ExceptionCatch> exceptionHandlers() {
194         if (exceptionTable == null) {
195             inflateMetadata();
196             exceptionTable = new ArrayList<>(exceptionHandlerCnt);
197             iterateExceptionHandlers(new ExceptionHandlerAction() {
198                 @Override
199                 public void accept(int s, int e, int h, int c) {
200                     ClassEntry catchTypeEntry = c == 0
201                                                              ? null
202                                                              : constantPool().entryByIndex(c, ClassEntry.class);
203                     exceptionTable.add(new AbstractPseudoInstruction.ExceptionCatchImpl(getLabel(h), getLabel(s), getLabel(e), catchTypeEntry));
204                 }
205             });
206             exceptionTable = Collections.unmodifiableList(exceptionTable);
207         }
208         return exceptionTable;
209     }
210 
211     private void generateUserAttributes(Consumer<? super CodeElement> consumer) {
212         for (var attr : attributes) {
213             if (attr instanceof CustomAttribute || attr instanceof UnknownAttribute) {
214                 consumer.accept((CodeElement) attr);
215             }
216         }
217     }
218 
219     public boolean compareCodeBytes(BufWriterImpl buf, int offset, int len) {
220         return codeLength == len
221                && classReader.compare(buf, offset, codeStart, codeLength);
222     }
223 
224     private int adjustForObjectOrUninitialized(int bci) {
225         int vt = classReader.readU1(bci);
226         //inflate newTarget labels from Uninitialized VTIs
227         if (vt == 8) inflateLabel(classReader.readU2(bci + 1));
228         return (vt == 7 || vt == 8) ? bci + 3 : bci + 1;
229     }
230 
231     private void inflateLabel(int bci) {
232         if (bci < 0 || bci > codeLength)
233             throw new IllegalArgumentException(String.format("Bytecode offset out of range; bci=%d, codeLength=%d",
234                                                              bci, codeLength));
235         if (labels[bci] == null)
236             labels[bci] = new LabelImpl(this, bci);
237     }
238 
239     private void inflateLineNumbers() {
240         for (Attribute<?> a : attributes()) {
241             if (a.attributeMapper() == Attributes.lineNumberTable()) {
242                 BoundLineNumberTableAttribute attr = (BoundLineNumberTableAttribute) a;
243                 if (lineNumbers == null)
244                     lineNumbers = new int[codeLength + 1];
245 
246                 int nLn = classReader.readU2(attr.payloadStart);
247                 int p = attr.payloadStart + 2;
248                 int pEnd = p + (nLn * 4);
249                 for (; p < pEnd; p += 4) {
250                     int startPc = classReader.readU2(p);
251                     if (startPc > codeLength) {
252                         throw new IllegalArgumentException(String.format(
253                                 "Line number start_pc out of range; start_pc=%d, codeLength=%d", startPc, codeLength));
254                     }
255                     int lineNumber = classReader.readU2(p + 2);
256                     lineNumbers[startPc] = lineNumber;
257                 }
258             }
259         }
260     }
261 
262     private void inflateJumpTargets() {
263         Optional<StackMapTableAttribute> a = findAttribute(Attributes.stackMapTable());
264         if (a.isEmpty()) {
265             if (classReader.readU2(6) <= ClassFile.JAVA_6_VERSION) {
266                 //fallback to jump targets inflation without StackMapTableAttribute
267                 for (int pos=codeStart; pos<codeEnd; ) {
268                     var i = bcToInstruction(classReader.readU1(pos), pos);
269                     switch (i.opcode().kind()) {
270                         case BRANCH -> ((BranchInstruction) i).target();
271                         case DISCONTINUED_JSR -> ((DiscontinuedInstruction.JsrInstruction) i).target();
272                         case LOOKUP_SWITCH -> {
273                             var ls = (LookupSwitchInstruction) i;
274                             ls.defaultTarget();
275                             ls.cases();
276                         }
277                         case TABLE_SWITCH -> {
278                             var ts = (TableSwitchInstruction) i;
279                             ts.defaultTarget();
280                             ts.cases();
281                         }
282                         default -> {}
283                     }
284                     pos += i.sizeInBytes();
285                 }
286             }
287             return;
288         }
289         int stackMapPos = ((BoundAttribute<StackMapTableAttribute>) a.get()).payloadStart;
290 
291         int bci = -1; //compensate for offsetDelta + 1
292         int nEntries = classReader.readU2(stackMapPos);
293         int p = stackMapPos + 2;
294         for (int i = 0; i < nEntries; ++i) {
295             int frameType = classReader.readU1(p);
296             int offsetDelta = -1;
297             if (frameType <= SAME_FRAME_END) {
298                 offsetDelta = frameType;
299                 ++p;
300             }
301             else if (frameType <= SAME_LOCALS_1_STACK_ITEM_FRAME_END) {
302                 offsetDelta = frameType & 0x3f;
303                 p = adjustForObjectOrUninitialized(p + 1);
304             }
305             else {
306                 switch (frameType) {
307                     case EARLY_LARVAL -> {
308                         int numberOfUnsetFields = classReader.readU2(p + 1);
309                         p += 3;
310                         p += 2 * numberOfUnsetFields;
311                         i--; // one more enclosed frame
312                         continue;
313                     }
314                     case SAME_LOCALS_1_STACK_ITEM_EXTENDED -> {
315                         offsetDelta = classReader.readU2(p + 1);
316                         p = adjustForObjectOrUninitialized(p + 3);
317                     }
318                     case CHOP_FRAME_START, CHOP_FRAME_START + 1, CHOP_FRAME_END, SAME_FRAME_EXTENDED -> {
319                         offsetDelta = classReader.readU2(p + 1);
320                         p += 3;
321                     }
322                     case APPEND_FRAME_START, APPEND_FRAME_START + 1, APPEND_FRAME_END -> {
323                         offsetDelta = classReader.readU2(p + 1);
324                         int k = frameType - APPEND_FRAME_START + 1;
325                         p += 3;
326                         for (int c = 0; c < k; ++c) {
327                             p = adjustForObjectOrUninitialized(p);
328                         }
329                     }
330                     case FULL_FRAME -> {
331                         offsetDelta = classReader.readU2(p + 1);
332                         p += 3;
333                         int k = classReader.readU2(p);
334                         p += 2;
335                         for (int c = 0; c < k; ++c) {
336                             p = adjustForObjectOrUninitialized(p);
337                         }
338                         k = classReader.readU2(p);
339                         p += 2;
340                         for (int c = 0; c < k; ++c) {
341                             p = adjustForObjectOrUninitialized(p);
342                         }
343                     }
344                     default -> throw new IllegalArgumentException("Bad frame type: " + frameType);
345                 }
346             }
347             bci += offsetDelta + 1;
348             inflateLabel(bci);
349         }
350     }
351 
352     private void inflateTypeAnnotations() {
353         findAttribute(Attributes.runtimeVisibleTypeAnnotations()).ifPresent(RuntimeVisibleTypeAnnotationsAttribute::annotations);
354         findAttribute(Attributes.runtimeInvisibleTypeAnnotations()).ifPresent(RuntimeInvisibleTypeAnnotationsAttribute::annotations);
355     }
356 
357     private void generateCatchTargets(Consumer<? super CodeElement> consumer) {
358         // We attach all catch targets to bci zero, because trying to attach them
359         // to their range could subtly affect the order of exception processing
360         iterateExceptionHandlers(new ExceptionHandlerAction() {
361             @Override
362             public void accept(int s, int e, int h, int c) {
363                 ClassEntry catchType = c == 0
364                                                     ? null
365                                                     : classReader.entryByIndex(c, ClassEntry.class);
366                 consumer.accept(new AbstractPseudoInstruction.ExceptionCatchImpl(getLabel(h), getLabel(s), getLabel(e), catchType));
367             }
368         });
369     }
370 
371     private void generateDebugElements(Consumer<? super CodeElement> consumer) {
372         for (Attribute<?> a : attributes()) {
373             if (a.attributeMapper() == Attributes.characterRangeTable()) {
374                 var attr = (BoundCharacterRangeTableAttribute) a;
375                 int cnt = classReader.readU2(attr.payloadStart);
376                 int p = attr.payloadStart + 2;
377                 int pEnd = p + (cnt * 14);
378                 for (; p < pEnd; p += 14) {
379                     var instruction = new BoundCharacterRange(this, p);
380                     inflateLabel(instruction.startPc());
381                     inflateLabel(instruction.endPc() + 1);
382                     consumer.accept(instruction);
383                 }
384             }
385             else if (a.attributeMapper() == Attributes.localVariableTable()) {
386                 var attr = (BoundLocalVariableTableAttribute) a;
387                 int cnt = classReader.readU2(attr.payloadStart);
388                 int p = attr.payloadStart + 2;
389                 int pEnd = p + (cnt * 10);
390                 for (; p < pEnd; p += 10) {
391                     BoundLocalVariable instruction = new BoundLocalVariable(this, p);
392                     inflateLabel(instruction.startPc());
393                     inflateLabel(instruction.startPc() + instruction.length());
394                     consumer.accept(instruction);
395                 }
396             }
397             else if (a.attributeMapper() == Attributes.localVariableTypeTable()) {
398                 var attr = (BoundLocalVariableTypeTableAttribute) a;
399                 int cnt = classReader.readU2(attr.payloadStart);
400                 int p = attr.payloadStart + 2;
401                 int pEnd = p + (cnt * 10);
402                 for (; p < pEnd; p += 10) {
403                     BoundLocalVariableType instruction = new BoundLocalVariableType(this, p);
404                     inflateLabel(instruction.startPc());
405                     inflateLabel(instruction.startPc() + instruction.length());
406                     consumer.accept(instruction);
407                 }
408             }
409             else if (a.attributeMapper() == Attributes.runtimeVisibleTypeAnnotations()) {
410                 consumer.accept((BoundRuntimeVisibleTypeAnnotationsAttribute) a);
411             }
412             else if (a.attributeMapper() == Attributes.runtimeInvisibleTypeAnnotations()) {
413                 consumer.accept((BoundRuntimeInvisibleTypeAnnotationsAttribute) a);
414             }
415         }
416     }
417 
418     public interface ExceptionHandlerAction {
419         void accept(int start, int end, int handler, int catchTypeIndex);
420     }
421 
422     public void iterateExceptionHandlers(ExceptionHandlerAction a) {
423         int p = exceptionHandlerPos + 2;
424         for (int i = 0; i < exceptionHandlerCnt; ++i) {
425             a.accept(classReader.readU2(p), classReader.readU2(p + 2), classReader.readU2(p + 4), classReader.readU2(p + 6));
426             p += 8;
427         }
428     }
429 
430     private Instruction bcToInstruction(int bc, int pos) {
431         return switch (bc) {
432             case BIPUSH -> new AbstractInstruction.BoundArgumentConstantInstruction(Opcode.BIPUSH, CodeImpl.this, pos);
433             case SIPUSH -> new AbstractInstruction.BoundArgumentConstantInstruction(Opcode.SIPUSH, CodeImpl.this, pos);
434             case LDC -> new AbstractInstruction.BoundLoadConstantInstruction(Opcode.LDC, CodeImpl.this, pos);
435             case LDC_W -> new AbstractInstruction.BoundLoadConstantInstruction(Opcode.LDC_W, CodeImpl.this, pos);
436             case LDC2_W -> new AbstractInstruction.BoundLoadConstantInstruction(Opcode.LDC2_W, CodeImpl.this, pos);
437             case ILOAD -> new AbstractInstruction.BoundLoadInstruction(Opcode.ILOAD, CodeImpl.this, pos);
438             case LLOAD -> new AbstractInstruction.BoundLoadInstruction(Opcode.LLOAD, CodeImpl.this, pos);
439             case FLOAD -> new AbstractInstruction.BoundLoadInstruction(Opcode.FLOAD, CodeImpl.this, pos);
440             case DLOAD -> new AbstractInstruction.BoundLoadInstruction(Opcode.DLOAD, CodeImpl.this, pos);
441             case ALOAD -> new AbstractInstruction.BoundLoadInstruction(Opcode.ALOAD, CodeImpl.this, pos);
442             case ISTORE -> new AbstractInstruction.BoundStoreInstruction(Opcode.ISTORE, CodeImpl.this, pos);
443             case LSTORE -> new AbstractInstruction.BoundStoreInstruction(Opcode.LSTORE, CodeImpl.this, pos);
444             case FSTORE -> new AbstractInstruction.BoundStoreInstruction(Opcode.FSTORE, CodeImpl.this, pos);
445             case DSTORE -> new AbstractInstruction.BoundStoreInstruction(Opcode.DSTORE, CodeImpl.this, pos);
446             case ASTORE -> new AbstractInstruction.BoundStoreInstruction(Opcode.ASTORE, CodeImpl.this, pos);
447             case IINC -> new AbstractInstruction.BoundIncrementInstruction(Opcode.IINC, CodeImpl.this, pos);
448             case IFEQ -> new AbstractInstruction.BoundBranchInstruction(Opcode.IFEQ, CodeImpl.this, pos);
449             case IFNE -> new AbstractInstruction.BoundBranchInstruction(Opcode.IFNE, CodeImpl.this, pos);
450             case IFLT -> new AbstractInstruction.BoundBranchInstruction(Opcode.IFLT, CodeImpl.this, pos);
451             case IFGE -> new AbstractInstruction.BoundBranchInstruction(Opcode.IFGE, CodeImpl.this, pos);
452             case IFGT -> new AbstractInstruction.BoundBranchInstruction(Opcode.IFGT, CodeImpl.this, pos);
453             case IFLE -> new AbstractInstruction.BoundBranchInstruction(Opcode.IFLE, CodeImpl.this, pos);
454             case IF_ICMPEQ -> new AbstractInstruction.BoundBranchInstruction(Opcode.IF_ICMPEQ, CodeImpl.this, pos);
455             case IF_ICMPNE -> new AbstractInstruction.BoundBranchInstruction(Opcode.IF_ICMPNE, CodeImpl.this, pos);
456             case IF_ICMPLT -> new AbstractInstruction.BoundBranchInstruction(Opcode.IF_ICMPLT, CodeImpl.this, pos);
457             case IF_ICMPGE -> new AbstractInstruction.BoundBranchInstruction(Opcode.IF_ICMPGE, CodeImpl.this, pos);
458             case IF_ICMPGT -> new AbstractInstruction.BoundBranchInstruction(Opcode.IF_ICMPGT, CodeImpl.this, pos);
459             case IF_ICMPLE -> new AbstractInstruction.BoundBranchInstruction(Opcode.IF_ICMPLE, CodeImpl.this, pos);
460             case IF_ACMPEQ -> new AbstractInstruction.BoundBranchInstruction(Opcode.IF_ACMPEQ, CodeImpl.this, pos);
461             case IF_ACMPNE -> new AbstractInstruction.BoundBranchInstruction(Opcode.IF_ACMPNE, CodeImpl.this, pos);
462             case GOTO -> new AbstractInstruction.BoundBranchInstruction(Opcode.GOTO, CodeImpl.this, pos);
463             case TABLESWITCH -> new AbstractInstruction.BoundTableSwitchInstruction(Opcode.TABLESWITCH, CodeImpl.this, pos);
464             case LOOKUPSWITCH -> new AbstractInstruction.BoundLookupSwitchInstruction(Opcode.LOOKUPSWITCH, CodeImpl.this, pos);
465             case GETSTATIC -> new AbstractInstruction.BoundFieldInstruction(Opcode.GETSTATIC, CodeImpl.this, pos);
466             case PUTSTATIC -> new AbstractInstruction.BoundFieldInstruction(Opcode.PUTSTATIC, CodeImpl.this, pos);
467             case GETFIELD -> new AbstractInstruction.BoundFieldInstruction(Opcode.GETFIELD, CodeImpl.this, pos);
468             case PUTFIELD -> new AbstractInstruction.BoundFieldInstruction(Opcode.PUTFIELD, CodeImpl.this, pos);
469             case INVOKEVIRTUAL -> new AbstractInstruction.BoundInvokeInstruction(Opcode.INVOKEVIRTUAL, CodeImpl.this, pos);
470             case INVOKESPECIAL -> new AbstractInstruction.BoundInvokeInstruction(Opcode.INVOKESPECIAL, CodeImpl.this, pos);
471             case INVOKESTATIC -> new AbstractInstruction.BoundInvokeInstruction(Opcode.INVOKESTATIC, CodeImpl.this, pos);
472             case INVOKEINTERFACE -> new AbstractInstruction.BoundInvokeInterfaceInstruction(Opcode.INVOKEINTERFACE, CodeImpl.this, pos);
473             case INVOKEDYNAMIC -> new AbstractInstruction.BoundInvokeDynamicInstruction(Opcode.INVOKEDYNAMIC, CodeImpl.this, pos);
474             case NEW -> new AbstractInstruction.BoundNewObjectInstruction(CodeImpl.this, pos);
475             case NEWARRAY -> new AbstractInstruction.BoundNewPrimitiveArrayInstruction(Opcode.NEWARRAY, CodeImpl.this, pos);
476             case ANEWARRAY -> new AbstractInstruction.BoundNewReferenceArrayInstruction(Opcode.ANEWARRAY, CodeImpl.this, pos);
477             case CHECKCAST -> new AbstractInstruction.BoundTypeCheckInstruction(Opcode.CHECKCAST, CodeImpl.this, pos);
478             case INSTANCEOF -> new AbstractInstruction.BoundTypeCheckInstruction(Opcode.INSTANCEOF, CodeImpl.this, pos);
479 
480             case WIDE -> {
481                 int bclow = classReader.readU1(pos + 1);
482                 yield switch (bclow) {
483                     case ILOAD -> new AbstractInstruction.BoundLoadInstruction(Opcode.ILOAD_W, this, pos);
484                     case LLOAD -> new AbstractInstruction.BoundLoadInstruction(Opcode.LLOAD_W, this, pos);
485                     case FLOAD -> new AbstractInstruction.BoundLoadInstruction(Opcode.FLOAD_W, this, pos);
486                     case DLOAD -> new AbstractInstruction.BoundLoadInstruction(Opcode.DLOAD_W, this, pos);
487                     case ALOAD -> new AbstractInstruction.BoundLoadInstruction(Opcode.ALOAD_W, this, pos);
488                     case ISTORE -> new AbstractInstruction.BoundStoreInstruction(Opcode.ISTORE_W, this, pos);
489                     case LSTORE -> new AbstractInstruction.BoundStoreInstruction(Opcode.LSTORE_W, this, pos);
490                     case FSTORE -> new AbstractInstruction.BoundStoreInstruction(Opcode.FSTORE_W, this, pos);
491                     case DSTORE -> new AbstractInstruction.BoundStoreInstruction(Opcode.DSTORE_W, this, pos);
492                     case ASTORE -> new AbstractInstruction.BoundStoreInstruction(Opcode.ASTORE_W, this, pos);
493                     case IINC -> new AbstractInstruction.BoundIncrementInstruction(Opcode.IINC_W, this, pos);
494                     case RET ->  new AbstractInstruction.BoundRetInstruction(Opcode.RET_W, this, pos);
495                     default -> throw new IllegalArgumentException("unknown wide instruction: " + bclow);
496                 };
497             }
498 
499             case MULTIANEWARRAY -> new AbstractInstruction.BoundNewMultidimensionalArrayInstruction(Opcode.MULTIANEWARRAY, CodeImpl.this, pos);
500             case IFNULL -> new AbstractInstruction.BoundBranchInstruction(Opcode.IFNULL, CodeImpl.this, pos);
501             case IFNONNULL -> new AbstractInstruction.BoundBranchInstruction(Opcode.IFNONNULL, CodeImpl.this, pos);
502             case GOTO_W -> new AbstractInstruction.BoundBranchInstruction(Opcode.GOTO_W, CodeImpl.this, pos);
503 
504             case JSR -> new AbstractInstruction.BoundJsrInstruction(Opcode.JSR, CodeImpl.this, pos);
505             case RET ->  new AbstractInstruction.BoundRetInstruction(Opcode.RET, this, pos);
506             case JSR_W -> new AbstractInstruction.BoundJsrInstruction(Opcode.JSR_W, CodeImpl.this, pos);
507             default -> {
508                 Instruction instr = SINGLETON_INSTRUCTIONS[bc];
509                 if (instr == null)
510                     throw new IllegalArgumentException("unknown instruction: " + bc);
511                 yield instr;
512             }
513         };
514     }
515 
516     @Override
517     public String toString() {
518         return String.format("CodeModel[id=%d]", System.identityHashCode(this));
519     }
520 }