mirror of
https://github.com/fluencelabs/asmble
synced 2025-04-25 06:42:22 +00:00
initial commit
This commit is contained in:
parent
4c65740d03
commit
23665fa16b
@ -81,8 +81,9 @@ public class SplitMethod {
|
|||||||
localsMap.put(index, args.size() - 1);
|
localsMap.put(index, args.size() - 1);
|
||||||
});
|
});
|
||||||
// Create the new method
|
// Create the new method
|
||||||
|
String name = orig.name.replace("<", "__").replace(">", "__") + "$split";
|
||||||
MethodNode newMethod = new MethodNode(api,
|
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);
|
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
|
// Add the written locals to the map that are not already there
|
||||||
int newLocalIndex = args.size();
|
int newLocalIndex = args.size();
|
||||||
@ -102,9 +103,13 @@ public class SplitMethod {
|
|||||||
for (int i = 0; i < splitPoint.length; i++) {
|
for (int i = 0; i < splitPoint.length; i++) {
|
||||||
AbstractInsnNode insn = orig.instructions.get(i + splitPoint.start);
|
AbstractInsnNode insn = orig.instructions.get(i + splitPoint.start);
|
||||||
// Skip frames
|
// Skip frames
|
||||||
if (insn instanceof FrameNode) continue;
|
if (insn instanceof FrameNode) {
|
||||||
|
insn.accept(newMethod);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// Store the label
|
// 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
|
// Change the local if needed
|
||||||
if (insn instanceof VarInsnNode) {
|
if (insn instanceof VarInsnNode) {
|
||||||
insn = insn.clone(Collections.emptyMap());
|
insn = insn.clone(Collections.emptyMap());
|
||||||
@ -183,11 +188,18 @@ public class SplitMethod {
|
|||||||
Set<Label> seenLabels = new HashSet<>();
|
Set<Label> seenLabels = new HashSet<>();
|
||||||
// Also keep track of the locals that have been stored, need to know
|
// Also keep track of the locals that have been stored, need to know
|
||||||
Set<Integer> seenStoredLocals = new HashSet<>();
|
Set<Integer> seenStoredLocals = new HashSet<>();
|
||||||
|
int paramOffset = 0;
|
||||||
// If this is an instance method, we consider "0" (i.e. "this") as seen
|
// 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
|
// Add the insns before split
|
||||||
for (int i = 0; i < splitPoint.start; i++) {
|
for (int i = 0; i < splitPoint.start; i++) {
|
||||||
AbstractInsnNode insn = orig.instructions.get(i + splitPoint.start);
|
AbstractInsnNode insn = orig.instructions.get(i);
|
||||||
// Skip frames
|
// Skip frames
|
||||||
if (insn instanceof FrameNode) continue;
|
if (insn instanceof FrameNode) continue;
|
||||||
// Record label
|
// 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
|
// 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++) {
|
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
|
// Skip frames
|
||||||
if (insn instanceof FrameNode) continue;
|
if (insn instanceof FrameNode) continue;
|
||||||
// Record label
|
// Record label
|
||||||
|
@ -123,11 +123,22 @@ public class Splitter implements Iterable<Splitter.SplitPoint> {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int shiftIndexValue = 1;
|
||||||
|
|
||||||
protected SplitPoint nextOrNull() {
|
protected SplitPoint nextOrNull() {
|
||||||
// Try for each index
|
// Try for each index
|
||||||
while (++currIndex + minSize <= insns.length) {
|
while (++currIndex + minSize <= insns.length) {
|
||||||
|
if(shiftIndexValue > 0) {
|
||||||
|
currIndex += shiftIndexValue;
|
||||||
|
} else {
|
||||||
|
++currIndex;
|
||||||
|
}
|
||||||
|
|
||||||
SplitPoint longest = longestForCurrIndex();
|
SplitPoint longest = longestForCurrIndex();
|
||||||
if (longest != null) return longest;
|
if (longest != null) {
|
||||||
|
shiftIndexValue += longest.length;
|
||||||
|
return longest;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -139,6 +150,8 @@ public class Splitter implements Iterable<Splitter.SplitPoint> {
|
|||||||
InsnTraverseInfo info = new InsnTraverseInfo();
|
InsnTraverseInfo info = new InsnTraverseInfo();
|
||||||
info.startIndex = currIndex;
|
info.startIndex = currIndex;
|
||||||
info.endIndex = Math.min(currIndex + maxSize - 1, insns.length - 1);
|
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
|
// Reduce the end based on try/catch blocks the start is in or that jump to
|
||||||
constrainEndByTryCatchBlocks(info);
|
constrainEndByTryCatchBlocks(info);
|
||||||
// Reduce the end based on any jumps within
|
// 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
|
// Reduce the end based on any jumps into
|
||||||
constrainEndByExternalJumps(info);
|
constrainEndByExternalJumps(info);
|
||||||
// Make sure we didn't reduce the end too far
|
// 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
|
// 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
|
// For the stack, we are going to use the
|
||||||
return splitPointFromInfo(info);
|
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) {
|
protected void constrainEndByTryCatchBlocks(InsnTraverseInfo info) {
|
||||||
// Go over all the try/catch blocks, sorted by earliest
|
// Go over all the try/catch blocks, sorted by earliest
|
||||||
for (TryCatchBlockNode block : tryCatchBlocks) {
|
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) {
|
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
|
// 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);
|
StackAndLocalTrackingAdapter adapter = new StackAndLocalTrackingAdapter(Splitter.this);
|
||||||
// Visit all of the insns up our start.
|
// 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
|
// 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
|
// Take the stack at the start and copy it off
|
||||||
List<Object> stackAtStart = new ArrayList<>(adapter.stack);
|
List<Object> stackAtStart = new ArrayList<>(adapter.stack);
|
||||||
// Reset some adapter state
|
// Reset some adapter state
|
||||||
@ -264,7 +329,8 @@ public class Splitter implements Iterable<Splitter.SplitPoint> {
|
|||||||
adapter.localsRead.clear();
|
adapter.localsRead.clear();
|
||||||
adapter.localsWritten.clear();
|
adapter.localsWritten.clear();
|
||||||
// Now go over the remaining range
|
// 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
|
// Build the split point
|
||||||
return new SplitPoint(
|
return new SplitPoint(
|
||||||
localMapFromAdapterLocalMap(adapter.localsRead, adapter.uninitializedTypes),
|
localMapFromAdapterLocalMap(adapter.localsRead, adapter.uninitializedTypes),
|
||||||
@ -279,7 +345,13 @@ public class Splitter implements Iterable<Splitter.SplitPoint> {
|
|||||||
protected SortedMap<Integer, Type> localMapFromAdapterLocalMap(
|
protected SortedMap<Integer, Type> localMapFromAdapterLocalMap(
|
||||||
SortedMap<Integer, Object> map, Map<Object, Object> uninitializedTypes) {
|
SortedMap<Integer, Object> map, Map<Object, Object> uninitializedTypes) {
|
||||||
SortedMap<Integer, Type> ret = new TreeMap<>();
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ import org.objectweb.asm.tree.ClassNode
|
|||||||
open class AsmToBinary(val splitMethod: SplitMethod? = SplitMethod(Opcodes.ASM6)) {
|
open class AsmToBinary(val splitMethod: SplitMethod? = SplitMethod(Opcodes.ASM6)) {
|
||||||
fun fromClassNode(
|
fun fromClassNode(
|
||||||
cn: ClassNode,
|
cn: ClassNode,
|
||||||
newClassWriter: () -> ClassWriter = { ClassWriter(ClassWriter.COMPUTE_FRAMES + ClassWriter.COMPUTE_MAXS) }
|
newClassWriter: () -> ClassWriter = { ClassWriter(ClassWriter.COMPUTE_FRAMES) }
|
||||||
): ByteArray {
|
): ByteArray {
|
||||||
while (true) {
|
while (true) {
|
||||||
val cw = newClassWriter()
|
val cw = newClassWriter()
|
||||||
@ -29,6 +29,7 @@ open class AsmToBinary(val splitMethod: SplitMethod? = SplitMethod(Opcodes.ASM6)
|
|||||||
require(cn.name == e.className)
|
require(cn.name == e.className)
|
||||||
val tooLargeIndex = cn.methods.indexOfFirst { it.name == e.methodName && it.desc == e.descriptor }
|
val tooLargeIndex = cn.methods.indexOfFirst { it.name == e.methodName && it.desc == e.descriptor }
|
||||||
require(tooLargeIndex >= 0)
|
require(tooLargeIndex >= 0)
|
||||||
|
val tt = cn.methods[tooLargeIndex]
|
||||||
val split = splitMethod.split(cn.name, cn.methods[tooLargeIndex])
|
val split = splitMethod.split(cn.name, cn.methods[tooLargeIndex])
|
||||||
split ?: throw IllegalStateException("Failed to split", e)
|
split ?: throw IllegalStateException("Failed to split", e)
|
||||||
// Change the split off method's name if there's already one
|
// Change the split off method's name if there's already one
|
||||||
|
Loading…
x
Reference in New Issue
Block a user