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));
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
250 public static record ObjectVerificationTypeInfoImpl(
251 ClassEntry className) implements ObjectVerificationTypeInfo {
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.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.classfile.constantpool.NameAndTypeEntry;
39 import java.lang.classfile.constantpool.PoolEntry;
40 import java.lang.constant.ConstantDescs;
41 import java.lang.constant.MethodTypeDesc;
42 import java.lang.reflect.AccessFlag;
43 import java.util.ArrayList;
44 import java.util.Arrays;
45 import java.util.Comparator;
46 import java.util.List;
47 import java.util.Objects;
48
49 import jdk.internal.access.SharedSecrets;
50
51 import static java.lang.classfile.ClassFile.ACC_STATIC;
52 import static java.lang.classfile.attribute.StackMapFrameInfo.VerificationTypeInfo.*;
53 import static java.util.Objects.requireNonNull;
54
55 public class StackMapDecoder {
56
57 static final int
58 ASSERT_UNSET_FIELDS = 246,
59 SAME_LOCALS_1_STACK_ITEM_EXTENDED = 247,
60 SAME_EXTENDED = 251;
61 private static final int RESERVED_TAGS_UPPER_LIMIT = ASSERT_UNSET_FIELDS; // not inclusive
62 private static final StackMapFrameInfo[] NO_STACK_FRAME_INFOS = {};
63
64 private final ClassReader classReader;
65 private final int pos;
66 private final LabelContext ctx;
67 private final List<VerificationTypeInfo> initFrameLocals;
68 private int p;
69
70 StackMapDecoder(ClassReader classReader, int pos, LabelContext ctx, List<VerificationTypeInfo> initFrameLocals) {
71 this.classReader = classReader;
72 this.pos = pos;
73 this.ctx = ctx;
74 this.initFrameLocals = initFrameLocals;
75 }
76
77 static List<VerificationTypeInfo> initFrameLocals(MethodModel method) {
78 return initFrameLocals(method.parent().orElseThrow().thisClass(),
79 method.methodName().stringValue(),
80 method.methodTypeSymbol(),
81 method.flags().has(AccessFlag.STATIC));
171 for (int i = 0; i < compareSize; i++) {
172 if (!l1.get(i).equals(l2.get(i))) return false;
173 }
174 return true;
175 }
176
177 private static void writeTypeInfo(BufWriterImpl bw, VerificationTypeInfo vti) {
178 int tag = vti.tag();
179 switch (tag) {
180 case ITEM_TOP, ITEM_INTEGER, ITEM_FLOAT, ITEM_DOUBLE, ITEM_LONG, ITEM_NULL,
181 ITEM_UNINITIALIZED_THIS ->
182 bw.writeU1(tag);
183 case ITEM_OBJECT ->
184 bw.writeU1U2(tag, bw.cpIndex(((ObjectVerificationTypeInfo)vti).className()));
185 case ITEM_UNINITIALIZED ->
186 bw.writeU1U2(tag, bw.labelContext().labelToBci(((UninitializedVerificationTypeInfo)vti).newTarget()));
187 default -> throw new IllegalArgumentException("Invalid verification type tag: " + vti.tag());
188 }
189 }
190
191 // Copied from BoundAttribute
192 <E extends PoolEntry> List<E> readEntryList(int p, Class<E> type) {
193 int cnt = classReader.readU2(p);
194 p += 2;
195 var entries = new Object[cnt];
196 int end = p + (cnt * 2);
197 for (int i = 0; p < end; i++, p += 2) {
198 entries[i] = classReader.readEntry(p, type);
199 }
200 return SharedSecrets.getJavaUtilCollectionAccess().listFromTrustedArray(entries);
201 }
202
203 List<StackMapFrameInfo> entries() {
204 p = pos;
205 List<VerificationTypeInfo> locals = initFrameLocals, stack = List.of();
206 List<NameAndTypeEntry> unsetFields = List.of();
207 int bci = -1;
208 int len = u2();
209 var entries = new ArrayList<StackMapFrameInfo>(len);
210 List<List<NameAndTypeEntry>> deferredUnsetFields = new ArrayList<>();
211 for (int ei = 0; ei < len; ei++) {
212 var oldLocals = locals;
213 var oldStack = stack;
214 int frameType = classReader.readU1(p++);
215 if (frameType < 64) {
216 bci += frameType + 1;
217 stack = List.of();
218 } else if (frameType < 128) {
219 bci += frameType - 63;
220 stack = List.of(readVerificationTypeInfo());
221 } else {
222 if (frameType < RESERVED_TAGS_UPPER_LIMIT)
223 throw new IllegalArgumentException("Invalid stackmap frame type: " + frameType);
224 if (frameType == ASSERT_UNSET_FIELDS) {
225 unsetFields = readEntryList(p, NameAndTypeEntry.class);
226 p += 2 + unsetFields.size() * 2;
227 deferredUnsetFields.add(unsetFields);
228 continue; // defer entry until we can get the bci
229 }
230 bci += u2() + 1;
231 if (frameType == SAME_LOCALS_1_STACK_ITEM_EXTENDED) {
232 stack = List.of(readVerificationTypeInfo());
233 } else if (frameType < SAME_EXTENDED) {
234 locals = locals.subList(0, locals.size() + frameType - SAME_EXTENDED);
235 stack = List.of();
236 } else if (frameType == SAME_EXTENDED) {
237 stack = List.of();
238 } else if (frameType < SAME_EXTENDED + 4) {
239 int actSize = locals.size();
240 var newLocals = locals.toArray(new VerificationTypeInfo[actSize + frameType - SAME_EXTENDED]);
241 for (int i = actSize; i < newLocals.length; i++)
242 newLocals[i] = readVerificationTypeInfo();
243 locals = List.of(newLocals);
244 stack = List.of();
245 } else {
246 var newLocals = new VerificationTypeInfo[u2()];
247 for (int i=0; i<newLocals.length; i++)
248 newLocals[i] = readVerificationTypeInfo();
249 var newStack = new VerificationTypeInfo[u2()];
250 for (int i=0; i<newStack.length; i++)
251 newStack[i] = readVerificationTypeInfo();
252 locals = List.of(newLocals);
253 stack = List.of(newStack);
254 }
255 }
256 Label label = ctx.getLabel(bci);
257 if (!deferredUnsetFields.isEmpty()) {
258 // technically we only have one assert at once, just in case
259 // of duplicate asserts...
260 for (var deferredList : deferredUnsetFields) {
261 entries.add(new StackMapFrameImpl(ASSERT_UNSET_FIELDS,
262 label, oldLocals, oldStack, deferredList));
263 }
264 deferredUnsetFields.clear();
265 }
266 entries.add(new StackMapFrameImpl(frameType,
267 label,
268 locals,
269 stack));
270 }
271 return List.copyOf(entries);
272 }
273
274 private VerificationTypeInfo readVerificationTypeInfo() {
275 int tag = classReader.readU1(p++);
276 return switch (tag) {
277 case ITEM_TOP -> SimpleVerificationTypeInfo.TOP;
278 case ITEM_INTEGER -> SimpleVerificationTypeInfo.INTEGER;
279 case ITEM_FLOAT -> SimpleVerificationTypeInfo.FLOAT;
280 case ITEM_DOUBLE -> SimpleVerificationTypeInfo.DOUBLE;
281 case ITEM_LONG -> SimpleVerificationTypeInfo.LONG;
282 case ITEM_NULL -> SimpleVerificationTypeInfo.NULL;
283 case ITEM_UNINITIALIZED_THIS -> SimpleVerificationTypeInfo.UNINITIALIZED_THIS;
284 case ITEM_OBJECT -> new ObjectVerificationTypeInfoImpl(classReader.entryByIndex(u2(), ClassEntry.class));
285 case ITEM_UNINITIALIZED -> new UninitializedVerificationTypeInfoImpl(ctx.getLabel(u2()));
286 default -> throw new IllegalArgumentException("Invalid verification type tag: " + tag);
287 };
288 }
289
290 public static record ObjectVerificationTypeInfoImpl(
291 ClassEntry className) implements ObjectVerificationTypeInfo {
322 }
323
324 @Override
325 public int tag() { return ITEM_UNINITIALIZED; }
326
327 @Override
328 public String toString() {
329 return "UNINIT(" + newTarget +")";
330 }
331 }
332
333 private int u2() {
334 int v = classReader.readU2(p);
335 p += 2;
336 return v;
337 }
338
339 public static record StackMapFrameImpl(int frameType,
340 Label target,
341 List<VerificationTypeInfo> locals,
342 List<VerificationTypeInfo> stack,
343 List<NameAndTypeEntry> unsetFields)
344 implements StackMapFrameInfo {
345 public StackMapFrameImpl {
346 requireNonNull(target);
347 locals = List.copyOf(locals);
348 stack = List.copyOf(stack);
349 unsetFields = List.copyOf(unsetFields);
350 }
351
352 public StackMapFrameImpl(int frameType,
353 Label target,
354 List<VerificationTypeInfo> locals,
355 List<VerificationTypeInfo> stack) {
356 this(frameType, target, locals, stack, List.of());
357 }
358 }
359 }
|