/*
* This file is part of FalsePatternLib.
*
* Copyright (C) 2022-2025 FalsePattern
* All Rights Reserved
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* FalsePatternLib is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, only version 3 of the License.
*
* FalsePatternLib is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with FalsePatternLib. If not, see
* The constant_pool table is indexed from 1 to constant_pool_count - 1. */ @Expose public static final int constantPoolStart = constantPoolCountU16 + 2; /** * The value of the access_flags item is a mask of flags used to denote access permissions to and properties of this class or interface. The interpretation of each flag, when set, is specified in Table 4.1-B. */ @Expose public static final int pastCpAccessFlagsU16 = 0; /** * The value of the this_class item must be a valid index into the constant_pool table. The constant_pool entry at that index must be a CONSTANT_Class_info structure (§4.4.1) representing the class or interface defined by this class file. */ @Expose public static final int pastCpThisClassU16 = pastCpAccessFlagsU16 + 2; /** * For a class, the value of the super_class item either must be zero or must be a valid index into the constant_pool table. If the value of the super_class item is nonzero, the constant_pool entry at that index must be a CONSTANT_Class_info structure representing the direct superclass of the class defined by this class file. Neither the direct superclass nor any of its superclasses may have the ACC_FINAL flag set in the access_flags item of its ClassFile structure.
* If the value of the super_class item is zero, then this class file must represent the class Object, the only class or interface without a direct superclass.
* For an interface, the value of the super_class item must always be a valid index into the constant_pool table. The constant_pool entry at that index must be a CONSTANT_Class_info structure representing the class Object. */ @Expose public static final int pastCpSuperClassU16 = pastCpThisClassU16 + 2; /** The value of the interfaces_count item gives the number of direct superinterfaces of this class or interface type */ @Expose public static final int pastCpInterfacesCountU16 = pastCpSuperClassU16 + 2; } @StableAPI(since = "__EXPERIMENTAL__") public enum ConstantPoolEntryTypes { @Expose Utf8(1, 45, -1), @Expose Integer(3, 45, 4), @Expose Float(4, 45, 4), @Expose Long(5, 45, 8), @Expose Double(6, 45, 8), @Expose Class(7, 49, 2), @Expose String(8, 45, 2), @Expose FieldRef(9, 45, 4), @Expose MethodRef(10, 45, 4), @Expose InterfaceMethodRef(11, 45, 4), @Expose NameAndType(12, 45, 4), @Expose MethodHandle(15, 51, 3), @Expose MethodType(16, 51, 2), @Expose Dynamic(17, 55, 4), @Expose InvokeDynamic(18, 51, 4), @Expose Module(19, 53, 2), @Expose Package(20, 53, 2), ; private static final ConstantPoolEntryTypes[] lookup; static { final ConstantPoolEntryTypes[] values = values(); final int maxTag = values[values.length - 1].tag; final ConstantPoolEntryTypes[] lut = new ConstantPoolEntryTypes[maxTag + 1]; for (ConstantPoolEntryTypes entry : values) { lut[entry.tag] = entry; } lookup = lut; } @Expose public final int tag, minMajorVersion; /** Length in bytes of the entry (excluding the 1 byte tag), or -1 if length-encoded */ @Expose public final int maybeByteLength; ConstantPoolEntryTypes(int tag, int minMajorVersion, int maybeByteLength) { this.tag = tag; this.minMajorVersion = minMajorVersion; this.maybeByteLength = maybeByteLength; } /** * @param classFile Class bytes to scan for variable length entries * @param offset The offset where the entry starts * @return The total length of the entry, including the tag byte */ @Expose public int byteLength(final byte @NotNull [] classFile, final int offset) { if (this == ConstantPoolEntryTypes.Utf8) { return 3 + u16(classFile, offset + 1); } if (this.maybeByteLength < 0) { throw new UnsupportedOperationException("Missing byte length implementation for tag " + this); } return 1 + maybeByteLength; } @Expose public static @NotNull ConstantPoolEntryTypes parse(final byte @NotNull [] classFile, final int offset) { final int tag = u8(classFile, offset); final ConstantPoolEntryTypes type = tag >= lookup.length ? null : lookup[tag]; if (type == null) { throw new UnsupportedOperationException("Unknown constant pool tag " + tag); } return type; } } /** * Sanity-checks the validity of the class header. * @param classBytes Class data * @return If the class passes simple sanity checks. */ @Contract("null -> false") @Expose public static boolean isValidClass(byte @Nullable [] classBytes) { if (classBytes == null) { return false; } if (classBytes.length < Offsets.constantPoolStart + Offsets.pastCpSuperClassU16) { return false; } final int magic = (u8(classBytes, 0) << 24) | (u8(classBytes, 1) << 16) | (u8(classBytes, 2) << 8) | (u8(classBytes, 3)); return magic == 0xCAFEBABE; } /** * @param classBytes Class data * @return The major version number of the class file. See {@link org.objectweb.asm.Opcodes#V1_8} */ @Expose public static int majorVersion(byte @NotNull [] classBytes) { return u16(classBytes, Offsets.majorVersionU16); } /** * Searches for a sub"string" (byte array) in a longer byte array. Not efficient for long search strings. * @param classBytes The long byte string to search in. * @param substring The short substring to search for. * @return If the substring was found somewhere in the long string. */ @Expose public static boolean hasSubstring(final byte @Nullable [] classBytes, final byte @NotNull [] substring) { if (classBytes == null) { return false; } final int classLen = classBytes.length; final int subLen = substring.length; if (classLen < subLen) { return false; } outer: for (int startPos = 0; startPos + subLen - 1 < classLen; startPos++) { for (int i = 0; i < subLen; i++) { if (classBytes[startPos + i] != substring[i]) { continue outer; } } return true; } return false; } @Override public boolean isPublic() { return (accessFlags & Opcodes.ACC_PUBLIC) != 0; } @Override public boolean isFinal() { return (accessFlags & Opcodes.ACC_FINAL) != 0; } @Override public boolean isInterface() { return (accessFlags & Opcodes.ACC_INTERFACE) != 0; } @Override public boolean isAbstract() { return (accessFlags & Opcodes.ACC_ABSTRACT) != 0; } @Override public boolean isSynthetic() { return (accessFlags & Opcodes.ACC_SYNTHETIC) != 0; } @Override public boolean isAnnotation() { return (accessFlags & Opcodes.ACC_ANNOTATION) != 0; } @Override public boolean isEnum() { return (accessFlags & Opcodes.ACC_ENUM) != 0; } @Override public @NotNull String binaryThisName() { return binaryThisName; } @Override public @Nullable String binarySuperName() { return binarySuperName; } }