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.*; 54 import static java.util.Objects.requireNonNull; 55 import static jdk.internal.classfile.impl.StackMapGenerator.*; 56 57 public class StackMapDecoder { 58 59 private static final StackMapFrameInfo[] NO_STACK_FRAME_INFOS = {}; 60 61 private final ClassReader classReader; 62 private final int pos; 63 private final LabelContext ctx; 64 private final List<VerificationTypeInfo> initFrameLocals; 65 private final List<NameAndTypeEntry> initFrameUnsets; 66 private int p; 67 68 StackMapDecoder(ClassReader classReader, int pos, LabelContext ctx, List<VerificationTypeInfo> initFrameLocals, 69 List<NameAndTypeEntry> initFrameUnsets) { 70 this.classReader = classReader; 71 this.pos = pos; 72 this.ctx = ctx; 73 this.initFrameLocals = initFrameLocals; 74 this.initFrameUnsets = initFrameUnsets; 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)); 82 } 83 84 public static List<VerificationTypeInfo> initFrameLocals(ClassEntry thisClass, String methodName, MethodTypeDesc methodType, boolean isStatic) { 85 VerificationTypeInfo vtis[]; 86 int i = 0; 87 if (!isStatic) { 88 vtis = new VerificationTypeInfo[methodType.parameterCount() + 1]; 89 if ("<init>".equals(methodName) && !ConstantDescs.CD_Object.equals(thisClass.asSymbol())) { 90 vtis[i++] = SimpleVerificationTypeInfo.UNINITIALIZED_THIS; 91 } else { 92 vtis[i++] = new StackMapDecoder.ObjectVerificationTypeInfoImpl(thisClass); 93 } 94 } else { 95 vtis = new VerificationTypeInfo[methodType.parameterCount()]; 96 } 97 for (int pi = 0; pi < methodType.parameterCount(); pi++) { 98 var arg = methodType.parameterType(pi); 99 vtis[i++] = switch (arg.descriptorString().charAt(0)) { 100 case 'I', 'S', 'C' ,'B', 'Z' -> SimpleVerificationTypeInfo.INTEGER; 101 case 'J' -> SimpleVerificationTypeInfo.LONG; 102 case 'F' -> SimpleVerificationTypeInfo.FLOAT; 103 case 'D' -> SimpleVerificationTypeInfo.DOUBLE; 104 case 'V' -> throw new IllegalArgumentException("Illegal method argument type: " + arg); 105 default -> new StackMapDecoder.ObjectVerificationTypeInfoImpl(TemporaryConstantPool.INSTANCE.classEntry(arg)); 106 }; 107 } 108 return List.of(vtis); 109 } 110 111 static List<NameAndTypeEntry> initFrameUnsets(MethodModel method) { 112 return initFrameUnsets(method.parent().orElseThrow(), 113 method.methodName()); 114 } 115 116 private static List<NameAndTypeEntry> initFrameUnsets(ClassModel clazz, Utf8Entry methodName) { 117 if (!methodName.equalsString(ConstantDescs.INIT_NAME)) 118 return List.of(); 119 if (clazz.minorVersion() != PREVIEW_MINOR_VERSION || clazz.majorVersion() < Util.VALUE_OBJECTS_MAJOR) 120 return List.of(); 121 var l = new ArrayList<NameAndTypeEntry>(clazz.fields().size()); 122 for (var field : clazz.fields()) { 123 if ((field.flags().flagsMask() & (ACC_STATIC | ACC_STRICT_INIT)) == ACC_STRICT_INIT) { // instance strict 124 l.add(TemporaryConstantPool.INSTANCE.nameAndTypeEntry(field.fieldName(), field.fieldType())); 125 } 126 } 127 return List.copyOf(l); 128 } 129 130 private static List<NameAndTypeEntry> initFrameUnsets(MethodInfo mi, WritableField.UnsetField[] unsets) { 131 if (!mi.methodName().equalsString(ConstantDescs.INIT_NAME)) 132 return List.of(); 133 var l = new ArrayList<NameAndTypeEntry>(unsets.length); 134 for (var field : unsets) { 135 l.add(TemporaryConstantPool.INSTANCE.nameAndTypeEntry(field.name(), field.type())); 136 } 137 return List.copyOf(l); 138 } 139 140 public static void writeFrames(BufWriter b, List<StackMapFrameInfo> entries) { 141 var buf = (BufWriterImpl)b; 142 var dcb = (DirectCodeBuilder)buf.labelContext(); 143 var mi = dcb.methodInfo(); 144 var prevLocals = StackMapDecoder.initFrameLocals(buf.thisClass(), 145 mi.methodName().stringValue(), 146 mi.methodTypeSymbol(), 147 (mi.methodFlags() & ACC_STATIC) != 0); 148 var prevUnsets = initFrameUnsets(mi, buf.getStrictInstanceFields()); 149 int prevOffset = -1; 150 // avoid using method handles due to early bootstrap 151 StackMapFrameInfo[] infos = entries.toArray(NO_STACK_FRAME_INFOS); 152 //sort by resolved label offsets first to allow unordered entries 153 Arrays.sort(infos, new Comparator<StackMapFrameInfo>() { 154 public int compare(final StackMapFrameInfo o1, final StackMapFrameInfo o2) { 155 return Integer.compare(dcb.labelToBci(o1.target()), dcb.labelToBci(o2.target())); 156 } 157 }); 158 b.writeU2(infos.length); 159 for (var fr : infos) { 160 int offset = dcb.labelToBci(fr.target()); 161 if (offset == prevOffset) { 162 throw new IllegalArgumentException("Duplicated stack frame bytecode index: " + offset); 163 } 164 writeFrame(buf, offset - prevOffset - 1, prevLocals, prevUnsets, fr); 165 prevOffset = offset; 166 prevLocals = fr.locals(); 167 prevUnsets = fr.unsetFields(); 168 } 169 } 170 171 // In sync with StackMapGenerator::needsLarvalFrame 172 private static boolean needsLarvalFrameForTransition(List<NameAndTypeEntry> prevUnsets, StackMapFrameInfo fr) { 173 if (prevUnsets.equals(fr.unsetFields())) 174 return false; 175 if (!fr.locals().contains(SimpleVerificationTypeInfo.UNINITIALIZED_THIS)) { 176 assert fr.unsetFields().isEmpty() : fr; // should be checked in StackMapFrameInfo constructor 177 return false; 178 } 179 return true; 180 } 181 182 private static void writeFrame(BufWriterImpl out, int offsetDelta, List<VerificationTypeInfo> prevLocals, List<NameAndTypeEntry> prevUnsets, StackMapFrameInfo fr) { 183 if (offsetDelta < 0) throw new IllegalArgumentException("Invalid stack map frames order"); 184 // enclosing frames 185 if (needsLarvalFrameForTransition(prevUnsets, fr)) { 186 out.writeU1(EARLY_LARVAL); 187 Util.writeListIndices(out, fr.unsetFields()); 188 } 189 // base frame 190 if (fr.stack().isEmpty()) { 191 int commonLocalsSize = Math.min(prevLocals.size(), fr.locals().size()); 192 int diffLocalsSize = fr.locals().size() - prevLocals.size(); 193 if (-3 <= diffLocalsSize && diffLocalsSize <= 3 && equals(fr.locals(), prevLocals, commonLocalsSize)) { 194 if (diffLocalsSize == 0 && offsetDelta <= SAME_FRAME_END) { //same frame 195 out.writeU1(offsetDelta); 196 } else { //chop, same extended or append frame 197 out.writeU1U2(SAME_FRAME_EXTENDED + diffLocalsSize, offsetDelta); 198 for (int i=commonLocalsSize; i<fr.locals().size(); i++) writeTypeInfo(out, fr.locals().get(i)); 199 } 200 return; 201 } 202 } else if (fr.stack().size() == 1 && fr.locals().equals(prevLocals)) { 203 if (offsetDelta <= SAME_LOCALS_1_STACK_ITEM_FRAME_END - SAME_LOCALS_1_STACK_ITEM_FRAME_START) { //same locals 1 stack item frame 204 out.writeU1(SAME_LOCALS_1_STACK_ITEM_FRAME_START + offsetDelta); 205 } else { //same locals 1 stack item extended frame 206 out.writeU1U2(SAME_LOCALS_1_STACK_ITEM_EXTENDED, offsetDelta); 207 } 208 writeTypeInfo(out, fr.stack().get(0)); 209 return; 210 } 211 //full frame 212 out.writeU1U2U2(FULL_FRAME, offsetDelta, fr.locals().size()); 213 for (var l : fr.locals()) writeTypeInfo(out, l); 214 out.writeU2(fr.stack().size()); 215 for (var s : fr.stack()) writeTypeInfo(out, s); 216 } 217 218 private static boolean equals(List<VerificationTypeInfo> l1, List<VerificationTypeInfo> l2, int compareSize) { 219 for (int i = 0; i < compareSize; i++) { 220 if (!l1.get(i).equals(l2.get(i))) return false; 221 } 222 return true; 223 } 224 225 private static void writeTypeInfo(BufWriterImpl bw, VerificationTypeInfo vti) { 226 int tag = vti.tag(); 227 switch (tag) { 228 case ITEM_TOP, ITEM_INTEGER, ITEM_FLOAT, ITEM_DOUBLE, ITEM_LONG, ITEM_NULL, 229 ITEM_UNINITIALIZED_THIS -> 230 bw.writeU1(tag); 231 case ITEM_OBJECT -> 232 bw.writeU1U2(tag, bw.cpIndex(((ObjectVerificationTypeInfo)vti).className())); 233 case ITEM_UNINITIALIZED -> 234 bw.writeU1U2(tag, bw.labelContext().labelToBci(((UninitializedVerificationTypeInfo)vti).newTarget())); 235 default -> throw new IllegalArgumentException("Invalid verification type tag: " + vti.tag()); 236 } 237 } 238 239 // Copied from BoundAttribute 240 <E extends PoolEntry> List<E> readEntryList(int p, Class<E> type) { 241 int cnt = classReader.readU2(p); 242 p += 2; 243 var entries = new Object[cnt]; 244 int end = p + (cnt * 2); 245 for (int i = 0; p < end; i++, p += 2) { 246 entries[i] = classReader.readEntry(p, type); 247 } 248 return SharedSecrets.getJavaUtilCollectionAccess().listFromTrustedArray(entries); 249 } 250 251 List<StackMapFrameInfo> entries() { 252 p = pos; 253 List<VerificationTypeInfo> locals = initFrameLocals, stack = List.of(); 254 List<NameAndTypeEntry> unsetFields = initFrameUnsets; 255 int bci = -1; 256 var entries = new StackMapFrameInfo[u2()]; 257 for (int ei = 0; ei < entries.length; ei++) { 258 int actualFrameType = classReader.readU1(p++); 259 int frameType = actualFrameType; // effective frame type for parsing 260 // enclosing frames handling 261 if (frameType == EARLY_LARVAL) { 262 unsetFields = readEntryList(p, NameAndTypeEntry.class); 263 p += 2 + unsetFields.size() * 2; 264 frameType = classReader.readU1(p++); 265 } 266 // base frame handling 267 if (frameType <= SAME_FRAME_END) { 268 bci += frameType + 1; 269 stack = List.of(); 270 } else if (frameType <= SAME_LOCALS_1_STACK_ITEM_FRAME_END) { 271 bci += frameType - SAME_LOCALS_1_STACK_ITEM_FRAME_START + 1; 272 stack = List.of(readVerificationTypeInfo()); 273 } else { 274 if (frameType < SAME_LOCALS_1_STACK_ITEM_EXTENDED) 275 throw new IllegalArgumentException("Invalid base frame type: " + frameType); 276 bci += u2() + 1; 277 if (frameType == SAME_LOCALS_1_STACK_ITEM_EXTENDED) { 278 stack = List.of(readVerificationTypeInfo()); 279 } else if (frameType < SAME_FRAME_EXTENDED) { 280 locals = locals.subList(0, locals.size() + frameType - SAME_FRAME_EXTENDED); 281 stack = List.of(); 282 } else if (frameType == SAME_FRAME_EXTENDED) { 283 stack = List.of(); 284 } else if (frameType <= APPEND_FRAME_END) { 285 int actSize = locals.size(); 286 var newLocals = locals.toArray(new VerificationTypeInfo[actSize + frameType - SAME_FRAME_EXTENDED]); 287 for (int i = actSize; i < newLocals.length; i++) 288 newLocals[i] = readVerificationTypeInfo(); 289 locals = List.of(newLocals); 290 stack = List.of(); 291 } else { 292 var newLocals = new VerificationTypeInfo[u2()]; 293 for (int i=0; i<newLocals.length; i++) 294 newLocals[i] = readVerificationTypeInfo(); 295 var newStack = new VerificationTypeInfo[u2()]; 296 for (int i=0; i<newStack.length; i++) 297 newStack[i] = readVerificationTypeInfo(); 298 locals = List.of(newLocals); 299 stack = List.of(newStack); 300 } 301 } 302 if (actualFrameType != EARLY_LARVAL && !unsetFields.isEmpty() && !locals.contains(SimpleVerificationTypeInfo.UNINITIALIZED_THIS)) { 303 // clear unsets post larval 304 unsetFields = List.of(); 305 } 306 entries[ei] = new StackMapFrameImpl(actualFrameType, 307 ctx.getLabel(bci), 308 locals, 309 stack, 310 unsetFields); 311 } 312 return List.of(entries); 313 } 314 315 private VerificationTypeInfo readVerificationTypeInfo() { 316 int tag = classReader.readU1(p++); 317 return switch (tag) { 318 case ITEM_TOP -> SimpleVerificationTypeInfo.TOP; 319 case ITEM_INTEGER -> SimpleVerificationTypeInfo.INTEGER; 320 case ITEM_FLOAT -> SimpleVerificationTypeInfo.FLOAT; 321 case ITEM_DOUBLE -> SimpleVerificationTypeInfo.DOUBLE; 322 case ITEM_LONG -> SimpleVerificationTypeInfo.LONG; 323 case ITEM_NULL -> SimpleVerificationTypeInfo.NULL; 324 case ITEM_UNINITIALIZED_THIS -> SimpleVerificationTypeInfo.UNINITIALIZED_THIS; 325 case ITEM_OBJECT -> new ObjectVerificationTypeInfoImpl(classReader.entryByIndex(u2(), ClassEntry.class)); 326 case ITEM_UNINITIALIZED -> new UninitializedVerificationTypeInfoImpl(ctx.getLabel(u2())); 327 default -> throw new IllegalArgumentException("Invalid verification type tag: " + tag); 328 }; 329 } 330 331 public static record ObjectVerificationTypeInfoImpl( 332 ClassEntry className) implements ObjectVerificationTypeInfo { 333 public ObjectVerificationTypeInfoImpl { 334 requireNonNull(className); 335 } 336 337 @Override 338 public int tag() { return ITEM_OBJECT; } 339 340 @Override 341 public boolean equals(Object o) { 342 if (this == o) return true; 343 if (o instanceof ObjectVerificationTypeInfoImpl that) { 344 return Objects.equals(className, that.className); 345 } 346 return false; 347 } 348 349 @Override 350 public int hashCode() { 351 return Objects.hash(className); 352 } 353 354 @Override 355 public String toString() { 356 return className.asInternalName(); 357 } 358 } 359 360 public static record UninitializedVerificationTypeInfoImpl(Label newTarget) implements UninitializedVerificationTypeInfo { 361 public UninitializedVerificationTypeInfoImpl { 362 requireNonNull(newTarget); 363 } 364 365 @Override 366 public int tag() { return ITEM_UNINITIALIZED; } 367 368 @Override 369 public String toString() { 370 return "UNINIT(" + newTarget +")"; 371 } 372 } 373 374 private int u2() { 375 int v = classReader.readU2(p); 376 p += 2; 377 return v; 378 } 379 380 public static record StackMapFrameImpl(int frameType, 381 Label target, 382 List<VerificationTypeInfo> locals, 383 List<VerificationTypeInfo> stack, 384 List<NameAndTypeEntry> unsetFields) 385 implements StackMapFrameInfo { 386 public StackMapFrameImpl { 387 requireNonNull(target); 388 locals = Util.sanitizeU2List(locals); 389 stack = Util.sanitizeU2List(stack); 390 unsetFields = Util.sanitizeU2List(unsetFields); 391 392 uninitializedThisCheck: 393 if (!unsetFields.isEmpty()) { 394 for (var local : locals) { 395 if (local == SimpleVerificationTypeInfo.UNINITIALIZED_THIS) { 396 break uninitializedThisCheck; 397 } 398 } 399 throw new IllegalArgumentException("unset fields requires uninitializedThis in locals"); 400 } 401 } 402 403 public StackMapFrameImpl(int frameType, 404 Label target, 405 List<VerificationTypeInfo> locals, 406 List<VerificationTypeInfo> stack) { 407 this(frameType, target, locals, stack, List.of()); 408 } 409 } 410 } --- EOF ---