< prev index next >

src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java

Print this page

  1 /*
  2  * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 
 26 package jdk.internal.classfile.impl;
 27 
 28 import java.lang.classfile.BufWriter;

 29 import java.lang.classfile.ClassReader;
 30 import java.lang.classfile.Label;
 31 import java.lang.classfile.MethodModel;
 32 import java.lang.classfile.attribute.StackMapFrameInfo;
 33 import java.lang.classfile.attribute.StackMapFrameInfo.ObjectVerificationTypeInfo;
 34 import java.lang.classfile.attribute.StackMapFrameInfo.SimpleVerificationTypeInfo;
 35 import java.lang.classfile.attribute.StackMapFrameInfo.UninitializedVerificationTypeInfo;
 36 import java.lang.classfile.attribute.StackMapFrameInfo.VerificationTypeInfo;
 37 import java.lang.classfile.constantpool.ClassEntry;



 38 import java.lang.constant.ConstantDescs;
 39 import java.lang.constant.MethodTypeDesc;
 40 import java.lang.reflect.AccessFlag;

 41 import java.util.Arrays;
 42 import java.util.Comparator;
 43 import java.util.List;
 44 import java.util.Objects;
 45 


 46 import static java.lang.classfile.ClassFile.ACC_STATIC;

 47 import static java.lang.classfile.attribute.StackMapFrameInfo.VerificationTypeInfo.*;
 48 import static java.util.Objects.requireNonNull;
 49 
 50 public class StackMapDecoder {
 51 
 52     private static final int

 53                     SAME_LOCALS_1_STACK_ITEM_EXTENDED = 247,
 54                     SAME_EXTENDED = 251;

 55     private static final StackMapFrameInfo[] NO_STACK_FRAME_INFOS = {};
 56 
 57     private final ClassReader classReader;
 58     private final int pos;
 59     private final LabelContext ctx;
 60     private final List<VerificationTypeInfo> initFrameLocals;

 61     private int p;
 62 
 63     StackMapDecoder(ClassReader classReader, int pos, LabelContext ctx, List<VerificationTypeInfo> initFrameLocals) {

 64         this.classReader = classReader;
 65         this.pos = pos;
 66         this.ctx = ctx;
 67         this.initFrameLocals = initFrameLocals;

 68     }
 69 
 70     static List<VerificationTypeInfo> initFrameLocals(MethodModel method) {
 71         return initFrameLocals(method.parent().orElseThrow().thisClass(),
 72                 method.methodName().stringValue(),
 73                 method.methodTypeSymbol(),
 74                 method.flags().has(AccessFlag.STATIC));
 75     }
 76 
 77     public static List<VerificationTypeInfo> initFrameLocals(ClassEntry thisClass, String methodName, MethodTypeDesc methodType, boolean isStatic) {
 78         VerificationTypeInfo vtis[];
 79         int i = 0;
 80         if (!isStatic) {
 81             vtis = new VerificationTypeInfo[methodType.parameterCount() + 1];
 82             if ("<init>".equals(methodName) && !ConstantDescs.CD_Object.equals(thisClass.asSymbol())) {
 83                 vtis[i++] = SimpleVerificationTypeInfo.UNINITIALIZED_THIS;
 84             } else {
 85                 vtis[i++] = new StackMapDecoder.ObjectVerificationTypeInfoImpl(thisClass);
 86             }
 87         } else {
 88             vtis = new VerificationTypeInfo[methodType.parameterCount()];
 89         }
 90         for (int pi = 0; pi < methodType.parameterCount(); pi++) {
 91             var arg = methodType.parameterType(pi);
 92             vtis[i++] = switch (arg.descriptorString().charAt(0)) {
 93                 case 'I', 'S', 'C' ,'B', 'Z' -> SimpleVerificationTypeInfo.INTEGER;
 94                 case 'J' -> SimpleVerificationTypeInfo.LONG;
 95                 case 'F' -> SimpleVerificationTypeInfo.FLOAT;
 96                 case 'D' -> SimpleVerificationTypeInfo.DOUBLE;
 97                 case 'V' -> throw new IllegalArgumentException("Illegal method argument type: " + arg);
 98                 default -> new StackMapDecoder.ObjectVerificationTypeInfoImpl(TemporaryConstantPool.INSTANCE.classEntry(arg));
 99             };
100         }
101         return List.of(vtis);
102     }
103 



























104     public static void writeFrames(BufWriter b, List<StackMapFrameInfo> entries) {
105         var buf = (BufWriterImpl)b;
106         var dcb = (DirectCodeBuilder)buf.labelContext();
107         var mi = dcb.methodInfo();
108         var prevLocals = StackMapDecoder.initFrameLocals(buf.thisClass(),
109                 mi.methodName().stringValue(),
110                 mi.methodTypeSymbol(),
111                 (mi.methodFlags() & ACC_STATIC) != 0);

112         int prevOffset = -1;
113         // avoid using method handles due to early bootstrap
114         StackMapFrameInfo[] infos = entries.toArray(NO_STACK_FRAME_INFOS);
115         //sort by resolved label offsets first to allow unordered entries
116         Arrays.sort(infos, new Comparator<StackMapFrameInfo>() {
117             public int compare(final StackMapFrameInfo o1, final StackMapFrameInfo o2) {
118                 return Integer.compare(dcb.labelToBci(o1.target()), dcb.labelToBci(o2.target()));
119             }
120         });
121         b.writeU2(infos.length);
122         for (var fr : infos) {
123             int offset = dcb.labelToBci(fr.target());
124             if (offset == prevOffset) {
125                 throw new IllegalArgumentException("Duplicated stack frame bytecode index: " + offset);
126             }
127             writeFrame(buf, offset - prevOffset - 1, prevLocals, fr);
128             prevOffset = offset;
129             prevLocals = fr.locals();











130         }

131     }
132 
133     private static void writeFrame(BufWriterImpl out, int offsetDelta, List<VerificationTypeInfo> prevLocals, StackMapFrameInfo fr) {
134         if (offsetDelta < 0) throw new IllegalArgumentException("Invalid stack map frames order");






135         if (fr.stack().isEmpty()) {
136             int commonLocalsSize = Math.min(prevLocals.size(), fr.locals().size());
137             int diffLocalsSize = fr.locals().size() - prevLocals.size();
138             if (-3 <= diffLocalsSize && diffLocalsSize <= 3 && equals(fr.locals(), prevLocals, commonLocalsSize)) {
139                 if (diffLocalsSize == 0 && offsetDelta < 64) { //same frame
140                     out.writeU1(offsetDelta);
141                 } else {   //chop, same extended or append frame
142                     out.writeU1U2(251 + diffLocalsSize, offsetDelta);
143                     for (int i=commonLocalsSize; i<fr.locals().size(); i++) writeTypeInfo(out, fr.locals().get(i));
144                 }
145                 return;
146             }
147         } else if (fr.stack().size() == 1 && fr.locals().equals(prevLocals)) {
148             if (offsetDelta < 64) {  //same locals 1 stack item frame
149                 out.writeU1(64 + offsetDelta);
150             } else {  //same locals 1 stack item extended frame
151                 out.writeU1U2(247, offsetDelta);
152             }
153             writeTypeInfo(out, fr.stack().get(0));
154             return;

164         for (int i = 0; i < compareSize; i++) {
165             if (!l1.get(i).equals(l2.get(i))) return false;
166         }
167         return true;
168     }
169 
170     private static void writeTypeInfo(BufWriterImpl bw, VerificationTypeInfo vti) {
171         int tag = vti.tag();
172         switch (tag) {
173             case ITEM_TOP, ITEM_INTEGER, ITEM_FLOAT, ITEM_DOUBLE, ITEM_LONG, ITEM_NULL,
174                  ITEM_UNINITIALIZED_THIS ->
175                 bw.writeU1(tag);
176             case ITEM_OBJECT ->
177                 bw.writeU1U2(tag, bw.cpIndex(((ObjectVerificationTypeInfo)vti).className()));
178             case ITEM_UNINITIALIZED ->
179                 bw.writeU1U2(tag, bw.labelContext().labelToBci(((UninitializedVerificationTypeInfo)vti).newTarget()));
180             default -> throw new IllegalArgumentException("Invalid verification type tag: " + vti.tag());
181         }
182     }
183 












184     List<StackMapFrameInfo> entries() {
185         p = pos;
186         List<VerificationTypeInfo> locals = initFrameLocals, stack = List.of();

187         int bci = -1;
188         var entries = new StackMapFrameInfo[u2()];
189         for (int ei = 0; ei < entries.length; ei++) {
190             int frameType = classReader.readU1(p++);








191             if (frameType < 64) {
192                 bci += frameType + 1;
193                 stack = List.of();
194             } else if (frameType < 128) {
195                 bci += frameType - 63;
196                 stack = List.of(readVerificationTypeInfo());
197             } else {
198                 if (frameType < SAME_LOCALS_1_STACK_ITEM_EXTENDED)
199                     throw new IllegalArgumentException("Invalid stackmap frame type: " + frameType);
200                 bci += u2() + 1;
201                 if (frameType == SAME_LOCALS_1_STACK_ITEM_EXTENDED) {
202                     stack = List.of(readVerificationTypeInfo());
203                 } else if (frameType < SAME_EXTENDED) {
204                     locals = locals.subList(0, locals.size() + frameType - SAME_EXTENDED);
205                     stack = List.of();
206                 } else if (frameType == SAME_EXTENDED) {
207                     stack = List.of();
208                 } else if (frameType < SAME_EXTENDED + 4) {
209                     int actSize = locals.size();
210                     var newLocals = locals.toArray(new VerificationTypeInfo[actSize + frameType - SAME_EXTENDED]);
211                     for (int i = actSize; i < newLocals.length; i++)
212                         newLocals[i] = readVerificationTypeInfo();
213                     locals = List.of(newLocals);
214                     stack = List.of();
215                 } else {
216                     var newLocals = new VerificationTypeInfo[u2()];
217                     for (int i=0; i<newLocals.length; i++)
218                         newLocals[i] = readVerificationTypeInfo();
219                     var newStack = new VerificationTypeInfo[u2()];
220                     for (int i=0; i<newStack.length; i++)
221                         newStack[i] = readVerificationTypeInfo();
222                     locals = List.of(newLocals);
223                     stack = List.of(newStack);
224                 }
225             }
226             entries[ei] = new StackMapFrameImpl(frameType,
227                         ctx.getLabel(bci),
228                         locals,
229                         stack);





230         }
231         return List.of(entries);
232     }
233 
234     private VerificationTypeInfo readVerificationTypeInfo() {
235         int tag = classReader.readU1(p++);
236         return switch (tag) {
237             case ITEM_TOP -> SimpleVerificationTypeInfo.TOP;
238             case ITEM_INTEGER -> SimpleVerificationTypeInfo.INTEGER;
239             case ITEM_FLOAT -> SimpleVerificationTypeInfo.FLOAT;
240             case ITEM_DOUBLE -> SimpleVerificationTypeInfo.DOUBLE;
241             case ITEM_LONG -> SimpleVerificationTypeInfo.LONG;
242             case ITEM_NULL -> SimpleVerificationTypeInfo.NULL;
243             case ITEM_UNINITIALIZED_THIS -> SimpleVerificationTypeInfo.UNINITIALIZED_THIS;
244             case ITEM_OBJECT -> new ObjectVerificationTypeInfoImpl(classReader.entryByIndex(u2(), ClassEntry.class));
245             case ITEM_UNINITIALIZED -> new UninitializedVerificationTypeInfoImpl(ctx.getLabel(u2()));
246             default -> throw new IllegalArgumentException("Invalid verification type tag: " + tag);
247         };
248     }
249 

282         }
283 
284         @Override
285         public int tag() { return ITEM_UNINITIALIZED; }
286 
287         @Override
288         public String toString() {
289             return "UNINIT(" + newTarget +")";
290         }
291     }
292 
293     private int u2() {
294         int v = classReader.readU2(p);
295         p += 2;
296         return v;
297     }
298 
299     public static record StackMapFrameImpl(int frameType,
300                                            Label target,
301                                            List<VerificationTypeInfo> locals,
302                                            List<VerificationTypeInfo> stack)

303             implements StackMapFrameInfo {
304         public StackMapFrameImpl {
305             requireNonNull(target);
306             locals = List.copyOf(locals);
307             stack = List.copyOf(stack);


















308         }
309     }
310 }

  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 
 26 package jdk.internal.classfile.impl;
 27 
 28 import java.lang.classfile.BufWriter;
 29 import java.lang.classfile.ClassModel;
 30 import java.lang.classfile.ClassReader;
 31 import java.lang.classfile.Label;
 32 import java.lang.classfile.MethodModel;
 33 import java.lang.classfile.attribute.StackMapFrameInfo;
 34 import java.lang.classfile.attribute.StackMapFrameInfo.ObjectVerificationTypeInfo;
 35 import java.lang.classfile.attribute.StackMapFrameInfo.SimpleVerificationTypeInfo;
 36 import java.lang.classfile.attribute.StackMapFrameInfo.UninitializedVerificationTypeInfo;
 37 import java.lang.classfile.attribute.StackMapFrameInfo.VerificationTypeInfo;
 38 import java.lang.classfile.constantpool.ClassEntry;
 39 import java.lang.classfile.constantpool.NameAndTypeEntry;
 40 import java.lang.classfile.constantpool.PoolEntry;
 41 import java.lang.classfile.constantpool.Utf8Entry;
 42 import java.lang.constant.ConstantDescs;
 43 import java.lang.constant.MethodTypeDesc;
 44 import java.lang.reflect.AccessFlag;
 45 import java.util.ArrayList;
 46 import java.util.Arrays;
 47 import java.util.Comparator;
 48 import java.util.List;
 49 import java.util.Objects;
 50 
 51 import jdk.internal.access.SharedSecrets;
 52 
 53 import static java.lang.classfile.ClassFile.ACC_STATIC;
 54 import static java.lang.classfile.ClassFile.ACC_STRICT;
 55 import static java.lang.classfile.attribute.StackMapFrameInfo.VerificationTypeInfo.*;
 56 import static java.util.Objects.requireNonNull;
 57 
 58 public class StackMapDecoder {
 59 
 60     static final int
 61                     EARLY_LARVAL = 246,
 62                     SAME_LOCALS_1_STACK_ITEM_EXTENDED = 247,
 63                     SAME_EXTENDED = 251;
 64     private static final int BASE_FRAMES_UPPER_LIMIT = SAME_LOCALS_1_STACK_ITEM_EXTENDED; // not inclusive
 65     private static final StackMapFrameInfo[] NO_STACK_FRAME_INFOS = {};
 66 
 67     private final ClassReader classReader;
 68     private final int pos;
 69     private final LabelContext ctx;
 70     private final List<VerificationTypeInfo> initFrameLocals;
 71     private final List<NameAndTypeEntry> initFrameUnsets;
 72     private int p;
 73 
 74     StackMapDecoder(ClassReader classReader, int pos, LabelContext ctx, List<VerificationTypeInfo> initFrameLocals,
 75                     List<NameAndTypeEntry> initFrameUnsets) {
 76         this.classReader = classReader;
 77         this.pos = pos;
 78         this.ctx = ctx;
 79         this.initFrameLocals = initFrameLocals;
 80         this.initFrameUnsets = initFrameUnsets;
 81     }
 82 
 83     static List<VerificationTypeInfo> initFrameLocals(MethodModel method) {
 84         return initFrameLocals(method.parent().orElseThrow().thisClass(),
 85                 method.methodName().stringValue(),
 86                 method.methodTypeSymbol(),
 87                 method.flags().has(AccessFlag.STATIC));
 88     }
 89 
 90     public static List<VerificationTypeInfo> initFrameLocals(ClassEntry thisClass, String methodName, MethodTypeDesc methodType, boolean isStatic) {
 91         VerificationTypeInfo vtis[];
 92         int i = 0;
 93         if (!isStatic) {
 94             vtis = new VerificationTypeInfo[methodType.parameterCount() + 1];
 95             if ("<init>".equals(methodName) && !ConstantDescs.CD_Object.equals(thisClass.asSymbol())) {
 96                 vtis[i++] = SimpleVerificationTypeInfo.UNINITIALIZED_THIS;
 97             } else {
 98                 vtis[i++] = new StackMapDecoder.ObjectVerificationTypeInfoImpl(thisClass);
 99             }
100         } else {
101             vtis = new VerificationTypeInfo[methodType.parameterCount()];
102         }
103         for (int pi = 0; pi < methodType.parameterCount(); pi++) {
104             var arg = methodType.parameterType(pi);
105             vtis[i++] = switch (arg.descriptorString().charAt(0)) {
106                 case 'I', 'S', 'C' ,'B', 'Z' -> SimpleVerificationTypeInfo.INTEGER;
107                 case 'J' -> SimpleVerificationTypeInfo.LONG;
108                 case 'F' -> SimpleVerificationTypeInfo.FLOAT;
109                 case 'D' -> SimpleVerificationTypeInfo.DOUBLE;
110                 case 'V' -> throw new IllegalArgumentException("Illegal method argument type: " + arg);
111                 default -> new StackMapDecoder.ObjectVerificationTypeInfoImpl(TemporaryConstantPool.INSTANCE.classEntry(arg));
112             };
113         }
114         return List.of(vtis);
115     }
116 
117     static List<NameAndTypeEntry> initFrameUnsets(MethodModel method) {
118         return initFrameUnsets(method.parent().orElseThrow(),
119                 method.methodName());
120     }
121 
122     private static List<NameAndTypeEntry> initFrameUnsets(ClassModel clazz, Utf8Entry methodName) {
123         if (!methodName.equalsString(ConstantDescs.INIT_NAME))
124             return List.of();
125         var l = new ArrayList<NameAndTypeEntry>(clazz.fields().size());
126         for (var field : clazz.fields()) {
127             if ((field.flags().flagsMask() & (ACC_STATIC | ACC_STRICT)) == ACC_STRICT) { // instance strict
128                 l.add(TemporaryConstantPool.INSTANCE.nameAndTypeEntry(field.fieldName(), field.fieldType()));
129             }
130         }
131         return List.copyOf(l);
132     }
133 
134     private static List<NameAndTypeEntry> initFrameUnsets(MethodInfo mi, WritableField.UnsetField[] unsets) {
135         if (!mi.methodName().equalsString(ConstantDescs.INIT_NAME))
136             return List.of();
137         var l = new ArrayList<NameAndTypeEntry>(unsets.length);
138         for (var field : unsets) {
139             l.add(TemporaryConstantPool.INSTANCE.nameAndTypeEntry(field.name(), field.type()));
140         }
141         return List.copyOf(l);
142     }
143 
144     public static void writeFrames(BufWriter b, List<StackMapFrameInfo> entries) {
145         var buf = (BufWriterImpl)b;
146         var dcb = (DirectCodeBuilder)buf.labelContext();
147         var mi = dcb.methodInfo();
148         var prevLocals = StackMapDecoder.initFrameLocals(buf.thisClass(),
149                 mi.methodName().stringValue(),
150                 mi.methodTypeSymbol(),
151                 (mi.methodFlags() & ACC_STATIC) != 0);
152         var prevUnsets = initFrameUnsets(mi, buf.getStrictInstanceFields());
153         int prevOffset = -1;
154         // avoid using method handles due to early bootstrap
155         StackMapFrameInfo[] infos = entries.toArray(NO_STACK_FRAME_INFOS);
156         //sort by resolved label offsets first to allow unordered entries
157         Arrays.sort(infos, new Comparator<StackMapFrameInfo>() {
158             public int compare(final StackMapFrameInfo o1, final StackMapFrameInfo o2) {
159                 return Integer.compare(dcb.labelToBci(o1.target()), dcb.labelToBci(o2.target()));
160             }
161         });
162         b.writeU2(infos.length);
163         for (var fr : infos) {
164             int offset = dcb.labelToBci(fr.target());
165             if (offset == prevOffset) {
166                 throw new IllegalArgumentException("Duplicated stack frame bytecode index: " + offset);
167             }
168             writeFrame(buf, offset - prevOffset - 1, prevLocals, prevUnsets, fr);
169             prevOffset = offset;
170             prevLocals = fr.locals();
171             prevUnsets = fr.unsetFields();
172         }
173     }
174 
175     // In sync with StackMapGenerator::needsLarvalFrame
176     private static boolean needsLarvalFrameForTransition(List<NameAndTypeEntry> prevUnsets, StackMapFrameInfo fr) {
177         if (prevUnsets.equals(fr.unsetFields()))
178             return false;
179         if (!fr.locals().contains(SimpleVerificationTypeInfo.UNINITIALIZED_THIS)) {
180             assert fr.unsetFields().isEmpty() : fr; // should be checked in StackMapFrameInfo constructor
181             return false;
182         }
183         return true;
184     }
185 
186     private static void writeFrame(BufWriterImpl out, int offsetDelta, List<VerificationTypeInfo> prevLocals, List<NameAndTypeEntry> prevUnsets, StackMapFrameInfo fr) {
187         if (offsetDelta < 0) throw new IllegalArgumentException("Invalid stack map frames order");
188         // enclosing frames
189         if (needsLarvalFrameForTransition(prevUnsets, fr)) {
190             out.writeU1(EARLY_LARVAL);
191             Util.writeListIndices(out, fr.unsetFields());
192         }
193         // base frame
194         if (fr.stack().isEmpty()) {
195             int commonLocalsSize = Math.min(prevLocals.size(), fr.locals().size());
196             int diffLocalsSize = fr.locals().size() - prevLocals.size();
197             if (-3 <= diffLocalsSize && diffLocalsSize <= 3 && equals(fr.locals(), prevLocals, commonLocalsSize)) {
198                 if (diffLocalsSize == 0 && offsetDelta < 64) { //same frame
199                     out.writeU1(offsetDelta);
200                 } else {   //chop, same extended or append frame
201                     out.writeU1U2(251 + diffLocalsSize, offsetDelta);
202                     for (int i=commonLocalsSize; i<fr.locals().size(); i++) writeTypeInfo(out, fr.locals().get(i));
203                 }
204                 return;
205             }
206         } else if (fr.stack().size() == 1 && fr.locals().equals(prevLocals)) {
207             if (offsetDelta < 64) {  //same locals 1 stack item frame
208                 out.writeU1(64 + offsetDelta);
209             } else {  //same locals 1 stack item extended frame
210                 out.writeU1U2(247, offsetDelta);
211             }
212             writeTypeInfo(out, fr.stack().get(0));
213             return;

223         for (int i = 0; i < compareSize; i++) {
224             if (!l1.get(i).equals(l2.get(i))) return false;
225         }
226         return true;
227     }
228 
229     private static void writeTypeInfo(BufWriterImpl bw, VerificationTypeInfo vti) {
230         int tag = vti.tag();
231         switch (tag) {
232             case ITEM_TOP, ITEM_INTEGER, ITEM_FLOAT, ITEM_DOUBLE, ITEM_LONG, ITEM_NULL,
233                  ITEM_UNINITIALIZED_THIS ->
234                 bw.writeU1(tag);
235             case ITEM_OBJECT ->
236                 bw.writeU1U2(tag, bw.cpIndex(((ObjectVerificationTypeInfo)vti).className()));
237             case ITEM_UNINITIALIZED ->
238                 bw.writeU1U2(tag, bw.labelContext().labelToBci(((UninitializedVerificationTypeInfo)vti).newTarget()));
239             default -> throw new IllegalArgumentException("Invalid verification type tag: " + vti.tag());
240         }
241     }
242 
243     // Copied from BoundAttribute
244     <E extends PoolEntry> List<E> readEntryList(int p, Class<E> type) {
245         int cnt = classReader.readU2(p);
246         p += 2;
247         var entries = new Object[cnt];
248         int end = p + (cnt * 2);
249         for (int i = 0; p < end; i++, p += 2) {
250             entries[i] = classReader.readEntry(p, type);
251         }
252         return SharedSecrets.getJavaUtilCollectionAccess().listFromTrustedArray(entries);
253     }
254 
255     List<StackMapFrameInfo> entries() {
256         p = pos;
257         List<VerificationTypeInfo> locals = initFrameLocals, stack = List.of();
258         List<NameAndTypeEntry> unsetFields = initFrameUnsets;
259         int bci = -1;
260         var entries = new StackMapFrameInfo[u2()];
261         for (int ei = 0; ei < entries.length; ei++) {
262             int actualFrameType = classReader.readU1(p++);
263             int frameType = actualFrameType; // effective frame type for parsing
264             // enclosing frames handling
265             if (frameType == EARLY_LARVAL) {
266                 unsetFields = readEntryList(p, NameAndTypeEntry.class);
267                 p += 2 + unsetFields.size() * 2;
268                 frameType = classReader.readU1(p++);
269             }
270             // base frame handling
271             if (frameType < 64) {
272                 bci += frameType + 1;
273                 stack = List.of();
274             } else if (frameType < 128) {
275                 bci += frameType - 63;
276                 stack = List.of(readVerificationTypeInfo());
277             } else {
278                 if (frameType < BASE_FRAMES_UPPER_LIMIT)
279                     throw new IllegalArgumentException("Invalid base frame type: " + frameType);
280                 bci += u2() + 1;
281                 if (frameType == SAME_LOCALS_1_STACK_ITEM_EXTENDED) {
282                     stack = List.of(readVerificationTypeInfo());
283                 } else if (frameType < SAME_EXTENDED) {
284                     locals = locals.subList(0, locals.size() + frameType - SAME_EXTENDED);
285                     stack = List.of();
286                 } else if (frameType == SAME_EXTENDED) {
287                     stack = List.of();
288                 } else if (frameType < SAME_EXTENDED + 4) {
289                     int actSize = locals.size();
290                     var newLocals = locals.toArray(new VerificationTypeInfo[actSize + frameType - SAME_EXTENDED]);
291                     for (int i = actSize; i < newLocals.length; i++)
292                         newLocals[i] = readVerificationTypeInfo();
293                     locals = List.of(newLocals);
294                     stack = List.of();
295                 } else {
296                     var newLocals = new VerificationTypeInfo[u2()];
297                     for (int i=0; i<newLocals.length; i++)
298                         newLocals[i] = readVerificationTypeInfo();
299                     var newStack = new VerificationTypeInfo[u2()];
300                     for (int i=0; i<newStack.length; i++)
301                         newStack[i] = readVerificationTypeInfo();
302                     locals = List.of(newLocals);
303                     stack = List.of(newStack);
304                 }
305             }
306             if (actualFrameType != EARLY_LARVAL && !unsetFields.isEmpty() && !locals.contains(SimpleVerificationTypeInfo.UNINITIALIZED_THIS)) {
307                 // clear unsets post larval
308                 unsetFields = List.of();
309             }
310             entries[ei] = new StackMapFrameImpl(actualFrameType,
311                     ctx.getLabel(bci),
312                     locals,
313                     stack,
314                     unsetFields);
315         }
316         return List.of(entries);
317     }
318 
319     private VerificationTypeInfo readVerificationTypeInfo() {
320         int tag = classReader.readU1(p++);
321         return switch (tag) {
322             case ITEM_TOP -> SimpleVerificationTypeInfo.TOP;
323             case ITEM_INTEGER -> SimpleVerificationTypeInfo.INTEGER;
324             case ITEM_FLOAT -> SimpleVerificationTypeInfo.FLOAT;
325             case ITEM_DOUBLE -> SimpleVerificationTypeInfo.DOUBLE;
326             case ITEM_LONG -> SimpleVerificationTypeInfo.LONG;
327             case ITEM_NULL -> SimpleVerificationTypeInfo.NULL;
328             case ITEM_UNINITIALIZED_THIS -> SimpleVerificationTypeInfo.UNINITIALIZED_THIS;
329             case ITEM_OBJECT -> new ObjectVerificationTypeInfoImpl(classReader.entryByIndex(u2(), ClassEntry.class));
330             case ITEM_UNINITIALIZED -> new UninitializedVerificationTypeInfoImpl(ctx.getLabel(u2()));
331             default -> throw new IllegalArgumentException("Invalid verification type tag: " + tag);
332         };
333     }
334 

367         }
368 
369         @Override
370         public int tag() { return ITEM_UNINITIALIZED; }
371 
372         @Override
373         public String toString() {
374             return "UNINIT(" + newTarget +")";
375         }
376     }
377 
378     private int u2() {
379         int v = classReader.readU2(p);
380         p += 2;
381         return v;
382     }
383 
384     public static record StackMapFrameImpl(int frameType,
385                                            Label target,
386                                            List<VerificationTypeInfo> locals,
387                                            List<VerificationTypeInfo> stack,
388                                            List<NameAndTypeEntry> unsetFields)
389             implements StackMapFrameInfo {
390         public StackMapFrameImpl {
391             requireNonNull(target);
392             locals = List.copyOf(locals);
393             stack = List.copyOf(stack);
394             unsetFields = List.copyOf(unsetFields);
395 
396             uninitializedThisCheck:
397             if (!unsetFields.isEmpty()) {
398                 for (var local : locals) {
399                     if (local == SimpleVerificationTypeInfo.UNINITIALIZED_THIS) {
400                         break uninitializedThisCheck;
401                     }
402                 }
403                 throw new IllegalArgumentException("unset fields requires uninitializedThis in locals");
404             }
405         }
406 
407         public StackMapFrameImpl(int frameType,
408                                  Label target,
409                                  List<VerificationTypeInfo> locals,
410                                  List<VerificationTypeInfo> stack) {
411             this(frameType, target, locals, stack, List.of());
412         }
413     }
414 }
< prev index next >