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