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 boolean labelsMatch; 57 private final ClassEntry thisClass; 58 private final int majorVersion; 59 byte[] elems; 60 int offset = 0; 61 62 public BufWriterImpl(ConstantPoolBuilder constantPool, ClassFileImpl context) { 63 this(constantPool, context, 64, null, 0); 64 } 65 66 public BufWriterImpl(ConstantPoolBuilder constantPool, ClassFileImpl context, int initialSize) { 67 this(constantPool, context, initialSize, null, 0); 68 } 69 70 public BufWriterImpl(ConstantPoolBuilder constantPool, ClassFileImpl context, int initialSize, ClassEntry thisClass, int majorVersion) { 71 this.constantPool = constantPool; 72 this.context = context; 73 elems = new byte[initialSize]; 74 this.thisClass = thisClass; 75 this.majorVersion = majorVersion; 76 } 77 78 public boolean strictFieldsMatch(ClassModel cm) { 79 // We have a cache because this check will be called multiple times 80 // if a MethodModel is sent wholesale 81 if (lastStrictCheckClass == cm) { 82 return lastStrictCheckResult; 83 } 84 85 var result = doStrictFieldsMatchCheck(cm); 86 lastStrictCheckClass = cm; 87 lastStrictCheckResult = result; 88 return result; 89 } 90 91 private boolean doStrictFieldsMatchCheck(ClassModel cm) { 92 // TODO only check for preview class files? 93 // UTF8 Entry can be used as equality objects 94 var checks = new HashSet<>(Arrays.asList(getStrictInstanceFields())); 95 for (var f : cm.fields()) { 96 if ((f.flags().flagsMask() & (ACC_STATIC | ACC_STRICT)) == ACC_STRICT) { 97 if (!checks.remove(new WritableField.UnsetField(f.fieldName(), f.fieldType()))) { 98 return false; // Field mismatch! 99 } 100 } 101 } 102 return checks.isEmpty(); 103 } 104 105 @Override 106 public ConstantPoolBuilder constantPool() { 107 return constantPool; 108 } 109 110 public LabelContext labelContext() { 111 return labelContext; 112 } 113 114 public void setLabelContext(LabelContext labelContext, boolean labelsMatch) { 115 this.labelContext = labelContext; 116 this.labelsMatch = labelsMatch; 117 } 118 119 public WritableField.UnsetField[] getStrictInstanceFields() { 120 assert strictInstanceFields != null : "should access only after setter call in DirectClassBuilder"; 121 return strictInstanceFields; 122 } 123 124 public void setStrictInstanceFields(WritableField.UnsetField[] strictInstanceFields) { 125 this.strictInstanceFields = strictInstanceFields; 126 } 127 128 129 public boolean labelsMatch(LabelContext lc) { 130 return labelsMatch 131 && labelContext instanceof DirectCodeBuilder dcb 132 && dcb.original == lc; 133 } 134 135 @Override 136 public boolean canWriteDirect(ConstantPool other) { 137 return constantPool.canWriteDirect(other); 138 } 139 140 public ClassEntry thisClass() { 141 return thisClass; 142 } 143 144 public int getMajorVersion() { 145 return majorVersion; 146 } 147 148 public ClassFileImpl context() { 149 return context; 150 } 151 152 @Override 153 public void writeU1(int x) { 154 reserveSpace(1); 155 elems[offset++] = (byte) x; 156 } 157 158 @ForceInline 159 @Override 160 public void writeU2(int x) { 161 reserveSpace(2); 162 byte[] elems = this.elems; 163 int offset = this.offset; 164 elems[offset ] = (byte) (x >> 8); 165 elems[offset + 1] = (byte) x; 166 this.offset = offset + 2; 167 } 168 169 @ForceInline 170 public void writeU1U1(int x1, int x2) { 171 reserveSpace(2); 172 byte[] elems = this.elems; 173 int offset = this.offset; 174 elems[offset ] = (byte) x1; 175 elems[offset + 1] = (byte) x2; 176 this.offset = offset + 2; 177 } 178 179 public void writeU1U2(int u1, int u2) { 180 reserveSpace(3); 181 byte[] elems = this.elems; 182 int offset = this.offset; 183 elems[offset ] = (byte) u1; 184 elems[offset + 1] = (byte) (u2 >> 8); 185 elems[offset + 2] = (byte) u2; 186 this.offset = offset + 3; 187 } 188 189 public void writeU1U1U1(int x1, int x2, int x3) { 190 reserveSpace(3); 191 byte[] elems = this.elems; 192 int offset = this.offset; 193 elems[offset ] = (byte) x1; 194 elems[offset + 1] = (byte) x2; 195 elems[offset + 2] = (byte) x3; 196 this.offset = offset + 3; 197 } 198 199 public void writeU1U1U2(int x1, int x2, int x3) { 200 reserveSpace(4); 201 byte[] elems = this.elems; 202 int offset = this.offset; 203 elems[offset ] = (byte) x1; 204 elems[offset + 1] = (byte) x2; 205 elems[offset + 2] = (byte) (x3 >> 8); 206 elems[offset + 3] = (byte) x3; 207 this.offset = offset + 4; 208 } 209 210 public void writeU1U2U2(int x1, int x2, int x3) { 211 reserveSpace(5); 212 byte[] elems = this.elems; 213 int offset = this.offset; 214 elems[offset ] = (byte) x1; 215 elems[offset + 1] = (byte) (x2 >> 8); 216 elems[offset + 2] = (byte) x2; 217 elems[offset + 3] = (byte) (x3 >> 8); 218 elems[offset + 4] = (byte) x3; 219 this.offset = offset + 5; 220 } 221 222 public void writeU2U1(int x1, int x2) { 223 reserveSpace(3); 224 byte[] elems = this.elems; 225 int offset = this.offset; 226 elems[offset ] = (byte) (x1 >> 8); 227 elems[offset + 1] = (byte) x1; 228 elems[offset + 2] = (byte) x2; 229 this.offset = offset + 3; 230 } 231 232 public void writeU2U2(int x1, int x2) { 233 reserveSpace(4); 234 byte[] elems = this.elems; 235 int offset = this.offset; 236 elems[offset ] = (byte) (x1 >> 8); 237 elems[offset + 1] = (byte) x1; 238 elems[offset + 2] = (byte) (x2 >> 8); 239 elems[offset + 3] = (byte) x2; 240 this.offset = offset + 4; 241 } 242 243 public void writeU2U2U2(int x1, int x2, int x3) { 244 reserveSpace(6); 245 byte[] elems = this.elems; 246 int offset = this.offset; 247 elems[offset ] = (byte) (x1 >> 8); 248 elems[offset + 1] = (byte) x1; 249 elems[offset + 2] = (byte) (x2 >> 8); 250 elems[offset + 3] = (byte) x2; 251 elems[offset + 4] = (byte) (x3 >> 8); 252 elems[offset + 5] = (byte) x3; 253 this.offset = offset + 6; 254 } 255 256 @Override 257 public void writeInt(int x) { 258 reserveSpace(4); 259 byte[] elems = this.elems; 260 int offset = this.offset; 261 elems[offset ] = (byte) (x >> 24); 262 elems[offset + 1] = (byte) (x >> 16); 263 elems[offset + 2] = (byte) (x >> 8); 264 elems[offset + 3] = (byte) x; 265 this.offset = offset + 4; 266 } 267 268 public void writeIntInt(int x1, int x2) { 269 reserveSpace(8); 270 byte[] elems = this.elems; 271 int offset = this.offset; 272 elems[offset ] = (byte) (x1 >> 24); 273 elems[offset + 1] = (byte) (x1 >> 16); 274 elems[offset + 2] = (byte) (x1 >> 8); 275 elems[offset + 3] = (byte) x1; 276 elems[offset + 4] = (byte) (x2 >> 24); 277 elems[offset + 5] = (byte) (x2 >> 16); 278 elems[offset + 6] = (byte) (x2 >> 8); 279 elems[offset + 7] = (byte) x2; 280 this.offset = offset + 8; 281 } 282 283 @Override 284 public void writeFloat(float x) { 285 writeInt(Float.floatToIntBits(x)); 286 } 287 288 @Override 289 public void writeLong(long x) { 290 reserveSpace(8); 291 byte[] elems = this.elems; 292 int offset = this.offset; 293 elems[offset ] = (byte) (x >> 56); 294 elems[offset + 1] = (byte) (x >> 48); 295 elems[offset + 2] = (byte) (x >> 40); 296 elems[offset + 3] = (byte) (x >> 32); 297 elems[offset + 4] = (byte) (x >> 24); 298 elems[offset + 5] = (byte) (x >> 16); 299 elems[offset + 6] = (byte) (x >> 8); 300 elems[offset + 7] = (byte) x; 301 this.offset = offset + 8; 302 } 303 304 @Override 305 public void writeDouble(double x) { 306 writeLong(Double.doubleToLongBits(x)); 307 } 308 309 @Override 310 public void writeBytes(byte[] arr) { 311 writeBytes(arr, 0, arr.length); 312 } 313 314 public void writeBytes(BufWriterImpl other) { 315 writeBytes(other.elems, 0, other.offset); 316 } 317 318 @SuppressWarnings("deprecation") 319 void writeUtfEntry(String str) { 320 int strlen = str.length(); 321 int countNonZeroAscii = JLA.countNonZeroAscii(str); 322 int utflen = utfLen(str, countNonZeroAscii); 323 if (utflen > 65535) { 324 throw new IllegalArgumentException("string too long"); 325 } 326 reserveSpace(utflen + 3); 327 328 int offset = this.offset; 329 byte[] elems = this.elems; 330 331 elems[offset ] = (byte) TAG_UTF8; 332 elems[offset + 1] = (byte) (utflen >> 8); 333 elems[offset + 2] = (byte) utflen; 334 offset += 3; 335 336 str.getBytes(0, countNonZeroAscii, elems, offset); 337 offset += countNonZeroAscii; 338 339 for (int i = countNonZeroAscii; i < strlen; i++) { 340 offset = putChar(elems, offset, str.charAt(i)); 341 } 342 343 this.offset = offset; 344 } 345 346 @Override 347 public void writeBytes(byte[] arr, int start, int length) { 348 reserveSpace(length); 349 System.arraycopy(arr, start, elems, offset, length); 350 offset += length; 351 } 352 353 @Override 354 public void patchInt(int offset, int size, int value) { 355 int prevOffset = this.offset; 356 this.offset = offset; 357 writeIntBytes(size, value); 358 this.offset = prevOffset; 359 } 360 361 public void patchU2(int offset, int x) { 362 byte[] elems = this.elems; 363 elems[offset ] = (byte) (x >> 8); 364 elems[offset + 1] = (byte) x; 365 } 366 367 public void patchInt(int offset, int x) { 368 byte[] elems = this.elems; 369 elems[offset ] = (byte) (x >> 24); 370 elems[offset + 1] = (byte) (x >> 16); 371 elems[offset + 2] = (byte) (x >> 8); 372 elems[offset + 3] = (byte) x; 373 } 374 375 @Override 376 public void writeIntBytes(int intSize, long intValue) { 377 reserveSpace(intSize); 378 for (int i = 0; i < intSize; i++) { 379 elems[offset++] = (byte) ((intValue >> 8 * (intSize - i - 1)) & 0xFF); 380 } 381 } 382 383 /** 384 * Skip a few bytes in the output buffer. The skipped area has undefined value. 385 * @param bytes number of bytes to skip 386 * @return the index, for later patching 387 */ 388 public int skip(int bytes) { 389 int now = offset; 390 reserveSpace(bytes); 391 offset += bytes; 392 return now; 393 } 394 395 @Override 396 public void reserveSpace(int freeBytes) { 397 int minCapacity = offset + freeBytes; 398 if (minCapacity > elems.length) { 399 grow(minCapacity); 400 } 401 } 402 403 private void grow(int minCapacity) { 404 int newsize = elems.length * 2; 405 while (minCapacity > newsize) { 406 newsize *= 2; 407 } 408 elems = Arrays.copyOf(elems, newsize); 409 } 410 411 @Override 412 public int size() { 413 return offset; 414 } 415 416 public RawBytecodeHelper.CodeRange bytecodeView() { 417 return RawBytecodeHelper.of(elems, offset); 418 } 419 420 public void copyTo(byte[] array, int bufferOffset) { 421 System.arraycopy(elems, 0, array, bufferOffset, size()); 422 } 423 424 // writeIndex methods ensure that any CP info written 425 // is relative to the correct constant pool 426 427 public int cpIndex(PoolEntry entry) { 428 int idx = AbstractPoolEntry.maybeClone(constantPool, entry).index(); 429 if (idx < 1 || idx > Character.MAX_VALUE) 430 throw invalidIndex(idx, entry); 431 return idx; 432 } 433 434 public int cpIndexOrZero(PoolEntry entry) { 435 if (entry == null || entry.index() == 0) 436 return 0; 437 return cpIndex(entry); 438 } 439 440 @ForceInline 441 @Override 442 public void writeIndex(PoolEntry entry) { 443 writeU2(cpIndex(entry)); 444 } 445 446 public void writeIndex(int bytecode, PoolEntry entry) { 447 writeU1U2(bytecode, cpIndex(entry)); 448 } 449 450 static IllegalArgumentException invalidIndex(int idx, PoolEntry entry) { 451 return new IllegalArgumentException(idx + " is not a valid index. Entry: " + entry); 452 } 453 454 @Override 455 public void writeIndexOrZero(PoolEntry entry) { 456 writeU2(cpIndexOrZero(entry)); 457 } 458 459 /** 460 * Join head and tail into an exact-size buffer 461 */ 462 static byte[] join(BufWriterImpl head, BufWriterImpl tail) { 463 byte[] result = new byte[head.size() + tail.size()]; 464 head.copyTo(result, 0); 465 tail.copyTo(result, head.size()); 466 return result; 467 } 468 }