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.*;
54 import static java.lang.classfile.attribute.StackMapFrameInfo.VerificationTypeInfo.*;
55 import static java.util.Objects.requireNonNull;
56
57 public class StackMapDecoder {
58
59 static final int
60 EARLY_LARVAL = 246,
61 SAME_LOCALS_1_STACK_ITEM_EXTENDED = 247,
62 SAME_EXTENDED = 251;
63 private static final int BASE_FRAMES_UPPER_LIMIT = SAME_LOCALS_1_STACK_ITEM_EXTENDED; // not inclusive
64 private static final StackMapFrameInfo[] NO_STACK_FRAME_INFOS = {};
65
66 private final ClassReader classReader;
67 private final int pos;
68 private final LabelContext ctx;
69 private final List<VerificationTypeInfo> initFrameLocals;
70 private final List<NameAndTypeEntry> initFrameUnsets;
71 private int p;
72
73 StackMapDecoder(ClassReader classReader, int pos, LabelContext ctx, List<VerificationTypeInfo> initFrameLocals,
74 List<NameAndTypeEntry> initFrameUnsets) {
75 this.classReader = classReader;
76 this.pos = pos;
77 this.ctx = ctx;
78 this.initFrameLocals = initFrameLocals;
79 this.initFrameUnsets = initFrameUnsets;
80 }
81
82 static List<VerificationTypeInfo> initFrameLocals(MethodModel method) {
83 return initFrameLocals(method.parent().orElseThrow().thisClass(),
84 method.methodName().stringValue(),
85 method.methodTypeSymbol(),
86 method.flags().has(AccessFlag.STATIC));
87 }
88
89 public static List<VerificationTypeInfo> initFrameLocals(ClassEntry thisClass, String methodName, MethodTypeDesc methodType, boolean isStatic) {
90 VerificationTypeInfo vtis[];
91 int i = 0;
92 if (!isStatic) {
93 vtis = new VerificationTypeInfo[methodType.parameterCount() + 1];
94 if ("<init>".equals(methodName) && !ConstantDescs.CD_Object.equals(thisClass.asSymbol())) {
95 vtis[i++] = SimpleVerificationTypeInfo.UNINITIALIZED_THIS;
96 } else {
97 vtis[i++] = new StackMapDecoder.ObjectVerificationTypeInfoImpl(thisClass);
98 }
99 } else {
100 vtis = new VerificationTypeInfo[methodType.parameterCount()];
101 }
102 for (int pi = 0; pi < methodType.parameterCount(); pi++) {
103 var arg = methodType.parameterType(pi);
104 vtis[i++] = switch (arg.descriptorString().charAt(0)) {
105 case 'I', 'S', 'C' ,'B', 'Z' -> SimpleVerificationTypeInfo.INTEGER;
106 case 'J' -> SimpleVerificationTypeInfo.LONG;
107 case 'F' -> SimpleVerificationTypeInfo.FLOAT;
108 case 'D' -> SimpleVerificationTypeInfo.DOUBLE;
109 case 'V' -> throw new IllegalArgumentException("Illegal method argument type: " + arg);
110 default -> new StackMapDecoder.ObjectVerificationTypeInfoImpl(TemporaryConstantPool.INSTANCE.classEntry(arg));
111 };
112 }
113 return List.of(vtis);
114 }
115
116 static List<NameAndTypeEntry> initFrameUnsets(MethodModel method) {
117 return initFrameUnsets(method.parent().orElseThrow(),
118 method.methodName());
119 }
120
121 private static List<NameAndTypeEntry> initFrameUnsets(ClassModel clazz, Utf8Entry methodName) {
122 if (!methodName.equalsString(ConstantDescs.INIT_NAME))
123 return List.of();
124 var l = new ArrayList<NameAndTypeEntry>(clazz.fields().size());
125 for (var field : clazz.fields()) {
126 if ((field.flags().flagsMask() & (ACC_STATIC | ACC_STRICT_INIT)) == ACC_STRICT_INIT) { // instance strict
127 l.add(TemporaryConstantPool.INSTANCE.nameAndTypeEntry(field.fieldName(), field.fieldType()));
128 }
129 }
130 return List.copyOf(l);
131 }
132
133 private static List<NameAndTypeEntry> initFrameUnsets(MethodInfo mi, WritableField.UnsetField[] unsets) {
134 if (!mi.methodName().equalsString(ConstantDescs.INIT_NAME))
135 return List.of();
136 var l = new ArrayList<NameAndTypeEntry>(unsets.length);
137 for (var field : unsets) {
138 l.add(TemporaryConstantPool.INSTANCE.nameAndTypeEntry(field.name(), field.type()));
139 }
140 return List.copyOf(l);
141 }
142
143 public static void writeFrames(BufWriter b, List<StackMapFrameInfo> entries) {
144 var buf = (BufWriterImpl)b;
145 var dcb = (DirectCodeBuilder)buf.labelContext();
146 var mi = dcb.methodInfo();
147 var prevLocals = StackMapDecoder.initFrameLocals(buf.thisClass(),
148 mi.methodName().stringValue(),
149 mi.methodTypeSymbol(),
150 (mi.methodFlags() & ACC_STATIC) != 0);
151 var prevUnsets = initFrameUnsets(mi, buf.getStrictInstanceFields());
152 int prevOffset = -1;
153 // avoid using method handles due to early bootstrap
154 StackMapFrameInfo[] infos = entries.toArray(NO_STACK_FRAME_INFOS);
155 //sort by resolved label offsets first to allow unordered entries
156 Arrays.sort(infos, new Comparator<StackMapFrameInfo>() {
157 public int compare(final StackMapFrameInfo o1, final StackMapFrameInfo o2) {
158 return Integer.compare(dcb.labelToBci(o1.target()), dcb.labelToBci(o2.target()));
159 }
160 });
161 b.writeU2(infos.length);
162 for (var fr : infos) {
163 int offset = dcb.labelToBci(fr.target());
164 if (offset == prevOffset) {
165 throw new IllegalArgumentException("Duplicated stack frame bytecode index: " + offset);
166 }
167 writeFrame(buf, offset - prevOffset - 1, prevLocals, prevUnsets, fr);
168 prevOffset = offset;
169 prevLocals = fr.locals();
170 prevUnsets = fr.unsetFields();
171 }
172 }
173
174 // In sync with StackMapGenerator::needsLarvalFrame
175 private static boolean needsLarvalFrameForTransition(List<NameAndTypeEntry> prevUnsets, StackMapFrameInfo fr) {
176 if (prevUnsets.equals(fr.unsetFields()))
177 return false;
178 if (!fr.locals().contains(SimpleVerificationTypeInfo.UNINITIALIZED_THIS)) {
179 assert fr.unsetFields().isEmpty() : fr; // should be checked in StackMapFrameInfo constructor
180 return false;
181 }
182 return true;
183 }
184
185 private static void writeFrame(BufWriterImpl out, int offsetDelta, List<VerificationTypeInfo> prevLocals, List<NameAndTypeEntry> prevUnsets, StackMapFrameInfo fr) {
186 if (offsetDelta < 0) throw new IllegalArgumentException("Invalid stack map frames order");
187 // enclosing frames
188 if (needsLarvalFrameForTransition(prevUnsets, fr)) {
189 out.writeU1(EARLY_LARVAL);
190 Util.writeListIndices(out, fr.unsetFields());
191 }
192 // base frame
193 if (fr.stack().isEmpty()) {
194 int commonLocalsSize = Math.min(prevLocals.size(), fr.locals().size());
195 int diffLocalsSize = fr.locals().size() - prevLocals.size();
196 if (-3 <= diffLocalsSize && diffLocalsSize <= 3 && equals(fr.locals(), prevLocals, commonLocalsSize)) {
197 if (diffLocalsSize == 0 && offsetDelta < 64) { //same frame
198 out.writeU1(offsetDelta);
199 } else { //chop, same extended or append frame
200 out.writeU1U2(251 + diffLocalsSize, offsetDelta);
201 for (int i=commonLocalsSize; i<fr.locals().size(); i++) writeTypeInfo(out, fr.locals().get(i));
202 }
203 return;
204 }
205 } else if (fr.stack().size() == 1 && fr.locals().equals(prevLocals)) {
206 if (offsetDelta < 64) { //same locals 1 stack item frame
207 out.writeU1(64 + offsetDelta);
208 } else { //same locals 1 stack item extended frame
209 out.writeU1U2(247, offsetDelta);
210 }
211 writeTypeInfo(out, fr.stack().get(0));
212 return;
222 for (int i = 0; i < compareSize; i++) {
223 if (!l1.get(i).equals(l2.get(i))) return false;
224 }
225 return true;
226 }
227
228 private static void writeTypeInfo(BufWriterImpl bw, VerificationTypeInfo vti) {
229 int tag = vti.tag();
230 switch (tag) {
231 case ITEM_TOP, ITEM_INTEGER, ITEM_FLOAT, ITEM_DOUBLE, ITEM_LONG, ITEM_NULL,
232 ITEM_UNINITIALIZED_THIS ->
233 bw.writeU1(tag);
234 case ITEM_OBJECT ->
235 bw.writeU1U2(tag, bw.cpIndex(((ObjectVerificationTypeInfo)vti).className()));
236 case ITEM_UNINITIALIZED ->
237 bw.writeU1U2(tag, bw.labelContext().labelToBci(((UninitializedVerificationTypeInfo)vti).newTarget()));
238 default -> throw new IllegalArgumentException("Invalid verification type tag: " + vti.tag());
239 }
240 }
241
242 // Copied from BoundAttribute
243 <E extends PoolEntry> List<E> readEntryList(int p, Class<E> type) {
244 int cnt = classReader.readU2(p);
245 p += 2;
246 var entries = new Object[cnt];
247 int end = p + (cnt * 2);
248 for (int i = 0; p < end; i++, p += 2) {
249 entries[i] = classReader.readEntry(p, type);
250 }
251 return SharedSecrets.getJavaUtilCollectionAccess().listFromTrustedArray(entries);
252 }
253
254 List<StackMapFrameInfo> entries() {
255 p = pos;
256 List<VerificationTypeInfo> locals = initFrameLocals, stack = List.of();
257 List<NameAndTypeEntry> unsetFields = initFrameUnsets;
258 int bci = -1;
259 var entries = new StackMapFrameInfo[u2()];
260 for (int ei = 0; ei < entries.length; ei++) {
261 int actualFrameType = classReader.readU1(p++);
262 int frameType = actualFrameType; // effective frame type for parsing
263 // enclosing frames handling
264 if (frameType == EARLY_LARVAL) {
265 unsetFields = readEntryList(p, NameAndTypeEntry.class);
266 p += 2 + unsetFields.size() * 2;
267 frameType = classReader.readU1(p++);
268 }
269 // base frame handling
270 if (frameType < 64) {
271 bci += frameType + 1;
272 stack = List.of();
273 } else if (frameType < 128) {
274 bci += frameType - 63;
275 stack = List.of(readVerificationTypeInfo());
276 } else {
277 if (frameType < BASE_FRAMES_UPPER_LIMIT)
278 throw new IllegalArgumentException("Invalid base frame type: " + frameType);
279 bci += u2() + 1;
280 if (frameType == SAME_LOCALS_1_STACK_ITEM_EXTENDED) {
281 stack = List.of(readVerificationTypeInfo());
282 } else if (frameType < SAME_EXTENDED) {
283 locals = locals.subList(0, locals.size() + frameType - SAME_EXTENDED);
284 stack = List.of();
285 } else if (frameType == SAME_EXTENDED) {
286 stack = List.of();
287 } else if (frameType < SAME_EXTENDED + 4) {
288 int actSize = locals.size();
289 var newLocals = locals.toArray(new VerificationTypeInfo[actSize + frameType - SAME_EXTENDED]);
290 for (int i = actSize; i < newLocals.length; i++)
291 newLocals[i] = readVerificationTypeInfo();
292 locals = List.of(newLocals);
293 stack = List.of();
294 } else {
295 var newLocals = new VerificationTypeInfo[u2()];
296 for (int i=0; i<newLocals.length; i++)
297 newLocals[i] = readVerificationTypeInfo();
298 var newStack = new VerificationTypeInfo[u2()];
299 for (int i=0; i<newStack.length; i++)
300 newStack[i] = readVerificationTypeInfo();
301 locals = List.of(newLocals);
302 stack = List.of(newStack);
303 }
304 }
305 if (actualFrameType != EARLY_LARVAL && !unsetFields.isEmpty() && !locals.contains(SimpleVerificationTypeInfo.UNINITIALIZED_THIS)) {
306 // clear unsets post larval
307 unsetFields = List.of();
308 }
309 entries[ei] = new StackMapFrameImpl(actualFrameType,
310 ctx.getLabel(bci),
311 locals,
312 stack,
313 unsetFields);
314 }
315 return List.of(entries);
316 }
317
318 private VerificationTypeInfo readVerificationTypeInfo() {
319 int tag = classReader.readU1(p++);
320 return switch (tag) {
321 case ITEM_TOP -> SimpleVerificationTypeInfo.TOP;
322 case ITEM_INTEGER -> SimpleVerificationTypeInfo.INTEGER;
323 case ITEM_FLOAT -> SimpleVerificationTypeInfo.FLOAT;
324 case ITEM_DOUBLE -> SimpleVerificationTypeInfo.DOUBLE;
325 case ITEM_LONG -> SimpleVerificationTypeInfo.LONG;
326 case ITEM_NULL -> SimpleVerificationTypeInfo.NULL;
327 case ITEM_UNINITIALIZED_THIS -> SimpleVerificationTypeInfo.UNINITIALIZED_THIS;
328 case ITEM_OBJECT -> new ObjectVerificationTypeInfoImpl(classReader.entryByIndex(u2(), ClassEntry.class));
329 case ITEM_UNINITIALIZED -> new UninitializedVerificationTypeInfoImpl(ctx.getLabel(u2()));
330 default -> throw new IllegalArgumentException("Invalid verification type tag: " + tag);
331 };
332 }
333
366 }
367
368 @Override
369 public int tag() { return ITEM_UNINITIALIZED; }
370
371 @Override
372 public String toString() {
373 return "UNINIT(" + newTarget +")";
374 }
375 }
376
377 private int u2() {
378 int v = classReader.readU2(p);
379 p += 2;
380 return v;
381 }
382
383 public static record StackMapFrameImpl(int frameType,
384 Label target,
385 List<VerificationTypeInfo> locals,
386 List<VerificationTypeInfo> stack,
387 List<NameAndTypeEntry> unsetFields)
388 implements StackMapFrameInfo {
389 public StackMapFrameImpl {
390 requireNonNull(target);
391 locals = List.copyOf(locals);
392 stack = List.copyOf(stack);
393 unsetFields = List.copyOf(unsetFields);
394
395 uninitializedThisCheck:
396 if (!unsetFields.isEmpty()) {
397 for (var local : locals) {
398 if (local == SimpleVerificationTypeInfo.UNINITIALIZED_THIS) {
399 break uninitializedThisCheck;
400 }
401 }
402 throw new IllegalArgumentException("unset fields requires uninitializedThis in locals");
403 }
404 }
405
406 public StackMapFrameImpl(int frameType,
407 Label target,
408 List<VerificationTypeInfo> locals,
409 List<VerificationTypeInfo> stack) {
410 this(frameType, target, locals, stack, List.of());
411 }
412 }
413 }
|