|
|
|
|
@@ -6,6 +6,7 @@ import jef.asm.access.model.ConstructorParameterDescription;
|
|
|
|
|
import jef.asm.access.model.GetterDescription;
|
|
|
|
|
import jef.asm.access.model.SetterDescription;
|
|
|
|
|
import jef.asm.access.model.SuperConstructorParameterDescription;
|
|
|
|
|
import lombok.AllArgsConstructor;
|
|
|
|
|
import lombok.Getter;
|
|
|
|
|
import org.objectweb.asm.Handle;
|
|
|
|
|
import org.objectweb.asm.Label;
|
|
|
|
|
@@ -14,6 +15,7 @@ import org.objectweb.asm.Opcodes;
|
|
|
|
|
import org.objectweb.asm.Type;
|
|
|
|
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.Arrays;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.Set;
|
|
|
|
|
import java.util.Stack;
|
|
|
|
|
@@ -22,14 +24,15 @@ import java.util.function.Consumer;
|
|
|
|
|
@Getter
|
|
|
|
|
public class MethodAnalyzerVisitor extends MethodVisitor {//TODO verify param count
|
|
|
|
|
private final Consumer<ClassDescription> callback;
|
|
|
|
|
private final String methodClassName;
|
|
|
|
|
private final String declaringClassName;
|
|
|
|
|
private final String methodName;
|
|
|
|
|
private final int access;
|
|
|
|
|
private final String returnClassName;
|
|
|
|
|
private final String[] parameterClassNames;
|
|
|
|
|
private final String[] parameterClassNames;
|
|
|
|
|
|
|
|
|
|
private List<ConstructorParameterDescription> constructorParameterDescriptions = new ArrayList<>();
|
|
|
|
|
private List<SuperConstructorParameterDescription> superConstructorParameterDescriptions = new ArrayList<>();
|
|
|
|
|
private Stack<Integer> varIndexStack = new Stack<>();
|
|
|
|
|
private Stack<Var> varStack = new Stack<>();
|
|
|
|
|
private String fieldDeclaringClassName = null;
|
|
|
|
|
private String fieldName = null;
|
|
|
|
|
|
|
|
|
|
@@ -69,12 +72,19 @@ public class MethodAnalyzerVisitor extends MethodVisitor {//TODO verify param co
|
|
|
|
|
INVALID
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected MethodAnalyzerVisitor(int api, String methodClassName, String methodName, String returnClassName, String[] parameterClassNames, Consumer<ClassDescription> callback) {
|
|
|
|
|
protected MethodAnalyzerVisitor(int api, String declaringClassName, String methodName, int access, String returnClassName, String[] parameterClassNames, Consumer<ClassDescription> callback) {
|
|
|
|
|
super(api);
|
|
|
|
|
this.methodClassName = methodClassName;
|
|
|
|
|
this.declaringClassName = declaringClassName;
|
|
|
|
|
this.methodName = methodName;
|
|
|
|
|
this.access = access;
|
|
|
|
|
this.returnClassName = returnClassName;
|
|
|
|
|
this.parameterClassNames = parameterClassNames;
|
|
|
|
|
if (hasThis()) {
|
|
|
|
|
this.parameterClassNames = new String[parameterClassNames.length + 1];
|
|
|
|
|
System.arraycopy(parameterClassNames, 0, this.parameterClassNames, 1, parameterClassNames.length);
|
|
|
|
|
this.parameterClassNames[0] = declaringClassName;
|
|
|
|
|
} else {
|
|
|
|
|
this.parameterClassNames = parameterClassNames;
|
|
|
|
|
}
|
|
|
|
|
this.callback = callback;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -122,7 +132,11 @@ public class MethodAnalyzerVisitor extends MethodVisitor {//TODO verify param co
|
|
|
|
|
@Override
|
|
|
|
|
public void visitVarInsn(int opcode, int varIndex) {
|
|
|
|
|
if (Set.of(Opcodes.ILOAD, Opcodes.LLOAD, Opcodes.FLOAD, Opcodes.DLOAD, Opcodes.ALOAD).contains(opcode)) {
|
|
|
|
|
varIndexStack.push(varIndex);
|
|
|
|
|
System.out.println(methodName + ": load_" + varIndex + ": " + (varIndex < parameterClassNames.length ? parameterClassNames[varIndex] : null));
|
|
|
|
|
if (varIndex == 5) {
|
|
|
|
|
int x = 0;
|
|
|
|
|
}
|
|
|
|
|
varStack.push(new Var(varIndex < parameterClassNames.length ? parameterClassNames[varIndex] : null, varIndex));
|
|
|
|
|
}
|
|
|
|
|
if (opcode == Opcodes.ALOAD && varIndex == 0) {
|
|
|
|
|
if (constructorStep == ConstructorSteps.CODE) {
|
|
|
|
|
@@ -160,6 +174,10 @@ public class MethodAnalyzerVisitor extends MethodVisitor {//TODO verify param co
|
|
|
|
|
super.visitVarInsn(opcode, varIndex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private boolean hasThis() {
|
|
|
|
|
return (access & Opcodes.ACC_STATIC) == 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void visitTypeInsn(int opcode, String type) {
|
|
|
|
|
constructorStep = ConstructorSteps.INVALID;
|
|
|
|
|
@@ -170,15 +188,15 @@ public class MethodAnalyzerVisitor extends MethodVisitor {//TODO verify param co
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void visitFieldInsn(int opcode, String owner, String name, String descriptor) {
|
|
|
|
|
var lastLoadedVarIndex = -1;
|
|
|
|
|
Var lastLoadedVar = null;
|
|
|
|
|
if (opcode == Opcodes.PUTFIELD) {
|
|
|
|
|
lastLoadedVarIndex = varIndexStack.pop();
|
|
|
|
|
varIndexStack.pop();
|
|
|
|
|
lastLoadedVar = varStack.pop();
|
|
|
|
|
varStack.pop();
|
|
|
|
|
constructorStep = constructorStep == ConstructorSteps.VAR_LOAD ? ConstructorSteps.PUT_FIELD : ConstructorSteps.INVALID;
|
|
|
|
|
getterStep = GetterSteps.INVALID;
|
|
|
|
|
setterStep = setterStep == SetterSteps.VAR_LOAD ? SetterSteps.PUT_FIELD : SetterSteps.INVALID;
|
|
|
|
|
} else if (opcode == Opcodes.GETFIELD) {
|
|
|
|
|
lastLoadedVarIndex = varIndexStack.pop();
|
|
|
|
|
lastLoadedVar = varStack.pop();
|
|
|
|
|
constructorStep = ConstructorSteps.INVALID;
|
|
|
|
|
getterStep = getterStep == GetterSteps.ALOAD_0 ? GetterSteps.GET_FIELD : GetterSteps.INVALID;
|
|
|
|
|
setterStep = SetterSteps.INVALID;
|
|
|
|
|
@@ -189,21 +207,21 @@ public class MethodAnalyzerVisitor extends MethodVisitor {//TODO verify param co
|
|
|
|
|
}
|
|
|
|
|
this.fieldDeclaringClassName = owner.replace("/", ".");
|
|
|
|
|
this.fieldName = name;
|
|
|
|
|
this.constructorParameterDescriptions.add(new ConstructorParameterDescription(lastLoadedVarIndex - 1, this.fieldDeclaringClassName, this.fieldName)); //lastLoadedVarIndex - 1 because arg 0 of an instance function is always this
|
|
|
|
|
this.constructorParameterDescriptions.add(new ConstructorParameterDescription(lastLoadedVar.getIndex() - 1, lastLoadedVar.getClassName(), this.fieldDeclaringClassName, this.fieldName)); //lastLoadedVar - 1 because arg 0 of an instance function is always this
|
|
|
|
|
super.visitFieldInsn(opcode, owner, name, descriptor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
|
|
|
|
|
var argIndexes = new ArrayList<Integer>();
|
|
|
|
|
var args = new ArrayList<Var>();
|
|
|
|
|
for (var i = 0; i < Type.getArgumentTypes(descriptor).length; i++) {
|
|
|
|
|
argIndexes.add(0, varIndexStack.pop());
|
|
|
|
|
args.add(0, varStack.pop());
|
|
|
|
|
}
|
|
|
|
|
varIndexStack.pop(); // also pop ALOAD0
|
|
|
|
|
varStack.pop(); // also pop ALOAD0
|
|
|
|
|
if (opcode == Opcodes.INVOKESPECIAL && name.equals("<init>") //TODO does it have to be called '<init>' or is this just convention
|
|
|
|
|
&& Set.of(ConstructorSteps.PRE_SUPER_INIT_ALOAD_0, ConstructorSteps.PRE_SUPER_INIT_VAR_LOAD).contains(constructorStep)) {
|
|
|
|
|
for (int i = 0; i < argIndexes.size(); i++) {
|
|
|
|
|
superConstructorParameterDescriptions.add(new SuperConstructorParameterDescription(argIndexes.get(i), i));
|
|
|
|
|
&& Set.of(ConstructorSteps.PRE_SUPER_INIT_ALOAD_0, ConstructorSteps.PRE_SUPER_INIT_VAR_LOAD).contains(constructorStep)) {
|
|
|
|
|
for (int i = 0; i < args.size(); i++) {
|
|
|
|
|
superConstructorParameterDescriptions.add(new SuperConstructorParameterDescription(args.get(i).getIndex(), i));
|
|
|
|
|
}
|
|
|
|
|
constructorStep = ConstructorSteps.SUPER_INIT;
|
|
|
|
|
} else {
|
|
|
|
|
@@ -232,9 +250,6 @@ public class MethodAnalyzerVisitor extends MethodVisitor {//TODO verify param co
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void visitLabel(Label label) {
|
|
|
|
|
// constructorStep = ConstructorSteps.INVALID;
|
|
|
|
|
// getterStep = GetterSteps.INVALID;
|
|
|
|
|
// setterStep = SetterSteps.INVALID;
|
|
|
|
|
super.visitLabel(label);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -300,9 +315,16 @@ public class MethodAnalyzerVisitor extends MethodVisitor {//TODO verify param co
|
|
|
|
|
public void visitEnd() {
|
|
|
|
|
super.visitEnd();
|
|
|
|
|
List<ConstructorDescription> constructors = constructorStep == ConstructorSteps.RETURN ? List.of(new ConstructorDescription(constructorParameterDescriptions, superConstructorParameterDescriptions)) : List.of();
|
|
|
|
|
List<GetterDescription> getters = getterStep == GetterSteps.RETURN ? List.of(new GetterDescription(methodClassName, methodName, returnClassName, parameterClassNames, fieldDeclaringClassName, fieldName)) : List.of();
|
|
|
|
|
List<SetterDescription> setters = setterStep == SetterSteps.RETURN ? List.of(new SetterDescription(methodClassName, methodName, returnClassName, parameterClassNames, fieldDeclaringClassName, fieldName)) : List.of();
|
|
|
|
|
List<GetterDescription> getters = getterStep == GetterSteps.RETURN ? List.of(new GetterDescription(declaringClassName, methodName, returnClassName, Arrays.stream(parameterClassNames).skip(hasThis() ? 1 : 0).toArray(String[]::new), fieldDeclaringClassName, fieldName)) : List.of();
|
|
|
|
|
List<SetterDescription> setters = setterStep == SetterSteps.RETURN ? List.of(new SetterDescription(declaringClassName, methodName, returnClassName, Arrays.stream(parameterClassNames).skip(hasThis() ? 1 : 0).toArray(String[]::new), fieldDeclaringClassName, fieldName)) : List.of();
|
|
|
|
|
var access = new ClassDescription(null, null, null, constructors, getters, setters);
|
|
|
|
|
callback.accept(access);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@AllArgsConstructor
|
|
|
|
|
@Getter
|
|
|
|
|
private static class Var {
|
|
|
|
|
private final String className;
|
|
|
|
|
private final int index;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|