mirror of
https://github.com/fluencelabs/asmble
synced 2025-04-24 14:22:20 +00:00
initial commit
This commit is contained in:
parent
4c65740d03
commit
23665fa16b
@ -81,9 +81,10 @@ 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",
|
||||
Type.getMethodDescriptor(Type.getType(Object[].class), args.toArray(new Type[0])), null, null);
|
||||
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();
|
||||
for (Integer key : splitPoint.localsWritten.keySet()) {
|
||||
@ -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());
|
||||
@ -168,13 +173,13 @@ public class SplitMethod {
|
||||
}
|
||||
|
||||
protected MethodNode createTrimmedMethod(String owner, MethodNode orig,
|
||||
MethodNode splitOff, Splitter.SplitPoint splitPoint) {
|
||||
MethodNode splitOff, Splitter.SplitPoint splitPoint) {
|
||||
// The trimmed method is the same as the original, yet the split area is replaced with a call to the split off
|
||||
// portion. Before calling the split-off, we have to add locals to the stack part. Then afterwards, we have to
|
||||
// replace the stack and written locals.
|
||||
// Effectively clone the orig
|
||||
MethodNode newMethod = new MethodNode(api, orig.access, orig.name, orig.desc,
|
||||
orig.signature, orig.exceptions.toArray(new String[0]));
|
||||
orig.signature, orig.exceptions.toArray(new String[0]));
|
||||
orig.accept(newMethod);
|
||||
// Remove all insns, we'll re-add the ones outside the split range
|
||||
newMethod.instructions.clear();
|
||||
@ -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
|
||||
|
@ -67,7 +67,7 @@ public class Splitter implements Iterable<Splitter.SplitPoint> {
|
||||
public final int length;
|
||||
|
||||
public SplitPoint(SortedMap<Integer, Type> localsRead, SortedMap<Integer, Type>localsWritten,
|
||||
List<Type> neededFromStackAtStart, List<Type> putOnStackAtEnd, int start, int length) {
|
||||
List<Type> neededFromStackAtStart, List<Type> putOnStackAtEnd, int start, int length) {
|
||||
this.localsRead = localsRead;
|
||||
this.localsWritten = localsWritten;
|
||||
this.neededFromStackAtStart = neededFromStackAtStart;
|
||||
@ -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,27 +329,34 @@ 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),
|
||||
localMapFromAdapterLocalMap(adapter.localsWritten, adapter.uninitializedTypes),
|
||||
typesFromAdapterStackRange(stackAtStart, adapter.lowestStackSize, adapter.uninitializedTypes),
|
||||
typesFromAdapterStackRange(adapter.stack, adapter.lowestStackSize, adapter.uninitializedTypes),
|
||||
info.startIndex,
|
||||
info.getSize()
|
||||
localMapFromAdapterLocalMap(adapter.localsRead, adapter.uninitializedTypes),
|
||||
localMapFromAdapterLocalMap(adapter.localsWritten, adapter.uninitializedTypes),
|
||||
typesFromAdapterStackRange(stackAtStart, adapter.lowestStackSize, adapter.uninitializedTypes),
|
||||
typesFromAdapterStackRange(adapter.stack, adapter.lowestStackSize, adapter.uninitializedTypes),
|
||||
info.startIndex,
|
||||
info.getSize()
|
||||
);
|
||||
}
|
||||
|
||||
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<>();
|
||||
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;
|
||||
}
|
||||
|
||||
protected List<Type> typesFromAdapterStackRange(
|
||||
List<Object> stack, int start, Map<Object, Object> uninitializedTypes) {
|
||||
List<Object> stack, int start, Map<Object, Object> uninitializedTypes) {
|
||||
List<Type> ret = new ArrayList<>();
|
||||
for (int i = start; i < stack.size(); i++) {
|
||||
Object item = stack.get(i);
|
||||
|
@ -19,7 +19,7 @@ class Util {
|
||||
|
||||
static boolean isStoreOp(int opcode) {
|
||||
return opcode == Opcodes.ISTORE || opcode == Opcodes.LSTORE || opcode == Opcodes.FSTORE ||
|
||||
opcode == Opcodes.DSTORE || opcode == Opcodes.ASTORE;
|
||||
opcode == Opcodes.DSTORE || opcode == Opcodes.ASTORE;
|
||||
}
|
||||
|
||||
static int storeOpFromType(Type type) {
|
||||
@ -55,13 +55,13 @@ class Util {
|
||||
|
||||
static void unboxStackIfNecessary(Type type, MethodNode method) {
|
||||
if (type == Type.INT_TYPE) method.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
|
||||
"java/lang/Integer", "intValue", Type.getMethodDescriptor(Type.INT_TYPE), false);
|
||||
"java/lang/Integer", "intValue", Type.getMethodDescriptor(Type.INT_TYPE), false);
|
||||
else if (type == Type.FLOAT_TYPE) method.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
|
||||
"java/lang/Float", "floatValue", Type.getMethodDescriptor(Type.FLOAT_TYPE), false);
|
||||
"java/lang/Float", "floatValue", Type.getMethodDescriptor(Type.FLOAT_TYPE), false);
|
||||
else if (type == Type.LONG_TYPE) method.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
|
||||
"java/lang/Long", "longValue", Type.getMethodDescriptor(Type.LONG_TYPE), false);
|
||||
"java/lang/Long", "longValue", Type.getMethodDescriptor(Type.LONG_TYPE), false);
|
||||
else if (type == Type.DOUBLE_TYPE) method.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
|
||||
"java/lang/Double", "doubleValue", Type.getMethodDescriptor(Type.DOUBLE_TYPE), false);
|
||||
"java/lang/Double", "doubleValue", Type.getMethodDescriptor(Type.DOUBLE_TYPE), false);
|
||||
}
|
||||
|
||||
static AbstractInsnNode intConst(int v) {
|
||||
@ -79,6 +79,6 @@ class Util {
|
||||
|
||||
static MethodInsnNode boxCall(Class<?> boxType, Type primType) {
|
||||
return new MethodInsnNode(Opcodes.INVOKESTATIC, Type.getInternalName(boxType),
|
||||
"valueOf", Type.getMethodDescriptor(Type.getType(boxType), primType), false);
|
||||
"valueOf", Type.getMethodDescriptor(Type.getType(boxType), primType), false);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user