1 /* 2 * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. 3 * Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved. 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. Oracle designates this 9 * particular file as subject to the "Classpath" exception as provided 10 * by Oracle in the LICENSE file that accompanied this code. 11 * 12 * This code is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 * version 2 for more details (a copy is included in the LICENSE file that 16 * accompanied this code). 17 * 18 * You should have received a copy of the GNU General Public License version 19 * 2 along with this work; if not, write to the Free Software Foundation, 20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 21 * 22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 23 * or visit www.oracle.com if you need additional information or have any 24 * questions. 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.constantpool.ClassEntry; 31 import java.lang.classfile.constantpool.ConstantPool; 32 import java.lang.classfile.constantpool.ConstantPoolBuilder; 33 import java.lang.classfile.constantpool.PoolEntry; 34 import java.util.Arrays; 35 import java.util.HashSet; 36 37 import jdk.internal.access.JavaLangAccess; 38 import jdk.internal.access.SharedSecrets; 39 import jdk.internal.vm.annotation.ForceInline; 40 41 import static java.lang.classfile.ClassFile.ACC_STATIC; 42 import static java.lang.classfile.ClassFile.ACC_STRICT; 43 import static java.lang.classfile.constantpool.PoolEntry.TAG_UTF8; 44 import static jdk.internal.util.ModifiedUtf.putChar; 45 import static jdk.internal.util.ModifiedUtf.utfLen; 46 47 public final class BufWriterImpl implements BufWriter { 48 private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); 49 50 private final ConstantPoolBuilder constantPool; 51 private final ClassFileImpl context; 52 private LabelContext labelContext; 53 private WritableField.UnsetField[] strictInstanceFields; // do not modify array contents 54 private ClassModel lastStrictCheckClass; // buf writer has short life, so do not need weak here 55 private boolean lastStrictCheckResult; 56 private final ClassEntry thisClass; 57 private final int majorVersion; 58 byte[] elems; 59 int offset = 0; 60 61 public BufWriterImpl(ConstantPoolBuilder constantPool, ClassFileImpl context) { 62 this(constantPool, context, 64, null, 0); 63 } 64 65 public BufWriterImpl(ConstantPoolBuilder constantPool, ClassFileImpl context, int initialSize) { 66 this(constantPool, context, initialSize, null, 0); 67 } 68 69 public BufWriterImpl(ConstantPoolBuilder constantPool, ClassFileImpl context, int initialSize, ClassEntry thisClass, int majorVersion) { 70 this.constantPool = constantPool; 71 this.context = context; 72 elems = new byte[initialSize]; 73 this.thisClass = thisClass; 74 this.majorVersion = majorVersion; 75 } 76 77 public boolean strictFieldsMatch(ClassModel cm) { 78 // We have a cache because this check will be called multiple times 79 // if a MethodModel is sent wholesale 80 if (lastStrictCheckClass == cm) { 81 return lastStrictCheckResult; 82 } 83 84 var result = doStrictFieldsMatchCheck(cm); 85 lastStrictCheckClass = cm; 86 lastStrictCheckResult = result; 87 return result; 88 } 89 90 private boolean doStrictFieldsMatchCheck(ClassModel cm) { 91 // TODO only check for preview class files? 92 // UTF8 Entry can be used as equality objects 93 var checks = new HashSet<>(Arrays.asList(getStrictInstanceFields())); 94 for (var f : cm.fields()) { 95 if ((f.flags().flagsMask() & (ACC_STATIC | ACC_STRICT)) == ACC_STRICT) { 96 if (!checks.remove(new WritableField.UnsetField(f.fieldName(), f.fieldType()))) { 97 return false; // Field mismatch! 98 } 99 } 100 } 101 return checks.isEmpty(); 102 } 103 104 @Override 105 public ConstantPoolBuilder constantPool() { 106 return constantPool; 107 } 108 109 public LabelContext labelContext() { 110 return labelContext; 111 } 112 113 public void setLabelContext(LabelContext labelContext) { 114 this.labelContext = labelContext; 115 } 116 117 public WritableField.UnsetField[] getStrictInstanceFields() { 118 assert strictInstanceFields != null : "should access only after setter call in DirectClassBuilder"; 119 return strictInstanceFields; 120 } 121 122 public void setStrictInstanceFields(WritableField.UnsetField[] strictInstanceFields) { 123 this.strictInstanceFields = strictInstanceFields; 124 } 125 126 @Override 127 public boolean canWriteDirect(ConstantPool other) { 128 return constantPool.canWriteDirect(other); 129 } 130 131 public ClassEntry thisClass() { 132 return thisClass; 133 } 134 135 public int getMajorVersion() { 136 return majorVersion; 137 } 138 139 public ClassFileImpl context() { 140 return context; 141 } 142 143 @Override 144 public void writeU1(int x) { 145 reserveSpace(1); 146 elems[offset++] = (byte) x; 147 } 148 149 @ForceInline 150 @Override 151 public void writeU2(int x) { 152 reserveSpace(2); 153 byte[] elems = this.elems; 154 int offset = this.offset; 155 elems[offset ] = (byte) (x >> 8); 156 elems[offset + 1] = (byte) x; 157 this.offset = offset + 2; 158 } 159 160 @ForceInline 161 public void writeU1U1(int x1, int x2) { 162 reserveSpace(2); 163 byte[] elems = this.elems; 164 int offset = this.offset; 165 elems[offset ] = (byte) x1; 166 elems[offset + 1] = (byte) x2; 167 this.offset = offset + 2; 168 } 169 170 public void writeU1U2(int u1, int u2) { 171 reserveSpace(3); 172 byte[] elems = this.elems; 173 int offset = this.offset; 174 elems[offset ] = (byte) u1; 175 elems[offset + 1] = (byte) (u2 >> 8); 176 elems[offset + 2] = (byte) u2; 177 this.offset = offset + 3; 178 } 179 180 public void writeU1U1U1(int x1, int x2, int x3) { 181 reserveSpace(3); 182 byte[] elems = this.elems; 183 int offset = this.offset; 184 elems[offset ] = (byte) x1; 185 elems[offset + 1] = (byte) x2; 186 elems[offset + 2] = (byte) x3; 187 this.offset = offset + 3; 188 } 189 190 public void writeU1U1U2(int x1, int x2, int x3) { 191 reserveSpace(4); 192 byte[] elems = this.elems; 193 int offset = this.offset; 194 elems[offset ] = (byte) x1; 195 elems[offset + 1] = (byte) x2; 196 elems[offset + 2] = (byte) (x3 >> 8); 197 elems[offset + 3] = (byte) x3; 198 this.offset = offset + 4; 199 } 200 201 public void writeU1U2U2(int x1, int x2, int x3) { 202 reserveSpace(5); 203 byte[] elems = this.elems; 204 int offset = this.offset; 205 elems[offset ] = (byte) x1; 206 elems[offset + 1] = (byte) (x2 >> 8); 207 elems[offset + 2] = (byte) x2; 208 elems[offset + 3] = (byte) (x3 >> 8); 209 elems[offset + 4] = (byte) x3; 210 this.offset = offset + 5; 211 } 212 213 public void writeU2U1(int x1, int x2) { 214 reserveSpace(3); 215 byte[] elems = this.elems; 216 int offset = this.offset; 217 elems[offset ] = (byte) (x1 >> 8); 218 elems[offset + 1] = (byte) x1; 219 elems[offset + 2] = (byte) x2; 220 this.offset = offset + 3; 221 } 222 223 public void writeU2U2(int x1, int x2) { 224 reserveSpace(4); 225 byte[] elems = this.elems; 226 int offset = this.offset; 227 elems[offset ] = (byte) (x1 >> 8); 228 elems[offset + 1] = (byte) x1; 229 elems[offset + 2] = (byte) (x2 >> 8); 230 elems[offset + 3] = (byte) x2; 231 this.offset = offset + 4; 232 } 233 234 public void writeU2U2U2(int x1, int x2, int x3) { 235 reserveSpace(6); 236 byte[] elems = this.elems; 237 int offset = this.offset; 238 elems[offset ] = (byte) (x1 >> 8); 239 elems[offset + 1] = (byte) x1; 240 elems[offset + 2] = (byte) (x2 >> 8); 241 elems[offset + 3] = (byte) x2; 242 elems[offset + 4] = (byte) (x3 >> 8); 243 elems[offset + 5] = (byte) x3; 244 this.offset = offset + 6; 245 } 246 247 @Override 248 public void writeInt(int x) { 249 reserveSpace(4); 250 byte[] elems = this.elems; 251 int offset = this.offset; 252 elems[offset ] = (byte) (x >> 24); 253 elems[offset + 1] = (byte) (x >> 16); 254 elems[offset + 2] = (byte) (x >> 8); 255 elems[offset + 3] = (byte) x; 256 this.offset = offset + 4; 257 } 258 259 public void writeIntInt(int x1, int x2) { 260 reserveSpace(8); 261 byte[] elems = this.elems; 262 int offset = this.offset; 263 elems[offset ] = (byte) (x1 >> 24); 264 elems[offset + 1] = (byte) (x1 >> 16); 265 elems[offset + 2] = (byte) (x1 >> 8); 266 elems[offset + 3] = (byte) x1; 267 elems[offset + 4] = (byte) (x2 >> 24); 268 elems[offset + 5] = (byte) (x2 >> 16); 269 elems[offset + 6] = (byte) (x2 >> 8); 270 elems[offset + 7] = (byte) x2; 271 this.offset = offset + 8; 272 } 273 274 @Override 275 public void writeFloat(float x) { 276 writeInt(Float.floatToIntBits(x)); 277 } 278 279 @Override 280 public void writeLong(long x) { 281 reserveSpace(8); 282 byte[] elems = this.elems; 283 int offset = this.offset; 284 elems[offset ] = (byte) (x >> 56); 285 elems[offset + 1] = (byte) (x >> 48); 286 elems[offset + 2] = (byte) (x >> 40); 287 elems[offset + 3] = (byte) (x >> 32); 288 elems[offset + 4] = (byte) (x >> 24); 289 elems[offset + 5] = (byte) (x >> 16); 290 elems[offset + 6] = (byte) (x >> 8); 291 elems[offset + 7] = (byte) x; 292 this.offset = offset + 8; 293 } 294 295 @Override 296 public void writeDouble(double x) { 297 writeLong(Double.doubleToLongBits(x)); 298 } 299 300 @Override 301 public void writeBytes(byte[] arr) { 302 writeBytes(arr, 0, arr.length); 303 } 304 305 public void writeBytes(BufWriterImpl other) { 306 writeBytes(other.elems, 0, other.offset); 307 } 308 309 @SuppressWarnings("deprecation") 310 void writeUtfEntry(String str) { 311 int strlen = str.length(); 312 int countNonZeroAscii = JLA.countNonZeroAscii(str); 313 int utflen = utfLen(str, countNonZeroAscii); 314 if (utflen > 65535) { 315 throw new IllegalArgumentException("string too long"); 316 } 317 reserveSpace(utflen + 3); 318 319 int offset = this.offset; 320 byte[] elems = this.elems; 321 322 elems[offset ] = (byte) TAG_UTF8; 323 elems[offset + 1] = (byte) (utflen >> 8); 324 elems[offset + 2] = (byte) utflen; 325 offset += 3; 326 327 str.getBytes(0, countNonZeroAscii, elems, offset); 328 offset += countNonZeroAscii; 329 330 for (int i = countNonZeroAscii; i < strlen; i++) { 331 offset = putChar(elems, offset, str.charAt(i)); 332 } 333 334 this.offset = offset; 335 } 336 337 @Override 338 public void writeBytes(byte[] arr, int start, int length) { 339 reserveSpace(length); 340 System.arraycopy(arr, start, elems, offset, length); 341 offset += length; 342 } 343 344 @Override 345 public void patchInt(int offset, int size, int value) { 346 int prevOffset = this.offset; 347 this.offset = offset; 348 writeIntBytes(size, value); 349 this.offset = prevOffset; 350 } 351 352 public void patchU2(int offset, int x) { 353 byte[] elems = this.elems; 354 elems[offset ] = (byte) (x >> 8); 355 elems[offset + 1] = (byte) x; 356 } 357 358 public void patchInt(int offset, int x) { 359 byte[] elems = this.elems; 360 elems[offset ] = (byte) (x >> 24); 361 elems[offset + 1] = (byte) (x >> 16); 362 elems[offset + 2] = (byte) (x >> 8); 363 elems[offset + 3] = (byte) x; 364 } 365 366 @Override 367 public void writeIntBytes(int intSize, long intValue) { 368 reserveSpace(intSize); 369 for (int i = 0; i < intSize; i++) { 370 elems[offset++] = (byte) ((intValue >> 8 * (intSize - i - 1)) & 0xFF); 371 } 372 } 373 374 /** 375 * Skip a few bytes in the output buffer. The skipped area has undefined value. 376 * @param bytes number of bytes to skip 377 * @return the index, for later patching 378 */ 379 public int skip(int bytes) { 380 int now = offset; 381 reserveSpace(bytes); 382 offset += bytes; 383 return now; 384 } 385 386 @Override 387 public void reserveSpace(int freeBytes) { 388 int minCapacity = offset + freeBytes; 389 if (minCapacity > elems.length) { 390 grow(minCapacity); 391 } 392 } 393 394 private void grow(int minCapacity) { 395 int newsize = elems.length * 2; 396 while (minCapacity > newsize) { 397 newsize *= 2; 398 } 399 elems = Arrays.copyOf(elems, newsize); 400 } 401 402 @Override 403 public int size() { 404 return offset; 405 } 406 407 public RawBytecodeHelper.CodeRange bytecodeView() { 408 return RawBytecodeHelper.of(elems, offset); 409 } 410 411 public void copyTo(byte[] array, int bufferOffset) { 412 System.arraycopy(elems, 0, array, bufferOffset, size()); 413 } 414 415 // writeIndex methods ensure that any CP info written 416 // is relative to the correct constant pool 417 418 public int cpIndex(PoolEntry entry) { 419 int idx = AbstractPoolEntry.maybeClone(constantPool, entry).index(); 420 if (idx < 1 || idx > Character.MAX_VALUE) 421 throw invalidIndex(idx, entry); 422 return idx; 423 } 424 425 public int cpIndexOrZero(PoolEntry entry) { 426 if (entry == null || entry.index() == 0) 427 return 0; 428 return cpIndex(entry); 429 } 430 431 @ForceInline 432 @Override 433 public void writeIndex(PoolEntry entry) { 434 writeU2(cpIndex(entry)); 435 } 436 437 public void writeIndex(int bytecode, PoolEntry entry) { 438 writeU1U2(bytecode, cpIndex(entry)); 439 } 440 441 static IllegalArgumentException invalidIndex(int idx, PoolEntry entry) { 442 return new IllegalArgumentException(idx + " is not a valid index. Entry: " + entry); 443 } 444 445 @Override 446 public void writeIndexOrZero(PoolEntry entry) { 447 writeU2(cpIndexOrZero(entry)); 448 } 449 450 /** 451 * Join head and tail into an exact-size buffer 452 */ 453 static byte[] join(BufWriterImpl head, BufWriterImpl tail) { 454 byte[] result = new byte[head.size() + tail.size()]; 455 head.copyTo(result, 0); 456 tail.copyTo(result, head.size()); 457 return result; 458 } 459 }