initial commit

This commit is contained in:
vms 2019-09-05 17:45:42 +03:00
parent 4c65740d03
commit 23665fa16b
4 changed files with 115 additions and 30 deletions

View File

@ -81,8 +81,9 @@ public class SplitMethod {
localsMap.put(index, args.size() - 1);
});
// Create the new method
String name = orig.name.replace("<", "__").replace(">", "__") + "$split";
MethodNode newMethod = new MethodNode(api,
Opcodes.ACC_STATIC + Opcodes.ACC_PRIVATE + Opcodes.ACC_SYNTHETIC, orig.name + "$split",
Opcodes.ACC_STATIC + Opcodes.ACC_PRIVATE + Opcodes.ACC_SYNTHETIC, name,
Type.getMethodDescriptor(Type.getType(Object[].class), args.toArray(new Type[0])), null, null);
// Add the written locals to the map that are not already there
int newLocalIndex = args.size();
@ -102,9 +103,13 @@ public class SplitMethod {
for (int i = 0; i < splitPoint.length; i++) {
AbstractInsnNode insn = orig.instructions.get(i + splitPoint.start);
// Skip frames
if (insn instanceof FrameNode) continue;
if (insn instanceof FrameNode) {
insn.accept(newMethod);
continue;
}
// Store the label
if (insn instanceof LabelNode) seenLabels.add(((LabelNode) insn).getLabel());
if (insn instanceof LabelNode)
seenLabels.add(((LabelNode) insn).getLabel());
// Change the local if needed
if (insn instanceof VarInsnNode) {
insn = insn.clone(Collections.emptyMap());
@ -183,11 +188,18 @@ public class SplitMethod {
Set<Label> seenLabels = new HashSet<>();
// Also keep track of the locals that have been stored, need to know
Set<Integer> seenStoredLocals = new HashSet<>();
int paramOffset = 0;
// If this is an instance method, we consider "0" (i.e. "this") as seen
if ((orig.access & Opcodes.ACC_STATIC) == 0) seenStoredLocals.add(0);
if ((orig.access & Opcodes.ACC_STATIC) == 0) {
seenStoredLocals.add(0);
paramOffset = 1;
}
// We also consider parameters as seen
int paramCount = Type.getArgumentTypes(orig.desc).length;
for (int i = 0; i < paramCount; i++) seenStoredLocals.add(i + paramOffset);
// Add the insns before split
for (int i = 0; i < splitPoint.start; i++) {
AbstractInsnNode insn = orig.instructions.get(i + splitPoint.start);
AbstractInsnNode insn = orig.instructions.get(i);
// Skip frames
if (insn instanceof FrameNode) continue;
// Record label
@ -255,7 +267,7 @@ public class SplitMethod {
}
// Now we have restored all locals and all stack...add the rest of the insns after the split
for (int i = splitPoint.start + splitPoint.length; i < orig.instructions.size(); i++) {
AbstractInsnNode insn = orig.instructions.get(i + splitPoint.start);
AbstractInsnNode insn = orig.instructions.get(i);
// Skip frames
if (insn instanceof FrameNode) continue;
// Record label

View File

@ -123,11 +123,22 @@ public class Splitter implements Iterable<Splitter.SplitPoint> {
return ret;
}
private int shiftIndexValue = 1;
protected SplitPoint nextOrNull() {
// Try for each index
while (++currIndex + minSize <= insns.length) {
if(shiftIndexValue > 0) {
currIndex += shiftIndexValue;
} else {
++currIndex;
}
SplitPoint longest = longestForCurrIndex();
if (longest != null) return longest;
if (longest != null) {
shiftIndexValue += longest.length;
return longest;
}
}
return null;
}
@ -139,6 +150,8 @@ public class Splitter implements Iterable<Splitter.SplitPoint> {
InsnTraverseInfo info = new InsnTraverseInfo();
info.startIndex = currIndex;
info.endIndex = Math.min(currIndex + maxSize - 1, insns.length - 1);
// Reduce the end by special calls
constrainEndByInvokeSpecial(info);
// Reduce the end based on try/catch blocks the start is in or that jump to
constrainEndByTryCatchBlocks(info);
// Reduce the end based on any jumps within
@ -146,12 +159,23 @@ public class Splitter implements Iterable<Splitter.SplitPoint> {
// Reduce the end based on any jumps into
constrainEndByExternalJumps(info);
// Make sure we didn't reduce the end too far
if (info.getSize() < minSize) return null;
//if (info.getSize() < minSize) return null;
// Now that we have our largest range from the start index, we can go over each updating the local refs and stack
// For the stack, we are going to use the
return splitPointFromInfo(info);
}
protected void constrainEndByInvokeSpecial(InsnTraverseInfo info) {
// Can't have an invoke special of <init>
for (int i = info.startIndex; i <= info.endIndex; i++) {
AbstractInsnNode node = insns[i];
if (node.getOpcode() == Opcodes.INVOKESPECIAL && ((MethodInsnNode) node).name.equals("<init>")) {
info.endIndex = Math.max(info.startIndex, i - 1);
return;
}
}
}
protected void constrainEndByTryCatchBlocks(InsnTraverseInfo info) {
// Go over all the try/catch blocks, sorted by earliest
for (TryCatchBlockNode block : tryCatchBlocks) {
@ -251,12 +275,53 @@ public class Splitter implements Iterable<Splitter.SplitPoint> {
}
}
// visits instructions with given adaptor
private void visitInstructions(int begin, int end, AnalyzerAdapter adapter) {
if(end - begin <= 0) {
return;
}
List<Object> stack = new ArrayList<>();
List<Object> locals = new ArrayList<>();
boolean gotoSeen = false;
for (int i = begin; i < end; ++i) {
if(insns[i].getOpcode() == Opcodes.GOTO || insns[i].getOpcode() == Opcodes.ATHROW ||
(insns[i].getOpcode() >= Opcodes.TABLESWITCH && insns[i].getOpcode() <= Opcodes.RETURN) ) {
stack = adapter.stack;
locals = adapter.locals;
gotoSeen = true;
} else if(gotoSeen) {
gotoSeen = false;
if(insns[i].getOpcode() >= Opcodes.IRETURN && insns[i].getOpcode() <= Opcodes.RETURN) {
stack.clear();
locals.clear();
}
adapter.visitFrame(Opcodes.F_NEW, locals.size(), locals.toArray(), stack.size(), stack.toArray());
}
insns[i].accept(adapter);
}
AbstractInsnNode lastInsn = insns[end - 1];
if(lastInsn.getOpcode() == Opcodes.GOTO || lastInsn.getOpcode() == Opcodes.ATHROW ||
(lastInsn.getOpcode() >= Opcodes.TABLESWITCH && lastInsn.getOpcode() <= Opcodes.RETURN) ) {
if(lastInsn.getOpcode() >= Opcodes.IRETURN && lastInsn.getOpcode() <= Opcodes.RETURN) {
stack.clear();
locals.clear();
}
adapter.visitFrame(Opcodes.F_NEW, locals.size(), locals.toArray(), stack.size(), stack.toArray());
}
}
protected SplitPoint splitPointFromInfo(InsnTraverseInfo info) {
// We're going to use the analyzer adapter and run it for the up until the end, a step at a time
StackAndLocalTrackingAdapter adapter = new StackAndLocalTrackingAdapter(Splitter.this);
// Visit all of the insns up our start.
// XXX: I checked the source of AnalyzerAdapter to confirm I don't need any of the surrounding stuff
for (int i = 0; i < info.startIndex; i++) insns[i].accept(adapter);
visitInstructions(0, info.startIndex, adapter);
// Take the stack at the start and copy it off
List<Object> stackAtStart = new ArrayList<>(adapter.stack);
// Reset some adapter state
@ -264,7 +329,8 @@ public class Splitter implements Iterable<Splitter.SplitPoint> {
adapter.localsRead.clear();
adapter.localsWritten.clear();
// Now go over the remaining range
for (int i = info.startIndex; i <= info.endIndex; i++) insns[i].accept(adapter);
visitInstructions(info.startIndex, info.endIndex + 1, adapter);
// Build the split point
return new SplitPoint(
localMapFromAdapterLocalMap(adapter.localsRead, adapter.uninitializedTypes),
@ -279,7 +345,13 @@ public class Splitter implements Iterable<Splitter.SplitPoint> {
protected SortedMap<Integer, Type> localMapFromAdapterLocalMap(
SortedMap<Integer, Object> map, Map<Object, Object> uninitializedTypes) {
SortedMap<Integer, Type> ret = new TreeMap<>();
map.forEach((k, v) -> ret.put(k, typeFromAdapterStackItem(v, uninitializedTypes)));
map.forEach((k, v) ->
{
if(v != Opcodes.TOP ) {
ret.put(k, typeFromAdapterStackItem(v, uninitializedTypes));
}
}
);
return ret;
}

View File

@ -13,7 +13,7 @@ import org.objectweb.asm.tree.ClassNode
open class AsmToBinary(val splitMethod: SplitMethod? = SplitMethod(Opcodes.ASM6)) {
fun fromClassNode(
cn: ClassNode,
newClassWriter: () -> ClassWriter = { ClassWriter(ClassWriter.COMPUTE_FRAMES + ClassWriter.COMPUTE_MAXS) }
newClassWriter: () -> ClassWriter = { ClassWriter(ClassWriter.COMPUTE_FRAMES) }
): ByteArray {
while (true) {
val cw = newClassWriter()
@ -29,6 +29,7 @@ open class AsmToBinary(val splitMethod: SplitMethod? = SplitMethod(Opcodes.ASM6)
require(cn.name == e.className)
val tooLargeIndex = cn.methods.indexOfFirst { it.name == e.methodName && it.desc == e.descriptor }
require(tooLargeIndex >= 0)
val tt = cn.methods[tooLargeIndex]
val split = splitMethod.split(cn.name, cn.methods[tooLargeIndex])
split ?: throw IllegalStateException("Failed to split", e)
// Change the split off method's name if there's already one