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