1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package android.net.apf; 17 18 import static android.net.apf.BaseApfGenerator.Rbit.Rbit0; 19 import static android.net.apf.BaseApfGenerator.Rbit.Rbit1; 20 import static android.net.apf.BaseApfGenerator.Register.R0; 21 import static android.net.apf.BaseApfGenerator.Register.R1; 22 23 import android.annotation.NonNull; 24 25 import com.android.net.module.util.HexDump; 26 27 import java.nio.ByteBuffer; 28 import java.util.Collections; 29 import java.util.List; 30 import java.util.Objects; 31 import java.util.Set; 32 33 /** 34 * The abstract class for APFv6 assembler/generator. 35 * 36 * @param <Type> the generator class 37 * 38 * @hide 39 */ 40 public abstract class ApfV6GeneratorBase<Type extends ApfV6GeneratorBase<Type>> extends 41 ApfV4GeneratorBase<Type> { 42 43 final int mMaximumApfProgramSize; 44 45 /** 46 * Creates an ApfV6GeneratorBase instance which is able to emit instructions for the specified 47 * {@code version} of the APF interpreter. Throws {@code IllegalInstructionException} if 48 * the requested version is unsupported. 49 * 50 */ ApfV6GeneratorBase(int maximumApfProgramSize)51 public ApfV6GeneratorBase(int maximumApfProgramSize) throws IllegalInstructionException { 52 super(APF_VERSION_6, false); 53 this.mMaximumApfProgramSize = maximumApfProgramSize; 54 } 55 56 /** 57 * Add an instruction to the end of the program to increment the counter value and 58 * immediately return PASS. 59 * 60 * @param cnt the counter number to be incremented. 61 */ addCountAndPass(int cnt)62 public final Type addCountAndPass(int cnt) { 63 checkRange("CounterNumber", cnt /* value */, 1 /* lowerBound */, 64 1000 /* upperBound */); 65 // PASS requires using Rbit0 because it shares opcode with DROP 66 return append(new Instruction(Opcodes.PASSDROP, Rbit0).addUnsigned(cnt)); 67 } 68 69 /** 70 * Add an instruction to the end of the program to let the program immediately return DROP. 71 */ addDrop()72 public final Type addDrop() { 73 // DROP requires using Rbit1 because it shares opcode with PASS 74 return append(new Instruction(Opcodes.PASSDROP, Rbit1)); 75 } 76 77 /** 78 * Add an instruction to the end of the program to increment the counter value and 79 * immediately return DROP. 80 * 81 * @param cnt the counter number to be incremented. 82 */ addCountAndDrop(int cnt)83 public final Type addCountAndDrop(int cnt) { 84 checkRange("CounterNumber", cnt /* value */, 1 /* lowerBound */, 85 1000 /* upperBound */); 86 // DROP requires using Rbit1 because it shares opcode with PASS 87 return append(new Instruction(Opcodes.PASSDROP, Rbit1).addUnsigned(cnt)); 88 } 89 90 /** 91 * Add an instruction to the end of the program to call the apf_allocate_buffer() function. 92 * Buffer length to be allocated is stored in register 0. 93 */ addAllocateR0()94 public final Type addAllocateR0() { 95 return append(new Instruction(ExtendedOpcodes.ALLOCATE)); 96 } 97 98 /** 99 * Add an instruction to the end of the program to call the apf_allocate_buffer() function. 100 * 101 * @param size the buffer length to be allocated. 102 */ addAllocate(int size)103 public final Type addAllocate(int size) { 104 // Rbit1 means the extra be16 immediate is present 105 return append(new Instruction(ExtendedOpcodes.ALLOCATE, Rbit1).addU16(size)); 106 } 107 108 /** 109 * Add an instruction to the beginning of the program to reserve the empty data region. 110 */ addData()111 public final Type addData() throws IllegalInstructionException { 112 return addData(new byte[0]); 113 } 114 115 /** 116 * Add an instruction to the beginning of the program to reserve the data region. 117 * @param data the actual data byte 118 */ addData(byte[] data)119 public final Type addData(byte[] data) throws IllegalInstructionException { 120 if (!mInstructions.isEmpty()) { 121 throw new IllegalInstructionException("data instruction has to come first"); 122 } 123 if (data.length > 65535) { 124 throw new IllegalArgumentException("data size larger than 65535"); 125 } 126 return append(new Instruction(Opcodes.JMP, Rbit1).addUnsigned(data.length) 127 .setBytesImm(data).overrideImmSize(2)); 128 } 129 130 /** 131 * Add an instruction to the end of the program to set the exception buffer size. 132 * @param bufSize the exception buffer size 133 */ addExceptionBuffer(int bufSize)134 public final Type addExceptionBuffer(int bufSize) throws IllegalInstructionException { 135 return append(new Instruction(ExtendedOpcodes.EXCEPTIONBUFFER).addU16(bufSize)); 136 } 137 138 /** 139 * Add an instruction to the end of the program to transmit the allocated buffer without 140 * checksum. 141 */ addTransmitWithoutChecksum()142 public final Type addTransmitWithoutChecksum() { 143 return addTransmit(-1 /* ipOfs */); 144 } 145 146 /** 147 * Add an instruction to the end of the program to transmit the allocated buffer. 148 */ addTransmit(int ipOfs)149 public final Type addTransmit(int ipOfs) { 150 if (ipOfs >= 255) { 151 throw new IllegalArgumentException("IP offset of " + ipOfs + " must be < 255"); 152 } 153 if (ipOfs == -1) ipOfs = 255; 154 return append(new Instruction(ExtendedOpcodes.TRANSMIT, Rbit0).addU8(ipOfs).addU8(255)); 155 } 156 157 /** 158 * Add an instruction to the end of the program to transmit the allocated buffer. 159 */ addTransmitL4(int ipOfs, int csumOfs, int csumStart, int partialCsum, boolean isUdp)160 public final Type addTransmitL4(int ipOfs, int csumOfs, int csumStart, int partialCsum, 161 boolean isUdp) { 162 if (ipOfs >= 255) { 163 throw new IllegalArgumentException("IP offset of " + ipOfs + " must be < 255"); 164 } 165 if (ipOfs == -1) ipOfs = 255; 166 if (csumOfs >= 255) { 167 throw new IllegalArgumentException("L4 checksum requires csum offset of " 168 + csumOfs + " < 255"); 169 } 170 return append(new Instruction(ExtendedOpcodes.TRANSMIT, isUdp ? Rbit1 : Rbit0) 171 .addU8(ipOfs).addU8(csumOfs).addU8(csumStart).addU16(partialCsum)); 172 } 173 174 /** 175 * Add an instruction to the end of the program to write 1 byte value to output buffer. 176 */ addWriteU8(int val)177 public final Type addWriteU8(int val) { 178 return append(new Instruction(Opcodes.WRITE).overrideImmSize(1).addU8(val)); 179 } 180 181 /** 182 * Add an instruction to the end of the program to write 2 bytes value to output buffer. 183 */ addWriteU16(int val)184 public final Type addWriteU16(int val) { 185 return append(new Instruction(Opcodes.WRITE).overrideImmSize(2).addU16(val)); 186 } 187 188 /** 189 * Add an instruction to the end of the program to write 4 bytes value to output buffer. 190 */ addWriteU32(long val)191 public final Type addWriteU32(long val) { 192 return append(new Instruction(Opcodes.WRITE).overrideImmSize(4).addU32(val)); 193 } 194 195 /** 196 * Add an instruction to the end of the program to encode int value as 4 bytes to output buffer. 197 */ addWrite32(int val)198 public final Type addWrite32(int val) { 199 return addWriteU32((long) val & 0xffffffffL); 200 } 201 202 /** 203 * Add an instruction to the end of the program to write 4 bytes array to output buffer. 204 */ addWrite32(@onNull byte[] bytes)205 public final Type addWrite32(@NonNull byte[] bytes) { 206 Objects.requireNonNull(bytes); 207 if (bytes.length != 4) { 208 throw new IllegalArgumentException( 209 "bytes array size must be 4, current size: " + bytes.length); 210 } 211 return addWrite32(((bytes[0] & 0xff) << 24) 212 | ((bytes[1] & 0xff) << 16) 213 | ((bytes[2] & 0xff) << 8) 214 | (bytes[3] & 0xff)); 215 } 216 217 /** 218 * Add an instruction to the end of the program to write 1 byte value from register to output 219 * buffer. 220 */ addWriteU8(Register reg)221 public final Type addWriteU8(Register reg) { 222 return append(new Instruction(ExtendedOpcodes.EWRITE1, reg)); 223 } 224 225 /** 226 * Add an instruction to the end of the program to write 2 byte value from register to output 227 * buffer. 228 */ addWriteU16(Register reg)229 public final Type addWriteU16(Register reg) { 230 return append(new Instruction(ExtendedOpcodes.EWRITE2, reg)); 231 } 232 233 /** 234 * Add an instruction to the end of the program to write 4 byte value from register to output 235 * buffer. 236 */ addWriteU32(Register reg)237 public final Type addWriteU32(Register reg) { 238 return append(new Instruction(ExtendedOpcodes.EWRITE4, reg)); 239 } 240 241 /** 242 * Add an instruction to the end of the program to copy data from APF program/data region to 243 * output buffer and auto-increment the output buffer pointer. 244 * This method requires the {@code addData} method to be called beforehand. 245 * It will first attempt to match {@code content} with existing data bytes. If not exist, then 246 * append the {@code content} to the data bytes. 247 */ addDataCopy(@onNull byte[] content)248 public final Type addDataCopy(@NonNull byte[] content) throws IllegalInstructionException { 249 if (mInstructions.isEmpty()) { 250 throw new IllegalInstructionException("There is no instructions"); 251 } 252 Objects.requireNonNull(content); 253 int copySrc = mInstructions.get(0).maybeUpdateBytesImm(content); 254 return addDataCopy(copySrc, content.length); 255 } 256 257 /** 258 * Add an instruction to the end of the program to copy data from APF program/data region to 259 * output buffer and auto-increment the output buffer pointer. 260 * 261 * @param src the offset inside the APF program/data region for where to start copy. 262 * @param len the length of bytes needed to be copied, only <= 255 bytes can be copied at 263 * one time. 264 * @return the Type object 265 */ addDataCopy(int src, int len)266 public final Type addDataCopy(int src, int len) { 267 return append(new Instruction(Opcodes.PKTDATACOPY, Rbit1).addDataOffset(src).addU8(len)); 268 } 269 270 /** 271 * Add an instruction to the end of the program to copy data from input packet to output 272 * buffer and auto-increment the output buffer pointer. 273 * 274 * @param src the offset inside the input packet for where to start copy. 275 * @param len the length of bytes needed to be copied, only <= 255 bytes can be copied at 276 * one time. 277 * @return the Type object 278 */ addPacketCopy(int src, int len)279 public final Type addPacketCopy(int src, int len) { 280 return append(new Instruction(Opcodes.PKTDATACOPY, Rbit0).addPacketOffset(src).addU8(len)); 281 } 282 283 /** 284 * Add an instruction to the end of the program to copy data from APF program/data region to 285 * output buffer and auto-increment the output buffer pointer. 286 * Source offset is stored in R0. 287 * 288 * @param len the number of bytes to be copied, only <= 255 bytes can be copied at once. 289 * @return the Type object 290 */ addDataCopyFromR0(int len)291 public final Type addDataCopyFromR0(int len) { 292 return append(new Instruction(ExtendedOpcodes.EPKTDATACOPYIMM, Rbit1).addU8(len)); 293 } 294 295 /** 296 * Add an instruction to the end of the program to copy data from input packet to output 297 * buffer and auto-increment the output buffer pointer. 298 * Source offset is stored in R0. 299 * 300 * @param len the number of bytes to be copied, only <= 255 bytes can be copied at once. 301 * @return the Type object 302 */ addPacketCopyFromR0(int len)303 public final Type addPacketCopyFromR0(int len) { 304 return append(new Instruction(ExtendedOpcodes.EPKTDATACOPYIMM, Rbit0).addU8(len)); 305 } 306 307 /** 308 * Add an instruction to the end of the program to copy data from APF program/data region to 309 * output buffer and auto-increment the output buffer pointer. 310 * Source offset is stored in R0. 311 * Copy length is stored in R1. 312 * 313 * @return the Type object 314 */ addDataCopyFromR0LenR1()315 public final Type addDataCopyFromR0LenR1() { 316 return append(new Instruction(ExtendedOpcodes.EPKTDATACOPYR1, Rbit1)); 317 } 318 319 /** 320 * Add an instruction to the end of the program to copy data from input packet to output 321 * buffer and auto-increment the output buffer pointer. 322 * Source offset is stored in R0. 323 * Copy length is stored in R1. 324 * 325 * @return the Type object 326 */ addPacketCopyFromR0LenR1()327 public final Type addPacketCopyFromR0LenR1() { 328 return append(new Instruction(ExtendedOpcodes.EPKTDATACOPYR1, Rbit0)); 329 } 330 331 /** 332 * Appends a conditional jump instruction to the program: Jumps to {@code tgt} if the UDP 333 * payload's DNS questions do NOT contain the QNAMEs specified in {@code qnames} and qtype 334 * equals {@code qtype}. Examines the payload starting at the offset in R0. 335 * R = 0 means check for "does not contain". 336 * Drops packets if packets are corrupted. 337 */ addJumpIfPktAtR0DoesNotContainDnsQ(@onNull byte[] qnames, int qtype, @NonNull String tgt)338 public final Type addJumpIfPktAtR0DoesNotContainDnsQ(@NonNull byte[] qnames, int qtype, 339 @NonNull String tgt) { 340 validateNames(qnames); 341 return append(new Instruction(ExtendedOpcodes.JDNSQMATCH, Rbit0).setTargetLabel(tgt).addU8( 342 qtype).setBytesImm(qnames)); 343 } 344 345 /** 346 * Same as {@link #addJumpIfPktAtR0DoesNotContainDnsQ} except passes packets if packets are 347 * corrupted. 348 */ addJumpIfPktAtR0DoesNotContainDnsQSafe(@onNull byte[] qnames, int qtype, @NonNull String tgt)349 public final Type addJumpIfPktAtR0DoesNotContainDnsQSafe(@NonNull byte[] qnames, int qtype, 350 @NonNull String tgt) { 351 validateNames(qnames); 352 return append(new Instruction(ExtendedOpcodes.JDNSQMATCHSAFE, Rbit0).setTargetLabel( 353 tgt).addU8(qtype).setBytesImm(qnames)); 354 } 355 356 /** 357 * Appends a conditional jump instruction to the program: Jumps to {@code tgt} if the UDP 358 * payload's DNS questions contain the QNAMEs specified in {@code qnames} and qtype 359 * equals {@code qtype}. Examines the payload starting at the offset in R0. 360 * R = 1 means check for "contain". 361 * Drops packets if packets are corrupted. 362 */ addJumpIfPktAtR0ContainDnsQ(@onNull byte[] qnames, int qtype, @NonNull String tgt)363 public final Type addJumpIfPktAtR0ContainDnsQ(@NonNull byte[] qnames, int qtype, 364 @NonNull String tgt) { 365 validateNames(qnames); 366 return append(new Instruction(ExtendedOpcodes.JDNSQMATCH, Rbit1).setTargetLabel(tgt).addU8( 367 qtype).setBytesImm(qnames)); 368 } 369 370 /** 371 * Same as {@link #addJumpIfPktAtR0ContainDnsQ} except passes packets if packets are 372 * corrupted. 373 */ addJumpIfPktAtR0ContainDnsQSafe(@onNull byte[] qnames, int qtype, @NonNull String tgt)374 public final Type addJumpIfPktAtR0ContainDnsQSafe(@NonNull byte[] qnames, int qtype, 375 @NonNull String tgt) { 376 validateNames(qnames); 377 return append(new Instruction(ExtendedOpcodes.JDNSQMATCHSAFE, Rbit1).setTargetLabel( 378 tgt).addU8(qtype).setBytesImm(qnames)); 379 } 380 381 /** 382 * Appends a conditional jump instruction to the program: Jumps to {@code tgt} if the UDP 383 * payload's DNS answers/authority/additional records do NOT contain the NAMEs 384 * specified in {@code Names}. Examines the payload starting at the offset in R0. 385 * R = 0 means check for "does not contain". 386 * Drops packets if packets are corrupted. 387 */ addJumpIfPktAtR0DoesNotContainDnsA(@onNull byte[] names, @NonNull String tgt)388 public final Type addJumpIfPktAtR0DoesNotContainDnsA(@NonNull byte[] names, 389 @NonNull String tgt) { 390 validateNames(names); 391 return append(new Instruction(ExtendedOpcodes.JDNSAMATCH, Rbit0).setTargetLabel(tgt) 392 .setBytesImm(names)); 393 } 394 395 /** 396 * Same as {@link #addJumpIfPktAtR0DoesNotContainDnsA} except passes packets if packets are 397 * corrupted. 398 */ addJumpIfPktAtR0DoesNotContainDnsASafe(@onNull byte[] names, @NonNull String tgt)399 public final Type addJumpIfPktAtR0DoesNotContainDnsASafe(@NonNull byte[] names, 400 @NonNull String tgt) { 401 validateNames(names); 402 return append(new Instruction(ExtendedOpcodes.JDNSAMATCHSAFE, Rbit0).setTargetLabel(tgt) 403 .setBytesImm(names)); 404 } 405 406 /** 407 * Appends a conditional jump instruction to the program: Jumps to {@code tgt} if the UDP 408 * payload's DNS answers/authority/additional records contain the NAMEs 409 * specified in {@code Names}. Examines the payload starting at the offset in R0. 410 * R = 1 means check for "contain". 411 * Drops packets if packets are corrupted. 412 */ addJumpIfPktAtR0ContainDnsA(@onNull byte[] names, @NonNull String tgt)413 public final Type addJumpIfPktAtR0ContainDnsA(@NonNull byte[] names, 414 @NonNull String tgt) { 415 validateNames(names); 416 return append(new Instruction(ExtendedOpcodes.JDNSAMATCH, Rbit1).setTargetLabel( 417 tgt).setBytesImm(names)); 418 } 419 420 /** 421 * Same as {@link #addJumpIfPktAtR0ContainDnsA} except passes packets if packets are 422 * corrupted. 423 */ addJumpIfPktAtR0ContainDnsASafe(@onNull byte[] names, @NonNull String tgt)424 public final Type addJumpIfPktAtR0ContainDnsASafe(@NonNull byte[] names, 425 @NonNull String tgt) { 426 validateNames(names); 427 return append(new Instruction(ExtendedOpcodes.JDNSAMATCHSAFE, Rbit1).setTargetLabel( 428 tgt).setBytesImm(names)); 429 } 430 431 /** 432 * Add an instruction to the end of the program to jump to {@code tgt} if the bytes of the 433 * packet at an offset specified by register0 match {@code bytes}. 434 * R=1 means check for equal. 435 */ addJumpIfBytesAtR0Equal(@onNull byte[] bytes, String tgt)436 public final Type addJumpIfBytesAtR0Equal(@NonNull byte[] bytes, String tgt) 437 throws IllegalInstructionException { 438 validateBytes(bytes); 439 return append(new Instruction(Opcodes.JBSMATCH, R1).addUnsigned( 440 bytes.length).setTargetLabel(tgt).setBytesImm(bytes)); 441 } 442 addJumpIfBytesAtR0EqualsHelper(@onNull List<byte[]> bytesList, String tgt, boolean jumpOnMatch)443 private Type addJumpIfBytesAtR0EqualsHelper(@NonNull List<byte[]> bytesList, String tgt, 444 boolean jumpOnMatch) { 445 final List<byte[]> deduplicatedList = validateDeduplicateBytesList(bytesList); 446 final int elementSize = deduplicatedList.get(0).length; 447 final int totalElements = deduplicatedList.size(); 448 final int totalSize = elementSize * totalElements; 449 final ByteBuffer buffer = ByteBuffer.allocate(totalSize); 450 for (byte[] array : deduplicatedList) { 451 buffer.put(array); 452 } 453 final Rbit rbit = jumpOnMatch ? Rbit1 : Rbit0; 454 final byte[] combinedBytes = buffer.array(); 455 return append(new Instruction(Opcodes.JBSMATCH, rbit) 456 .addUnsigned((totalElements - 1) << 11 | elementSize) 457 .setTargetLabel(tgt) 458 .setBytesImm(combinedBytes)); 459 } 460 461 /** 462 * Add an instruction to the end of the program to jump to {@code tgt} if the bytes of the 463 * packet at an offset specified by register0 match any of the elements in {@code bytesSet}. 464 * R=1 means check for equal. 465 */ addJumpIfBytesAtR0EqualsAnyOf(@onNull List<byte[]> bytesList, String tgt)466 public final Type addJumpIfBytesAtR0EqualsAnyOf(@NonNull List<byte[]> bytesList, String tgt) { 467 return addJumpIfBytesAtR0EqualsHelper(bytesList, tgt, true /* jumpOnMatch */); 468 } 469 470 /** 471 * Add an instruction to the end of the program to jump to {@code tgt} if the bytes of the 472 * packet at an offset specified by register0 match none of the elements in {@code bytesSet}. 473 * R=0 means check for not equal. 474 */ addJumpIfBytesAtR0EqualNoneOf(@onNull List<byte[]> bytesList, String tgt)475 public final Type addJumpIfBytesAtR0EqualNoneOf(@NonNull List<byte[]> bytesList, String tgt) { 476 return addJumpIfBytesAtR0EqualsHelper(bytesList, tgt, false /* jumpOnMatch */); 477 } 478 479 480 /** 481 * Check if the byte is valid dns character: A-Z,0-9,-,_ 482 */ isValidDnsCharacter(byte c)483 private static boolean isValidDnsCharacter(byte c) { 484 return (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '_' || c == '%'; 485 } 486 validateNames(@onNull byte[] names)487 private static void validateNames(@NonNull byte[] names) { 488 final int len = names.length; 489 if (len < 4) { 490 throw new IllegalArgumentException("qnames must have at least length 4"); 491 } 492 final String errorMessage = "qname: " + HexDump.toHexString(names) 493 + "is not null-terminated list of TLV-encoded names"; 494 int i = 0; 495 while (i < len - 1) { 496 int label_len = names[i++]; 497 // byte == 0xff means it is a '*' wildcard 498 if (label_len == -1) continue; 499 if (label_len < 1 || label_len > 63) { 500 throw new IllegalArgumentException( 501 "label len: " + label_len + " must be between 1 and 63"); 502 } 503 if (i + label_len >= len - 1) { 504 throw new IllegalArgumentException(errorMessage); 505 } 506 while (label_len-- > 0) { 507 if (!isValidDnsCharacter(names[i++])) { 508 throw new IllegalArgumentException("qname: " + HexDump.toHexString(names) 509 + " contains invalid character"); 510 } 511 } 512 if (names[i] == 0) { 513 i++; // skip null terminator. 514 } 515 } 516 if (names[len - 1] != 0) { 517 throw new IllegalArgumentException(errorMessage); 518 } 519 } 520 addJumpIfOneOfHelper(Register reg, @NonNull Set<Long> values, boolean jumpOnMatch, @NonNull String tgt)521 private Type addJumpIfOneOfHelper(Register reg, @NonNull Set<Long> values, 522 boolean jumpOnMatch, @NonNull String tgt) { 523 if (values == null || values.size() < 2 || values.size() > 33) { 524 throw new IllegalArgumentException( 525 "size of values set must be >= 2 and <= 33, current size: " + values.size()); 526 } 527 final Long max = Collections.max(values); 528 final Long min = Collections.min(values); 529 checkRange("max value in set", max, 0, 4294967295L); 530 checkRange("min value in set", min, 0, 4294967295L); 531 // Since sets are always of size > 1 and in range [0, uint32_max], max is guaranteed > 0, 532 // so maxImmSize can never be 0. 533 final int maxImmSize = calculateImmSize(max.intValue(), false); 534 535 // imm3(u8): top 5 bits - number of following u8/be16/be32 values - 2 536 // middle 2 bits - 1..4 length of immediates - 1 537 // bottom 1 bit - =0 jmp if in set, =1 if not in set 538 Instruction instruction = new Instruction(ExtendedOpcodes.JONEOF, reg) 539 .setTargetLabel(tgt) 540 .addU8((values.size() - 2) << 3 | (maxImmSize - 1) << 1 | (jumpOnMatch ? 0 : 1)); 541 for (Long v : values) { 542 switch (maxImmSize) { 543 case 1: 544 instruction.addU8(v.intValue()); 545 break; 546 case 2: 547 instruction.addU16(v.intValue()); 548 break; 549 // case 3: instruction.addU24(v); break; -- not supported by generator 550 case 4: 551 instruction.addU32(v); 552 break; 553 default: 554 throw new IllegalArgumentException( 555 "immLen is not in {1, 2, 4}, immLen: " + maxImmSize); 556 } 557 } 558 return append(instruction); 559 } 560 561 /** 562 * Add an instruction to the end of the program to jump to {@code tgt} if {@code reg} is 563 * one of the {@code values}. 564 */ addJumpIfOneOf(Register reg, @NonNull Set<Long> values, @NonNull String tgt)565 public final Type addJumpIfOneOf(Register reg, @NonNull Set<Long> values, 566 @NonNull String tgt) { 567 return addJumpIfOneOfHelper(reg, values, true /* jumpOnMatch */, tgt); 568 } 569 570 /** 571 * Add an instruction to the end of the program to jump to {@code tgt} if {@code reg} is 572 * not one of the {@code values}. 573 */ addJumpIfNoneOf(Register reg, @NonNull Set<Long> values, @NonNull String tgt)574 public final Type addJumpIfNoneOf(Register reg, @NonNull Set<Long> values, 575 @NonNull String tgt) { 576 return addJumpIfOneOfHelper(reg, values, false /* jumpOnMatch */, tgt); 577 } 578 579 @Override addR0ArithR1(Opcodes opcode)580 void addR0ArithR1(Opcodes opcode) { 581 append(new Instruction(opcode, R0)); // APFv6+: R0 op= R1 582 } 583 584 /** 585 * Add an instruction to the end of the program to increment the counter value and 586 * immediately return PASS. 587 * 588 * @param counter the counter enum to be incremented. 589 */ 590 @Override addCountAndPass(ApfCounterTracker.Counter counter)591 public final Type addCountAndPass(ApfCounterTracker.Counter counter) { 592 checkPassCounterRange(counter); 593 return addCountAndPass(counter.value()); 594 } 595 596 /** 597 * Add an instruction to the end of the program to increment the counter value and 598 * immediately return DROP. 599 * 600 * @param counter the counter enum to be incremented. 601 */ 602 @Override addCountAndDrop(ApfCounterTracker.Counter counter)603 public final Type addCountAndDrop(ApfCounterTracker.Counter counter) { 604 checkDropCounterRange(counter); 605 return addCountAndDrop(counter.value()); 606 } 607 608 @Override addCountAndDropIfR0Equals(long val, ApfCounterTracker.Counter cnt)609 public final Type addCountAndDropIfR0Equals(long val, ApfCounterTracker.Counter cnt) 610 throws IllegalInstructionException { 611 final String tgt = getUniqueLabel(); 612 return addJumpIfR0NotEquals(val, tgt).addCountAndDrop(cnt).defineLabel(tgt); 613 } 614 615 @Override addCountAndPassIfR0Equals(long val, ApfCounterTracker.Counter cnt)616 public final Type addCountAndPassIfR0Equals(long val, ApfCounterTracker.Counter cnt) 617 throws IllegalInstructionException { 618 final String tgt = getUniqueLabel(); 619 return addJumpIfR0NotEquals(val, tgt).addCountAndPass(cnt).defineLabel(tgt); 620 } 621 622 @Override addCountAndDropIfR0NotEquals(long val, ApfCounterTracker.Counter cnt)623 public final Type addCountAndDropIfR0NotEquals(long val, ApfCounterTracker.Counter cnt) 624 throws IllegalInstructionException { 625 final String tgt = getUniqueLabel(); 626 return addJumpIfR0Equals(val, tgt).addCountAndDrop(cnt).defineLabel(tgt); 627 } 628 629 @Override addCountAndPassIfR0NotEquals(long val, ApfCounterTracker.Counter cnt)630 public final Type addCountAndPassIfR0NotEquals(long val, ApfCounterTracker.Counter cnt) 631 throws IllegalInstructionException { 632 final String tgt = getUniqueLabel(); 633 return addJumpIfR0Equals(val, tgt).addCountAndPass(cnt).defineLabel(tgt); 634 } 635 636 @Override addCountAndDropIfR0AnyBitsSet(long val, ApfCounterTracker.Counter cnt)637 public Type addCountAndDropIfR0AnyBitsSet(long val, ApfCounterTracker.Counter cnt) 638 throws IllegalInstructionException { 639 final String countAndDropLabel = getUniqueLabel(); 640 final String skipLabel = getUniqueLabel(); 641 return addJumpIfR0AnyBitsSet(val, countAndDropLabel) 642 .addJump(skipLabel) 643 .defineLabel(countAndDropLabel) 644 .addCountAndDrop(cnt) 645 .defineLabel(skipLabel); 646 } 647 648 @Override addCountAndPassIfR0AnyBitsSet(long val, ApfCounterTracker.Counter cnt)649 public Type addCountAndPassIfR0AnyBitsSet(long val, ApfCounterTracker.Counter cnt) 650 throws IllegalInstructionException { 651 final String countAndPassLabel = getUniqueLabel(); 652 final String skipLabel = getUniqueLabel(); 653 return addJumpIfR0AnyBitsSet(val, countAndPassLabel) 654 .addJump(skipLabel) 655 .defineLabel(countAndPassLabel) 656 .addCountAndPass(cnt) 657 .defineLabel(skipLabel); 658 } 659 660 @Override addCountAndDropIfR0LessThan(long val, ApfCounterTracker.Counter cnt)661 public final Type addCountAndDropIfR0LessThan(long val, ApfCounterTracker.Counter cnt) 662 throws IllegalInstructionException { 663 if (val <= 0) { 664 throw new IllegalArgumentException("val must > 0, current val: " + val); 665 } 666 final String tgt = getUniqueLabel(); 667 return addJumpIfR0GreaterThan(val - 1, tgt).addCountAndDrop(cnt).defineLabel(tgt); 668 } 669 670 @Override addCountAndPassIfR0LessThan(long val, ApfCounterTracker.Counter cnt)671 public final Type addCountAndPassIfR0LessThan(long val, ApfCounterTracker.Counter cnt) 672 throws IllegalInstructionException { 673 if (val <= 0) { 674 throw new IllegalArgumentException("val must > 0, current val: " + val); 675 } 676 final String tgt = getUniqueLabel(); 677 return addJumpIfR0GreaterThan(val - 1, tgt).addCountAndPass(cnt).defineLabel(tgt); 678 } 679 680 @Override addCountAndDropIfR0GreaterThan(long val, ApfCounterTracker.Counter cnt)681 public Type addCountAndDropIfR0GreaterThan(long val, ApfCounterTracker.Counter cnt) 682 throws IllegalInstructionException { 683 if (val < 0 || val >= 4294967295L) { 684 throw new IllegalArgumentException("val must >= 0 and < 2^32-1, current val: " + val); 685 } 686 final String tgt = getUniqueLabel(); 687 return addJumpIfR0LessThan(val + 1, tgt).addCountAndDrop(cnt).defineLabel(tgt); 688 } 689 690 @Override addCountAndPassIfR0GreaterThan(long val, ApfCounterTracker.Counter cnt)691 public Type addCountAndPassIfR0GreaterThan(long val, ApfCounterTracker.Counter cnt) 692 throws IllegalInstructionException { 693 if (val < 0 || val >= 4294967295L) { 694 throw new IllegalArgumentException("val must >= 0 and < 2^32-1, current val: " + val); 695 } 696 final String tgt = getUniqueLabel(); 697 return addJumpIfR0LessThan(val + 1, tgt).addCountAndPass(cnt).defineLabel(tgt); 698 } 699 700 @Override addCountAndDropIfBytesAtR0NotEqual(byte[] bytes, ApfCounterTracker.Counter cnt)701 public final Type addCountAndDropIfBytesAtR0NotEqual(byte[] bytes, 702 ApfCounterTracker.Counter cnt) throws IllegalInstructionException { 703 final String tgt = getUniqueLabel(); 704 return addJumpIfBytesAtR0Equal(bytes, tgt).addCountAndDrop(cnt).defineLabel(tgt); 705 } 706 707 @Override addCountAndPassIfBytesAtR0NotEqual(byte[] bytes, ApfCounterTracker.Counter cnt)708 public final Type addCountAndPassIfBytesAtR0NotEqual(byte[] bytes, 709 ApfCounterTracker.Counter cnt) throws IllegalInstructionException { 710 final String tgt = getUniqueLabel(); 711 return addJumpIfBytesAtR0Equal(bytes, tgt).addCountAndPass(cnt).defineLabel(tgt); 712 } 713 714 @Override addCountAndDropIfBytesAtR0Equal(byte[] bytes, ApfCounterTracker.Counter cnt)715 public final Type addCountAndDropIfBytesAtR0Equal(byte[] bytes, 716 ApfCounterTracker.Counter cnt) throws IllegalInstructionException { 717 final String tgt = getUniqueLabel(); 718 return addJumpIfBytesAtR0NotEqual(bytes, tgt).addCountAndDrop(cnt).defineLabel(tgt); 719 } 720 721 @Override addCountAndPassIfBytesAtR0Equal(byte[] bytes, ApfCounterTracker.Counter cnt)722 public final Type addCountAndPassIfBytesAtR0Equal(byte[] bytes, 723 ApfCounterTracker.Counter cnt) throws IllegalInstructionException { 724 final String tgt = getUniqueLabel(); 725 return addJumpIfBytesAtR0NotEqual(bytes, tgt).addCountAndPass(cnt).defineLabel(tgt); 726 } 727 728 @Override addCountAndPassIfR0IsOneOf(@onNull Set<Long> values, ApfCounterTracker.Counter cnt)729 public Type addCountAndPassIfR0IsOneOf(@NonNull Set<Long> values, 730 ApfCounterTracker.Counter cnt) throws IllegalInstructionException { 731 if (values.isEmpty()) { 732 throw new IllegalArgumentException("values cannot be empty"); 733 } 734 if (values.size() == 1) { 735 return addCountAndPassIfR0Equals(values.iterator().next(), cnt); 736 } 737 final String tgt = getUniqueLabel(); 738 return addJumpIfNoneOf(R0, values, tgt).addCountAndPass(cnt).defineLabel(tgt); 739 } 740 741 @Override addCountAndDropIfR0IsOneOf(@onNull Set<Long> values, ApfCounterTracker.Counter cnt)742 public Type addCountAndDropIfR0IsOneOf(@NonNull Set<Long> values, 743 ApfCounterTracker.Counter cnt) throws IllegalInstructionException { 744 if (values.isEmpty()) { 745 throw new IllegalArgumentException("values cannot be empty"); 746 } 747 if (values.size() == 1) { 748 return addCountAndDropIfR0Equals(values.iterator().next(), cnt); 749 } 750 final String tgt = getUniqueLabel(); 751 return addJumpIfNoneOf(R0, values, tgt).addCountAndDrop(cnt).defineLabel(tgt); 752 } 753 754 @Override addCountAndPassIfR0IsNoneOf(@onNull Set<Long> values, ApfCounterTracker.Counter cnt)755 public Type addCountAndPassIfR0IsNoneOf(@NonNull Set<Long> values, 756 ApfCounterTracker.Counter cnt) throws IllegalInstructionException { 757 if (values.isEmpty()) { 758 throw new IllegalArgumentException("values cannot be empty"); 759 } 760 if (values.size() == 1) { 761 return addCountAndPassIfR0NotEquals(values.iterator().next(), cnt); 762 } 763 final String tgt = getUniqueLabel(); 764 return addJumpIfOneOf(R0, values, tgt).addCountAndPass(cnt).defineLabel(tgt); 765 } 766 767 @Override addCountAndDropIfBytesAtR0EqualsAnyOf(@onNull List<byte[]> bytesList, ApfCounterTracker.Counter cnt)768 public Type addCountAndDropIfBytesAtR0EqualsAnyOf(@NonNull List<byte[]> bytesList, 769 ApfCounterTracker.Counter cnt) 770 throws IllegalInstructionException { 771 final String tgt = getUniqueLabel(); 772 return addJumpIfBytesAtR0EqualNoneOf(bytesList, tgt).addCountAndDrop(cnt).defineLabel(tgt); 773 } 774 775 @Override addCountAndPassIfBytesAtR0EqualsAnyOf(@onNull List<byte[]> bytesList, ApfCounterTracker.Counter cnt)776 public Type addCountAndPassIfBytesAtR0EqualsAnyOf(@NonNull List<byte[]> bytesList, 777 ApfCounterTracker.Counter cnt) 778 throws IllegalInstructionException { 779 final String tgt = getUniqueLabel(); 780 return addJumpIfBytesAtR0EqualNoneOf(bytesList, tgt).addCountAndPass(cnt).defineLabel(tgt); 781 } 782 783 @Override addCountAndDropIfBytesAtR0EqualsNoneOf(@onNull List<byte[]> bytesList, ApfCounterTracker.Counter cnt)784 public Type addCountAndDropIfBytesAtR0EqualsNoneOf(@NonNull List<byte[]> bytesList, 785 ApfCounterTracker.Counter cnt) 786 throws IllegalInstructionException { 787 final String tgt = getUniqueLabel(); 788 return addJumpIfBytesAtR0EqualsAnyOf(bytesList, tgt).addCountAndDrop(cnt).defineLabel(tgt); 789 } 790 791 @Override addCountAndPassIfBytesAtR0EqualsNoneOf(@onNull List<byte[]> bytesList, ApfCounterTracker.Counter cnt)792 public Type addCountAndPassIfBytesAtR0EqualsNoneOf(@NonNull List<byte[]> bytesList, 793 ApfCounterTracker.Counter cnt) 794 throws IllegalInstructionException { 795 final String tgt = getUniqueLabel(); 796 return addJumpIfBytesAtR0EqualsAnyOf(bytesList, tgt).addCountAndPass(cnt).defineLabel(tgt); 797 } 798 799 @Override addCountAndDropIfR0IsNoneOf(@onNull Set<Long> values, ApfCounterTracker.Counter cnt)800 public Type addCountAndDropIfR0IsNoneOf(@NonNull Set<Long> values, 801 ApfCounterTracker.Counter cnt) throws IllegalInstructionException { 802 if (values.isEmpty()) { 803 throw new IllegalArgumentException("values cannot be empty"); 804 } 805 if (values.size() == 1) { 806 return addCountAndDropIfR0NotEquals(values.iterator().next(), cnt); 807 } 808 final String tgt = getUniqueLabel(); 809 return addJumpIfOneOf(R0, values, tgt).addCountAndDrop(cnt).defineLabel(tgt); 810 } 811 812 @Override addLoadCounter(Register register, ApfCounterTracker.Counter counter)813 public final Type addLoadCounter(Register register, ApfCounterTracker.Counter counter) 814 throws IllegalInstructionException { 815 return append(new Instruction(Opcodes.LDDW, register).addUnsigned(counter.value())); 816 } 817 818 @Override addStoreCounter(ApfCounterTracker.Counter counter, Register register)819 public final Type addStoreCounter(ApfCounterTracker.Counter counter, Register register) 820 throws IllegalInstructionException { 821 return append(new Instruction(Opcodes.STDW, register).addUnsigned(counter.value())); 822 } 823 824 /** 825 * This method is noop in APFv6. 826 */ 827 @Override addCountTrampoline()828 public final Type addCountTrampoline() { 829 return self(); 830 } 831 } 832