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 if (utflen > 65535) { 323 throw new IllegalArgumentException("string too long"); 324 } 325 reserveSpace(utflen + 3); 326 327 int offset = this.offset; 328 byte[] elems = this.elems; 329 330 elems[offset ] = (byte) TAG_UTF8; 331 elems[offset + 1] = (byte) (utflen >> 8); 332 elems[offset + 2] = (byte) utflen; 333 offset += 3; 334 335 str.getBytes(0, countNonZeroAscii, elems, offset); 336 offset += countNonZeroAscii; 337 338 for (int i = countNonZeroAscii; i < strlen; i++) { 339 offset = putChar(elems, offset, str.charAt(i)); 340 } 341 342 this.offset = offset; 343 } 344 345 @Override 346 public void writeBytes(byte[] arr, int start, int length) { 347 reserveSpace(length); 348 System.arraycopy(arr, start, elems, offset, length); 349 offset += length; 350 } 351 352 @Override 353 public void patchInt(int offset, int size, int value) { 354 int prevOffset = this.offset; 355 this.offset = offset; 356 writeIntBytes(size, value); 357 this.offset = prevOffset; 358 } 359 360 public void patchU2(int offset, int x) { 361 byte[] elems = this.elems; 362 elems[offset ] = (byte) (x >> 8); 363 elems[offset + 1] = (byte) x; 364 } 365 366 public void patchInt(int offset, int x) { 367 byte[] elems = this.elems; 368 elems[offset ] = (byte) (x >> 24); 369 elems[offset + 1] = (byte) (x >> 16); 370 elems[offset + 2] = (byte) (x >> 8); 371 elems[offset + 3] = (byte) x; 372 } 373 374 @Override 375 public void writeIntBytes(int intSize, long intValue) { 376 reserveSpace(intSize); 377 for (int i = 0; i < intSize; i++) { 378 elems[offset++] = (byte) ((intValue >> 8 * (intSize - i - 1)) & 0xFF); 379 } 380 } 381 382 /** 383 * Skip a few bytes in the output buffer. The skipped area has undefined value. 384 * @param bytes number of bytes to skip 385 * @return the index, for later patching 386 */ 387 public int skip(int bytes) { 388 int now = offset; 389 reserveSpace(bytes); 390 offset += bytes; 391 return now; 392 } 393 394 @Override 395 public void reserveSpace(int freeBytes) { 396 int minCapacity = offset + freeBytes; 397 if (minCapacity > elems.length) { 398 grow(minCapacity); 399 } 400 } 401 402 private void grow(int minCapacity) { 403 int newsize = elems.length * 2; 404 while (minCapacity > newsize) { 405 newsize *= 2; 406 } 407 elems = Arrays.copyOf(elems, newsize); 408 } 409 410 @Override 411 public int size() { 412 return offset; 413 } 414 415 public RawBytecodeHelper.CodeRange bytecodeView() { 416 return RawBytecodeHelper.of(elems, offset); 417 } 418 419 public void copyTo(byte[] array, int bufferOffset) { 420 System.arraycopy(elems, 0, array, bufferOffset, size()); 421 } 422 423 // writeIndex methods ensure that any CP info written 424 // is relative to the correct constant pool 425 426 public int cpIndex(PoolEntry entry) { 427 int idx = AbstractPoolEntry.maybeClone(constantPool, entry).index(); 428 if (idx < 1 || idx > Character.MAX_VALUE) 429 throw invalidIndex(idx, entry); 430 return idx; 431 } 432 433 public int cpIndexOrZero(PoolEntry entry) { 434 if (entry == null || entry.index() == 0) 435 return 0; 436 return cpIndex(entry); 437 } 438 439 @ForceInline 440 @Override 441 public void writeIndex(PoolEntry entry) { 442 writeU2(cpIndex(entry)); 443 } 444 445 public void writeIndex(int bytecode, PoolEntry entry) { 446 writeU1U2(bytecode, cpIndex(entry)); 447 } 448 449 static IllegalArgumentException invalidIndex(int idx, PoolEntry entry) { 450 return new IllegalArgumentException(idx + " is not a valid index. Entry: " + entry); 451 } 452 453 @Override 454 public void writeIndexOrZero(PoolEntry entry) { 455 writeU2(cpIndexOrZero(entry)); 456 } 457 458 /** 459 * Join head and tail into an exact-size buffer 460 */ 461 static byte[] join(BufWriterImpl head, BufWriterImpl tail) { 462 byte[] result = new byte[head.size() + tail.size()]; 463 head.copyTo(result, 0); 464 tail.copyTo(result, head.size()); 465 return result; 466 } 467 }