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