Open-source multiplayer game server compatible with the RuneScape client
https://www.openrs2.org/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1654 lines
60 KiB
1654 lines
60 KiB
package org.openrs2.asm.packclass
|
|
|
|
import io.netty.buffer.ByteBuf
|
|
import io.netty.buffer.ByteBufAllocator
|
|
import org.objectweb.asm.Opcodes
|
|
import org.objectweb.asm.Type
|
|
import org.objectweb.asm.tree.AbstractInsnNode
|
|
import org.objectweb.asm.tree.ClassNode
|
|
import org.objectweb.asm.tree.FieldInsnNode
|
|
import org.objectweb.asm.tree.FieldNode
|
|
import org.objectweb.asm.tree.IincInsnNode
|
|
import org.objectweb.asm.tree.InsnNode
|
|
import org.objectweb.asm.tree.IntInsnNode
|
|
import org.objectweb.asm.tree.JumpInsnNode
|
|
import org.objectweb.asm.tree.LabelNode
|
|
import org.objectweb.asm.tree.LdcInsnNode
|
|
import org.objectweb.asm.tree.LineNumberNode
|
|
import org.objectweb.asm.tree.LookupSwitchInsnNode
|
|
import org.objectweb.asm.tree.MethodInsnNode
|
|
import org.objectweb.asm.tree.MethodNode
|
|
import org.objectweb.asm.tree.MultiANewArrayInsnNode
|
|
import org.objectweb.asm.tree.TableSwitchInsnNode
|
|
import org.objectweb.asm.tree.TryCatchBlockNode
|
|
import org.objectweb.asm.tree.TypeInsnNode
|
|
import org.objectweb.asm.tree.VarInsnNode
|
|
import org.openrs2.asm.nextReal
|
|
import org.openrs2.buffer.readUnsignedShortSmart
|
|
import org.openrs2.buffer.readVarInt
|
|
import org.openrs2.buffer.writeUnsignedShortSmart
|
|
import org.openrs2.buffer.writeVarInt
|
|
|
|
public object PackClass {
|
|
public const val CLASS_GROUP: Int = 0
|
|
public const val CONSTANT_POOL_GROUP: Int = 1
|
|
public const val CONSTANT_POOL_FILE: Int = 0
|
|
|
|
private const val TRAILER_LEN = 6
|
|
|
|
private val SUPPORTED_VERSIONS = mutableSetOf(
|
|
Opcodes.V1_1,
|
|
Opcodes.V1_2,
|
|
Opcodes.V1_3,
|
|
Opcodes.V1_4,
|
|
Opcodes.V1_5,
|
|
Opcodes.V1_6,
|
|
)
|
|
|
|
private const val INT_DESCRIPTOR = "I"
|
|
private const val LONG_DESCRIPTOR = "J"
|
|
private const val FLOAT_DESCRIPTOR = "F"
|
|
private const val DOUBLE_DESCRIPTOR = "D"
|
|
private const val STRING_DESCRIPTOR = "Ljava/lang/String;"
|
|
|
|
// opcodes
|
|
private const val WIDE = 0xC4
|
|
private const val LDC_INT = Opcodes.LDC
|
|
private const val LDC_FLOAT = 0x13
|
|
private const val LDC_LONG = 0x14
|
|
private const val LDC_STRING = Opcodes.INVOKEDYNAMIC
|
|
private const val LDC_DOUBLE = 0xCA
|
|
private const val LDC_CLASS = 0xCB
|
|
private const val ILOAD_0 = 0x1A
|
|
private const val ILOAD_3 = 0x1D
|
|
private const val LLOAD_0 = 0x1E
|
|
private const val LLOAD_3 = 0x21
|
|
private const val FLOAD_0 = 0x22
|
|
private const val FLOAD_3 = 0x25
|
|
private const val DLOAD_0 = 0x26
|
|
private const val DLOAD_3 = 0x29
|
|
private const val ALOAD_0 = 0x2A
|
|
private const val ALOAD_3 = 0x2D
|
|
private const val ISTORE_0 = 0x3B
|
|
private const val ISTORE_3 = 0x3E
|
|
private const val LSTORE_0 = 0x3F
|
|
private const val LSTORE_3 = 0x42
|
|
private const val FSTORE_0 = 0x43
|
|
private const val FSTORE_3 = 0x46
|
|
private const val DSTORE_0 = 0x47
|
|
private const val DSTORE_3 = 0x4A
|
|
private const val ASTORE_0 = 0x4B
|
|
private const val ASTORE_3 = 0x4E
|
|
private const val GOTO_W = 0xC8
|
|
private const val JSR_W = 0xC9
|
|
private const val EOF = 0xCC
|
|
|
|
// opcode flags
|
|
private const val OPCODE_INVALID = 0x1
|
|
private const val OPCODE_LOCAL_VAR = 0x2
|
|
private const val OPCODE_CONSTANT = 0x4
|
|
private const val OPCODE_WIDE_CONSTANT = 0x10
|
|
private const val OPCODE_CLASS = 0x20
|
|
private const val OPCODE_FIELD_REF = 0x40
|
|
private const val OPCODE_METHOD_REF = 0x80
|
|
private const val OPCODE_BRANCH = 0x100
|
|
private const val OPCODE_WIDE_BRANCH = 0x200
|
|
private const val OPCODE_WIDE_LOCAL_VAR = 0x400
|
|
|
|
private val OPCODE_FLAGS = intArrayOf(
|
|
0, // NOP
|
|
0, // ACONST_NULL
|
|
0, // ICONST_M1
|
|
0, // ICONST_0
|
|
0, // ICONST_1
|
|
0, // ICONST_2
|
|
0, // ICONST_3
|
|
0, // ICONST_4
|
|
0, // ICONST_5
|
|
0, // LCONST_0
|
|
0, // LCONST_1
|
|
0, // FCONST_0
|
|
0, // FCONST_1
|
|
0, // FCONST_2
|
|
0, // DCONST_0
|
|
0, // DCONST_1
|
|
0, // BIPUSH
|
|
0, // SIPUSH
|
|
OPCODE_CONSTANT, // LDC (used as LDC_INTEGER)
|
|
OPCODE_CONSTANT, // LDC_W (used as LDC_FLOAT)
|
|
OPCODE_WIDE_CONSTANT, // LDC2_W (used as LDC_STRING)
|
|
OPCODE_LOCAL_VAR or OPCODE_WIDE_LOCAL_VAR, // ILOAD
|
|
OPCODE_LOCAL_VAR or OPCODE_WIDE_LOCAL_VAR, // LLOAD
|
|
OPCODE_LOCAL_VAR or OPCODE_WIDE_LOCAL_VAR, // FLOAD
|
|
OPCODE_LOCAL_VAR or OPCODE_WIDE_LOCAL_VAR, // DLOAD
|
|
OPCODE_LOCAL_VAR or OPCODE_WIDE_LOCAL_VAR, // ALOAD
|
|
0, // ILOAD_0
|
|
0, // ILOAD_1
|
|
0, // ILOAD_2
|
|
0, // ILOAD_3
|
|
0, // LLOAD_0
|
|
0, // LLOAD_1
|
|
0, // LLOAD_2
|
|
0, // LLOAD_3
|
|
0, // FLOAD_0
|
|
0, // FLOAD_1
|
|
0, // FLOAD_2
|
|
0, // FLOAD_3
|
|
0, // DLOAD_0
|
|
0, // DLOAD_1
|
|
0, // DLOAD_2
|
|
0, // DLOAD_3
|
|
0, // ALOAD_0
|
|
0, // ALOAD_1
|
|
0, // ALOAD_2
|
|
0, // ALOAD_3
|
|
0, // IALOAD
|
|
0, // LALOAD
|
|
0, // FALOAD
|
|
0, // DALOAD
|
|
0, // AALOAD
|
|
0, // BALOAD
|
|
0, // CALOAD
|
|
0, // SALOAD
|
|
OPCODE_LOCAL_VAR or OPCODE_WIDE_LOCAL_VAR, // ISTORE
|
|
OPCODE_LOCAL_VAR or OPCODE_WIDE_LOCAL_VAR, // LSTORE
|
|
OPCODE_LOCAL_VAR or OPCODE_WIDE_LOCAL_VAR, // FSTORE
|
|
OPCODE_LOCAL_VAR or OPCODE_WIDE_LOCAL_VAR, // DSTORE
|
|
OPCODE_LOCAL_VAR or OPCODE_WIDE_LOCAL_VAR, // ASTORE
|
|
0, // ISTORE_0
|
|
0, // ISTORE_1
|
|
0, // ISTORE_2
|
|
0, // ISTORE_3
|
|
0, // LSTORE_0
|
|
0, // LSTORE_1
|
|
0, // LSTORE_2
|
|
0, // LSTORE_3
|
|
0, // FSTORE_0
|
|
0, // FSTORE_1
|
|
0, // FSTORE_2
|
|
0, // FSTORE_3
|
|
0, // DSTORE_0
|
|
0, // DSTORE_1
|
|
0, // DSTORE_2
|
|
0, // DSTORE_3
|
|
0, // ASTORE_0
|
|
0, // ASTORE_1
|
|
0, // ASTORE_2
|
|
0, // ASTORE_3
|
|
0, // IASTORE
|
|
0, // LASTORE
|
|
0, // FASTORE
|
|
0, // DASTORE
|
|
0, // AASTORE
|
|
0, // BASTORE
|
|
0, // CASTORE
|
|
0, // SASTORE
|
|
0, // POP
|
|
0, // POP2
|
|
0, // DUP
|
|
0, // DUP_X1
|
|
0, // DUP_X2
|
|
0, // DUP2
|
|
0, // DUP2_X1
|
|
0, // DUP2_X2
|
|
0, // SWAP
|
|
0, // IADD
|
|
0, // LADD
|
|
0, // FADD
|
|
0, // DADD
|
|
0, // ISUB
|
|
0, // LSUB
|
|
0, // FSUB
|
|
0, // DSUB
|
|
0, // IMUL
|
|
0, // LMUL
|
|
0, // FMUL
|
|
0, // DMUL
|
|
0, // IDIV
|
|
0, // LDIV
|
|
0, // FDIV
|
|
0, // DDIV
|
|
0, // IREM
|
|
0, // LREM
|
|
0, // FREM
|
|
0, // DREM
|
|
0, // INEG
|
|
0, // LNEG
|
|
0, // FNEG
|
|
0, // DNEG
|
|
0, // ISHL
|
|
0, // LSHL
|
|
0, // ISHR
|
|
0, // LSHR
|
|
0, // IUSHR
|
|
0, // LUSHR
|
|
0, // IAND
|
|
0, // LAND
|
|
0, // IOR
|
|
0, // LOR
|
|
0, // IXOR
|
|
0, // LXOR
|
|
OPCODE_LOCAL_VAR or OPCODE_WIDE_LOCAL_VAR, // IINC
|
|
0, // I2L
|
|
0, // I2F
|
|
0, // I2D
|
|
0, // L2I
|
|
0, // L2F
|
|
0, // L2D
|
|
0, // F2I
|
|
0, // F2L
|
|
0, // F2D
|
|
0, // D2I
|
|
0, // D2L
|
|
0, // D2F
|
|
0, // I2B
|
|
0, // I2C
|
|
0, // I2S
|
|
0, // LCMP
|
|
0, // FCMPL
|
|
0, // FCMPG
|
|
0, // DCMPL
|
|
0, // DCMPG
|
|
OPCODE_BRANCH, // IFEQ
|
|
OPCODE_BRANCH, // IFNE
|
|
OPCODE_BRANCH, // IFLT
|
|
OPCODE_BRANCH, // IFGE
|
|
OPCODE_BRANCH, // IFGT
|
|
OPCODE_BRANCH, // IFLE
|
|
OPCODE_BRANCH, // IF_ICMPEQ
|
|
OPCODE_BRANCH, // IF_ICMPNE
|
|
OPCODE_BRANCH, // IF_ICMPLT
|
|
OPCODE_BRANCH, // IF_ICMPGE
|
|
OPCODE_BRANCH, // IF_ICMPGT
|
|
OPCODE_BRANCH, // IF_ICMPLE
|
|
OPCODE_BRANCH, // IF_ACMPEQ
|
|
OPCODE_BRANCH, // IF_ACMPNE
|
|
OPCODE_BRANCH, // GOTO
|
|
OPCODE_BRANCH, // JSR
|
|
OPCODE_LOCAL_VAR or OPCODE_WIDE_LOCAL_VAR, // RET
|
|
0, // TABLESWITCH
|
|
0, // LOOKUPSWITCH
|
|
0, // IRETURN
|
|
0, // LRETURN
|
|
0, // FRETURN
|
|
0, // DRETURN
|
|
0, // ARETURN
|
|
0, // RETURN
|
|
OPCODE_FIELD_REF, // GETSTATIC
|
|
OPCODE_FIELD_REF, // PUTSTATIC
|
|
OPCODE_FIELD_REF, // GETFIELD
|
|
OPCODE_FIELD_REF, // PUTFIELD
|
|
OPCODE_METHOD_REF, // INVOKEVIRTUAL
|
|
OPCODE_METHOD_REF, // INVOKESPECIAL
|
|
OPCODE_METHOD_REF, // INVOKESTATIC
|
|
0, // INVOKEINTERFACE
|
|
OPCODE_CONSTANT, // INVOKEDYNAMIC (used as LDC_STRING)
|
|
OPCODE_CLASS, // NEW
|
|
0, // NEWARRAY
|
|
OPCODE_CLASS, // ANEWARRAY
|
|
0, // ARRAYLENGTH
|
|
0, // ATHROW
|
|
OPCODE_CLASS, // CHECKCAST
|
|
OPCODE_CLASS, // INSTANCEOF
|
|
0, // MONITORENTER
|
|
0, // MONITOREXIT
|
|
0, // WIDE
|
|
OPCODE_CLASS, // MULTIANEWARRAY
|
|
OPCODE_BRANCH, // IFNULL
|
|
OPCODE_BRANCH, // IFNONNULL
|
|
OPCODE_WIDE_BRANCH, // GOTO_W
|
|
OPCODE_WIDE_BRANCH, // JSR_W
|
|
OPCODE_WIDE_CONSTANT, // BREAKPOINT (used as LDC_DOUBLE)
|
|
OPCODE_CONSTANT, // (unused, used as LDC_CLASS)
|
|
OPCODE_INVALID, // (unused, used as EOF)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // (unused)
|
|
OPCODE_INVALID, // IMPDEP1
|
|
OPCODE_INVALID // IMPDEP2
|
|
)
|
|
|
|
public fun read(buf: ByteBuf, constantPool: ConstantPool): ClassNode {
|
|
val clazz = ClassNode()
|
|
|
|
// read trailer
|
|
buf.markReaderIndex()
|
|
buf.readerIndex(buf.writerIndex() - TRAILER_LEN)
|
|
|
|
val interfaceCount = buf.readUnsignedShort()
|
|
val fieldCount = buf.readUnsignedShort()
|
|
val methodCount = buf.readUnsignedShort()
|
|
|
|
buf.resetReaderIndex()
|
|
|
|
// read attributes
|
|
val fieldAttributeCounts = readAttributeCounts(buf, fieldCount)
|
|
val methodAttributeCounts = readAttributeCounts(buf, methodCount)
|
|
|
|
val fieldAttributes = readAttributes(buf, constantPool, fieldAttributeCounts)
|
|
val methodAttributes = readAttributes(buf, constantPool, methodAttributeCounts)
|
|
|
|
// read method metadata
|
|
val exceptionCounts = readExceptionCounts(buf, methodAttributes)
|
|
val tryCatchCounts = readMaxs(buf, methodAttributes)
|
|
val maxStacks = readMaxs(buf, methodAttributes)
|
|
val maxLocals = readMaxs(buf, methodAttributes)
|
|
val lineStartPcs = readLinePcs(buf, methodAttributes)
|
|
val lineNumbers = readLineNumbers(buf, methodAttributes, lineStartPcs)
|
|
|
|
// read access flags
|
|
clazz.access = buf.readUnsignedByte().toInt() shl 8
|
|
|
|
val fieldAccess = readAccessFlagsMsb(buf, fieldCount)
|
|
val methodAccess = readAccessFlagsMsb(buf, methodCount)
|
|
|
|
clazz.access = clazz.access or buf.readUnsignedByte().toInt()
|
|
|
|
readAccessFlagsLsb(buf, fieldAccess)
|
|
readAccessFlagsLsb(buf, methodAccess)
|
|
|
|
// read source file
|
|
clazz.sourceFile = constantPool.readOptionalString(buf)
|
|
|
|
// read exception types
|
|
val exceptions = readExceptions(buf, constantPool, methodAttributes, exceptionCounts)
|
|
val tryCatchTypes = readTryCatchTypes(buf, constantPool, methodAttributes, tryCatchCounts)
|
|
|
|
// read class and superclass name
|
|
clazz.name = constantPool.readString(buf)
|
|
clazz.superName = constantPool.readString(buf)
|
|
|
|
// read interface names
|
|
readInterfaces(buf, constantPool, interfaceCount, clazz)
|
|
|
|
// read field and method names and types
|
|
val fieldNamesAndTypes = readFieldNamesAndTypes(buf, constantPool, fieldCount)
|
|
val methodNamesAndTypes = readMethodNamesAndTypes(buf, constantPool, methodCount)
|
|
|
|
// read version
|
|
val minor = buf.readUnsignedShort()
|
|
val major = buf.readUnsignedShort()
|
|
clazz.version = (minor shl 16) or major
|
|
|
|
require(clazz.version in SUPPORTED_VERSIONS) {
|
|
"Unsupported class version $major.$minor"
|
|
}
|
|
|
|
// read constant field values
|
|
val fieldConstants = readConstants(buf, constantPool, fieldAttributes, fieldNamesAndTypes)
|
|
|
|
// read try/catch blocks
|
|
val tryCatchStartPcs = readTryCatchStartPcs(buf, methodAttributes, tryCatchCounts)
|
|
val tryCatchEndPcsToHandlerPcs = readTryCatchEndPcsToHandlerPcs(buf, methodAttributes, tryCatchCounts)
|
|
val tryCatchReverseHandlerPcs = readTryCatchReverseHandlerPcs(buf, methodAttributes, tryCatchCounts)
|
|
|
|
// calculate operand buffer sizes
|
|
buf.markReaderIndex()
|
|
|
|
var newArrayLen = 0
|
|
var localVarLen = 0
|
|
var wideLocalVarLen = 0
|
|
var sipushAndSwitchLen = 0
|
|
var constantLen = 0
|
|
var wideConstantLen = 0
|
|
var classLen = 0
|
|
var fieldRefLen = 0
|
|
var methodRefLen = 0
|
|
var interfaceMethodRefLen = 0
|
|
var branchLen = 0
|
|
var bipushLen = 0
|
|
var wideIincLen = 0
|
|
var iincLen = 0
|
|
var multiNewArrayLen = 0
|
|
|
|
val methodOpcodes = IntArray(methodAttributes.size) { i ->
|
|
if (methodAttributes[i].contains(ConstantPool.CODE)) {
|
|
var pc = 0
|
|
|
|
while (true) {
|
|
val opcode = buf.readUnsignedByte().toInt()
|
|
if (opcode == EOF) {
|
|
break
|
|
}
|
|
|
|
val flags = OPCODE_FLAGS[opcode]
|
|
if ((flags and OPCODE_INVALID) != 0) {
|
|
throw IllegalArgumentException("Invalid opcode: $opcode")
|
|
}
|
|
|
|
if ((flags and OPCODE_LOCAL_VAR) != 0) {
|
|
localVarLen++
|
|
}
|
|
|
|
if ((flags and OPCODE_CONSTANT) != 0) {
|
|
constantLen += 2
|
|
}
|
|
|
|
if ((flags and OPCODE_WIDE_CONSTANT) != 0) {
|
|
wideConstantLen += 2
|
|
}
|
|
|
|
if ((flags and OPCODE_CLASS) != 0) {
|
|
classLen += 2
|
|
}
|
|
|
|
if ((flags and OPCODE_FIELD_REF) != 0) {
|
|
fieldRefLen += 2
|
|
}
|
|
|
|
if ((flags and OPCODE_METHOD_REF) != 0) {
|
|
methodRefLen += 2
|
|
}
|
|
|
|
if ((flags and OPCODE_BRANCH) != 0) {
|
|
branchLen += 2
|
|
}
|
|
|
|
if ((flags and OPCODE_WIDE_BRANCH) != 0) {
|
|
branchLen += 4
|
|
}
|
|
|
|
when (opcode) {
|
|
Opcodes.BIPUSH -> bipushLen++
|
|
Opcodes.SIPUSH -> sipushAndSwitchLen += 2
|
|
Opcodes.IINC -> iincLen++
|
|
Opcodes.TABLESWITCH -> {
|
|
val cases = buf.readVarInt()
|
|
branchLen += (cases + 2) * 4
|
|
sipushAndSwitchLen += 4
|
|
}
|
|
Opcodes.LOOKUPSWITCH -> {
|
|
val cases = buf.readVarInt()
|
|
branchLen += (cases + 1) * 4
|
|
sipushAndSwitchLen += cases * 4
|
|
}
|
|
Opcodes.INVOKEINTERFACE -> interfaceMethodRefLen += 2
|
|
Opcodes.NEWARRAY -> newArrayLen++
|
|
Opcodes.MULTIANEWARRAY -> multiNewArrayLen++
|
|
WIDE -> {
|
|
val wideOpcode = buf.readUnsignedByte().toInt()
|
|
val wideFlags = OPCODE_FLAGS[opcode]
|
|
|
|
if ((wideFlags and OPCODE_WIDE_LOCAL_VAR) != 0) {
|
|
throw IllegalArgumentException("Invalid wide opcode: $wideOpcode")
|
|
}
|
|
|
|
wideLocalVarLen += 2
|
|
|
|
if (wideOpcode == Opcodes.IINC) {
|
|
wideIincLen += 2
|
|
}
|
|
}
|
|
}
|
|
|
|
pc++
|
|
}
|
|
|
|
pc
|
|
} else {
|
|
0
|
|
}
|
|
}
|
|
|
|
// read operand buffers
|
|
val newArrayBuf = buf.readSlice(newArrayLen)
|
|
val localVarBuf = buf.readSlice(localVarLen)
|
|
val wideLocalVarBuf = buf.readSlice(wideLocalVarLen)
|
|
val sipushAndSwitchBuf = buf.readSlice(sipushAndSwitchLen)
|
|
val constantBuf = buf.readSlice(constantLen)
|
|
val wideConstantBuf = buf.readSlice(wideConstantLen)
|
|
val classBuf = buf.readSlice(classLen)
|
|
val fieldRefBuf = buf.readSlice(fieldRefLen)
|
|
val methodRefBuf = buf.readSlice(methodRefLen)
|
|
val interfaceMethodRefBuf = buf.readSlice(interfaceMethodRefLen)
|
|
val branchBuf = buf.readSlice(branchLen)
|
|
val bipushBuf = buf.readSlice(bipushLen)
|
|
val wideIincBuf = buf.readSlice(wideIincLen)
|
|
val iincBuf = buf.readSlice(iincLen)
|
|
val multiNewArrayBuf = buf.readSlice(multiNewArrayLen)
|
|
|
|
buf.resetReaderIndex()
|
|
|
|
// create fields
|
|
for (i in 0 until fieldCount) {
|
|
var access = fieldAccess[i]
|
|
|
|
for (attribute in fieldAttributes[i]) {
|
|
if (attribute == ConstantPool.SYNTHETIC) {
|
|
access = access or Opcodes.ACC_SYNTHETIC
|
|
} else {
|
|
throw IllegalArgumentException("Unsupported field attribute: $attribute")
|
|
}
|
|
}
|
|
|
|
val nameAndType = fieldNamesAndTypes[i]
|
|
val name = nameAndType.name
|
|
val descriptor = nameAndType.descriptor
|
|
|
|
val constant = fieldConstants[i]
|
|
|
|
clazz.fields.add(FieldNode(access, name, descriptor, null, constant))
|
|
}
|
|
|
|
// create methods
|
|
for (i in 0 until methodCount) {
|
|
var access = methodAccess[i]
|
|
var code = false
|
|
|
|
for (attribute in methodAttributes[i]) {
|
|
when (attribute) {
|
|
ConstantPool.SYNTHETIC -> access = access or Opcodes.ACC_SYNTHETIC
|
|
ConstantPool.CODE -> code = true
|
|
ConstantPool.EXCEPTIONS -> Unit
|
|
else -> throw IllegalArgumentException("Unsupported method attribute: $attribute")
|
|
}
|
|
}
|
|
|
|
val nameAndType = methodNamesAndTypes[i]
|
|
val name = nameAndType.name
|
|
val descriptor = nameAndType.descriptor
|
|
|
|
val method = MethodNode(access, name, descriptor, null, exceptions[i])
|
|
|
|
if (code) {
|
|
method.maxLocals = maxLocals[i]
|
|
method.maxStack = maxStacks[i]
|
|
|
|
val labels = Array(methodOpcodes[i]) {
|
|
val label = LabelNode()
|
|
method.instructions.add(label)
|
|
label
|
|
}
|
|
|
|
for (j in 0 until tryCatchCounts[i]) {
|
|
val startPc = tryCatchStartPcs[i]!![j]
|
|
val handlerPc = labels.size - tryCatchReverseHandlerPcs[i]!![j]
|
|
val endPc = handlerPc - tryCatchEndPcsToHandlerPcs[i]!![j]
|
|
|
|
val start = labels[startPc]
|
|
val end = labels[endPc]
|
|
val handler = labels[handlerPc]
|
|
|
|
val type = tryCatchTypes[i]!![j]
|
|
|
|
method.tryCatchBlocks.add(TryCatchBlockNode(start, end, handler, type))
|
|
}
|
|
|
|
val numbers = lineNumbers[i]
|
|
if (numbers != null) {
|
|
val startPcs = lineStartPcs[i]!!
|
|
|
|
for ((j, number) in numbers.withIndex()) {
|
|
val startPc = startPcs[j]
|
|
val label = labels[startPc]
|
|
method.instructions.insert(label, LineNumberNode(number, label))
|
|
}
|
|
}
|
|
|
|
var pc = 0
|
|
|
|
while (true) {
|
|
val opcode = buf.readUnsignedByte().toInt()
|
|
if (opcode == EOF) {
|
|
break
|
|
}
|
|
|
|
val flags = OPCODE_FLAGS[opcode]
|
|
if ((flags and OPCODE_INVALID) != 0) {
|
|
throw IllegalArgumentException("Invalid opcode: $opcode")
|
|
}
|
|
|
|
val insn = if ((flags and OPCODE_LOCAL_VAR) != 0 && opcode != Opcodes.IINC) {
|
|
VarInsnNode(opcode, localVarBuf.readUnsignedByte().toInt())
|
|
} else if ((flags and OPCODE_CONSTANT) != 0) {
|
|
val value: Any = when (opcode) {
|
|
LDC_INT -> constantPool.readInt(constantBuf)
|
|
LDC_FLOAT -> constantPool.readFloat(constantBuf)
|
|
LDC_STRING -> constantPool.readString(constantBuf)
|
|
LDC_CLASS -> Type.getObjectType(constantPool.readString(constantBuf))
|
|
else -> throw IllegalArgumentException("Invalid constant opcode: $opcode")
|
|
}
|
|
LdcInsnNode(value)
|
|
} else if ((flags and OPCODE_WIDE_CONSTANT) != 0) {
|
|
val value: Any = when (opcode) {
|
|
LDC_LONG -> constantPool.readLong(wideConstantBuf)
|
|
LDC_DOUBLE -> constantPool.readDouble(wideConstantBuf)
|
|
else -> IllegalArgumentException("Invalid wide constant opcode: $opcode")
|
|
}
|
|
LdcInsnNode(value)
|
|
} else if ((flags and OPCODE_CLASS) != 0 && opcode != Opcodes.MULTIANEWARRAY) {
|
|
TypeInsnNode(opcode, constantPool.readString(classBuf))
|
|
} else if ((flags and OPCODE_FIELD_REF) != 0) {
|
|
val fieldRef = constantPool.readFieldRef(fieldRefBuf)
|
|
FieldInsnNode(opcode, fieldRef.clazz, fieldRef.name, fieldRef.descriptor)
|
|
} else if ((flags and OPCODE_METHOD_REF) != 0) {
|
|
val methodRef = constantPool.readMethodRef(methodRefBuf)
|
|
MethodInsnNode(opcode, methodRef.clazz, methodRef.name, methodRef.descriptor)
|
|
} else if ((flags and OPCODE_BRANCH) != 0) {
|
|
val targetPc = pc + branchBuf.readShort().toInt()
|
|
JumpInsnNode(opcode, labels[targetPc])
|
|
} else if ((flags and OPCODE_WIDE_BRANCH) != 0) {
|
|
val wideOpcode = when (opcode) {
|
|
GOTO_W -> Opcodes.GOTO
|
|
JSR_W -> Opcodes.JSR
|
|
else -> throw IllegalArgumentException("Invalid wide branch opcode: $opcode")
|
|
}
|
|
val targetPc = pc + branchBuf.readInt()
|
|
JumpInsnNode(wideOpcode, labels[targetPc])
|
|
} else if (opcode == Opcodes.BIPUSH) {
|
|
IntInsnNode(opcode, bipushBuf.readByte().toInt())
|
|
} else if (opcode == Opcodes.SIPUSH) {
|
|
IntInsnNode(opcode, sipushAndSwitchBuf.readShort().toInt())
|
|
} else if (opcode == Opcodes.IINC) {
|
|
IincInsnNode(localVarBuf.readUnsignedByte().toInt(), iincBuf.readByte().toInt())
|
|
} else if (opcode == Opcodes.TABLESWITCH) {
|
|
val defaultPc = pc + branchBuf.readInt()
|
|
val defaultLabel = labels[defaultPc]
|
|
|
|
val min = sipushAndSwitchBuf.readInt()
|
|
val max = min + buf.readVarInt()
|
|
|
|
val targetLabels = Array(max - min + 1) {
|
|
val targetPc = pc + branchBuf.readInt()
|
|
labels[targetPc]
|
|
}
|
|
|
|
TableSwitchInsnNode(min, max, defaultLabel, *targetLabels)
|
|
} else if (opcode == Opcodes.LOOKUPSWITCH) {
|
|
val defaultPc = pc + branchBuf.readInt()
|
|
val defaultLabel = labels[defaultPc]
|
|
|
|
val cases = buf.readVarInt()
|
|
|
|
val keys = IntArray(cases)
|
|
var key = 0
|
|
val targetLabels = Array(cases) { j ->
|
|
key += sipushAndSwitchBuf.readInt()
|
|
keys[j] = key
|
|
|
|
val targetPc = pc + branchBuf.readInt()
|
|
labels[targetPc]
|
|
}
|
|
|
|
LookupSwitchInsnNode(defaultLabel, keys, targetLabels)
|
|
} else if (opcode == Opcodes.INVOKEINTERFACE) {
|
|
val methodRef = constantPool.readInterfaceMethodRef(interfaceMethodRefBuf)
|
|
MethodInsnNode(opcode, methodRef.clazz, methodRef.name, methodRef.descriptor)
|
|
} else if (opcode == Opcodes.NEWARRAY) {
|
|
IntInsnNode(opcode, newArrayBuf.readUnsignedByte().toInt())
|
|
} else if (opcode == Opcodes.MULTIANEWARRAY) {
|
|
val arrayDescriptor = constantPool.readString(classBuf)
|
|
val dimensions = multiNewArrayBuf.readUnsignedByte().toInt()
|
|
MultiANewArrayInsnNode(arrayDescriptor, dimensions)
|
|
} else if (opcode == WIDE) {
|
|
val wideOpcode = buf.readUnsignedByte().toInt()
|
|
val wideFlags = OPCODE_FLAGS[wideOpcode]
|
|
|
|
if ((wideFlags and OPCODE_WIDE_LOCAL_VAR) == 0) {
|
|
throw IllegalArgumentException("Invalid wide opcode: $wideOpcode")
|
|
}
|
|
|
|
val variable = wideLocalVarBuf.readUnsignedShort()
|
|
if (wideOpcode == Opcodes.IINC) {
|
|
IincInsnNode(variable, wideIincBuf.readShort().toInt())
|
|
} else {
|
|
VarInsnNode(wideOpcode, variable)
|
|
}
|
|
} else if (opcode in ILOAD_0..ILOAD_3) {
|
|
VarInsnNode(Opcodes.ILOAD, opcode - ILOAD_0)
|
|
} else if (opcode in LLOAD_0..LLOAD_3) {
|
|
VarInsnNode(Opcodes.LLOAD, opcode - LLOAD_0)
|
|
} else if (opcode in FLOAD_0..FLOAD_3) {
|
|
VarInsnNode(Opcodes.FLOAD, opcode - FLOAD_0)
|
|
} else if (opcode in DLOAD_0..DLOAD_3) {
|
|
VarInsnNode(Opcodes.DLOAD, opcode - DLOAD_0)
|
|
} else if (opcode in ALOAD_0..ALOAD_3) {
|
|
VarInsnNode(Opcodes.ALOAD, opcode - ALOAD_0)
|
|
} else if (opcode in ISTORE_0..ISTORE_3) {
|
|
VarInsnNode(Opcodes.ISTORE, opcode - ISTORE_0)
|
|
} else if (opcode in LSTORE_0..LSTORE_3) {
|
|
VarInsnNode(Opcodes.LSTORE, opcode - LSTORE_0)
|
|
} else if (opcode in FSTORE_0..FSTORE_3) {
|
|
VarInsnNode(Opcodes.FSTORE, opcode - FSTORE_0)
|
|
} else if (opcode in DSTORE_0..DSTORE_3) {
|
|
VarInsnNode(Opcodes.DSTORE, opcode - DSTORE_0)
|
|
} else if (opcode in ASTORE_0..ASTORE_3) {
|
|
VarInsnNode(Opcodes.ASTORE, opcode - ASTORE_0)
|
|
} else {
|
|
InsnNode(opcode)
|
|
}
|
|
|
|
method.instructions.insert(labels[pc++], insn)
|
|
}
|
|
}
|
|
|
|
clazz.methods.add(method)
|
|
}
|
|
|
|
buf.skipBytes(newArrayLen)
|
|
buf.skipBytes(localVarLen)
|
|
buf.skipBytes(wideLocalVarLen)
|
|
buf.skipBytes(sipushAndSwitchLen)
|
|
buf.skipBytes(constantLen)
|
|
buf.skipBytes(wideConstantLen)
|
|
buf.skipBytes(classLen)
|
|
buf.skipBytes(fieldRefLen)
|
|
buf.skipBytes(methodRefLen)
|
|
buf.skipBytes(interfaceMethodRefLen)
|
|
buf.skipBytes(branchLen)
|
|
buf.skipBytes(bipushLen)
|
|
buf.skipBytes(wideIincLen)
|
|
buf.skipBytes(iincLen)
|
|
buf.skipBytes(multiNewArrayLen)
|
|
buf.skipBytes(TRAILER_LEN)
|
|
|
|
return clazz
|
|
}
|
|
|
|
private fun readAttributeCounts(buf: ByteBuf, count: Int): IntArray {
|
|
return IntArray(count) {
|
|
buf.readUnsignedByte().toInt()
|
|
}
|
|
}
|
|
|
|
private fun readAttributes(buf: ByteBuf, constantPool: ConstantPool, counts: IntArray): Array<Array<String>> {
|
|
return Array(counts.size) { i ->
|
|
Array(counts[i]) {
|
|
constantPool.readString(buf)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun readExceptionCounts(buf: ByteBuf, attributes: Array<Array<String>>): IntArray {
|
|
return IntArray(attributes.size) { i ->
|
|
if (attributes[i].contains(ConstantPool.EXCEPTIONS)) {
|
|
buf.readUnsignedByte().toInt()
|
|
} else {
|
|
0
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun readMaxs(buf: ByteBuf, attributes: Array<Array<String>>): IntArray {
|
|
return IntArray(attributes.size) { i ->
|
|
if (attributes[i].contains(ConstantPool.CODE)) {
|
|
buf.readUnsignedByte().toInt()
|
|
} else {
|
|
0
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun readLinePcs(buf: ByteBuf, attributes: Array<Array<String>>): Array<IntArray?> {
|
|
return Array(attributes.size) { i ->
|
|
if (attributes[i].contains(ConstantPool.CODE)) {
|
|
buf.markReaderIndex()
|
|
|
|
var lines = 0
|
|
while (buf.readUnsignedShortSmart() != 0) {
|
|
lines++
|
|
}
|
|
|
|
buf.resetReaderIndex()
|
|
|
|
var pc = -1
|
|
val pcs = IntArray(lines) {
|
|
pc += buf.readUnsignedShortSmart()
|
|
pc
|
|
}
|
|
|
|
check(buf.readUnsignedShortSmart() == 0)
|
|
|
|
pcs
|
|
} else {
|
|
null
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun readLineNumbers(
|
|
buf: ByteBuf,
|
|
attributes: Array<Array<String>>,
|
|
pcs: Array<IntArray?>
|
|
): Array<IntArray?> {
|
|
require(attributes.size == pcs.size)
|
|
|
|
var line = 0
|
|
|
|
return Array(attributes.size) { i ->
|
|
if (attributes[i].contains(ConstantPool.CODE)) {
|
|
IntArray(pcs[i]!!.size) {
|
|
line += buf.readUnsignedShort()
|
|
line and 0xFFFF
|
|
}
|
|
} else {
|
|
null
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun readAccessFlagsMsb(buf: ByteBuf, count: Int): IntArray {
|
|
return IntArray(count) {
|
|
buf.readUnsignedByte().toInt() shl 8
|
|
}
|
|
}
|
|
|
|
private fun readAccessFlagsLsb(buf: ByteBuf, access: IntArray) {
|
|
for (i in access.indices) {
|
|
access[i] = access[i] or buf.readUnsignedByte().toInt()
|
|
}
|
|
}
|
|
|
|
private fun readExceptions(
|
|
buf: ByteBuf,
|
|
constantPool: ConstantPool,
|
|
attributes: Array<Array<String>>,
|
|
counts: IntArray
|
|
): Array<Array<String>?> {
|
|
require(attributes.size == counts.size)
|
|
|
|
return Array(attributes.size) { i ->
|
|
if (attributes[i].contains(ConstantPool.EXCEPTIONS)) {
|
|
Array(counts[i]) {
|
|
constantPool.readString(buf)
|
|
}
|
|
} else {
|
|
null
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun readTryCatchTypes(
|
|
buf: ByteBuf,
|
|
constantPool: ConstantPool,
|
|
attributes: Array<Array<String>>,
|
|
counts: IntArray
|
|
): Array<Array<String?>?> {
|
|
require(attributes.size == counts.size)
|
|
|
|
return Array(attributes.size) { i ->
|
|
if (attributes[i].contains(ConstantPool.CODE)) {
|
|
Array(counts[i]) {
|
|
constantPool.readOptionalString(buf)
|
|
}
|
|
} else {
|
|
null
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun readInterfaces(buf: ByteBuf, constantPool: ConstantPool, interfaceCount: Int, clazz: ClassNode) {
|
|
for (i in 0 until interfaceCount) {
|
|
clazz.interfaces.add(constantPool.readString(buf))
|
|
}
|
|
}
|
|
|
|
private fun readFieldNamesAndTypes(buf: ByteBuf, constantPool: ConstantPool, count: Int): Array<NameAndType> {
|
|
return Array(count) {
|
|
constantPool.readFieldNameAndType(buf)
|
|
}
|
|
}
|
|
|
|
private fun readMethodNamesAndTypes(buf: ByteBuf, constantPool: ConstantPool, count: Int): Array<NameAndType> {
|
|
return Array(count) {
|
|
constantPool.readMethodNameAndType(buf)
|
|
}
|
|
}
|
|
|
|
private fun readConstants(
|
|
buf: ByteBuf,
|
|
constantPool: ConstantPool,
|
|
attributes: Array<Array<String>>,
|
|
namesAndTypes: Array<NameAndType>
|
|
): Array<Any?> {
|
|
require(attributes.size == namesAndTypes.size)
|
|
|
|
return Array(attributes.size) { i ->
|
|
if (attributes[i].contains(ConstantPool.CONSTANT_VALUE)) {
|
|
when (val descriptor = namesAndTypes[i].descriptor) {
|
|
INT_DESCRIPTOR -> constantPool.readInt(buf)
|
|
LONG_DESCRIPTOR -> constantPool.readLong(buf)
|
|
FLOAT_DESCRIPTOR -> constantPool.readFloat(buf)
|
|
DOUBLE_DESCRIPTOR -> constantPool.readDouble(buf)
|
|
STRING_DESCRIPTOR -> constantPool.readString(buf)
|
|
else -> throw IllegalArgumentException("Unsupported constant descriptor $descriptor")
|
|
}
|
|
} else {
|
|
null
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun readTryCatchStartPcs(
|
|
buf: ByteBuf,
|
|
attributes: Array<Array<String>>,
|
|
counts: IntArray
|
|
): Array<IntArray?> {
|
|
require(attributes.size == counts.size)
|
|
|
|
return Array(attributes.size) { i ->
|
|
if (attributes[i].contains(ConstantPool.CODE)) {
|
|
var prevStartPc = 0
|
|
|
|
IntArray(counts[i]) {
|
|
var startPc = buf.readUnsignedShort()
|
|
|
|
if (startPc == 0) {
|
|
startPc = prevStartPc
|
|
} else if (startPc == prevStartPc) {
|
|
startPc = 0
|
|
}
|
|
|
|
prevStartPc = startPc
|
|
startPc
|
|
}
|
|
} else {
|
|
null
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun readTryCatchEndPcsToHandlerPcs(
|
|
buf: ByteBuf,
|
|
attributes: Array<Array<String>>,
|
|
counts: IntArray
|
|
): Array<IntArray?> {
|
|
require(attributes.size == counts.size)
|
|
|
|
return Array(attributes.size) { i ->
|
|
if (attributes[i].contains(ConstantPool.CODE)) {
|
|
IntArray(counts[i]) {
|
|
buf.readShort().toInt()
|
|
}
|
|
} else {
|
|
null
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun readTryCatchReverseHandlerPcs(
|
|
buf: ByteBuf,
|
|
attributes: Array<Array<String>>,
|
|
counts: IntArray
|
|
): Array<IntArray?> {
|
|
require(attributes.size == counts.size)
|
|
|
|
return Array(attributes.size) { i ->
|
|
if (attributes[i].contains(ConstantPool.CODE)) {
|
|
IntArray(counts[i]) {
|
|
buf.readUnsignedShort()
|
|
}
|
|
} else {
|
|
null
|
|
}
|
|
}
|
|
}
|
|
|
|
public fun write(buf: ByteBuf, constantPool: ConstantPool, clazz: ClassNode) {
|
|
require(clazz.version in SUPPORTED_VERSIONS) {
|
|
val major = clazz.version and 0xFFFF
|
|
val minor = (clazz.version shl 16) and 0xFFFF
|
|
"Unsupported class version $major.$minor"
|
|
}
|
|
|
|
val insns = createInstructions(clazz)
|
|
|
|
// write attributes
|
|
writeFieldAttributeCounts(buf, clazz)
|
|
writeMethodAttributeCounts(buf, clazz)
|
|
|
|
writeFieldAttributes(buf, constantPool, clazz)
|
|
writeMethodAttributes(buf, constantPool, clazz)
|
|
|
|
// write method metadata
|
|
writeExceptionCounts(buf, clazz)
|
|
writeTryCatchCounts(buf, clazz)
|
|
writeMaxStacks(buf, clazz)
|
|
writeMaxLocals(buf, clazz)
|
|
writeLinePcs(buf, clazz, insns)
|
|
writeLineNumbers(buf, clazz)
|
|
|
|
// write access flags
|
|
buf.writeByte(clazz.access shr 8)
|
|
|
|
writeFieldAccessFlagsMsb(buf, clazz)
|
|
writeMethodAccessFlagsMsb(buf, clazz)
|
|
|
|
buf.writeByte(clazz.access)
|
|
|
|
writeFieldAccessFlagsLsb(buf, clazz)
|
|
writeMethodAccessFlagsLsb(buf, clazz)
|
|
|
|
// write source file
|
|
constantPool.writeOptionalString(buf, clazz.sourceFile)
|
|
|
|
// write exception types
|
|
writeExceptions(buf, constantPool, clazz)
|
|
writeTryCatchTypes(buf, constantPool, clazz)
|
|
|
|
// write class and superclass name
|
|
constantPool.writeString(buf, clazz.name)
|
|
constantPool.writeString(buf, clazz.superName)
|
|
|
|
// write interface names
|
|
writeInterfaces(buf, constantPool, clazz)
|
|
|
|
// write field and method names and types
|
|
writeFieldNamesAndTypes(buf, constantPool, clazz)
|
|
writeMethodNamesAndTypes(buf, constantPool, clazz)
|
|
|
|
// write version
|
|
buf.writeShort(clazz.version shr 16)
|
|
buf.writeShort(clazz.version)
|
|
|
|
// write constant field values
|
|
writeConstants(buf, constantPool, clazz)
|
|
|
|
// write try/catch blocks
|
|
writeTryCatchStartPcs(buf, clazz, insns)
|
|
writeTryCatchEndPcsToHandlerPcs(buf, clazz, insns)
|
|
writeTryCatchReverseHandlerPcs(buf, clazz, insns)
|
|
|
|
// write operand buffers
|
|
val operandBuffers = OperandBuffers.alloc(buf.alloc())
|
|
try {
|
|
val newArrayBuf = operandBuffers.newArrayBuf
|
|
val localVarBuf = operandBuffers.localVarBuf
|
|
val wideLocalVarBuf = operandBuffers.wideLocalVarBuf
|
|
val sipushAndSwitchBuf = operandBuffers.sipushAndSwitchBuf
|
|
val constantBuf = operandBuffers.constantBuf
|
|
val wideConstantBuf = operandBuffers.wideConstantBuf
|
|
val classBuf = operandBuffers.classBuf
|
|
val fieldRefBuf = operandBuffers.fieldRefBuf
|
|
val methodRefBuf = operandBuffers.methodRefBuf
|
|
val interfaceMethodRefBuf = operandBuffers.interfaceMethodRefBuf
|
|
val branchBuf = operandBuffers.branchBuf
|
|
val bipushBuf = operandBuffers.bipushBuf
|
|
val wideIincBuf = operandBuffers.wideIincBuf
|
|
val iincBuf = operandBuffers.iincBuf
|
|
val multiNewArrayBuf = operandBuffers.multiNewArrayBuf
|
|
|
|
for ((i, list) in insns.withIndex()) {
|
|
if (list.isEmpty()) {
|
|
continue
|
|
}
|
|
|
|
for ((pc, insn) in list.withIndex()) {
|
|
when (insn) {
|
|
is VarInsnNode -> {
|
|
when {
|
|
insn.`var` in 0..3 -> {
|
|
when (insn.opcode) {
|
|
Opcodes.ILOAD -> buf.writeByte(ILOAD_0 + insn.`var`)
|
|
Opcodes.LLOAD -> buf.writeByte(LLOAD_0 + insn.`var`)
|
|
Opcodes.FLOAD -> buf.writeByte(FLOAD_0 + insn.`var`)
|
|
Opcodes.DLOAD -> buf.writeByte(DLOAD_0 + insn.`var`)
|
|
Opcodes.ALOAD -> buf.writeByte(ALOAD_0 + insn.`var`)
|
|
Opcodes.ISTORE -> buf.writeByte(ISTORE_0 + insn.`var`)
|
|
Opcodes.LSTORE -> buf.writeByte(LSTORE_0 + insn.`var`)
|
|
Opcodes.FSTORE -> buf.writeByte(FSTORE_0 + insn.`var`)
|
|
Opcodes.DSTORE -> buf.writeByte(DSTORE_0 + insn.`var`)
|
|
Opcodes.ASTORE -> buf.writeByte(ASTORE_0 + insn.`var`)
|
|
else -> throw IllegalArgumentException(
|
|
"Unsupported VarInsnNode opcode: ${insn.opcode}"
|
|
)
|
|
}
|
|
}
|
|
insn.`var` < 256 -> {
|
|
buf.writeByte(insn.opcode)
|
|
localVarBuf.writeByte(insn.`var`)
|
|
}
|
|
else -> {
|
|
buf.writeByte(WIDE)
|
|
buf.writeByte(insn.opcode)
|
|
wideLocalVarBuf.writeShort(insn.`var`)
|
|
}
|
|
}
|
|
}
|
|
is LdcInsnNode -> {
|
|
when (val value = insn.cst) {
|
|
is Int -> {
|
|
buf.writeByte(LDC_INT)
|
|
constantPool.writeInt(constantBuf, value)
|
|
}
|
|
is Long -> {
|
|
buf.writeByte(LDC_LONG)
|
|
constantPool.writeLong(wideConstantBuf, value)
|
|
}
|
|
is Float -> {
|
|
buf.writeByte(LDC_FLOAT)
|
|
constantPool.writeFloat(constantBuf, value)
|
|
}
|
|
is Double -> {
|
|
buf.writeByte(LDC_DOUBLE)
|
|
constantPool.writeDouble(wideConstantBuf, value)
|
|
}
|
|
is String -> {
|
|
buf.writeByte(LDC_STRING)
|
|
constantPool.writeString(constantBuf, value)
|
|
}
|
|
is Type -> {
|
|
if (value.sort == Type.OBJECT) {
|
|
buf.writeByte(LDC_CLASS)
|
|
constantPool.writeString(constantBuf, value.internalName)
|
|
} else {
|
|
throw IllegalArgumentException(
|
|
"Unsupported constant type: ${value.sort}"
|
|
)
|
|
}
|
|
}
|
|
else -> throw IllegalArgumentException(
|
|
"Unsupported constant type: ${value.javaClass.name}"
|
|
)
|
|
}
|
|
}
|
|
is TypeInsnNode -> {
|
|
buf.writeByte(insn.opcode)
|
|
constantPool.writeString(classBuf, insn.desc)
|
|
}
|
|
is FieldInsnNode -> {
|
|
buf.writeByte(insn.opcode)
|
|
constantPool.writeFieldRef(fieldRefBuf, MemberRef(insn.owner, insn.name, insn.desc))
|
|
}
|
|
is MethodInsnNode -> {
|
|
buf.writeByte(insn.opcode)
|
|
|
|
val methodRef = MemberRef(insn.owner, insn.name, insn.desc)
|
|
if (insn.itf) {
|
|
constantPool.writeInterfaceMethodRef(interfaceMethodRefBuf, methodRef)
|
|
} else {
|
|
constantPool.writeMethodRef(methodRefBuf, methodRef)
|
|
}
|
|
}
|
|
is JumpInsnNode -> {
|
|
val targetPc = insns[i].indexOf(insn.label.nextReal)
|
|
val delta = targetPc - pc
|
|
|
|
if (delta >= -32768 && delta <= 32767) {
|
|
buf.writeByte(insn.opcode)
|
|
branchBuf.writeShort(delta)
|
|
} else {
|
|
val wideOpcode = when (insn.opcode) {
|
|
Opcodes.GOTO -> GOTO_W
|
|
Opcodes.JSR -> JSR_W
|
|
else -> throw IllegalArgumentException("Jump too far for opcode: ${insn.opcode}")
|
|
}
|
|
|
|
buf.writeByte(wideOpcode)
|
|
branchBuf.writeInt(delta)
|
|
}
|
|
}
|
|
is IntInsnNode -> {
|
|
buf.writeByte(insn.opcode)
|
|
|
|
when (insn.opcode) {
|
|
Opcodes.BIPUSH -> bipushBuf.writeByte(insn.operand)
|
|
Opcodes.SIPUSH -> sipushAndSwitchBuf.writeShort(insn.operand)
|
|
Opcodes.NEWARRAY -> newArrayBuf.writeByte(insn.operand)
|
|
else -> throw IllegalArgumentException("Unsupported IntInsnNode opcode: ${insn.opcode}")
|
|
}
|
|
}
|
|
is IincInsnNode -> {
|
|
if (insn.`var` < 256 && (insn.incr >= -128 && insn.incr <= 127)) {
|
|
buf.writeByte(insn.opcode)
|
|
localVarBuf.writeByte(insn.`var`)
|
|
iincBuf.writeByte(insn.incr)
|
|
} else {
|
|
buf.writeByte(WIDE)
|
|
buf.writeByte(insn.opcode)
|
|
wideLocalVarBuf.writeShort(insn.`var`)
|
|
wideIincBuf.writeShort(insn.incr)
|
|
}
|
|
}
|
|
is TableSwitchInsnNode -> {
|
|
buf.writeByte(insn.opcode)
|
|
|
|
val defaultPc = insns[i].indexOf(insn.dflt.nextReal)
|
|
branchBuf.writeInt(defaultPc - pc)
|
|
|
|
sipushAndSwitchBuf.writeInt(insn.min)
|
|
buf.writeVarInt(insn.max - insn.min)
|
|
|
|
for (target in insn.labels) {
|
|
val targetPc = insns[i].indexOf(target.nextReal)
|
|
branchBuf.writeInt(targetPc - pc)
|
|
}
|
|
}
|
|
is LookupSwitchInsnNode -> {
|
|
buf.writeByte(insn.opcode)
|
|
|
|
val defaultPc = insns[i].indexOf(insn.dflt.nextReal)
|
|
branchBuf.writeInt(defaultPc - pc)
|
|
|
|
val cases = insn.keys.size
|
|
buf.writeVarInt(cases)
|
|
|
|
var prevKey = 0
|
|
for (key in insn.keys) {
|
|
sipushAndSwitchBuf.writeInt(key - prevKey)
|
|
prevKey = key
|
|
}
|
|
|
|
for (target in insn.labels) {
|
|
val targetPc = insns[i].indexOf(target.nextReal)
|
|
branchBuf.writeInt(targetPc - pc)
|
|
}
|
|
}
|
|
is MultiANewArrayInsnNode -> {
|
|
buf.writeByte(insn.opcode)
|
|
constantPool.writeString(classBuf, insn.desc)
|
|
multiNewArrayBuf.writeByte(insn.dims)
|
|
}
|
|
is InsnNode -> buf.writeByte(insn.opcode)
|
|
else -> throw IllegalArgumentException("Unsupported instruction type: ${insn.javaClass.name}")
|
|
}
|
|
}
|
|
|
|
buf.writeByte(EOF)
|
|
}
|
|
|
|
buf.writeBytes(newArrayBuf)
|
|
buf.writeBytes(localVarBuf)
|
|
buf.writeBytes(wideLocalVarBuf)
|
|
buf.writeBytes(sipushAndSwitchBuf)
|
|
buf.writeBytes(constantBuf)
|
|
buf.writeBytes(wideConstantBuf)
|
|
buf.writeBytes(classBuf)
|
|
buf.writeBytes(fieldRefBuf)
|
|
buf.writeBytes(methodRefBuf)
|
|
buf.writeBytes(interfaceMethodRefBuf)
|
|
buf.writeBytes(branchBuf)
|
|
buf.writeBytes(bipushBuf)
|
|
buf.writeBytes(wideIincBuf)
|
|
buf.writeBytes(iincBuf)
|
|
buf.writeBytes(multiNewArrayBuf)
|
|
} finally {
|
|
operandBuffers.release()
|
|
}
|
|
|
|
// write trailer
|
|
buf.writeShort(clazz.interfaces.size)
|
|
buf.writeShort(clazz.fields.size)
|
|
buf.writeShort(clazz.methods.size)
|
|
}
|
|
|
|
private fun createInstructions(clazz: ClassNode): Array<Array<AbstractInsnNode>> {
|
|
return Array(clazz.methods.size) { i ->
|
|
clazz.methods[i].instructions.filter { it.opcode != -1 }.toTypedArray()
|
|
}
|
|
}
|
|
|
|
private fun writeFieldAttributeCounts(buf: ByteBuf, clazz: ClassNode) {
|
|
for (field in clazz.fields) {
|
|
var attributes = 0
|
|
|
|
if ((field.access and Opcodes.ACC_SYNTHETIC) != 0) {
|
|
attributes++
|
|
}
|
|
|
|
if (field.value != null) {
|
|
attributes++
|
|
}
|
|
|
|
buf.writeByte(attributes)
|
|
}
|
|
}
|
|
|
|
private fun writeMethodAttributeCounts(buf: ByteBuf, clazz: ClassNode) {
|
|
for (method in clazz.methods) {
|
|
var attributes = 0
|
|
|
|
if ((method.access and Opcodes.ACC_SYNTHETIC) != 0) {
|
|
attributes++
|
|
}
|
|
|
|
if (method.instructions.size() != 0) {
|
|
attributes++
|
|
}
|
|
|
|
if (method.exceptions.isNotEmpty()) {
|
|
attributes++
|
|
}
|
|
|
|
buf.writeByte(attributes)
|
|
}
|
|
}
|
|
|
|
private fun writeFieldAttributes(buf: ByteBuf, constantPool: ConstantPool, clazz: ClassNode) {
|
|
for (field in clazz.fields) {
|
|
if ((field.access and Opcodes.ACC_SYNTHETIC) != 0) {
|
|
constantPool.writeString(buf, ConstantPool.SYNTHETIC)
|
|
}
|
|
|
|
if (field.value != null) {
|
|
constantPool.writeString(buf, ConstantPool.CONSTANT_VALUE)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun writeMethodAttributes(buf: ByteBuf, constantPool: ConstantPool, clazz: ClassNode) {
|
|
for (method in clazz.methods) {
|
|
if ((method.access and Opcodes.ACC_SYNTHETIC) != 0) {
|
|
constantPool.writeString(buf, ConstantPool.SYNTHETIC)
|
|
}
|
|
|
|
if (method.instructions.size() != 0) {
|
|
constantPool.writeString(buf, ConstantPool.CODE)
|
|
}
|
|
|
|
if (method.exceptions.isNotEmpty()) {
|
|
constantPool.writeString(buf, ConstantPool.EXCEPTIONS)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun writeExceptionCounts(buf: ByteBuf, clazz: ClassNode) {
|
|
for (method in clazz.methods) {
|
|
if (method.exceptions.isNotEmpty()) {
|
|
buf.writeByte(method.exceptions.size)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun writeTryCatchCounts(buf: ByteBuf, clazz: ClassNode) {
|
|
for (method in clazz.methods) {
|
|
if (method.instructions.size() != 0) {
|
|
buf.writeByte(method.tryCatchBlocks.size)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun writeMaxStacks(buf: ByteBuf, clazz: ClassNode) {
|
|
for (method in clazz.methods) {
|
|
if (method.instructions.size() != 0) {
|
|
buf.writeByte(method.maxStack)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun writeMaxLocals(buf: ByteBuf, clazz: ClassNode) {
|
|
for (method in clazz.methods) {
|
|
if (method.instructions.size() != 0) {
|
|
buf.writeByte(method.maxLocals)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun writeLinePcs(buf: ByteBuf, clazz: ClassNode, insns: Array<Array<AbstractInsnNode>>) {
|
|
for ((i, method) in clazz.methods.withIndex()) {
|
|
if (method.instructions.size() == 0) {
|
|
continue
|
|
}
|
|
|
|
var prevPc = -1
|
|
|
|
for (insn in method.instructions) {
|
|
if (insn !is LineNumberNode) {
|
|
continue
|
|
}
|
|
|
|
val pc = insns[i].indexOf(insn.nextReal)
|
|
buf.writeUnsignedShortSmart(pc - prevPc)
|
|
prevPc = pc
|
|
}
|
|
|
|
buf.writeUnsignedShortSmart(0)
|
|
}
|
|
}
|
|
|
|
private fun writeLineNumbers(buf: ByteBuf, clazz: ClassNode) {
|
|
var prevLine = 0
|
|
|
|
for (method in clazz.methods) {
|
|
if (method.instructions.size() == 0) {
|
|
continue
|
|
}
|
|
|
|
for (insn in method.instructions) {
|
|
if (insn !is LineNumberNode) {
|
|
continue
|
|
}
|
|
|
|
val line = insn.line
|
|
buf.writeShort(line - prevLine)
|
|
prevLine = line
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun writeFieldAccessFlagsMsb(buf: ByteBuf, clazz: ClassNode) {
|
|
for (field in clazz.fields) {
|
|
buf.writeByte(field.access shr 8)
|
|
}
|
|
}
|
|
|
|
private fun writeMethodAccessFlagsMsb(buf: ByteBuf, clazz: ClassNode) {
|
|
for (method in clazz.methods) {
|
|
buf.writeByte(method.access shr 8)
|
|
}
|
|
}
|
|
|
|
private fun writeFieldAccessFlagsLsb(buf: ByteBuf, clazz: ClassNode) {
|
|
for (field in clazz.fields) {
|
|
buf.writeByte(field.access)
|
|
}
|
|
}
|
|
|
|
private fun writeMethodAccessFlagsLsb(buf: ByteBuf, clazz: ClassNode) {
|
|
for (method in clazz.methods) {
|
|
buf.writeByte(method.access)
|
|
}
|
|
}
|
|
|
|
private fun writeExceptions(buf: ByteBuf, constantPool: ConstantPool, clazz: ClassNode) {
|
|
for (method in clazz.methods) {
|
|
for (exception in method.exceptions) {
|
|
constantPool.writeString(buf, exception)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun writeTryCatchTypes(buf: ByteBuf, constantPool: ConstantPool, clazz: ClassNode) {
|
|
for (method in clazz.methods) {
|
|
for (tryCatch in method.tryCatchBlocks) {
|
|
constantPool.writeOptionalString(buf, tryCatch.type)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun writeInterfaces(buf: ByteBuf, constantPool: ConstantPool, clazz: ClassNode) {
|
|
for (name in clazz.interfaces) {
|
|
constantPool.writeString(buf, name)
|
|
}
|
|
}
|
|
|
|
private fun writeFieldNamesAndTypes(buf: ByteBuf, constantPool: ConstantPool, clazz: ClassNode) {
|
|
for (field in clazz.fields) {
|
|
constantPool.writeFieldNameAndType(buf, NameAndType(field.name, field.desc))
|
|
}
|
|
}
|
|
|
|
private fun writeMethodNamesAndTypes(buf: ByteBuf, constantPool: ConstantPool, clazz: ClassNode) {
|
|
for (method in clazz.methods) {
|
|
constantPool.writeMethodNameAndType(buf, NameAndType(method.name, method.desc))
|
|
}
|
|
}
|
|
|
|
private fun writeConstants(buf: ByteBuf, constantPool: ConstantPool, clazz: ClassNode) {
|
|
for (field in clazz.fields) {
|
|
val value = field.value ?: continue
|
|
|
|
when (value) {
|
|
is Int -> constantPool.writeInt(buf, value)
|
|
is Long -> constantPool.writeLong(buf, value)
|
|
is Float -> constantPool.writeFloat(buf, value)
|
|
is Double -> constantPool.writeDouble(buf, value)
|
|
is String -> constantPool.writeString(buf, value)
|
|
is Type -> {
|
|
if (value.sort == Type.OBJECT) {
|
|
constantPool.writeString(buf, value.internalName)
|
|
} else {
|
|
throw IllegalArgumentException("Unsupported constant type: ${value.sort}")
|
|
}
|
|
}
|
|
else -> throw IllegalArgumentException("Unsupported constant type: ${value.javaClass.name}")
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun writeTryCatchStartPcs(buf: ByteBuf, clazz: ClassNode, insns: Array<Array<AbstractInsnNode>>) {
|
|
for ((i, method) in clazz.methods.withIndex()) {
|
|
var prevStartPc = 0
|
|
|
|
for (tryCatch in method.tryCatchBlocks) {
|
|
val startPc = insns[i].indexOf(tryCatch.start.nextReal)
|
|
|
|
if (startPc == 0) {
|
|
buf.writeShort(prevStartPc)
|
|
} else if (startPc == prevStartPc) {
|
|
buf.writeShort(0)
|
|
} else {
|
|
buf.writeShort(startPc)
|
|
}
|
|
|
|
prevStartPc = startPc
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun writeTryCatchEndPcsToHandlerPcs(buf: ByteBuf, clazz: ClassNode, insns: Array<Array<AbstractInsnNode>>) {
|
|
for ((i, method) in clazz.methods.withIndex()) {
|
|
for (tryCatch in method.tryCatchBlocks) {
|
|
val list = insns[i]
|
|
|
|
val endPc = list.indexOf(tryCatch.end.nextReal)
|
|
val handlerPc = list.indexOf(tryCatch.handler.nextReal)
|
|
|
|
buf.writeShort(handlerPc - endPc)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun writeTryCatchReverseHandlerPcs(buf: ByteBuf, clazz: ClassNode, insns: Array<Array<AbstractInsnNode>>) {
|
|
for ((i, method) in clazz.methods.withIndex()) {
|
|
for (tryCatch in method.tryCatchBlocks) {
|
|
val list = insns[i]
|
|
val handlerPc = list.indexOf(tryCatch.handler.nextReal)
|
|
buf.writeShort(list.size - handlerPc)
|
|
}
|
|
}
|
|
}
|
|
|
|
private class OperandBuffers private constructor(
|
|
val newArrayBuf: ByteBuf,
|
|
val localVarBuf: ByteBuf,
|
|
val wideLocalVarBuf: ByteBuf,
|
|
val sipushAndSwitchBuf: ByteBuf,
|
|
val constantBuf: ByteBuf,
|
|
val wideConstantBuf: ByteBuf,
|
|
val classBuf: ByteBuf,
|
|
val fieldRefBuf: ByteBuf,
|
|
val methodRefBuf: ByteBuf,
|
|
val interfaceMethodRefBuf: ByteBuf,
|
|
val branchBuf: ByteBuf,
|
|
val bipushBuf: ByteBuf,
|
|
val wideIincBuf: ByteBuf,
|
|
val iincBuf: ByteBuf,
|
|
val multiNewArrayBuf: ByteBuf
|
|
) {
|
|
fun release() {
|
|
newArrayBuf.release()
|
|
localVarBuf.release()
|
|
wideLocalVarBuf.release()
|
|
sipushAndSwitchBuf.release()
|
|
constantBuf.release()
|
|
wideConstantBuf.release()
|
|
classBuf.release()
|
|
fieldRefBuf.release()
|
|
methodRefBuf.release()
|
|
interfaceMethodRefBuf.release()
|
|
branchBuf.release()
|
|
bipushBuf.release()
|
|
wideIincBuf.release()
|
|
iincBuf.release()
|
|
multiNewArrayBuf.release()
|
|
}
|
|
|
|
companion object {
|
|
fun alloc(alloc: ByteBufAllocator): OperandBuffers {
|
|
val bufs = mutableListOf<ByteBuf>()
|
|
try {
|
|
for (i in 0..14) {
|
|
bufs += alloc.buffer()
|
|
}
|
|
|
|
return OperandBuffers(
|
|
bufs[0].retain(),
|
|
bufs[1].retain(),
|
|
bufs[2].retain(),
|
|
bufs[3].retain(),
|
|
bufs[4].retain(),
|
|
bufs[5].retain(),
|
|
bufs[6].retain(),
|
|
bufs[7].retain(),
|
|
bufs[8].retain(),
|
|
bufs[9].retain(),
|
|
bufs[10].retain(),
|
|
bufs[11].retain(),
|
|
bufs[12].retain(),
|
|
bufs[13].retain(),
|
|
bufs[14].retain()
|
|
)
|
|
} finally {
|
|
bufs.forEach(ByteBuf::release)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|