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