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.*; 42 import static java.lang.classfile.constantpool.PoolEntry.TAG_UTF8; 43 import static jdk.internal.util.ModifiedUtf.putChar; 44 import static jdk.internal.util.ModifiedUtf.utfLen; 45 46 public final class BufWriterImpl implements BufWriter { 47 private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); 48 49 private final ConstantPoolBuilder constantPool; 50 private final ClassFileImpl context; 51 private LabelContext labelContext; 52 private WritableField.UnsetField[] strictInstanceFields; // do not modify array contents 53 private ClassModel lastStrictCheckClass; // buf writer has short life, so do not need weak here 54 private boolean lastStrictCheckResult; 55 private boolean labelsMatch; 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_INIT)) == ACC_STRICT_INIT) { 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, boolean labelsMatch) { 114 this.labelContext = labelContext; 115 this.labelsMatch = labelsMatch; 116 } 117 118 public WritableField.UnsetField[] getStrictInstanceFields() { 119 assert strictInstanceFields != null : "should access only after setter call in DirectClassBuilder"; 120 return strictInstanceFields; 121 } 122 123 public void setStrictInstanceFields(WritableField.UnsetField[] strictInstanceFields) { 124 this.strictInstanceFields = strictInstanceFields; 125 } 126 127 128 public boolean labelsMatch(LabelContext lc) { 129 return labelsMatch 130 && labelContext instanceof DirectCodeBuilder dcb 131 && dcb.original == lc; 132 } 133 134 @Override 135 public boolean canWriteDirect(ConstantPool other) { 136 return constantPool.canWriteDirect(other); 137 } 138 139 public ClassEntry thisClass() { 140 return thisClass; 141 } 142 143 public int getMajorVersion() { 144 return majorVersion; 145 } 146 147 public ClassFileImpl context() { 148 return context; 149 } 150 151 @Override 152 public void writeU1(int x) { 153 reserveSpace(1); 154 elems[offset++] = (byte) x; 155 } 156 157 @ForceInline 158 @Override 159 public void writeU2(int x) { 160 reserveSpace(2); 161 byte[] elems = this.elems; 162 int offset = this.offset; 163 elems[offset ] = (byte) (x >> 8); 164 elems[offset + 1] = (byte) x; 165 this.offset = offset + 2; 166 } 167 168 @ForceInline 169 public void writeU1U1(int x1, int x2) { 170 reserveSpace(2); 171 byte[] elems = this.elems; 172 int offset = this.offset; 173 elems[offset ] = (byte) x1; 174 elems[offset + 1] = (byte) x2; 175 this.offset = offset + 2; 176 } 177 178 public void writeU1U2(int u1, int u2) { 179 reserveSpace(3); 180 byte[] elems = this.elems; 181 int offset = this.offset; 182 elems[offset ] = (byte) u1; 183 elems[offset + 1] = (byte) (u2 >> 8); 184 elems[offset + 2] = (byte) u2; 185 this.offset = offset + 3; 186 } 187 188 public void writeU1U1U1(int x1, int x2, int x3) { 189 reserveSpace(3); 190 byte[] elems = this.elems; 191 int offset = this.offset; 192 elems[offset ] = (byte) x1; 193 elems[offset + 1] = (byte) x2; 194 elems[offset + 2] = (byte) x3; 195 this.offset = offset + 3; 196 } 197 198 public void writeU1U1U2(int x1, int x2, int x3) { 199 reserveSpace(4); 200 byte[] elems = this.elems; 201 int offset = this.offset; 202 elems[offset ] = (byte) x1; 203 elems[offset + 1] = (byte) x2; 204 elems[offset + 2] = (byte) (x3 >> 8); 205 elems[offset + 3] = (byte) x3; 206 this.offset = offset + 4; 207 } 208 209 public void writeU1U2U2(int x1, int x2, int x3) { 210 reserveSpace(5); 211 byte[] elems = this.elems; 212 int offset = this.offset; 213 elems[offset ] = (byte) x1; 214 elems[offset + 1] = (byte) (x2 >> 8); 215 elems[offset + 2] = (byte) x2; 216 elems[offset + 3] = (byte) (x3 >> 8); 217 elems[offset + 4] = (byte) x3; 218 this.offset = offset + 5; 219 } 220 221 public void writeU2U1(int x1, int x2) { 222 reserveSpace(3); 223 byte[] elems = this.elems; 224 int offset = this.offset; 225 elems[offset ] = (byte) (x1 >> 8); 226 elems[offset + 1] = (byte) x1; 227 elems[offset + 2] = (byte) x2; 228 this.offset = offset + 3; 229 } 230 231 public void writeU2U2(int x1, int x2) { 232 reserveSpace(4); 233 byte[] elems = this.elems; 234 int offset = this.offset; 235 elems[offset ] = (byte) (x1 >> 8); 236 elems[offset + 1] = (byte) x1; 237 elems[offset + 2] = (byte) (x2 >> 8); 238 elems[offset + 3] = (byte) x2; 239 this.offset = offset + 4; 240 } 241 242 public void writeU2U2U2(int x1, int x2, int x3) { 243 reserveSpace(6); 244 byte[] elems = this.elems; 245 int offset = this.offset; 246 elems[offset ] = (byte) (x1 >> 8); 247 elems[offset + 1] = (byte) x1; 248 elems[offset + 2] = (byte) (x2 >> 8); 249 elems[offset + 3] = (byte) x2; 250 elems[offset + 4] = (byte) (x3 >> 8); 251 elems[offset + 5] = (byte) x3; 252 this.offset = offset + 6; 253 } 254 255 @Override 256 public void writeInt(int x) { 257 reserveSpace(4); 258 byte[] elems = this.elems; 259 int offset = this.offset; 260 elems[offset ] = (byte) (x >> 24); 261 elems[offset + 1] = (byte) (x >> 16); 262 elems[offset + 2] = (byte) (x >> 8); 263 elems[offset + 3] = (byte) x; 264 this.offset = offset + 4; 265 } 266 267 public void writeIntInt(int x1, int x2) { 268 reserveSpace(8); 269 byte[] elems = this.elems; 270 int offset = this.offset; 271 elems[offset ] = (byte) (x1 >> 24); 272 elems[offset + 1] = (byte) (x1 >> 16); 273 elems[offset + 2] = (byte) (x1 >> 8); 274 elems[offset + 3] = (byte) x1; 275 elems[offset + 4] = (byte) (x2 >> 24); 276 elems[offset + 5] = (byte) (x2 >> 16); 277 elems[offset + 6] = (byte) (x2 >> 8); 278 elems[offset + 7] = (byte) x2; 279 this.offset = offset + 8; 280 } 281 282 @Override 283 public void writeFloat(float x) { 284 writeInt(Float.floatToIntBits(x)); 285 } 286 287 @Override 288 public void writeLong(long x) { 289 reserveSpace(8); 290 byte[] elems = this.elems; 291 int offset = this.offset; 292 elems[offset ] = (byte) (x >> 56); 293 elems[offset + 1] = (byte) (x >> 48); 294 elems[offset + 2] = (byte) (x >> 40); 295 elems[offset + 3] = (byte) (x >> 32); 296 elems[offset + 4] = (byte) (x >> 24); 297 elems[offset + 5] = (byte) (x >> 16); 298 elems[offset + 6] = (byte) (x >> 8); 299 elems[offset + 7] = (byte) x; 300 this.offset = offset + 8; 301 } 302 303 @Override 304 public void writeDouble(double x) { 305 writeLong(Double.doubleToLongBits(x)); 306 } 307 308 @Override 309 public void writeBytes(byte[] arr) { 310 writeBytes(arr, 0, arr.length); 311 } 312 313 public void writeBytes(BufWriterImpl other) { 314 writeBytes(other.elems, 0, other.offset); 315 } 316 317 @SuppressWarnings("deprecation") 318 void writeUtfEntry(String str) { 319 int strlen = str.length(); 320 int countNonZeroAscii = JLA.countNonZeroAscii(str); 321 int utflen = utfLen(str, countNonZeroAscii); 322 Util.checkU2(utflen, "utf8 length"); 323 reserveSpace(utflen + 3); 324 325 int offset = this.offset; 326 byte[] elems = this.elems; 327 328 elems[offset ] = (byte) TAG_UTF8; 329 elems[offset + 1] = (byte) (utflen >> 8); 330 elems[offset + 2] = (byte) utflen; 331 offset += 3; 332 333 str.getBytes(0, countNonZeroAscii, elems, offset); 334 offset += countNonZeroAscii; 335 336 for (int i = countNonZeroAscii; i < strlen; i++) { 337 offset = putChar(elems, offset, str.charAt(i)); 338 } 339 340 this.offset = offset; 341 } 342 343 @Override 344 public void writeBytes(byte[] arr, int start, int length) { 345 reserveSpace(length); 346 System.arraycopy(arr, start, elems, offset, length); 347 offset += length; 348 } 349 350 @Override 351 public void patchInt(int offset, int size, int value) { 352 int prevOffset = this.offset; 353 this.offset = offset; 354 writeIntBytes(size, value); 355 this.offset = prevOffset; 356 } 357 358 public void patchU2(int offset, int x) { 359 byte[] elems = this.elems; 360 elems[offset ] = (byte) (x >> 8); 361 elems[offset + 1] = (byte) x; 362 } 363 364 public void patchInt(int offset, int x) { 365 byte[] elems = this.elems; 366 elems[offset ] = (byte) (x >> 24); 367 elems[offset + 1] = (byte) (x >> 16); 368 elems[offset + 2] = (byte) (x >> 8); 369 elems[offset + 3] = (byte) x; 370 } 371 372 @Override 373 public void writeIntBytes(int intSize, long intValue) { 374 reserveSpace(intSize); 375 for (int i = 0; i < intSize; i++) { 376 elems[offset++] = (byte) ((intValue >> 8 * (intSize - i - 1)) & 0xFF); 377 } 378 } 379 380 /** 381 * Skip a few bytes in the output buffer. The skipped area has undefined value. 382 * @param bytes number of bytes to skip 383 * @return the index, for later patching 384 */ 385 public int skip(int bytes) { 386 int now = offset; 387 reserveSpace(bytes); 388 offset += bytes; 389 return now; 390 } 391 392 @Override 393 public void reserveSpace(int freeBytes) { 394 int minCapacity = offset + freeBytes; 395 if (minCapacity > elems.length) { 396 grow(minCapacity); 397 } 398 } 399 400 private void grow(int minCapacity) { 401 int newsize = elems.length * 2; 402 while (minCapacity > newsize) { 403 newsize *= 2; 404 } 405 elems = Arrays.copyOf(elems, newsize); 406 } 407 408 @Override 409 public int size() { 410 return offset; 411 } 412 413 public RawBytecodeHelper.CodeRange bytecodeView() { 414 return RawBytecodeHelper.of(elems, offset); 415 } 416 417 public void copyTo(byte[] array, int bufferOffset) { 418 System.arraycopy(elems, 0, array, bufferOffset, size()); 419 } 420 421 // writeIndex methods ensure that any CP info written 422 // is relative to the correct constant pool 423 424 public int cpIndex(PoolEntry entry) { 425 int idx = AbstractPoolEntry.maybeClone(constantPool, entry).index(); 426 if (idx < 1 || idx > Character.MAX_VALUE) 427 throw invalidIndex(idx, entry); 428 return idx; 429 } 430 431 public int cpIndexOrZero(PoolEntry entry) { 432 if (entry == null || entry.index() == 0) 433 return 0; 434 return cpIndex(entry); 435 } 436 437 @ForceInline 438 @Override 439 public void writeIndex(PoolEntry entry) { 440 writeU2(cpIndex(entry)); 441 } 442 443 // Null checks entry 444 public void writeIndex(int bytecode, PoolEntry entry) { 445 writeU1U2(bytecode, cpIndex(entry)); 446 } 447 448 static IllegalArgumentException invalidIndex(int idx, PoolEntry entry) { 449 return new IllegalArgumentException(idx + " is not a valid index. Entry: " + entry); 450 } 451 452 @Override 453 public void writeIndexOrZero(PoolEntry entry) { 454 writeU2(cpIndexOrZero(entry)); 455 } 456 457 /** 458 * Join head and tail into an exact-size buffer 459 */ 460 static byte[] join(BufWriterImpl head, BufWriterImpl tail) { 461 byte[] result = new byte[head.size() + tail.size()]; 462 head.copyTo(result, 0); 463 tail.copyTo(result, head.size()); 464 return result; 465 } 466 }