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