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 }
|