diff --git a/src/main/java/org/momento/lycoris/Lycoris.java b/src/main/java/org/momento/lycoris/Lycoris.java index 986c5f8..ef6a579 100644 --- a/src/main/java/org/momento/lycoris/Lycoris.java +++ b/src/main/java/org/momento/lycoris/Lycoris.java @@ -1,13 +1,30 @@ package org.momento.lycoris; +import org.bukkit.Bukkit; import org.bukkit.plugin.java.JavaPlugin; +import org.momento.lycoris.agent.JavaAgent; + +import javax.xml.transform.Transformer; +import java.lang.instrument.ClassDefinition; +import java.lang.instrument.Instrumentation; +import java.util.logging.Level; +import java.util.logging.Logger; public final class Lycoris extends JavaPlugin { + public static Logger LOGGER; + @Override public void onEnable() { - // Plugin startup logic - + LOGGER = getLogger(); + Instrumentation inst; + try { + inst = JavaAgent.load(); + } catch (Exception error) { + LOGGER.log(Level.SEVERE, error.getMessage()); + Bukkit.getPluginManager().disablePlugin(this); + return; + } } @Override diff --git a/src/main/java/org/momento/lycoris/agent/JavaAgent.java b/src/main/java/org/momento/lycoris/agent/JavaAgent.java new file mode 100644 index 0000000..bacd6b6 --- /dev/null +++ b/src/main/java/org/momento/lycoris/agent/JavaAgent.java @@ -0,0 +1,30 @@ +package org.momento.lycoris.agent; + +import com.sun.tools.attach.AgentInitializationException; +import com.sun.tools.attach.AgentLoadException; +import com.sun.tools.attach.AttachNotSupportedException; +import com.sun.tools.attach.VirtualMachine; + +import java.io.IOException; +import java.lang.instrument.Instrumentation; +import java.lang.management.ManagementFactory; + +public class JavaAgent { + + public static final String PID = ManagementFactory.getRuntimeMXBean().getName().split("@")[0]; + + private static Instrumentation ins; + + public static Instrumentation load() throws IOException, AttachNotSupportedException, AgentLoadException, AgentInitializationException { + VirtualMachine vm = VirtualMachine.attach(PID); + vm.loadAgent(JavaAgent.class.getProtectionDomain().getCodeSource().getLocation().getPath()); + vm.detach(); + if (ins == null) + throw new IllegalStateException("JavaAgent not initialized"); + return ins; + } + + public static void agentmain(final String args, final Instrumentation instrumentation) { + ins = instrumentation; + } +} diff --git a/src/main/java/org/momento/lycoris/api/MixinRegistry.java b/src/main/java/org/momento/lycoris/api/MixinRegistry.java new file mode 100644 index 0000000..50cdb55 --- /dev/null +++ b/src/main/java/org/momento/lycoris/api/MixinRegistry.java @@ -0,0 +1,9 @@ +package org.momento.lycoris.api; + +public class MixinRegistry { + + public static void addMixin() { + + } + +} diff --git a/src/main/java/org/momento/lycoris/mixins/Bootstrapper.java b/src/main/java/org/momento/lycoris/mixins/Bootstrapper.java new file mode 100644 index 0000000..7aa8e93 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/Bootstrapper.java @@ -0,0 +1,7 @@ +package org.momento.lycoris.mixins; + +public class Bootstrapper { + + + +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/Mixin.java b/src/main/java/org/momento/lycoris/mixins/mixin/Mixin.java new file mode 100644 index 0000000..5423b1d --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/Mixin.java @@ -0,0 +1,11 @@ +package org.momento.lycoris.mixins.mixin; + +public class Mixin { + + private Class classe; + + public Mixin(Class classe) { + this.classe = classe; + } + +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/ByteCodec.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/ByteCodec.java new file mode 100644 index 0000000..0a1a107 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/ByteCodec.java @@ -0,0 +1,9 @@ +package org.momento.lycoris.mixins.mixin.classe; + +import java.nio.ByteBuffer; + +public interface ByteCodec { + + void encode(ByteBuffer buffer); + +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/ClassWrapper.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/ClassWrapper.java new file mode 100644 index 0000000..27921d9 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/ClassWrapper.java @@ -0,0 +1,164 @@ +package org.momento.lycoris.mixins.mixin.classe; + +import org.momento.lycoris.mixins.mixin.classe.structures.ConstantPool; +import org.momento.lycoris.mixins.mixin.classe.structures.infos.AttributeInfo; +import org.momento.lycoris.mixins.mixin.classe.structures.infos.FieldInfo; +import org.momento.lycoris.mixins.mixin.classe.structures.infos.MethodInfo; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +public class ClassWrapper implements ByteCodec { + + public enum AccessFlag { + + MASKED((char) 0x0), + PUBLIC((char) 0x1), + FINAL((char) 0x10), + SUPER((char) 0x20), + INTERFACE((char) 0x200), + ABSTRACT((char) 0x400), + SYNTHETIC((char) 0x1000), + ANNOTATION((char) 0x2000), + ENUM((char) 0x4000), + MODULE((char) 0x8000); + + private char value; + + AccessFlag(char value) { + this.value = value; + } + + public char getValue() { return value; } + public char setValue(char value) { this.value = value; return value; } + + + public static AccessFlag fromValue(final char value) { + for (final AccessFlag flag : AccessFlag.values()) { + if (flag.getValue() == value) + return flag; + } + AccessFlag flag = MASKED; + flag.setValue(value); + return flag; + } + + public AccessFlag[] getAccessFlags() { + List flags = new ArrayList<>(); + for (AccessFlag flag : AccessFlag.values()) { + if ((value & flag.getValue()) != 0) + flags.add(flag); + } + return flags.toArray(new AccessFlag[0]); + } + + public void addAccessFlag(final AccessFlag flag) { + this.value = (char) (value | flag.value); + } + + public void removeAccessFlag(final AccessFlag flag) { + this.value = (char) (value & ~flag.value); + } + } + + private final int magic; + private final char minorVersion; + private final char majorVersion; + private final ConstantPool[] constantPool; + private final AccessFlag flags; + private final char thisClass; + private final char superClass; + private final char[] interfaces; + private final FieldInfo[] fields; + private final MethodInfo[] methods; + private final AttributeInfo[] attributes; + + public ClassWrapper(final int magic, final char minorVersion, final char majorVersion, + final ConstantPool[] constantPool, final AccessFlag flags, final char thisClass, + final char superClass, final char[] interfaces, final FieldInfo[] fields, + final MethodInfo[] methods, final AttributeInfo[] attributes) { + this.magic = magic; + this.minorVersion = minorVersion; + this.majorVersion = majorVersion; + this.constantPool = constantPool; + this.flags = flags; + this.thisClass = thisClass; + this.superClass = superClass; + this.interfaces = interfaces; + this.fields = fields; + this.methods = methods; + this.attributes = attributes; + } + + public int getMagic() { return magic; } + public char getMinorVersion() { return minorVersion; } + public char getMajorVersion() { return majorVersion; } + public ConstantPool[] getConstantPool() { return constantPool; } + public AccessFlag getFlags() { return flags; } + public char getThisClass() { return thisClass; } + public char getSuperClass() { return superClass; } + public char[] getInterfaces() { return interfaces; } + public FieldInfo[] getFields() { return fields; } + public MethodInfo[] getMethods() { return methods; } + public AttributeInfo[] getAttributes() { return attributes; } + + public static ClassWrapper decode(ByteBuffer buffer) { + int magic = buffer.getInt(); + char minorVersion = buffer.getChar(); + char majorVersion = buffer.getChar(); + ConstantPool[] constantPool = new ConstantPool[buffer.getChar() - 1]; + for (int i = 0; i < constantPool.length; ++i) + constantPool[i] = ConstantPool.decode(buffer); + AccessFlag flag = AccessFlag.fromValue(buffer.getChar()); + char thisClass = buffer.getChar(); + char superClass = buffer.getChar(); + char[] interfaces = new char[buffer.getChar()]; + for (int i = 0; i < interfaces.length; ++i) + interfaces[i] = buffer.getChar(); + FieldInfo[] fields = new FieldInfo[buffer.getChar()]; + for (int i = 0; i < fields.length; ++i) + fields[i] = FieldInfo.decode(constantPool, buffer); + MethodInfo[] methods = new MethodInfo[buffer.getChar()]; + for (int i = 0; i < methods.length; ++i) + methods[i] = MethodInfo.decode(constantPool, buffer); + AttributeInfo[] attributes = new AttributeInfo[buffer.getChar()]; + for (int i = 0; i < attributes.length; ++i) + attributes[i] = AttributeInfo.decode(constantPool, buffer); + return new ClassWrapper(magic, minorVersion, majorVersion, constantPool, flag, thisClass, superClass, interfaces, fields, methods, attributes); + } + + public static ClassWrapper decode(Path path) throws IOException { + byte[] bytes = Files.readAllBytes(path); + ByteBuffer buffer = ByteBuffer.wrap(bytes); + return decode(buffer); + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putInt(magic); + buffer.putChar(minorVersion); + buffer.putChar(majorVersion); + buffer.putChar((char) (constantPool.length + 1)); + for (ConstantPool cp : constantPool) + cp.encode(buffer); + buffer.putChar(flags.getValue()); + buffer.putChar(thisClass); + buffer.putChar(superClass); + buffer.putChar((char) interfaces.length); + for (char c : interfaces) + buffer.putChar(c); + buffer.putChar((char) fields.length); + for (FieldInfo f : fields) + f.encode(buffer); + buffer.putChar((char) methods.length); + for (MethodInfo m : methods) + m.encode(buffer); + buffer.putChar((char) attributes.length); + for (AttributeInfo a : attributes) + a.encode(buffer); + } +} \ No newline at end of file diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/ConstantPool.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/ConstantPool.java new file mode 100644 index 0000000..7a96c3b --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/ConstantPool.java @@ -0,0 +1,90 @@ +package org.momento.lycoris.mixins.mixin.classe.structures; + +import org.momento.lycoris.mixins.mixin.classe.ByteCodec; +import org.momento.lycoris.mixins.mixin.classe.structures.constants.*; + +import java.nio.ByteBuffer; + +public class ConstantPool implements ByteCodec { + + public enum Tag { + + Class((byte) 7), + FieldRef((byte) 9), + MethodRef((byte) 10), + InterfaceMethodRef((byte) 11), + String((byte) 8), + Integer((byte) 3), + Float((byte) 4), + Long((byte) 5), + Double((byte) 6), + NameAndType((byte) 12), + UTF8((byte) 1), + MethodHandle((byte) 15), + MethodType((byte) 16), + Dynamic((byte) 17), + InvokeDynamic((byte) 18), + Module((byte) 19), + Package((byte) 20); + + private final byte value; + + Tag(final byte value) { + this.value = value; + } + + public byte getValue() { + return value; + } + + public static Tag getTag(final byte value) { + for (final Tag tag : Tag.values()) { + if (tag.value == value) + return tag; + } + throw new IllegalArgumentException("Unknown tag: " + value); + } + + } + + private Tag tag; + private ConstantInfo info; + + + public ConstantPool(final Tag tag, final ConstantInfo info) { + this.tag = tag; + this.info = info; + } + + public Tag getTag() { return tag; } + public ConstantInfo getInfo() { return info; } + + public static ConstantPool decode(ByteBuffer buffer) { + Tag tag = Tag.getTag(buffer.get()); + ConstantInfo info = switch (tag) { + case Class -> ClassInfo.decode(buffer); + case FieldRef, MethodRef, InterfaceMethodRef -> RefInfo.decode(tag, buffer); + case String -> StringInfo.decode(buffer); + case Integer -> IntegerInfo.decode(buffer); + case Float -> FloatInfo.decode(buffer); + case Long -> LongInfo.decode(buffer); + case Double -> DoubleInfo.decode(buffer); + case NameAndType -> NameTypeInfo.decode(buffer); + case UTF8 -> UTF8Info.decode(buffer); + case MethodHandle -> MethodHandleInfo.decode(buffer); + case MethodType -> MethodTypeInfo.decode(buffer); + case Dynamic -> DynamicInfo.decode(buffer); + case InvokeDynamic -> InvokeDynamicInfo.decode(buffer); + case Module -> ModuleInfo.decode(buffer); + case Package -> PackageInfo.decode(buffer); + }; + return new ConstantPool(tag, info); + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.put(tag.getValue()); + info.encode(buffer); + } + +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/ClassInfo.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/ClassInfo.java new file mode 100644 index 0000000..97b4c5e --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/ClassInfo.java @@ -0,0 +1,27 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.constants; + +import org.momento.lycoris.mixins.mixin.classe.structures.ConstantPool; + +import java.nio.ByteBuffer; + +public class ClassInfo extends ConstantInfo { + + private final char nameIndex; + + public ClassInfo(final ConstantPool.Tag tag, final char nameIndex) { + super(tag); + this.nameIndex = nameIndex; + } + + public char getNameIndex() { return nameIndex; } + + public static ClassInfo decode(ByteBuffer buffer) { + return new ClassInfo(ConstantPool.Tag.Class, buffer.getChar()); + } + + @Override + public void encode(ByteBuffer buffer) { + super.encode(buffer); + buffer.putChar(nameIndex); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/ConstantInfo.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/ConstantInfo.java new file mode 100644 index 0000000..5874da3 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/ConstantInfo.java @@ -0,0 +1,22 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.constants; + +import org.momento.lycoris.mixins.mixin.classe.ByteCodec; +import org.momento.lycoris.mixins.mixin.classe.structures.ConstantPool; + +import java.nio.ByteBuffer; + +public abstract class ConstantInfo implements ByteCodec { + + private ConstantPool.Tag tag; + + public ConstantInfo(final ConstantPool.Tag tag) { + this.tag = tag; + } + + public ConstantPool.Tag getTag() { return tag; } + + @Override + public void encode(ByteBuffer buffer) { + buffer.put(tag.getValue()); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/DoubleInfo.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/DoubleInfo.java new file mode 100644 index 0000000..93ea560 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/DoubleInfo.java @@ -0,0 +1,27 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.constants; + +import org.momento.lycoris.mixins.mixin.classe.structures.ConstantPool; + +import java.nio.ByteBuffer; + +public class DoubleInfo extends ConstantInfo { + + private final double bytes; + + public DoubleInfo(final ConstantPool.Tag tag, final double bytes) { + super(tag); + this.bytes = bytes; + } + + public double getValue() { return bytes; } + + public static DoubleInfo decode(ByteBuffer buffer) { + return new DoubleInfo(ConstantPool.Tag.Float, buffer.getDouble()); + } + + @Override + public void encode(ByteBuffer buffer) { + super.encode(buffer); + buffer.putDouble(bytes); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/DynamicInfo.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/DynamicInfo.java new file mode 100644 index 0000000..72a5c09 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/DynamicInfo.java @@ -0,0 +1,31 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.constants; + +import org.momento.lycoris.mixins.mixin.classe.structures.ConstantPool; + +import java.nio.ByteBuffer; + +public class DynamicInfo extends ConstantInfo { + + private final char bootstrapMethodAttrIndex; + private final char nameTypeIndex; + + public DynamicInfo(final ConstantPool.Tag tag, final char bootstrapMethodAttrIndex, final char nameTypeIndex) { + super(tag); + this.bootstrapMethodAttrIndex = bootstrapMethodAttrIndex; + this.nameTypeIndex = nameTypeIndex; + } + + public char getBootstrapMethodAttrIndex() { return bootstrapMethodAttrIndex; } + public char getNameTypeIndex() { return nameTypeIndex; } + + public static DynamicInfo decode(ByteBuffer buffer) { + return new DynamicInfo(ConstantPool.Tag.Dynamic, buffer.getChar(), buffer.getChar()); + } + + @Override + public void encode(ByteBuffer buffer) { + super.encode(buffer); + buffer.putChar(bootstrapMethodAttrIndex); + buffer.putChar(nameTypeIndex); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/FloatInfo.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/FloatInfo.java new file mode 100644 index 0000000..e777869 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/FloatInfo.java @@ -0,0 +1,27 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.constants; + +import org.momento.lycoris.mixins.mixin.classe.structures.ConstantPool; + +import java.nio.ByteBuffer; + +public class FloatInfo extends ConstantInfo { + + private final float bytes; + + public FloatInfo(final ConstantPool.Tag tag, final float bytes) { + super(tag); + this.bytes = bytes; + } + + public float getValue() { return bytes; } + + public static FloatInfo decode(ByteBuffer buffer) { + return new FloatInfo(ConstantPool.Tag.Float, buffer.getFloat()); + } + + @Override + public void encode(ByteBuffer buffer) { + super.encode(buffer); + buffer.putFloat(bytes); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/IntegerInfo.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/IntegerInfo.java new file mode 100644 index 0000000..200e505 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/IntegerInfo.java @@ -0,0 +1,27 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.constants; + +import org.momento.lycoris.mixins.mixin.classe.structures.ConstantPool; + +import java.nio.ByteBuffer; + +public class IntegerInfo extends ConstantInfo { + + private final int bytes; + + public IntegerInfo(final ConstantPool.Tag tag, final int bytes) { + super(tag); + this.bytes = bytes; + } + + public int getValue() { return bytes; } + + public static IntegerInfo decode(ByteBuffer buffer) { + return new IntegerInfo(ConstantPool.Tag.Integer, buffer.getInt()); + } + + @Override + public void encode(ByteBuffer buffer) { + super.encode(buffer); + buffer.putInt(bytes); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/InvokeDynamicInfo.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/InvokeDynamicInfo.java new file mode 100644 index 0000000..c53ecb9 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/InvokeDynamicInfo.java @@ -0,0 +1,31 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.constants; + +import org.momento.lycoris.mixins.mixin.classe.structures.ConstantPool; + +import java.nio.ByteBuffer; + +public class InvokeDynamicInfo extends ConstantInfo { + + private final char bootstrapMethodAttrIndex; + private final char nameTypeIndex; + + public InvokeDynamicInfo(final ConstantPool.Tag tag, final char bootstrapMethodAttrIndex, final char nameTypeIndex) { + super(tag); + this.bootstrapMethodAttrIndex = bootstrapMethodAttrIndex; + this.nameTypeIndex = nameTypeIndex; + } + + public char getBootstrapMethodAttrIndex() { return bootstrapMethodAttrIndex; } + public char getNameTypeIndex() { return nameTypeIndex; } + + public static InvokeDynamicInfo decode(ByteBuffer buffer) { + return new InvokeDynamicInfo(ConstantPool.Tag.InvokeDynamic, buffer.getChar(), buffer.getChar()); + } + + @Override + public void encode(ByteBuffer buffer) { + super.encode(buffer); + buffer.putChar(bootstrapMethodAttrIndex); + buffer.putChar(nameTypeIndex); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/LongInfo.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/LongInfo.java new file mode 100644 index 0000000..8ca0e3a --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/LongInfo.java @@ -0,0 +1,27 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.constants; + +import org.momento.lycoris.mixins.mixin.classe.structures.ConstantPool; + +import java.nio.ByteBuffer; + +public class LongInfo extends ConstantInfo { + + private final long bytes; + + public LongInfo(final ConstantPool.Tag tag, final long bytes) { + super(tag); + this.bytes = bytes; + } + + public long getValue() { return bytes; } + + public static LongInfo decode(ByteBuffer buffer) { + return new LongInfo(ConstantPool.Tag.Integer, buffer.getLong()); + } + + @Override + public void encode(ByteBuffer buffer) { + super.encode(buffer); + buffer.putLong(bytes); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/MethodHandleInfo.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/MethodHandleInfo.java new file mode 100644 index 0000000..6d5c75d --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/MethodHandleInfo.java @@ -0,0 +1,31 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.constants; + +import org.momento.lycoris.mixins.mixin.classe.structures.ConstantPool; + +import java.nio.ByteBuffer; + +public class MethodHandleInfo extends ConstantInfo { + + private final byte referenceKind; + private final char referenceIndex; + + public MethodHandleInfo(final ConstantPool.Tag tag, final byte referenceKind, final char referenceIndex) { + super(tag); + this.referenceKind = referenceKind; + this.referenceIndex = referenceIndex; + } + + public byte getReferenceKind() { return referenceKind; } + public char getReferenceIndex() { return referenceIndex; } + + public static MethodHandleInfo decode(ByteBuffer buffer) { + return new MethodHandleInfo(ConstantPool.Tag.MethodType, buffer.get(), buffer.getChar()); + } + + @Override + public void encode(ByteBuffer buffer) { + super.encode(buffer); + buffer.put(referenceKind); + buffer.putChar(referenceIndex); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/MethodTypeInfo.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/MethodTypeInfo.java new file mode 100644 index 0000000..0025ad9 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/MethodTypeInfo.java @@ -0,0 +1,27 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.constants; + +import org.momento.lycoris.mixins.mixin.classe.structures.ConstantPool; + +import java.nio.ByteBuffer; + +public class MethodTypeInfo extends ConstantInfo { + + private final char descriptorIndex; + + public MethodTypeInfo(final ConstantPool.Tag tag, final char descriptorIndex) { + super(tag); + this.descriptorIndex = descriptorIndex; + } + + public char getDescriptorIndex() { return descriptorIndex; } + + public static MethodTypeInfo decode(ByteBuffer buffer) { + return new MethodTypeInfo(ConstantPool.Tag.MethodType, buffer.getChar()); + } + + @Override + public void encode(ByteBuffer buffer) { + super.encode(buffer); + buffer.putChar(descriptorIndex); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/ModuleInfo.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/ModuleInfo.java new file mode 100644 index 0000000..b34818f --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/ModuleInfo.java @@ -0,0 +1,26 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.constants; + +import org.momento.lycoris.mixins.mixin.classe.structures.ConstantPool; + +import java.nio.ByteBuffer; + +public class ModuleInfo extends ConstantInfo { + + private char nameIndex; + + public ModuleInfo(ConstantPool.Tag tag, char nameIndex) { + super(tag); + } + + public char getNameIndex() { return nameIndex; } + + public static ModuleInfo decode(ByteBuffer buffer) { + return new ModuleInfo(ConstantPool.Tag.Module, buffer.getChar()); + } + + @Override + public void encode(ByteBuffer buffer) { + super.encode(buffer); + buffer.putChar(nameIndex); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/NameTypeInfo.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/NameTypeInfo.java new file mode 100644 index 0000000..ebcaab3 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/NameTypeInfo.java @@ -0,0 +1,31 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.constants; + +import org.momento.lycoris.mixins.mixin.classe.structures.ConstantPool; + +import java.nio.ByteBuffer; + +public class NameTypeInfo extends ConstantInfo { + + private final char nameIndex; + private final char descriptorIndex; + + public NameTypeInfo(final ConstantPool.Tag tag, final char nameIndex, final char descriptorIndex) { + super(tag); + this.nameIndex = nameIndex; + this.descriptorIndex = descriptorIndex; + } + + public char getNameIndex() { return nameIndex; } + public char getDescriptorIndex() { return descriptorIndex; } + + public static NameTypeInfo decode(ByteBuffer buffer) { + return new NameTypeInfo(ConstantPool.Tag.NameAndType, buffer.getChar(), buffer.getChar()); + } + + @Override + public void encode(ByteBuffer buffer) { + super.encode(buffer); + buffer.putChar(nameIndex); + buffer.putChar(descriptorIndex); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/PackageInfo.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/PackageInfo.java new file mode 100644 index 0000000..21e30e4 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/PackageInfo.java @@ -0,0 +1,26 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.constants; + +import org.momento.lycoris.mixins.mixin.classe.structures.ConstantPool; + +import java.nio.ByteBuffer; + +public class PackageInfo extends ConstantInfo { + + private char nameIndex; + + public PackageInfo(ConstantPool.Tag tag, char nameIndex) { + super(tag); + } + + public char getNameIndex() { return nameIndex; } + + public static PackageInfo decode(ByteBuffer buffer) { + return new PackageInfo(ConstantPool.Tag.Package, buffer.getChar()); + } + + @Override + public void encode(ByteBuffer buffer) { + super.encode(buffer); + buffer.putChar(nameIndex); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/RefInfo.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/RefInfo.java new file mode 100644 index 0000000..e488d24 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/RefInfo.java @@ -0,0 +1,31 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.constants; + +import org.momento.lycoris.mixins.mixin.classe.structures.ConstantPool; + +import java.nio.ByteBuffer; + +public class RefInfo extends ConstantInfo { + + private final char classeIndex; + private final char nameTypeIndex; + + public RefInfo(final ConstantPool.Tag tag, final char classeIndex, final char nameTypeIndex) { + super(tag); + this.classeIndex = classeIndex; + this.nameTypeIndex = nameTypeIndex; + } + + public char getClasseIndex() { return classeIndex; } + public char getNameTypeIndex() { return nameTypeIndex; } + + public static RefInfo decode(final ConstantPool.Tag tag, final ByteBuffer buffer) { + return new RefInfo(tag, buffer.getChar(), buffer.getChar()); + } + + @Override + public void encode(ByteBuffer buffer) { + super.encode(buffer); + buffer.putChar(classeIndex); + buffer.putChar(nameTypeIndex); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/StringInfo.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/StringInfo.java new file mode 100644 index 0000000..a3394b6 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/StringInfo.java @@ -0,0 +1,27 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.constants; + +import org.momento.lycoris.mixins.mixin.classe.structures.ConstantPool; + +import java.nio.ByteBuffer; + +public class StringInfo extends ConstantInfo { + + private final char stringIndex; + + public StringInfo(final ConstantPool.Tag tag, final char stringIndex) { + super(tag); + this.stringIndex = stringIndex; + } + + public char getStringIndex() { return stringIndex; } + + public static StringInfo decode(ByteBuffer buffer) { + return new StringInfo(ConstantPool.Tag.String, buffer.getChar()); + } + + @Override + public void encode(ByteBuffer buffer) { + super.encode(buffer); + buffer.putChar(stringIndex); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/UTF8Info.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/UTF8Info.java new file mode 100644 index 0000000..95cdb1a --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/constants/UTF8Info.java @@ -0,0 +1,32 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.constants; + +import org.momento.lycoris.mixins.mixin.classe.structures.ConstantPool; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; + +public class UTF8Info extends ConstantInfo { + + private final String string; + + public UTF8Info(final ConstantPool.Tag tag, String string) { + super(tag); + this.string = string; + } + + public String getString() { return string; } + + public static UTF8Info decode(ByteBuffer buffer) { + byte[] bytes = new byte[buffer.getChar()]; + buffer.get(bytes); + String string = new String(bytes, StandardCharsets.UTF_8); + return new UTF8Info(ConstantPool.Tag.UTF8, string); + } + + @Override + public void encode(ByteBuffer buffer) { + super.encode(buffer); + buffer.putChar((char) string.length()); + buffer.put(StandardCharsets.UTF_8.encode(string)); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/AttributeInfo.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/AttributeInfo.java new file mode 100644 index 0000000..2ffd69a --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/AttributeInfo.java @@ -0,0 +1,72 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos; + +import org.momento.lycoris.mixins.mixin.classe.ByteCodec; +import org.momento.lycoris.mixins.mixin.classe.structures.ConstantPool; +import org.momento.lycoris.mixins.mixin.classe.structures.constants.ConstantInfo; +import org.momento.lycoris.mixins.mixin.classe.structures.constants.UTF8Info; +import org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.*; +import org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.Module; +import org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.Record; + +import java.nio.ByteBuffer; + +public class AttributeInfo implements ByteCodec { + + private final char nameIndex; + private final SizedByteCodec attribute; + + public AttributeInfo(final char nameIndex, final SizedByteCodec attribute) { + this.nameIndex = nameIndex; + this.attribute = attribute; + } + + public static AttributeInfo decode(final ConstantPool[] constantPools, ByteBuffer buffer) { + char nameIndex = buffer.getChar(); + ConstantInfo info = constantPools[nameIndex - 1].getInfo(); + if (info == null || info.getTag() != ConstantPool.Tag.UTF8) + throw new RuntimeException("Unexpected tag: " + info.getTag()); + String attributeName = ((UTF8Info) info).getString(); + int length = buffer.getInt(); + SizedByteCodec attribute = switch (attributeName) { + case "ConstantValue" -> ConstantValue.decode(buffer); + case "Code" -> Code.decode(constantPools, buffer); + case "StackMapTable" -> StackMapTable.decode(buffer); + case "Exceptions" -> Exceptions.decode(buffer); + case "InnerClasses" -> InnerClasses.decode(buffer); + case "EnclosingMethod" -> EnclosingMethod.decode(buffer); + case "Signature" -> Signature.decode(buffer); + case "SourceFile" -> SourceFile.decode(buffer); + case "SourceDebugExtension" -> SourceDebugExtension.decode(length, buffer); + case "LineNumberTable" -> LineNumberTable.decode(buffer); + case "LocalVariableTable", "LocalVariableTypeTable" -> LocalVariableTable.decode(buffer); + case "RuntimeInvisibleAnnotations", "RuntimeVisibleAnnotations" -> RuntimeAnnotations.decode(buffer); + case "RuntimeInvisibleParameterAnnotations", "RuntimeVisibleParameterAnnotations" -> RuntimeParameterAnnotations.decode(buffer); + case "AnnotationDefault" -> AnnotationDefault.decode(buffer); + case "BootstrapMethods" -> BootstrapMethods.decode(buffer); + case "RuntimeVisibleTypeAnnotations", "RuntimeInvisibleTypeAnnotations" -> RuntimeTypeAnnotations.decode(buffer); + case "MethodParameters" -> MethodParameters.decode(buffer); + case "Module" -> Module.decode(buffer); + case "ModulePackages" -> ModulePackages.decode(buffer); + case "ModuleMainClass" -> ModuleMainClass.decode(buffer); + case "NestHost" -> NestHost.decode(buffer); + case "NestMembers" -> NestMembers.decode(buffer); + case "Record" -> Record.decode(constantPools, buffer); + case "PermittedSubclasses" -> PermittedSubclasses.decode(buffer); + default -> null; + }; + return new AttributeInfo(nameIndex, attribute); + } + + public int getSize() { return attribute.getSize(); } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar(nameIndex); + if (attribute == null) { + buffer.putInt(0); + return; + } + buffer.putInt(attribute.getSize()); + attribute.encode(buffer); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/FieldInfo.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/FieldInfo.java new file mode 100644 index 0000000..5fbae34 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/FieldInfo.java @@ -0,0 +1,106 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos; + +import org.momento.lycoris.mixins.mixin.classe.structures.ConstantPool; +import org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.SizedByteCodec; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; + +public class FieldInfo implements SizedByteCodec { + + public enum AccessFlag { + + MASKED((char) 0x0), + PUBLIC((char) 0x1), + PRIVATE((char) 0x2), + PROTECTED((char) 0x4), + STATIC((char) 0x8), + FINAL((char) 0x10), + VOLATILE((char) 0x40), + TRANSIENT((char) 0x80), + SYNTHETIC((char) 0x1000), + ENUM((char) 0x4000); + + private char value; + + AccessFlag(char value) { + this.value = value; + } + + public char getValue() { return value; } + public void setValue(char value) { this.value = value; } + + public static AccessFlag fromValue(final char value) { + for (final AccessFlag flag : AccessFlag.values()) { + if (flag.getValue() == value) + return flag; + } + AccessFlag flag = MASKED; + MASKED.setValue(value); + return flag; + } + + public AccessFlag[] getAccessFlags() { + List flags = new ArrayList<>(); + for (AccessFlag flag : AccessFlag.values()) { + if ((value & flag.getValue()) != 0) + flags.add(flag); + } + return flags.toArray(new AccessFlag[0]); + } + + public void addAccessFlag(final AccessFlag flag) { + this.value = (char) (value | flag.value); + } + + public void removeAccessFlag(final AccessFlag flag) { + this.value = (char) (value & ~flag.value); + } + } + + private final AccessFlag flags; + private final char nameIndex; + private final char descriptorIndex; + private final AttributeInfo[] attributes; + + public FieldInfo(final AccessFlag flags, final char nameIndex, final char descriptorIndex, final AttributeInfo[] attributes) { + this.flags = flags; + this.nameIndex = nameIndex; + this.descriptorIndex = descriptorIndex; + this.attributes = attributes; + } + + public AccessFlag getFlags() { return flags; } + public char getNameIndex() { return nameIndex; } + public char getDescriptorIndex() { return descriptorIndex; } + public AttributeInfo[] getAttributes() { return attributes; } + + public static FieldInfo decode(final ConstantPool[] constantPool, ByteBuffer buffer) { + AccessFlag flag = AccessFlag.fromValue(buffer.getChar()); + char nameIndex = buffer.getChar(); + char descriptorIndex = buffer.getChar(); + AttributeInfo[] attributes = new AttributeInfo[buffer.getChar()]; + for (int i = 0; i < attributes.length; i++) + attributes[i] = AttributeInfo.decode(constantPool, buffer); + return new FieldInfo(flag, nameIndex, descriptorIndex, attributes); + } + + @Override + public int getSize() { + int baseSize = 8; + for (AttributeInfo attribute : attributes) + baseSize += attribute.getSize(); + return baseSize; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar(flags.getValue()); + buffer.putChar(nameIndex); + buffer.putChar(descriptorIndex); + buffer.putChar((char) attributes.length); + for (AttributeInfo attribute : attributes) + attribute.encode(buffer); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/MethodInfo.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/MethodInfo.java new file mode 100644 index 0000000..38e82ae --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/MethodInfo.java @@ -0,0 +1,109 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos; + +import org.momento.lycoris.mixins.mixin.classe.structures.ConstantPool; +import org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.SizedByteCodec; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; + +public class MethodInfo implements SizedByteCodec { + + public enum AccessFlag { + + MASKED((char) 0x0), + PUBLIC((char) 0x1), + PRIVATE((char) 0x2), + PROTECTED((char) 0x4), + STATIC((char) 0x8), + FINAL((char) 0x10), + SYNCHRONIZED((char) 0x20), + BRIDGE((char) 0x40), + VARARGS((char) 0x80), + NATIVE((char) 0x100), + ABSTRACT((char) 0x400), + STRICT((char) 0x800), + SYNTHETIC((char) 0x1000); + + private char value; + + AccessFlag(char value) { + this.value = value; + } + + public char getValue() { return value; } + public void setValue(char value) { this.value = value; } + + public static AccessFlag fromValue(final char value) { + for (final AccessFlag flag : AccessFlag.values()) { + if (flag.getValue() == value) + return flag; + } + AccessFlag flag = MASKED; + MASKED.setValue(value); + return flag; + } + + public AccessFlag[] getAccessFlags() { + List flags = new ArrayList<>(); + for (AccessFlag flag : AccessFlag.values()) { + if ((value & flag.getValue()) != 0) + flags.add(flag); + } + return flags.toArray(new AccessFlag[0]); + } + + public void addAccessFlag(final AccessFlag flag) { + this.value = (char) (value | flag.value); + } + + public void removeAccessFlag(final AccessFlag flag) { + this.value = (char) (value & ~flag.value); + } + } + + private final AccessFlag flags; + private final char nameIndex; + private final char descriptorIndex; + private final AttributeInfo[] attributes; + + public MethodInfo(final AccessFlag flags, final char nameIndex, final char descriptorIndex, final AttributeInfo[] attributes) { + this.flags = flags; + this.nameIndex = nameIndex; + this.descriptorIndex = descriptorIndex; + this.attributes = attributes; + } + + public AccessFlag getFlags() { return flags; } + public char getNameIndex() { return nameIndex; } + public char getDescriptorIndex() { return descriptorIndex; } + public AttributeInfo[] getAttributes() { return attributes; } + + public static MethodInfo decode(final ConstantPool[] constantPool, ByteBuffer buffer) { + AccessFlag flag = AccessFlag.fromValue(buffer.getChar()); + char nameIndex = buffer.getChar(); + char descriptorIndex = buffer.getChar(); + AttributeInfo[] attributes = new AttributeInfo[buffer.getChar()]; + for (int i = 0; i < attributes.length; ++i) + attributes[i] = AttributeInfo.decode(constantPool, buffer); + return new MethodInfo(flag, nameIndex, descriptorIndex, attributes); + } + + @Override + public int getSize() { + int baseSize = 8; + for (AttributeInfo attribute : attributes) + baseSize += attribute.getSize(); + return baseSize; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar(flags.getValue()); + buffer.putChar(nameIndex); + buffer.putChar(descriptorIndex); + buffer.putChar((char) attributes.length); + for (AttributeInfo attribute : attributes) + attribute.encode(buffer); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/AccessFlag.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/AccessFlag.java new file mode 100644 index 0000000..5d6f74d --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/AccessFlag.java @@ -0,0 +1,32 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes; + +public enum AccessFlag { + PUBLIC((char) 0x1), + PRIVATE((char) 0x2), + PROTECTED((char) 0x4), + STATIC((char) 0x8), + FINAL((char) 0x10), + VOLATILE((char) 0x40), + TRANSIENT((char) 0x80), + INTERFACE((char) 0x200), + ABSTRACT((char) 0x400), + SYNTHETIC((char) 0x1000), + ANNOTATION((char) 0x2000), + ENUM((char) 0x4000); + + private final char value; + + AccessFlag(final char value) { + this.value = value; + } + + public char getValue() { return value; } + + public static AccessFlag fromValue(final char value) { + for (final AccessFlag flag : AccessFlag.values()) { + if (flag.getValue() == value) + return flag; + } + return null; + } +} \ No newline at end of file diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/Annotation.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/Annotation.java new file mode 100644 index 0000000..d3e1714 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/Annotation.java @@ -0,0 +1,52 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes; + +import org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.values.ElementValue; +import org.momento.lycoris.utils.Pair; + +import java.nio.ByteBuffer; + +public class Annotation implements SizedByteCodec { + + private final char typeIndex; + private final Pair[] pairs; + + public Annotation(final char typeIndex, final Pair[] pairs) { + this.typeIndex = typeIndex; + this.pairs = pairs; + } + + public char getTypeIndex() { + return typeIndex; + } + + public Pair[] getPairs() { + return pairs; + } + + public static Annotation decode(ByteBuffer buffer) { + char typeIndex = buffer.getChar(); + Pair[] pairs = new Pair[buffer.getChar()]; + for (int i = 0; i < pairs.length; i++) { + char nameIndex = buffer.getChar(); + pairs[i] = new Pair<>(nameIndex, ElementValue.decode(buffer)); + } + return new Annotation(typeIndex, pairs); + } + + @Override + public int getSize() { + int baseSize = 4; + for (Pair pair : pairs) + baseSize += 2 + pair.getSecond().getSize(); + return baseSize; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar(typeIndex); + for (Pair pair : pairs) { + buffer.putChar(pair.getFirst()); + pair.getSecond().encode(buffer); + } + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/AnnotationDefault.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/AnnotationDefault.java new file mode 100644 index 0000000..a1971ba --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/AnnotationDefault.java @@ -0,0 +1,30 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes; + +import org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.values.ElementValue; + +import java.nio.ByteBuffer; + +public class AnnotationDefault implements SizedByteCodec { + + private final ElementValue value; + + public AnnotationDefault(final ElementValue value) { + this.value = value; + } + + public ElementValue getValue() { return value; } + + public static AnnotationDefault decode(ByteBuffer buffer) { + return new AnnotationDefault(ElementValue.decode(buffer)); + } + + @Override + public int getSize() { + return value.getSize(); + } + + @Override + public void encode(ByteBuffer buffer) { + value.encode(buffer); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/BootstrapMethods.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/BootstrapMethods.java new file mode 100644 index 0000000..3dda967 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/BootstrapMethods.java @@ -0,0 +1,68 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes; + +import java.nio.ByteBuffer; + +public class BootstrapMethods implements SizedByteCodec { + + public static class BootsrapMethod implements SizedByteCodec { + + private final char ref; + private final char[] args; + + public BootsrapMethod(final char ref, final char[] args) { + this.ref = ref; + this.args = args; + } + + public char getRef() { return ref; } + public char[] getArgs() { return args; } + + public static BootsrapMethod decode(ByteBuffer buffer) { + char ref = buffer.getChar(); + char[] args = new char[buffer.getChar()]; + for (int i = 0; i < args.length; i++) + args[i] = buffer.getChar(); + return new BootsrapMethod(ref, args); + } + + @Override + public int getSize() { + return 2 + args.length * 2; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar(ref); + buffer.putChar((char) args.length); + for (int i = 0; i < args.length; i++) + buffer.putChar(args[i]); + } + } + + private final BootsrapMethod[] methods; + + public BootstrapMethods(final BootsrapMethod[] methods) { + this.methods = methods; + } + + public BootsrapMethod[] getMethods() { return methods; } + + public static BootstrapMethods decode(ByteBuffer buffer) { + BootsrapMethod[] methods = new BootsrapMethod[buffer.getChar()]; + for (int i = 0; i < methods.length; ++i) + methods[i] = BootsrapMethod.decode(buffer); + return new BootstrapMethods(methods); + } + + @Override + public int getSize() { + return 0; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar((char) methods.length); + for (BootsrapMethod method : methods) + method.encode(buffer); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/Code.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/Code.java new file mode 100644 index 0000000..950d81a --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/Code.java @@ -0,0 +1,98 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes; + +import org.momento.lycoris.mixins.mixin.classe.ByteCodec; +import org.momento.lycoris.mixins.mixin.classe.structures.ConstantPool; +import org.momento.lycoris.mixins.mixin.classe.structures.infos.AttributeInfo; + +import java.nio.ByteBuffer; + +public class Code implements SizedByteCodec { + + public static class Exception implements SizedByteCodec { + private final char startPC; + private final char endPC; + private final char handlerPC; + private final char catchType; + + public Exception(final char startPC, final char endPC, final char handlerPC, final char catchType) { + this.startPC = startPC; + this.endPC = endPC; + this.handlerPC = handlerPC; + this.catchType = catchType; + } + + public char getStartPC() { return startPC; } + public char getEndPC() { return endPC; } + public char getHandlerPC() { return handlerPC; } + public char getCatchType() { return catchType; } + + + public static Exception decode(ByteBuffer buffer) { + return new Exception(buffer.getChar(), buffer.getChar(), buffer.getChar(), buffer.getChar()); + } + + @Override + public int getSize() { return 4; } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar(startPC); + buffer.putChar(endPC); + buffer.putChar(handlerPC); + buffer.putChar(catchType); + } + } + + private final char maxStack; + private final char maxLocals; + private final byte[] code; + private final Exception[] exceptionTable; + private final AttributeInfo[] attributes; + + public Code(final char maxStack, final char maxLocals, final byte[] code, final Exception[] exceptionTable, final AttributeInfo[] attributes) { + this.maxStack = maxStack; + this.maxLocals = maxLocals; + this.code = code; + this.exceptionTable = exceptionTable; + this.attributes = attributes; + + } + + public static Code decode(final ConstantPool[] constantPools, ByteBuffer buffer) { + char maxStack = buffer.getChar(); + char maxLocals = buffer.getChar(); + byte[] code = new byte[buffer.getInt()]; + buffer.get(code); + Exception[] exceptionTable = new Exception[buffer.getChar()]; + for (int i = 0; i < exceptionTable.length; ++i) + exceptionTable[i] = Exception.decode(buffer); + AttributeInfo[] attributes = new AttributeInfo[buffer.getChar()]; + for (int i = 0; i < attributes.length; ++i) + attributes[i] = AttributeInfo.decode(constantPools, buffer); + return new Code(maxStack, maxLocals, code, exceptionTable, attributes); + } + + @Override + public int getSize() { + int baseSize = 18 + code.length; + for (Exception exception : exceptionTable) + baseSize += exception.getSize(); + for (AttributeInfo attribute : attributes) + baseSize += attribute.getSize(); + return baseSize; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar(maxStack); + buffer.putChar(maxLocals); + buffer.putInt(code.length); + buffer.put(code); + buffer.putChar((char) exceptionTable.length); + for (Exception exception : exceptionTable) + exception.encode(buffer); + buffer.putChar((char) attributes.length); + for (AttributeInfo attribute : attributes) + attribute.encode(buffer); + } +} \ No newline at end of file diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/ConstantValue.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/ConstantValue.java new file mode 100644 index 0000000..bca9424 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/ConstantValue.java @@ -0,0 +1,24 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes; + +import java.nio.ByteBuffer; + +public class ConstantValue implements SizedByteCodec { + + private final char valueIndex; + + public ConstantValue(final char valueIndex) { + this.valueIndex = valueIndex; + } + + @Override + public int getSize() { return 2; } + + public static ConstantValue decode(final ByteBuffer buffer) { + return new ConstantValue(buffer.getChar()); + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar(valueIndex); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/EnclosingMethod.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/EnclosingMethod.java new file mode 100644 index 0000000..86f3449 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/EnclosingMethod.java @@ -0,0 +1,32 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes; + +import java.nio.ByteBuffer; + +public class EnclosingMethod implements SizedByteCodec { + + private final char classIndex; + private final char methodIndex; + + public EnclosingMethod(final char classIndex, final char methodIndex) { + this.classIndex = classIndex; + this.methodIndex = methodIndex; + } + + public static EnclosingMethod decode(ByteBuffer buffer) { + return new EnclosingMethod(buffer.getChar(), buffer.getChar()); + } + + public char getClassIndex() { return classIndex; } + public char getMethodIndex() { return methodIndex; } + + @Override + public int getSize() { + return 4; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar(classIndex); + buffer.putChar(methodIndex); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/Exceptions.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/Exceptions.java new file mode 100644 index 0000000..ca02d57 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/Exceptions.java @@ -0,0 +1,33 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes; + +import java.nio.ByteBuffer; + +public class Exceptions implements SizedByteCodec { + + private final char[] exceptionIndexTable; + + public Exceptions(final char[] exceptionIndexTable) { + this.exceptionIndexTable = exceptionIndexTable; + } + + public static Exceptions decode(ByteBuffer buffer) { + char[] exceptionIndexTable = new char[buffer.getChar()]; + for (int i = 0; i < exceptionIndexTable.length; i++) + exceptionIndexTable[i] = buffer.getChar(); + return new Exceptions(exceptionIndexTable); + } + + public char[] getExceptionIndexTable() { return exceptionIndexTable; } + + @Override + public int getSize() { + return 2 * exceptionIndexTable.length; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar((char) exceptionIndexTable.length); + for (char index : exceptionIndexTable) + buffer.putChar(index); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/InnerClasses.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/InnerClasses.java new file mode 100644 index 0000000..5cad902 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/InnerClasses.java @@ -0,0 +1,67 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes; + +import java.nio.ByteBuffer; + +public class InnerClasses implements SizedByteCodec { + + public static class Classes implements SizedByteCodec { + + final char innerClassInfoIndex; + final char outerClassInfoIndex; + final char innerNameIndex; + final AccessFlag innerClassAccessFlags; + + public Classes(final char innerClassInfoIndex, final char outerClassInfoIndex, final char innerNameIndex, final AccessFlag innerClassAccessFlags) { + this.innerClassInfoIndex = innerClassInfoIndex; + this.outerClassInfoIndex = outerClassInfoIndex; + this.innerNameIndex = innerNameIndex; + this.innerClassAccessFlags = innerClassAccessFlags; + } + + public static Classes decode(ByteBuffer buffer) { + return new Classes(buffer.getChar(), buffer.getChar(), buffer.getChar(), AccessFlag.fromValue(buffer.getChar())); + } + + + @Override + public int getSize() { + return 8; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar(innerClassInfoIndex); + buffer.putChar(outerClassInfoIndex); + buffer.putChar(innerNameIndex); + buffer.putChar(innerClassAccessFlags.getValue()); + } + } + + private final Classes[] classes; + + public InnerClasses(final Classes[] classes) { + this.classes = classes; + } + + public static InnerClasses decode(ByteBuffer buffer) { + Classes[] classes = new Classes[buffer.getChar()]; + for (int i = 0; i < classes.length; i++) + classes[i] = Classes.decode(buffer); + return new InnerClasses(classes); + } + + @Override + public int getSize() { + int size = 0; + for (Classes classe : classes) + size += classe.getSize(); + return size; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar((char) classes.length); + for (Classes classe : classes) + classe.encode(buffer); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/LineNumberTable.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/LineNumberTable.java new file mode 100644 index 0000000..44da9f8 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/LineNumberTable.java @@ -0,0 +1,55 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes; + +import java.nio.ByteBuffer; + +public class LineNumberTable implements SizedByteCodec { + + public record Table(char startPC, char lineNumber) implements SizedByteCodec { + + public static Table decode(ByteBuffer buffer) { + return new Table(buffer.getChar(), buffer.getChar()); + } + + @Override + public int getSize() { + return 4; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar(startPC); + buffer.putChar(lineNumber); + } + } + + private final Table[] table; + + public LineNumberTable(final Table[] table) { + this.table = table; + } + + public Table[] getTable() { return table; } + + public static LineNumberTable decode(ByteBuffer buffer) { + char length = buffer.getChar(); + Table[] table = new Table[length]; + for (int i = 0; i < table.length; i++) + table[i] = Table.decode(buffer); + return new LineNumberTable(table); + } + + @Override + public int getSize() { + int baseSize = 2; + for (Table table : table) + baseSize += table.getSize(); + return baseSize; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar((char) table.length); + for (Table table : table) + table.encode(buffer); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/LocalVariableTable.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/LocalVariableTable.java new file mode 100644 index 0000000..57d7899 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/LocalVariableTable.java @@ -0,0 +1,58 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes; + +import java.nio.ByteBuffer; + +public class LocalVariableTable implements SizedByteCodec { + + public record Table(char startPC, char length, char nameIndex, char descriptorIndex, + char index) implements SizedByteCodec { + + public static Table decode(ByteBuffer buffer) { + return new Table(buffer.getChar(), buffer.getChar(), buffer.getChar(), buffer.getChar(), buffer.getChar()); + } + + @Override + public int getSize() { + return 10; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar(startPC); + buffer.putChar(length); + buffer.putChar(nameIndex); + buffer.putChar(descriptorIndex); + buffer.putChar(index); + } + } + + private final Table[] table; + + public LocalVariableTable(final Table[] table) { + this.table = table; + } + + public Table[] getTable() { return table; } + + public static LocalVariableTable decode(ByteBuffer buffer) { + Table[] table = new Table[buffer.getChar()]; + for (int i = 0; i < table.length; i++) + table[i] = Table.decode(buffer); + return new LocalVariableTable(table); + } + + @Override + public int getSize() { + int baseSize = 2; + for (Table table : table) + baseSize += table.getSize(); + return baseSize; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar((char) table.length); + for (Table table : table) + table.encode(buffer); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/MethodParameters.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/MethodParameters.java new file mode 100644 index 0000000..c59d784 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/MethodParameters.java @@ -0,0 +1,39 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes; + +import org.momento.lycoris.mixins.mixin.classe.structures.infos.AttributeInfo; +import org.momento.lycoris.utils.Pair; + +import java.nio.ByteBuffer; + +public class MethodParameters implements SizedByteCodec { + + private final Pair[] parameters; + + public MethodParameters(Pair[] parameters) { + this.parameters = parameters; + } + + public Pair[] getParameters() { return parameters; } + + public static MethodParameters decode(ByteBuffer buffer) { + Pair[] parameters = new Pair[buffer.get()]; + for (int i = 0; i < parameters.length; i++) { + parameters[i] = new Pair<>(buffer.getChar(), buffer.getChar()); + } + return new MethodParameters(parameters); + } + + @Override + public int getSize() { + return 1 + parameters.length * 4; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.put((byte) parameters.length); + for (Pair parameter : parameters) { + buffer.putChar(parameter.getFirst()); + buffer.putChar(parameter.getSecond()); + } + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/Module.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/Module.java new file mode 100644 index 0000000..2b2b439 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/Module.java @@ -0,0 +1,111 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes; + +import org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.counts.ExportsCount; +import org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.counts.OpensCount; +import org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.counts.ProvidesCount; +import org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.counts.RequiresCount; + +import java.nio.ByteBuffer; + +public class Module implements SizedByteCodec { + + public enum Flag { + ACC_OPEN((char ) 0x20), + ACC_SYNTHETIC((char) 0x1000), + ACC_MANDATED((char) 0x8000); + + private final char value; + + Flag(char value) { + this.value = value; + } + + public char getValue() { + return value; + } + + public static Flag fromValue(char value) { + for (Flag flag : Flag.values()) { + if (flag.getValue() == value) { + return flag; + } + } + return null; + } + } + + private char moduleNameIndex; + private Flag moduleFlags; + private char moduleVersionIndex; + private RequiresCount[] requires; + private ExportsCount[] exports; + private OpensCount[] opens; + private char[] usesIndex; + private ProvidesCount[] provides; + + public Module(char moduleNameIndex, Flag moduleFlags, char moduleVersionIndex, RequiresCount[] requires, ExportsCount[] exports, OpensCount[] opens, char[] usesIndex, ProvidesCount[] provides) { + this.moduleNameIndex = moduleNameIndex; + this.moduleFlags = moduleFlags; + this.moduleVersionIndex = moduleVersionIndex; + this.requires = requires; + this.exports = exports; + this.opens = opens; + this.usesIndex = usesIndex; + this.provides = provides; + } + + public static Module decode(ByteBuffer buffer) { + char moduleNameIndex = buffer.getChar(); + Flag moduleFlags = Flag.fromValue(buffer.getChar()); + char moduleVersionIndex = buffer.getChar(); + RequiresCount[] requires = new RequiresCount[buffer.getChar()]; + for (int i = 0; i < requires.length; i++) + requires[i] = RequiresCount.decode(buffer); + ExportsCount[] exports = new ExportsCount[buffer.getChar()]; + for (int i = 0; i < exports.length; i++) + exports[i] = ExportsCount.decode(buffer); + OpensCount[] opens = new OpensCount[buffer.getChar()]; + for (int i = 0; i < opens.length; i++) + opens[i] = OpensCount.decode(buffer); + char[] usesIndex = new char[buffer.getChar()]; + for (int i = 0; i < usesIndex.length; i++) + usesIndex[i] = buffer.getChar(); + ProvidesCount[] provides = new ProvidesCount[buffer.getChar()]; + for (int i = 0; i < provides.length; i++) + provides[i] = ProvidesCount.decode(buffer); + return new Module(moduleNameIndex, moduleFlags, moduleVersionIndex, requires, exports, opens,usesIndex, provides); + } + + @Override + public int getSize() { + int baseSize = 16; + for (RequiresCount requiresCount : requires) + baseSize += requiresCount.getSize(); + for (ExportsCount exportsCount : exports) + baseSize += exportsCount.getSize(); + for (OpensCount opensCount : opens) + baseSize += opensCount.getSize(); + baseSize += usesIndex.length * 2; + for (ProvidesCount providesCount : provides) + baseSize += providesCount.getSize(); + return baseSize; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar(moduleNameIndex); + buffer.putChar(moduleFlags.getValue()); + buffer.putChar(moduleVersionIndex); + buffer.putChar((char) requires.length); + for (RequiresCount requiresCount : requires) + requiresCount.encode(buffer); + buffer.putChar((char) exports.length); + for (ExportsCount exportsCount : exports) + exportsCount.encode(buffer); + buffer.putChar((char) usesIndex.length); + for (int i = 0; i < usesIndex.length; i++) + buffer.putChar(usesIndex[i]); + for (ProvidesCount providesCount : provides) + providesCount.encode(buffer); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/ModuleMainClass.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/ModuleMainClass.java new file mode 100644 index 0000000..56ec970 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/ModuleMainClass.java @@ -0,0 +1,30 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes; + +import java.nio.ByteBuffer; + +public class ModuleMainClass implements SizedByteCodec { + + private char mainClassIndex; + + public ModuleMainClass(char mainClassIndex) { + this.mainClassIndex = mainClassIndex; + } + + private char getMainClassIndex() { + return mainClassIndex; + } + + public static ModuleMainClass decode(ByteBuffer buffer) { + return new ModuleMainClass(buffer.getChar()); + } + + @Override + public int getSize() { + return 2; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar(mainClassIndex); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/ModulePackages.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/ModulePackages.java new file mode 100644 index 0000000..f51cee0 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/ModulePackages.java @@ -0,0 +1,31 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes; + +import java.nio.ByteBuffer; + +public class ModulePackages implements SizedByteCodec { + + private char[] packageIndex; + + public ModulePackages(char[] packageIndex) { + this.packageIndex = packageIndex; + } + + public char[] getPackageIndex() { return packageIndex; } + + public static ModulePackages decode(ByteBuffer buffer) { + char[] packageIndex = new char[buffer.getChar()]; + return new ModulePackages(packageIndex); + } + + @Override + public int getSize() { + return 2 + 2 * packageIndex.length; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar((char) packageIndex.length); + for (int i = 0; i < packageIndex.length; i++) + buffer.putChar(packageIndex[i]); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/NestHost.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/NestHost.java new file mode 100644 index 0000000..23c51d0 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/NestHost.java @@ -0,0 +1,28 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes; + +import java.nio.ByteBuffer; + +public class NestHost implements SizedByteCodec { + + private char hostClassIndex; + + public NestHost(char hostClassIndex) { + this.hostClassIndex = hostClassIndex; + } + + public char getHostClassIndex() { return hostClassIndex; } + + public static NestHost decode(ByteBuffer buffer) { + return new NestHost(buffer.getChar()); + } + + @Override + public int getSize() { + return 2; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar(hostClassIndex); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/NestMembers.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/NestMembers.java new file mode 100644 index 0000000..e21a9c4 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/NestMembers.java @@ -0,0 +1,33 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes; + +import java.nio.ByteBuffer; + +public class NestMembers implements SizedByteCodec { + + private final char[] classes; + + public NestMembers(char[] classes) { + this.classes = classes; + } + + public char[] getClasses() { return classes; } + + @Override + public int getSize() { + return 2 + classes.length * 2; + } + + public static NestMembers decode(ByteBuffer buffer) { + char[] classes = new char[buffer.getChar()]; + for (int i = 0; i < classes.length; i++) + classes[i] = buffer.getChar(); + return new NestMembers(classes); + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar((char) classes.length); + for (char classe : classes) + buffer.putChar(classe); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/PermittedSubclasses.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/PermittedSubclasses.java new file mode 100644 index 0000000..b22581f --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/PermittedSubclasses.java @@ -0,0 +1,35 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes; + +import org.momento.lycoris.mixins.mixin.Mixin; + +import java.nio.ByteBuffer; + +public class PermittedSubclasses implements SizedByteCodec { + + private char[] classes; + + public PermittedSubclasses(char[] classes) { + this.classes = classes; + } + + public char[] getClasses() { return classes; } + + public static PermittedSubclasses decode(ByteBuffer buffer) { + char[] classes = new char[buffer.getChar()]; + for (int i = 0; i < classes.length; i++) + classes[i] = buffer.getChar(); + return new PermittedSubclasses(classes); + } + + @Override + public int getSize() { + return 2 + 2 * classes.length; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar((char) classes.length); + for (int i = 0; i < classes.length; i++) + buffer.putChar(classes[i]); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/Record.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/Record.java new file mode 100644 index 0000000..306ba69 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/Record.java @@ -0,0 +1,40 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes; + +import org.momento.lycoris.mixins.mixin.classe.structures.ConstantPool; + +import java.nio.ByteBuffer; + +public class Record implements SizedByteCodec { + + private RecordComponentInfo[] components; + + public Record(RecordComponentInfo[] components) { + this.components = components; + } + + public RecordComponentInfo[] getComponents() { + return components; + } + + public static Record decode(ConstantPool[] constantPools, ByteBuffer buffer) { + RecordComponentInfo[] components = new RecordComponentInfo[buffer.getChar()]; + for (int i = 0; i < components.length; i++) + components[i] = RecordComponentInfo.decode(constantPools, buffer); + return new Record(components); + } + + @Override + public int getSize() { + int baseSize = 2; + for (RecordComponentInfo component : components) + baseSize += component.getSize(); + return baseSize; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar((char) components.length); + for (RecordComponentInfo component : components) + component.encode(buffer); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/RecordComponentInfo.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/RecordComponentInfo.java new file mode 100644 index 0000000..0c365be --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/RecordComponentInfo.java @@ -0,0 +1,48 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes; + +import org.momento.lycoris.mixins.mixin.classe.structures.ConstantPool; +import org.momento.lycoris.mixins.mixin.classe.structures.infos.AttributeInfo; + +import java.nio.ByteBuffer; + +public class RecordComponentInfo implements SizedByteCodec { + + private char nameIndex; + private char descriptorIndex; + private AttributeInfo[] attributes; + + public RecordComponentInfo(char nameIndex, char descriptorIndex, AttributeInfo[] attributes) { + this.nameIndex = nameIndex; + this.descriptorIndex = descriptorIndex; + this.attributes = attributes; + } + + public char getNameIndex() { return nameIndex; } + public char getDescriptorIndex() { return descriptorIndex; } + public AttributeInfo[] getAttributes() { return attributes; } + + public static RecordComponentInfo decode(final ConstantPool[] constantPools, ByteBuffer buffer) { + char nameIndex = buffer.getChar(); + char descriptorIndex = buffer.getChar(); + AttributeInfo[] attributes = new AttributeInfo[buffer.getChar()]; + for (int i = 0; i < attributes.length; i++) + attributes[i] = AttributeInfo.decode(constantPools, buffer); + return new RecordComponentInfo(nameIndex, descriptorIndex, attributes); + } + + @Override + public int getSize() { + int baseSize = 6; + for (AttributeInfo attribute : attributes) + baseSize += attribute.getSize(); + return baseSize; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar(nameIndex); + buffer.putChar(descriptorIndex); + for (AttributeInfo attribute : attributes) + attribute.encode(buffer); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/RuntimeAnnotations.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/RuntimeAnnotations.java new file mode 100644 index 0000000..5604ba5 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/RuntimeAnnotations.java @@ -0,0 +1,37 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes; + +import java.nio.ByteBuffer; + +public class RuntimeAnnotations implements SizedByteCodec { + + private final Annotation[] annotations; + + public RuntimeAnnotations(final Annotation[] annotations) { + this.annotations = annotations; + } + + public Annotation[] getAnnotations() { return annotations; } + + public static RuntimeAnnotations decode(ByteBuffer buffer) { + final Annotation[] annotations = new Annotation[buffer.getChar()]; + for (int i = 0; i < annotations.length; i++) { + annotations[i] = Annotation.decode(buffer); + } + return new RuntimeAnnotations(annotations); + } + + @Override + public int getSize() { + int baseSize = 2; + for (Annotation annotation : annotations) + baseSize += annotation.getSize(); + return baseSize; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar((char) annotations.length); + for (Annotation annotation : annotations) + annotation.encode(buffer); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/RuntimeParameterAnnotations.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/RuntimeParameterAnnotations.java new file mode 100644 index 0000000..2aac186 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/RuntimeParameterAnnotations.java @@ -0,0 +1,45 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes; + +import java.nio.ByteBuffer; + +public class RuntimeParameterAnnotations implements SizedByteCodec { + + private final Annotation[][] parameters; + + public RuntimeParameterAnnotations(final Annotation[][] parameters) { + this.parameters = parameters; + } + + public Annotation[] getParameters() { return parameters[0]; } + + public static RuntimeParameterAnnotations decode(ByteBuffer buffer) { + final Annotation[][] parameters = new Annotation[buffer.getChar()][]; + for (int i = 0; i < parameters.length; i++) { + parameters[i] = new Annotation[buffer.getChar()]; + for (int j = 0; j < parameters[i].length; j++) + parameters[i][j] = Annotation.decode(buffer); + } + return new RuntimeParameterAnnotations(parameters); + } + + @Override + public int getSize() { + int baseSize = 2; + for (Annotation[] annotations : parameters) { + baseSize += 2; + for (Annotation annotation : annotations) + baseSize += annotation.getSize(); + } + return baseSize; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar((char) parameters.length); + for (Annotation[] annotations : parameters) { + buffer.putChar((char) annotations.length); + for (Annotation annotation : annotations) + annotation.encode(buffer); + } + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/RuntimeTypeAnnotations.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/RuntimeTypeAnnotations.java new file mode 100644 index 0000000..71b8407 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/RuntimeTypeAnnotations.java @@ -0,0 +1,36 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes; + +import java.nio.ByteBuffer; + +public class RuntimeTypeAnnotations implements SizedByteCodec { + + private final TypeAnnotation[] typeAnnotations; + + public RuntimeTypeAnnotations(TypeAnnotation[] typeAnnotations) { + this.typeAnnotations = typeAnnotations; + } + + public TypeAnnotation[] getTypeAnnotations() { return typeAnnotations; } + + public static RuntimeTypeAnnotations decode(ByteBuffer buffer) { + TypeAnnotation[] typeAnnotations = new TypeAnnotation[buffer.getChar()]; + for (int i = 0; i < typeAnnotations.length; i++) + typeAnnotations[i] = TypeAnnotation.decode(buffer); + return new RuntimeTypeAnnotations(typeAnnotations); + } + + @Override + public int getSize() { + int baseSize = 2; + for (TypeAnnotation typeAnnotation : typeAnnotations) + baseSize += typeAnnotation.getSize(); + return baseSize; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar((char) typeAnnotations.length); + for (TypeAnnotation annotation : typeAnnotations) + annotation.encode(buffer); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/Signature.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/Signature.java new file mode 100644 index 0000000..95b7ab2 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/Signature.java @@ -0,0 +1,28 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes; + +import java.nio.ByteBuffer; + +public class Signature implements SizedByteCodec { + + private final char signatureIndex; + + public Signature(final char signatureIndex) { + this.signatureIndex = signatureIndex; + } + + public char getSignatureIndex() { return signatureIndex; } + + public static Signature decode(ByteBuffer buffer) { + return new Signature(buffer.getChar()); + } + + @Override + public int getSize() { + return 2; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar(signatureIndex); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/SizedByteCodec.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/SizedByteCodec.java new file mode 100644 index 0000000..2d030e6 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/SizedByteCodec.java @@ -0,0 +1,9 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes; + +import org.momento.lycoris.mixins.mixin.classe.ByteCodec; + +public interface SizedByteCodec extends ByteCodec { + + int getSize(); + +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/SourceDebugExtension.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/SourceDebugExtension.java new file mode 100644 index 0000000..2b2e91e --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/SourceDebugExtension.java @@ -0,0 +1,30 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes; + +import java.nio.ByteBuffer; + +public class SourceDebugExtension implements SizedByteCodec { + + private final byte[] debugExtension; + + public SourceDebugExtension(final byte[] debugExtension) { + this.debugExtension = debugExtension; + } + + public byte[] getDebugExtension() { return debugExtension; } + + public static SourceDebugExtension decode(final int length, ByteBuffer buffer) { + byte[] debugExtension = new byte[length]; + buffer.get(debugExtension); + return new SourceDebugExtension(debugExtension); + } + + @Override + public int getSize() { + return debugExtension.length; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.put(debugExtension); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/SourceFile.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/SourceFile.java new file mode 100644 index 0000000..3c1f72c --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/SourceFile.java @@ -0,0 +1,28 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes; + +import java.nio.ByteBuffer; + +public class SourceFile implements SizedByteCodec { + + private final char sourceFileIndex; + + public SourceFile(final char sourceFileIndex) { + this.sourceFileIndex = sourceFileIndex; + } + + public char getSourceFileIndex() { return sourceFileIndex; } + + public static SourceFile decode(ByteBuffer buffer) { + return new SourceFile(buffer.getChar()); + } + + @Override + public int getSize() { + return 2; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar(sourceFileIndex); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/StackMapTable.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/StackMapTable.java new file mode 100644 index 0000000..7b3b961 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/StackMapTable.java @@ -0,0 +1,50 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes; + +import org.momento.lycoris.mixins.mixin.classe.ByteCodec; +import org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.frames.*; + +import java.nio.ByteBuffer; + +public class StackMapTable implements SizedByteCodec { + + private final StackMapFrame[] entries; + + public StackMapTable(final StackMapFrame[] entries) { + this.entries = entries; + } + + @Override + public int getSize() { + int baseSize = 2; + for (StackMapFrame entry : entries) + baseSize += entry.getSize(); + return baseSize; + } + + public StackMapFrame[] getEntries() { return entries; } + + public static StackMapTable decode(ByteBuffer buffer) { + StackMapFrame[] entries = new StackMapFrame[buffer.getChar()]; + for (int i = 0; i < entries.length; ++i) { + char frameType = (char) (buffer.get() & 0xFF); + StackMapFrame.Type type = StackMapFrame.Type.fromValue(frameType); + entries[i] = switch (type) { + case SAME -> new SameFrame(type); + case SAME_LOCAL_1_STACk_ITEM -> SameLocalStackItemFrame.decode(type, buffer); + case SAME_LOCAL_1_STACK_ITEM_EXTENDED -> SameLocalStackItemExtFrame.decode(type, buffer); + case CHOP -> ChopFrame.decode(type, buffer); + case SAME_FRAME_EXTENDED -> SameExFrame.decode(type, buffer); + case APPEND -> AppendFrame.decode(type, buffer); + case FULL_FRAME -> FullFrame.decode(type, buffer); + }; + } + return new StackMapTable(entries); + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar((char) entries.length); + for (StackMapFrame entry : entries) + entry.encode(buffer); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/TypeAnnotation.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/TypeAnnotation.java new file mode 100644 index 0000000..b33f492 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/TypeAnnotation.java @@ -0,0 +1,101 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes; + +import org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.targets.*; +import org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.targets.EmptyTarget; +import org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.values.ElementValue; +import org.momento.lycoris.utils.Pair; + +import java.nio.ByteBuffer; + +public class TypeAnnotation implements SizedByteCodec { + + public enum Type { + GENERIC_CLASS((byte) 0x0), + GENERIC_METHOD((byte) 0x01), + SUPER_CLASS((byte) 0x10), + BOUND_GENERIC_CLASS((byte) 0x11), + BOUND_GENERIC_METHOD((byte) 0x12), + FIELD_TYPE((byte) 0x13), + RETURN_TYPE((byte) 0x14), + RECEIVER_TYPE((byte) 0x15), + PARAMETER_TYPE((byte) 0x16), + THROW_TYPE((byte) 0x17); + + + private final byte value; + + Type(byte value) { + this.value = value; + } + + public byte getValue() { return value; } + + public static Type fromValue(byte value) { + for (Type type : Type.values()) { + if (type.getValue() == value) { + return type; + } + } + return null; + } + } + + private final Type targetType; + private final EmptyTarget targetInfo; + private final TypePath targetPath; + private final char typeIndex; + private final Pair[] elementValuePairs; + + public TypeAnnotation(Type targetType, EmptyTarget targetInfo, TypePath targetPath, char typeIndex, Pair[] elementValuePairs) { + this.targetType = targetType; + this.targetInfo = targetInfo; + this.targetPath = targetPath; + this.typeIndex = typeIndex; + this.elementValuePairs = elementValuePairs; + } + + public Type getEmptyTargetType() { return targetType; } + public EmptyTarget getEmptyTargetInfo() { return targetInfo; } + public TypePath getEmptyTargetPath() { return targetPath; } + public char getTypeIndex() { return typeIndex; } + public Pair[] getElementValuePairs() { return elementValuePairs; } + + public static TypeAnnotation decode(ByteBuffer buffer) { + Type targetType = Type.fromValue(buffer.get()); + EmptyTarget target = switch (targetType) { + case GENERIC_CLASS, GENERIC_METHOD -> TypeParameterTarget.decode(buffer); + case SUPER_CLASS -> SuperTypeTarget.decode(buffer); + case BOUND_GENERIC_CLASS, BOUND_GENERIC_METHOD -> TypeParameterBoundTarget.decode(buffer); + case FIELD_TYPE, RETURN_TYPE, RECEIVER_TYPE -> EmptyTarget.decode(buffer); + case PARAMETER_TYPE -> FormalParameterTarget.decode(buffer); + case THROW_TYPE -> ThrowsTarget.decode(buffer); + }; + TypePath path = TypePath.decode(buffer); + char typeIndex = buffer.getChar(); + Pair[] elementValuePairs = new Pair[buffer.getChar()]; + for (int i = 0; i < elementValuePairs.length; i++) + elementValuePairs[i] = new Pair<>(buffer.getChar(), ElementValue.decode(buffer)); + return new TypeAnnotation(targetType, target, path, typeIndex, elementValuePairs); + } + + @Override + public int getSize() { + int baseSize = 1 + targetInfo.getSize() + targetPath.getSize() + 4; + for (Pair pair : elementValuePairs) + baseSize += 2 + pair.getSecond().getSize(); + return baseSize; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.put(targetType.value); + targetInfo.encode(buffer); + targetPath.encode(buffer); + buffer.putChar(typeIndex); + buffer.putChar((char) elementValuePairs.length); + for (Pair pair : elementValuePairs) { + buffer.putChar(pair.getFirst()); + pair.getSecond().encode(buffer); + } + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/TypePath.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/TypePath.java new file mode 100644 index 0000000..ee5c0d4 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/TypePath.java @@ -0,0 +1,39 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes; + +import org.momento.lycoris.utils.Pair; + +import java.nio.ByteBuffer; + +public class TypePath implements SizedByteCodec { + + private final Pair[] paths; + + public TypePath(Pair[] paths) { + this.paths = paths; + } + + + public Pair[] getPaths() { return paths; } + + @Override + public int getSize() { + return 1 + paths.length * 2; + } + + public static TypePath decode(ByteBuffer buffer) { + Pair[] paths = new Pair[buffer.get()]; + for (int i = 0; i < paths.length; i++) { + paths[i] = new Pair<>(buffer.get(), buffer.get()); + } + return new TypePath(paths); + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.put((byte) paths.length); + for (Pair path : paths) { + buffer.put(path.getFirst()); + buffer.put(path.getSecond()); + } + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/counts/ExportsCount.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/counts/ExportsCount.java new file mode 100644 index 0000000..26a6f9b --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/counts/ExportsCount.java @@ -0,0 +1,46 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.counts; + +import org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.SizedByteCodec; + +import java.nio.ByteBuffer; + +public class ExportsCount implements SizedByteCodec { + + private final char exportsIndex; + private final char exportsFlags; + private final char[] exportsToIndex; + + public ExportsCount(char exportsIndex, char exportsFlags, char[] exportsToIndex) { + this.exportsIndex = exportsIndex; + this.exportsFlags = exportsFlags; + this.exportsToIndex = exportsToIndex; + } + + public char getExportsIndex() { return exportsIndex; } + public char getExportsFlags() { return exportsFlags; } + public char[] getExportsToIndex() { return exportsToIndex; } + + public static ExportsCount decode(ByteBuffer buffer) { + char exportsIndex = buffer.getChar(); + char exportsFlags = buffer.getChar(); + char[] exportsToIndex = new char[buffer.getChar()]; + for (int i = 0; i < exportsToIndex.length; i++) + exportsToIndex[i] = buffer.getChar(); + return new ExportsCount(exportsIndex, exportsFlags, exportsToIndex); + } + + @Override + public int getSize() { + return 6 + 2 * exportsToIndex.length; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar(exportsIndex); + buffer.putChar(exportsFlags); + buffer.putChar(exportsIndex); + buffer.putChar((char) exportsToIndex.length); + for (int i = 0; i < exportsToIndex.length; i++) + buffer.putChar(exportsToIndex[i]); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/counts/OpensCount.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/counts/OpensCount.java new file mode 100644 index 0000000..c3e29ed --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/counts/OpensCount.java @@ -0,0 +1,46 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.counts; + +import org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.SizedByteCodec; + +import java.nio.ByteBuffer; + +public class OpensCount implements SizedByteCodec { + + private final char opensIndex; + private final char opensFlags; + private final char[] opensToIndex; + + public OpensCount(char opensIndex, char opensFlags, char[] opensToIndex) { + this.opensIndex = opensIndex; + this.opensFlags = opensFlags; + this.opensToIndex = opensToIndex; + } + + public char getIndex() { return opensIndex; } + public char getExportsFlags() { return opensFlags; } + public char[] getExportsToIndex() { return opensToIndex; } + + public static OpensCount decode(ByteBuffer buffer) { + char opensIndex = buffer.getChar(); + char opensFlags = buffer.getChar(); + char[] opensToIndex = new char[buffer.getChar()]; + for (int i = 0; i < opensToIndex.length; i++) + opensToIndex[i] = buffer.getChar(); + return new OpensCount(opensIndex, opensFlags, opensToIndex); + } + + @Override + public int getSize() { + return 6 + 2 * opensToIndex.length; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar(opensIndex); + buffer.putChar(opensFlags); + buffer.putChar(opensIndex); + buffer.putChar((char) opensToIndex.length); + for (int i = 0; i < opensToIndex.length; i++) + buffer.putChar(opensToIndex[i]); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/counts/ProvidesCount.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/counts/ProvidesCount.java new file mode 100644 index 0000000..e3e9a43 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/counts/ProvidesCount.java @@ -0,0 +1,40 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.counts; + +import org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.SizedByteCodec; + +import java.nio.ByteBuffer; + +public class ProvidesCount implements SizedByteCodec { + + private char providesIndex; + private char[] providesWithIndex; + + public ProvidesCount(char providesIndex, char[] providesWithIndex) { + this.providesIndex = providesIndex; + this.providesWithIndex = providesWithIndex; + } + + public char getProvidesIndex() { return providesIndex; } + public char[] getProvidesWithIndex() { return providesWithIndex; } + + public static ProvidesCount decode(ByteBuffer buffer) { + char providesIndex = buffer.getChar(); + char[] providesWithIndex = new char[buffer.getChar()]; + for (int i = 0; i < providesWithIndex.length; i++) + providesWithIndex[i] = buffer.getChar(); + return new ProvidesCount(providesIndex, providesWithIndex); + } + + @Override + public int getSize() { + return 4 + 2 * providesWithIndex.length; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar(providesIndex); + buffer.putChar((char) providesWithIndex.length); + for (int i = 0; i < providesWithIndex.length; i++) + buffer.putChar(providesWithIndex[i]); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/counts/RequiresCount.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/counts/RequiresCount.java new file mode 100644 index 0000000..b39d852 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/counts/RequiresCount.java @@ -0,0 +1,38 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.counts; + +import org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.SizedByteCodec; + +import java.nio.ByteBuffer; + +public class RequiresCount implements SizedByteCodec { + + private final char requiresIndex; + private final char requiresFlags; + private final char requiresVersionIndex; + + public RequiresCount(char requiresIndex, char requiresFlags, char requiresVersionIndex) { + this.requiresIndex = requiresIndex; + this.requiresFlags = requiresFlags; + this.requiresVersionIndex = requiresVersionIndex; + } + + public char getRequiresIndex() { return requiresIndex; } + public char getRequiresFlags() { return requiresFlags; } + public char getRequiresVersionIndex() { return requiresVersionIndex; } + + @Override + public int getSize() { + return 6; + } + + public static RequiresCount decode(ByteBuffer buffer) { + return new RequiresCount(buffer.getChar(), buffer.getChar(), buffer.getChar()); + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar(requiresIndex); + buffer.putChar(requiresFlags); + buffer.putChar(requiresVersionIndex); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/frames/AppendFrame.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/frames/AppendFrame.java new file mode 100644 index 0000000..06e7a1e --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/frames/AppendFrame.java @@ -0,0 +1,42 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.frames; + +import java.nio.ByteBuffer; + +public class AppendFrame extends StackMapFrame { + + private final char offsetDelta; + private final VerificationTypeInfo[] locals; + + public AppendFrame(final Type type, final char offsetDelta, final VerificationTypeInfo[] locals) { + super(type); + this.offsetDelta = offsetDelta; + this.locals = locals; + } + + public char getOffsetDelta() { return offsetDelta; } + public VerificationTypeInfo[] getLocals() { return locals; } + + public static AppendFrame decode(final Type type, ByteBuffer buffer) { + char offsetDelta = buffer.getChar(); + VerificationTypeInfo[] locals = new VerificationTypeInfo[type.getValue() - 251]; + for (int i = 0; i < locals.length; ++i) + locals[i] = VerificationTypeInfo.decode(buffer); + return new AppendFrame(type, offsetDelta, locals); + } + + @Override + public int getSize() { + int baseSize = super.getSize() + 2; + for (VerificationTypeInfo local: locals) + baseSize += local.getSize(); + return baseSize; + } + + @Override + public void encode(ByteBuffer buffer) { + super.encode(buffer); + buffer.putChar(offsetDelta); + for (VerificationTypeInfo local: locals) + local.encode(buffer); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/frames/ChopFrame.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/frames/ChopFrame.java new file mode 100644 index 0000000..5bda0cb --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/frames/ChopFrame.java @@ -0,0 +1,30 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.frames; + +import java.nio.ByteBuffer; + +public class ChopFrame extends StackMapFrame { + + private final char offsetDelta; + + public ChopFrame(final Type type, final char offsetDelta) { + super(type); + this.offsetDelta = offsetDelta; + } + + public char getOffsetDelta() { return offsetDelta; } + + public static ChopFrame decode(final Type type, ByteBuffer buffer) { + return new ChopFrame(type, buffer.getChar()); + } + + @Override + public int getSize() { + return super.getSize() + 2; + } + + @Override + public void encode(ByteBuffer buffer) { + super.encode(buffer); + buffer.putChar(offsetDelta); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/frames/FullFrame.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/frames/FullFrame.java new file mode 100644 index 0000000..22dc84c --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/frames/FullFrame.java @@ -0,0 +1,56 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.frames; + +import java.nio.ByteBuffer; + +public class FullFrame extends StackMapFrame { + + private final char offsetDelta; + private final VerificationTypeInfo[] locals; + private final VerificationTypeInfo[] stack; + + + public FullFrame(final Type type, final char offsetDelta, final VerificationTypeInfo[] locals, final VerificationTypeInfo[] stack) { + super(type); + this.offsetDelta = offsetDelta; + this.locals = locals; + this.stack = stack; + } + + public char getOffsetDelta() { return offsetDelta; } + public VerificationTypeInfo[] getLocals() { return locals; } + public VerificationTypeInfo[] getStack() { return stack; } + + public static FullFrame decode(Type type, ByteBuffer buffer) { + char offsetDelta = buffer.getChar(); + VerificationTypeInfo[] locals = new VerificationTypeInfo[buffer.getChar()]; + for (int i = 0; i < locals.length; ++i) + locals[i] = VerificationTypeInfo.decode(buffer); + VerificationTypeInfo[] stack = new VerificationTypeInfo[buffer.getChar()]; + for (int i = 0; i < stack.length; ++i) + stack[i] = VerificationTypeInfo.decode(buffer); + return new FullFrame(type, offsetDelta, locals, stack); + } + + @Override + public int getSize() { + int baseSize = super.getSize() + 6; + for (VerificationTypeInfo local: locals) + baseSize += local.getSize(); + for (VerificationTypeInfo stack: stack) + baseSize += stack.getSize(); + return baseSize; + } + + @Override + public void encode(ByteBuffer buffer) { + super.encode(buffer); + buffer.putChar(offsetDelta); + buffer.putChar((char) locals.length); + for (VerificationTypeInfo local: locals) + local.encode(buffer); + buffer.putChar((char) stack.length); + for (VerificationTypeInfo stack: stack) + stack.encode(buffer); + + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/frames/SameExFrame.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/frames/SameExFrame.java new file mode 100644 index 0000000..85d2f14 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/frames/SameExFrame.java @@ -0,0 +1,30 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.frames; + +import java.nio.ByteBuffer; + +public class SameExFrame extends SameFrame { + + private final char offsetDelta; + + public SameExFrame(final Type type, final char offsetDelta) { + super(type); + this.offsetDelta = offsetDelta; + } + + public char getOffsetDelta() { return offsetDelta; } + + public static SameExFrame decode(final Type type, ByteBuffer buffer) { + return new SameExFrame(type, buffer.getChar()); + } + + @Override + public int getSize() { + return super.getSize() + 2; + } + + @Override + public void encode(ByteBuffer buffer) { + super.encode(buffer); + buffer.putChar(offsetDelta); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/frames/SameFrame.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/frames/SameFrame.java new file mode 100644 index 0000000..b054177 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/frames/SameFrame.java @@ -0,0 +1,7 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.frames; + +public class SameFrame extends StackMapFrame { + public SameFrame(Type type) { + super(type); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/frames/SameLocalStackItemExtFrame.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/frames/SameLocalStackItemExtFrame.java new file mode 100644 index 0000000..010bf19 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/frames/SameLocalStackItemExtFrame.java @@ -0,0 +1,31 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.frames; + +import java.nio.ByteBuffer; + +public class SameLocalStackItemExtFrame extends SameLocalStackItemFrame { + + private final char offsetDelta; + + public SameLocalStackItemExtFrame(final Type type, final char offsetDelta, final VerificationTypeInfo verificationTypeInfo) { + super(type, verificationTypeInfo); + this.offsetDelta = offsetDelta; + } + + public char getOffsetDelta() { return offsetDelta; } + + @Override + public int getSize() { + return super.getSize() + 2; + } + + public static SameLocalStackItemExtFrame decode(final Type type, ByteBuffer buffer) { + return new SameLocalStackItemExtFrame(type, buffer.getChar(), VerificationTypeInfo.decode(buffer)); + } + + @Override + public void encode(ByteBuffer buffer) { + encodePrimitive(buffer); + buffer.putChar(offsetDelta); + verificationTypeInfo.encode(buffer); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/frames/SameLocalStackItemFrame.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/frames/SameLocalStackItemFrame.java new file mode 100644 index 0000000..aa0d197 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/frames/SameLocalStackItemFrame.java @@ -0,0 +1,34 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.frames; + +import java.nio.ByteBuffer; + +public class SameLocalStackItemFrame extends StackMapFrame { + + protected final VerificationTypeInfo verificationTypeInfo; + + public SameLocalStackItemFrame(final Type type, final VerificationTypeInfo verificationTypeInfo) { + super(type); + this.verificationTypeInfo = verificationTypeInfo; + } + + public VerificationTypeInfo getVerificationTypeInfo() { return verificationTypeInfo; } + + @Override + public int getSize() { + return super.getSize() + verificationTypeInfo.getSize(); + } + + public static SameLocalStackItemFrame decode(final Type type, ByteBuffer buffer) { + return new SameLocalStackItemFrame(type, VerificationTypeInfo.decode(buffer)); + } + + @Override + public void encode(ByteBuffer buffer) { + encodePrimitive(buffer); + verificationTypeInfo.encode(buffer); + } + + protected void encodePrimitive(ByteBuffer buffer) { + super.encode(buffer); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/frames/StackMapFrame.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/frames/StackMapFrame.java new file mode 100644 index 0000000..988ee54 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/frames/StackMapFrame.java @@ -0,0 +1,64 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.frames; + +import org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.SizedByteCodec; + +import java.nio.ByteBuffer; + +public abstract class StackMapFrame implements SizedByteCodec { + + public enum Type { + SAME((char) 0), + SAME_LOCAL_1_STACk_ITEM((char) 64), + SAME_LOCAL_1_STACK_ITEM_EXTENDED((char) 247), + CHOP((char) 248), + SAME_FRAME_EXTENDED((char) 251), + APPEND((char) 252), + FULL_FRAME((char) 255); + + private char value; + + Type(final char value) { + this.value = value; + } + + public char getValue() { return value; } + + public static Type fromValue(final char value) { + Type type; + if (value < 64) + type = SAME; + else if (value < 128) + type = SAME_LOCAL_1_STACk_ITEM; + else if (value < 248) + type = SAME_LOCAL_1_STACK_ITEM_EXTENDED; + else if (value < 251) + type = CHOP; + else if (value == 251) + type = SAME_FRAME_EXTENDED; + else if (value < 255) + type = APPEND; + else + type = FULL_FRAME; + type.value = value; + return type; + } + } + + private final Type type; + + public StackMapFrame(final Type type) { + this.type = type; + } + + public Type getType() { return type; } + + @Override + public int getSize() { + return 1; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.put((byte) type.value); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/frames/VerificationTypeInfo.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/frames/VerificationTypeInfo.java new file mode 100644 index 0000000..26636ab --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/frames/VerificationTypeInfo.java @@ -0,0 +1,52 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.frames; + +import org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.SizedByteCodec; +import org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.frames.variables.ObjectVariableInfo; +import org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.frames.variables.UninitializedVariableInfo; +import org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.frames.variables.VariableInfo; + +import java.nio.ByteBuffer; + +public class VerificationTypeInfo implements SizedByteCodec { + + private final VariableInfo variableInfo; + + public enum Tag { + TOP, + INTEGER, + FLOAT, + DOUBLE, + LONG, + NULL, + UNINITIALIZED_THIS, + OBJECT, + UNINITIALIZED; + } + + public VerificationTypeInfo(final VariableInfo variableInfo) { + this.variableInfo = variableInfo; + } + + + public VariableInfo getVariableInfo() { return variableInfo; } + + public static VerificationTypeInfo decode(ByteBuffer buffer) { + Tag tag = Tag.values()[buffer.get() & 0xFF]; + VariableInfo variableInfo = switch (tag) { + case TOP, INTEGER, FLOAT, LONG, DOUBLE, NULL, UNINITIALIZED_THIS -> new VariableInfo(tag); + case OBJECT -> ObjectVariableInfo.decode(buffer); + case UNINITIALIZED -> UninitializedVariableInfo.decode(buffer); + }; + return new VerificationTypeInfo(variableInfo); + } + + @Override + public int getSize() { + return variableInfo.getSize(); + } + + @Override + public void encode(ByteBuffer buffer) { + variableInfo.encode(buffer); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/frames/variables/ObjectVariableInfo.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/frames/variables/ObjectVariableInfo.java new file mode 100644 index 0000000..167cb16 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/frames/variables/ObjectVariableInfo.java @@ -0,0 +1,32 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.frames.variables; + +import org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.frames.VerificationTypeInfo; + +import java.nio.ByteBuffer; + +public class ObjectVariableInfo extends VariableInfo { + + private final char cPoolIndex; + + public ObjectVariableInfo(final char cPoolIndex) { + super(VerificationTypeInfo.Tag.OBJECT); + this.cPoolIndex = cPoolIndex; + } + + public char getPoolIndex() { return cPoolIndex; } + + public static ObjectVariableInfo decode(ByteBuffer buffer) { + return new ObjectVariableInfo(buffer.getChar()); + } + + @Override + public void encode(ByteBuffer buffer) { + super.encode(buffer); + buffer.putChar(cPoolIndex); + } + + @Override + public int getSize() { + return super.getSize() + 2; + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/frames/variables/UninitializedVariableInfo.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/frames/variables/UninitializedVariableInfo.java new file mode 100644 index 0000000..c6f829a --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/frames/variables/UninitializedVariableInfo.java @@ -0,0 +1,32 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.frames.variables; + +import org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.frames.VerificationTypeInfo; + +import java.nio.ByteBuffer; + +public class UninitializedVariableInfo extends VariableInfo { + + private final char offset; + + public UninitializedVariableInfo(final char offset) { + super(VerificationTypeInfo.Tag.OBJECT); + this.offset = offset; + } + + public char getOffset() { return offset; } + + public static UninitializedVariableInfo decode(ByteBuffer buffer) { + return new UninitializedVariableInfo(buffer.getChar()); + } + + @Override + public void encode(ByteBuffer buffer) { + super.encode(buffer); + buffer.putChar(offset); + } + + @Override + public int getSize() { + return super.getSize() + 2; + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/frames/variables/VariableInfo.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/frames/variables/VariableInfo.java new file mode 100644 index 0000000..d1a5d1c --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/frames/variables/VariableInfo.java @@ -0,0 +1,27 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.frames.variables; + +import org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.SizedByteCodec; +import org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.frames.VerificationTypeInfo; + +import java.nio.ByteBuffer; + +public class VariableInfo implements SizedByteCodec { + + private final VerificationTypeInfo.Tag tag; + + public VariableInfo(final VerificationTypeInfo.Tag tag) { + this.tag = tag; + } + + public VerificationTypeInfo.Tag getTag() { return tag; } + + @Override + public int getSize() { + return 1; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.put((byte) tag.ordinal()); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/targets/CatchTarget.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/targets/CatchTarget.java new file mode 100644 index 0000000..0d23239 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/targets/CatchTarget.java @@ -0,0 +1,30 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.targets; + +import org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.SizedByteCodec; + +import java.nio.ByteBuffer; + +public class CatchTarget extends EmptyTarget { + + private final char exceptionTableIndex; + + public CatchTarget(char exceptionTableIndex) { + this.exceptionTableIndex = exceptionTableIndex; + } + + public char getExceptionTableIndex() { return exceptionTableIndex; } + + public static CatchTarget decode(ByteBuffer buffer) { + return new CatchTarget(buffer.getChar()); + } + + @Override + public int getSize() { + return 2; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar(exceptionTableIndex); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/targets/EmptyTarget.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/targets/EmptyTarget.java new file mode 100644 index 0000000..3d1602c --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/targets/EmptyTarget.java @@ -0,0 +1,20 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.targets; + +import org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.SizedByteCodec; + +import java.nio.ByteBuffer; + +public class EmptyTarget implements SizedByteCodec { + + @Override + public int getSize() { + return 0; + } + + public static EmptyTarget decode(ByteBuffer buffer) { + return new EmptyTarget(); + } + + @Override + public void encode(ByteBuffer buffer) {} +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/targets/FormalParameterTarget.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/targets/FormalParameterTarget.java new file mode 100644 index 0000000..d14e340 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/targets/FormalParameterTarget.java @@ -0,0 +1,30 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.targets; + +import org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.SizedByteCodec; + +import java.nio.ByteBuffer; + +public class FormalParameterTarget extends EmptyTarget { + + private byte formalParameterIndex; + + public FormalParameterTarget(byte formalParameterIndex) { + this.formalParameterIndex = formalParameterIndex; + } + + public byte getFormalParameterIndex() { return formalParameterIndex; } + + public static FormalParameterTarget decode(ByteBuffer buffer) { + return new FormalParameterTarget(buffer.get()); + } + + @Override + public int getSize() { + return 1; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.put(formalParameterIndex); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/targets/LocalVarTarget.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/targets/LocalVarTarget.java new file mode 100644 index 0000000..75cd6d2 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/targets/LocalVarTarget.java @@ -0,0 +1,74 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.targets; + +import org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.SizedByteCodec; + +import java.nio.ByteBuffer; + +public class LocalVarTarget extends EmptyTarget { + + public static class Table implements SizedByteCodec { + + private final char startPC; + private final char length; + private final char index; + + public Table(char startPC, char length, char index) { + this.startPC = startPC; + this.length = length; + this.index = index; + } + + public char getStartPC() { return startPC; } + public char getLength() { return length; } + public char getIndex() { return index; } + + public static Table decode(ByteBuffer buffer) { + return new Table(buffer.getChar(), buffer.getChar(), buffer.getChar()); + } + + @Override + public int getSize() { + return 6; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar(startPC); + buffer.putChar(length); + buffer.putChar(index); + } + } + + private final Table[] tables; + + public LocalVarTarget(Table[] tables) { + this.tables = tables; + } + + public Table[] getTables() { return tables; } + + public static LocalVarTarget decode(ByteBuffer buffer) { + Table[] tables = new Table[buffer.getChar()]; + for (int i = 0; i < tables.length; i++) { + tables[i] = Table.decode(buffer); + } + return new LocalVarTarget(tables); + } + + @Override + public int getSize() { + int baseSize = 2; + for (Table table : tables) + baseSize += table.getSize(); + return baseSize; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar((char) tables.length); + for (Table table : tables) + table.encode(buffer); + } + + +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/targets/OffsetTarget.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/targets/OffsetTarget.java new file mode 100644 index 0000000..902b2f4 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/targets/OffsetTarget.java @@ -0,0 +1,30 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.targets; + +import org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.SizedByteCodec; + +import java.nio.ByteBuffer; + +public class OffsetTarget extends EmptyTarget { + + private final char offset; + + public OffsetTarget(char offset) { + this.offset = offset; + } + + public char getOffset() { return offset; } + + public static OffsetTarget decode(ByteBuffer buffer) { + return new OffsetTarget(buffer.getChar()); + } + + @Override + public int getSize() { + return 2; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar(offset); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/targets/SuperTypeTarget.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/targets/SuperTypeTarget.java new file mode 100644 index 0000000..7dba896 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/targets/SuperTypeTarget.java @@ -0,0 +1,31 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.targets; + +import org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.SizedByteCodec; + +import java.nio.ByteBuffer; + +public class SuperTypeTarget extends EmptyTarget { + + private final char superTypeIndex; + + + public SuperTypeTarget(char superTypeIndex) { + this.superTypeIndex = superTypeIndex; + } + + public char getSuperTypeIndex() { return superTypeIndex; } + + public static SuperTypeTarget decode(ByteBuffer buffer) { + return new SuperTypeTarget(buffer.getChar()); + } + + @Override + public int getSize() { + return 1; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar(superTypeIndex); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/targets/ThrowsTarget.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/targets/ThrowsTarget.java new file mode 100644 index 0000000..88f5e75 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/targets/ThrowsTarget.java @@ -0,0 +1,28 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.targets; + +import java.nio.ByteBuffer; + +public class ThrowsTarget extends EmptyTarget { + + private final char throwsTypeIndex; + + public ThrowsTarget(char throwsTypeIndex) { + this.throwsTypeIndex = throwsTypeIndex; + } + + public char getThrowsTypeIndex() { return throwsTypeIndex; } + + public static ThrowsTarget decode(ByteBuffer buffer) { + return new ThrowsTarget(buffer.getChar()); + } + + @Override + public int getSize() { + return 2; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar(throwsTypeIndex); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/targets/TypeArgumentTarget.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/targets/TypeArgumentTarget.java new file mode 100644 index 0000000..1cb8d52 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/targets/TypeArgumentTarget.java @@ -0,0 +1,34 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.targets; + +import org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.SizedByteCodec; + +import java.nio.ByteBuffer; + +public class TypeArgumentTarget extends EmptyTarget { + + private final char offset; + private final byte typeArgumentIndex; + + public TypeArgumentTarget(char offset, byte typeArgumentIndex) { + this.offset = offset; + this.typeArgumentIndex = typeArgumentIndex; + } + + public char getOffset() { return offset; } + public byte getTypeArgumentIndex() { return typeArgumentIndex; } + + public static TypeArgumentTarget decode(ByteBuffer buffer) { + return new TypeArgumentTarget(buffer.getChar(), buffer.get()); + } + + @Override + public int getSize() { + return 3; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.putChar(offset); + buffer.put(typeArgumentIndex); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/targets/TypeParameterBoundTarget.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/targets/TypeParameterBoundTarget.java new file mode 100644 index 0000000..92da69b --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/targets/TypeParameterBoundTarget.java @@ -0,0 +1,36 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.targets; + +import org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.SizedByteCodec; + +import java.lang.annotation.Target; +import java.nio.ByteBuffer; + +public class TypeParameterBoundTarget extends EmptyTarget { + + private final byte typeParameterIndex; + private final byte boundIndex; + + + public TypeParameterBoundTarget(byte typeParameterIndex, byte boundIndex) { + this.typeParameterIndex = typeParameterIndex; + this.boundIndex = boundIndex; + } + + public byte getTypeParameterIndex() { return typeParameterIndex; } + public byte getBoundIndex() { return boundIndex; } + + public static TypeParameterBoundTarget decode(ByteBuffer buffer) { + return new TypeParameterBoundTarget(buffer.get(), buffer.get()); + } + + @Override + public int getSize() { + return 1; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.put(typeParameterIndex); + buffer.put(boundIndex); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/targets/TypeParameterTarget.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/targets/TypeParameterTarget.java new file mode 100644 index 0000000..4759f41 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/targets/TypeParameterTarget.java @@ -0,0 +1,31 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.targets; + +import org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.SizedByteCodec; + +import java.nio.ByteBuffer; + +public class TypeParameterTarget extends EmptyTarget { + + private final byte typeParameterIndex; + + + public TypeParameterTarget(byte typeParameterIndex) { + this.typeParameterIndex = typeParameterIndex; + } + + public byte getTypeParameterIndex() { return typeParameterIndex; } + + public static TypeParameterTarget decode(ByteBuffer buffer) { + return new TypeParameterTarget(buffer.get()); + } + + @Override + public int getSize() { + return 1; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.put(typeParameterIndex); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/values/AnnotationValue.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/values/AnnotationValue.java new file mode 100644 index 0000000..ae99549 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/values/AnnotationValue.java @@ -0,0 +1,32 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.values; + +import org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.Annotation; + +import java.nio.ByteBuffer; + +public class AnnotationValue extends ElementValue { + + private final Annotation value; + + public AnnotationValue(final Tag tag, final Annotation value) { + super(tag); + this.value = value; + } + + public Annotation getValue() { return value; } + + public static AnnotationValue decode(final Tag tag, ByteBuffer buffer) { + return new AnnotationValue(tag, Annotation.decode(buffer)); + } + + @Override + public int getSize() { + return super.getSize() + value.getSize(); + } + + @Override + public void encode(ByteBuffer buffer) { + super.encode(buffer); + value.encode(buffer); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/values/ArrayValue.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/values/ArrayValue.java new file mode 100644 index 0000000..4da5348 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/values/ArrayValue.java @@ -0,0 +1,35 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.values; + +import java.nio.ByteBuffer; + +public class ArrayValue extends ElementValue { + + private final ElementValue[] elements; + + public ArrayValue(final Tag tag, final ElementValue[] elements) { + super(tag); + this.elements = elements; + } + + public ElementValue[] getElements() { return elements; } + + public static ArrayValue decode(final Tag tag, ByteBuffer buffer) { + ElementValue[] elements = new ElementValue[buffer.getChar()]; + for (int i = 0; i < elements.length; i++) + elements[i] = ElementValue.decode(buffer); + return new ArrayValue(tag, elements); + } + + @Override + public int getSize() { + return super.getSize() + 2 + elements.length; + } + + @Override + public void encode(ByteBuffer buffer) { + super.encode(buffer); + buffer.putChar((char) elements.length); + for (ElementValue element : elements) + element.encode(buffer); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/values/ClassConstValue.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/values/ClassConstValue.java new file mode 100644 index 0000000..819e6a2 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/values/ClassConstValue.java @@ -0,0 +1,30 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.values; + +import java.nio.ByteBuffer; + +public class ClassConstValue extends ElementValue { + + private final char infoIndex; + + public ClassConstValue(final Tag tag, final char infoIndex) { + super(tag); + this.infoIndex = infoIndex; + } + + public char getInfoIndex() { return infoIndex; } + + public static ClassConstValue decode(final Tag tag, ByteBuffer buffer) { + return new ClassConstValue(tag, buffer.getChar()); + } + + @Override + public int getSize() { + return super.getSize() + 2; + } + + @Override + public void encode(ByteBuffer buffer) { + super.encode(buffer); + buffer.putChar(infoIndex); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/values/ConstValue.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/values/ConstValue.java new file mode 100644 index 0000000..9884606 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/values/ConstValue.java @@ -0,0 +1,30 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.values; + +import java.nio.ByteBuffer; + +public class ConstValue extends ElementValue { + + private final char valueIndex; + + public ConstValue(final Tag tag, final char valueIndex) { + super(tag); + this.valueIndex = valueIndex; + } + + public char getValueIndex() { return valueIndex; } + + public static ConstValue decode(final Tag tag, ByteBuffer buffer) { + return new ConstValue(tag, buffer.getChar()); + } + + @Override + public int getSize() { + return super.getSize() + 2; + } + + @Override + public void encode(ByteBuffer buffer) { + super.encode(buffer); + buffer.putChar(valueIndex); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/values/ElementValue.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/values/ElementValue.java new file mode 100644 index 0000000..2843811 --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/values/ElementValue.java @@ -0,0 +1,70 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.values; + +import org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.SizedByteCodec; + +import java.nio.ByteBuffer; + +public abstract class ElementValue implements SizedByteCodec { + + public enum Tag { + BYTE((byte) 'B'), + CHAR((byte) 'C'), + DOUBLE((byte) 'D'), + FLOAT((byte) 'F'), + INT((byte) 'I'), + LONG((byte) 'J'), + SHORT((byte) 'S'), + BOOLEAN((byte) 'Z'), + STRING((byte) 's'), + ENUMCONSTANT((byte) 'e'), + CLASS((byte) 'c'), + ANNOTATION((byte) '@'), + ARRAY((byte) '['); + + private final byte value; + + Tag(final byte value) { + this.value = value; + } + + public byte getValue() { return value; } + + public static Tag fromValue(final byte value) { + for (final Tag tag : Tag.values()) { + if (tag.getValue() == value) + return tag; + } + return null; + } + } + + private final Tag tag; + + public ElementValue(final Tag tag) { + this.tag = tag; + } + + public Tag getTag() { return tag; } + + public static ElementValue decode(ByteBuffer buffer) { + ElementValue.Tag tag = ElementValue.Tag.fromValue(buffer.get()); + assert tag != null; + return switch (tag) { + case BYTE, CHAR, DOUBLE, FLOAT, INT, LONG, SHORT, BOOLEAN, STRING -> ConstValue.decode(tag, buffer); + case ENUMCONSTANT -> EnumConstValue.decode(tag, buffer); + case CLASS -> ClassConstValue.decode(tag, buffer); + case ANNOTATION -> AnnotationValue.decode(tag, buffer); + case ARRAY -> ArrayValue.decode(tag, buffer); + }; + } + + @Override + public int getSize() { + return 1; + } + + @Override + public void encode(ByteBuffer buffer) { + buffer.put(tag.getValue()); + } +} diff --git a/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/values/EnumConstValue.java b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/values/EnumConstValue.java new file mode 100644 index 0000000..747470e --- /dev/null +++ b/src/main/java/org/momento/lycoris/mixins/mixin/classe/structures/infos/attributes/values/EnumConstValue.java @@ -0,0 +1,34 @@ +package org.momento.lycoris.mixins.mixin.classe.structures.infos.attributes.values; + +import java.nio.ByteBuffer; + +public class EnumConstValue extends ElementValue { + + private final char typeNameIndex; + private final char constNameIndex; + + public EnumConstValue(final Tag tag, final char typeNameIndex, final char constNameIndex) { + super(tag); + this.typeNameIndex = typeNameIndex; + this.constNameIndex = constNameIndex; + } + + public char getTypeNameIndex() { return typeNameIndex; } + public char getConstNameIndex() { return constNameIndex; } + + public static EnumConstValue decode(final Tag tag, ByteBuffer buffer) { + return new EnumConstValue(tag, buffer.getChar(), buffer.getChar()); + } + + @Override + public int getSize() { + return super.getSize() + 4; + } + + @Override + public void encode(ByteBuffer buffer) { + super.encode(buffer); + buffer.putChar(typeNameIndex); + buffer.putChar(constNameIndex); + } +} diff --git a/src/main/java/org/momento/lycoris/utils/Pair.java b/src/main/java/org/momento/lycoris/utils/Pair.java new file mode 100644 index 0000000..f844ea7 --- /dev/null +++ b/src/main/java/org/momento/lycoris/utils/Pair.java @@ -0,0 +1,19 @@ +package org.momento.lycoris.utils; + +public class Pair { + + private T first; + private U second; + + public Pair(T first, U second) { + this.first = first; + this.second = second; + } + + public T getFirst() { return first; } + public U getSecond() { return second; } + + public void setFirst(T first) { this.first = first; } + public void setSecond(U second) { this.second = second; } + +}