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.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.util.Objects.requireNonNull;
48 import static jdk.internal.classfile.impl.StackMapGenerator.*;
49
50 public class StackMapDecoder {
51
52 private static final StackMapFrameInfo[] NO_STACK_FRAME_INFOS = {};
53
54 private final ClassReader classReader;
55 private final int pos;
56 private final LabelContext ctx;
57 private final List<VerificationTypeInfo> initFrameLocals;
58 private int p;
59
60 StackMapDecoder(ClassReader classReader, int pos, LabelContext ctx, List<VerificationTypeInfo> initFrameLocals) {
61 this.classReader = classReader;
62 this.pos = pos;
63 this.ctx = ctx;
64 this.initFrameLocals = initFrameLocals;
65 }
66
67 static List<VerificationTypeInfo> initFrameLocals(MethodModel method) {
68 return initFrameLocals(method.parent().orElseThrow().thisClass(),
69 method.methodName().stringValue(),
70 method.methodTypeSymbol(),
71 method.flags().has(AccessFlag.STATIC));
72 }
73
74 public static List<VerificationTypeInfo> initFrameLocals(ClassEntry thisClass, String methodName, MethodTypeDesc methodType, boolean isStatic) {
75 VerificationTypeInfo vtis[];
76 int i = 0;
77 if (!isStatic) {
78 vtis = new VerificationTypeInfo[methodType.parameterCount() + 1];
79 if ("<init>".equals(methodName) && !ConstantDescs.CD_Object.equals(thisClass.asSymbol())) {
80 vtis[i++] = SimpleVerificationTypeInfo.UNINITIALIZED_THIS;
81 } else {
82 vtis[i++] = new StackMapDecoder.ObjectVerificationTypeInfoImpl(thisClass);
83 }
84 } else {
85 vtis = new VerificationTypeInfo[methodType.parameterCount()];
86 }
87 for (int pi = 0; pi < methodType.parameterCount(); pi++) {
88 var arg = methodType.parameterType(pi);
89 vtis[i++] = switch (arg.descriptorString().charAt(0)) {
90 case 'I', 'S', 'C' ,'B', 'Z' -> SimpleVerificationTypeInfo.INTEGER;
91 case 'J' -> SimpleVerificationTypeInfo.LONG;
92 case 'F' -> SimpleVerificationTypeInfo.FLOAT;
93 case 'D' -> SimpleVerificationTypeInfo.DOUBLE;
94 case 'V' -> throw new IllegalArgumentException("Illegal method argument type: " + arg);
95 default -> new StackMapDecoder.ObjectVerificationTypeInfoImpl(TemporaryConstantPool.INSTANCE.classEntry(arg));
96 };
97 }
98 return List.of(vtis);
99 }
100
101 public static void writeFrames(BufWriter b, List<StackMapFrameInfo> entries) {
102 var buf = (BufWriterImpl)b;
103 var dcb = (DirectCodeBuilder)buf.labelContext();
104 var mi = dcb.methodInfo();
105 var prevLocals = StackMapDecoder.initFrameLocals(buf.thisClass(),
106 mi.methodName().stringValue(),
107 mi.methodTypeSymbol(),
108 (mi.methodFlags() & ACC_STATIC) != 0);
109 int prevOffset = -1;
110 // avoid using method handles due to early bootstrap
111 StackMapFrameInfo[] infos = entries.toArray(NO_STACK_FRAME_INFOS);
112 //sort by resolved label offsets first to allow unordered entries
113 Arrays.sort(infos, new Comparator<StackMapFrameInfo>() {
114 public int compare(final StackMapFrameInfo o1, final StackMapFrameInfo o2) {
115 return Integer.compare(dcb.labelToBci(o1.target()), dcb.labelToBci(o2.target()));
116 }
117 });
118 b.writeU2(infos.length);
119 for (var fr : infos) {
120 int offset = dcb.labelToBci(fr.target());
121 if (offset == prevOffset) {
122 throw new IllegalArgumentException("Duplicated stack frame bytecode index: " + offset);
123 }
124 writeFrame(buf, offset - prevOffset - 1, prevLocals, fr);
125 prevOffset = offset;
126 prevLocals = fr.locals();
127 }
128 }
129
130 private static void writeFrame(BufWriterImpl out, int offsetDelta, List<VerificationTypeInfo> prevLocals, StackMapFrameInfo fr) {
131 if (offsetDelta < 0) throw new IllegalArgumentException("Invalid stack map frames order");
132 if (fr.stack().isEmpty()) {
133 int commonLocalsSize = Math.min(prevLocals.size(), fr.locals().size());
134 int diffLocalsSize = fr.locals().size() - prevLocals.size();
135 if (-3 <= diffLocalsSize && diffLocalsSize <= 3 && equals(fr.locals(), prevLocals, commonLocalsSize)) {
136 if (diffLocalsSize == 0 && offsetDelta <= SAME_FRAME_END) { //same frame
137 out.writeU1(offsetDelta);
138 } else { //chop, same extended or append frame
139 out.writeU1U2(SAME_FRAME_EXTENDED + diffLocalsSize, offsetDelta);
140 for (int i=commonLocalsSize; i<fr.locals().size(); i++) writeTypeInfo(out, fr.locals().get(i));
141 }
142 return;
143 }
144 } else if (fr.stack().size() == 1 && fr.locals().equals(prevLocals)) {
145 if (offsetDelta <= SAME_LOCALS_1_STACK_ITEM_FRAME_END - SAME_LOCALS_1_STACK_ITEM_FRAME_START) { //same locals 1 stack item frame
146 out.writeU1(SAME_LOCALS_1_STACK_ITEM_FRAME_START + offsetDelta);
147 } else { //same locals 1 stack item extended frame
148 out.writeU1U2(SAME_LOCALS_1_STACK_ITEM_EXTENDED, offsetDelta);
149 }
150 writeTypeInfo(out, fr.stack().get(0));
151 return;
152 }
153 //full frame
154 out.writeU1U2U2(FULL_FRAME, offsetDelta, fr.locals().size());
155 for (var l : fr.locals()) writeTypeInfo(out, l);
156 out.writeU2(fr.stack().size());
157 for (var s : fr.stack()) writeTypeInfo(out, s);
158 }
159
160 private static boolean equals(List<VerificationTypeInfo> l1, List<VerificationTypeInfo> l2, int compareSize) {
161 for (int i = 0; i < compareSize; i++) {
162 if (!l1.get(i).equals(l2.get(i))) return false;
163 }
164 return true;
165 }
166
167 private static void writeTypeInfo(BufWriterImpl bw, VerificationTypeInfo vti) {
168 int tag = vti.tag();
169 switch (tag) {
170 case ITEM_TOP, ITEM_INTEGER, ITEM_FLOAT, ITEM_DOUBLE, ITEM_LONG, ITEM_NULL,
171 ITEM_UNINITIALIZED_THIS ->
172 bw.writeU1(tag);
173 case ITEM_OBJECT ->
174 bw.writeU1U2(tag, bw.cpIndex(((ObjectVerificationTypeInfo)vti).className()));
175 case ITEM_UNINITIALIZED ->
176 bw.writeU1U2(tag, bw.labelContext().labelToBci(((UninitializedVerificationTypeInfo)vti).newTarget()));
177 default -> throw new IllegalArgumentException("Invalid verification type tag: " + vti.tag());
178 }
179 }
180
181 List<StackMapFrameInfo> entries() {
182 p = pos;
183 List<VerificationTypeInfo> locals = initFrameLocals, stack = List.of();
184 int bci = -1;
185 var entries = new StackMapFrameInfo[u2()];
186 for (int ei = 0; ei < entries.length; ei++) {
187 int frameType = classReader.readU1(p++);
188 if (frameType <= SAME_FRAME_END) {
189 bci += frameType + 1;
190 stack = List.of();
191 } else if (frameType <= SAME_LOCALS_1_STACK_ITEM_FRAME_END) {
192 bci += frameType - SAME_LOCALS_1_STACK_ITEM_FRAME_START + 1;
193 stack = List.of(readVerificationTypeInfo());
194 } else {
195 if (frameType < SAME_LOCALS_1_STACK_ITEM_EXTENDED)
196 throw new IllegalArgumentException("Invalid stackmap frame type: " + frameType);
197 bci += u2() + 1;
198 if (frameType == SAME_LOCALS_1_STACK_ITEM_EXTENDED) {
199 stack = List.of(readVerificationTypeInfo());
200 } else if (frameType < SAME_FRAME_EXTENDED) {
201 locals = locals.subList(0, locals.size() + frameType - SAME_FRAME_EXTENDED);
202 stack = List.of();
203 } else if (frameType == SAME_FRAME_EXTENDED) {
204 stack = List.of();
205 } else if (frameType <= APPEND_FRAME_END) {
206 int actSize = locals.size();
207 var newLocals = locals.toArray(new VerificationTypeInfo[actSize + frameType - SAME_FRAME_EXTENDED]);
208 for (int i = actSize; i < newLocals.length; i++)
209 newLocals[i] = readVerificationTypeInfo();
210 locals = List.of(newLocals);
211 stack = List.of();
212 } else {
213 var newLocals = new VerificationTypeInfo[u2()];
214 for (int i=0; i<newLocals.length; i++)
215 newLocals[i] = readVerificationTypeInfo();
216 var newStack = new VerificationTypeInfo[u2()];
217 for (int i=0; i<newStack.length; i++)
218 newStack[i] = readVerificationTypeInfo();
219 locals = List.of(newLocals);
220 stack = List.of(newStack);
221 }
222 }
223 entries[ei] = new StackMapFrameImpl(frameType,
224 ctx.getLabel(bci),
225 locals,
226 stack);
227 }
228 return List.of(entries);
229 }
230
231 private VerificationTypeInfo readVerificationTypeInfo() {
232 int tag = classReader.readU1(p++);
233 return switch (tag) {
234 case ITEM_TOP -> SimpleVerificationTypeInfo.TOP;
235 case ITEM_INTEGER -> SimpleVerificationTypeInfo.INTEGER;
236 case ITEM_FLOAT -> SimpleVerificationTypeInfo.FLOAT;
237 case ITEM_DOUBLE -> SimpleVerificationTypeInfo.DOUBLE;
238 case ITEM_LONG -> SimpleVerificationTypeInfo.LONG;
239 case ITEM_NULL -> SimpleVerificationTypeInfo.NULL;
240 case ITEM_UNINITIALIZED_THIS -> SimpleVerificationTypeInfo.UNINITIALIZED_THIS;
241 case ITEM_OBJECT -> new ObjectVerificationTypeInfoImpl(classReader.entryByIndex(u2(), ClassEntry.class));
242 case ITEM_UNINITIALIZED -> new UninitializedVerificationTypeInfoImpl(ctx.getLabel(u2()));
243 default -> throw new IllegalArgumentException("Invalid verification type tag: " + tag);
244 };
245 }
246
247 public static record ObjectVerificationTypeInfoImpl(
248 ClassEntry className) implements ObjectVerificationTypeInfo {
249 public ObjectVerificationTypeInfoImpl {
250 requireNonNull(className);
251 }
252
253 @Override
254 public int tag() { return ITEM_OBJECT; }
255
256 @Override
257 public boolean equals(Object o) {
258 if (this == o) return true;
259 if (o instanceof ObjectVerificationTypeInfoImpl that) {
260 return Objects.equals(className, that.className);
261 }
262 return false;
263 }
264
265 @Override
266 public int hashCode() {
267 return Objects.hash(className);
268 }
269
270 @Override
271 public String toString() {
272 return className.asInternalName();
273 }
274 }
275
276 public static record UninitializedVerificationTypeInfoImpl(Label newTarget) implements UninitializedVerificationTypeInfo {
277 public UninitializedVerificationTypeInfoImpl {
278 requireNonNull(newTarget);
279 }
280
281 @Override
282 public int tag() { return ITEM_UNINITIALIZED; }
283
284 @Override
285 public String toString() {
286 return "UNINIT(" + newTarget +")";
287 }
288 }
289
290 private int u2() {
291 int v = classReader.readU2(p);
292 p += 2;
293 return v;
294 }
295
296 public static record StackMapFrameImpl(int frameType,
297 Label target,
298 List<VerificationTypeInfo> locals,
299 List<VerificationTypeInfo> stack)
300 implements StackMapFrameInfo {
301 public StackMapFrameImpl {
302 requireNonNull(target);
303 locals = Util.sanitizeU2List(locals);
304 stack = Util.sanitizeU2List(stack);
305 }
306 }
307 }