Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions src/main/java/org/momento/lycoris/Lycoris.java
Original file line number Diff line number Diff line change
@@ -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
Expand Down
30 changes: 30 additions & 0 deletions src/main/java/org/momento/lycoris/agent/JavaAgent.java
Original file line number Diff line number Diff line change
@@ -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;
}
}
9 changes: 9 additions & 0 deletions src/main/java/org/momento/lycoris/api/MixinRegistry.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.momento.lycoris.api;

public class MixinRegistry {

public static void addMixin() {

}

}
7 changes: 7 additions & 0 deletions src/main/java/org/momento/lycoris/mixins/Bootstrapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.momento.lycoris.mixins;

public class Bootstrapper {



}
11 changes: 11 additions & 0 deletions src/main/java/org/momento/lycoris/mixins/mixin/Mixin.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.momento.lycoris.mixins.mixin;

public class Mixin<T> {

private Class<T> classe;

public Mixin(Class<T> classe) {
this.classe = classe;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.momento.lycoris.mixins.mixin.classe;

import java.nio.ByteBuffer;

public interface ByteCodec {

void encode(ByteBuffer buffer);

}
Original file line number Diff line number Diff line change
@@ -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<AccessFlag> 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);
}
}
Original file line number Diff line number Diff line change
@@ -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);
}

}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Loading