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; 155 } 156 //full frame 157 out.writeU1U2U2(255, offsetDelta, fr.locals().size()); 158 for (var l : fr.locals()) writeTypeInfo(out, l); 159 out.writeU2(fr.stack().size()); 160 for (var s : fr.stack()) writeTypeInfo(out, s); 161 } 162 163 private static boolean equals(List<VerificationTypeInfo> l1, List<VerificationTypeInfo> l2, int compareSize) { 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 { 252 public ObjectVerificationTypeInfoImpl { 253 requireNonNull(className); 254 } 255 256 @Override 257 public int tag() { return ITEM_OBJECT; } 258 259 @Override 260 public boolean equals(Object o) { 261 if (this == o) return true; 262 if (o instanceof ObjectVerificationTypeInfoImpl that) { 263 return Objects.equals(className, that.className); 264 } 265 return false; 266 } 267 268 @Override 269 public int hashCode() { 270 return Objects.hash(className); 271 } 272 273 @Override 274 public String toString() { 275 return className.asInternalName(); 276 } 277 } 278 279 public static record UninitializedVerificationTypeInfoImpl(Label newTarget) implements UninitializedVerificationTypeInfo { 280 public UninitializedVerificationTypeInfoImpl { 281 requireNonNull(newTarget); 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 }