Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

finally fixed variable argument error in INDY using std_java mode #72

Merged
merged 5 commits into from
Jan 25, 2024
Merged
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
6 changes: 3 additions & 3 deletions obfuscator/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ repositories {
dependencies {
implementation project(':annotations')

implementation 'org.ow2.asm:asm:9.5'
implementation 'org.ow2.asm:asm-tree:9.5'
implementation 'org.ow2.asm:asm-commons:9.5'
implementation 'org.ow2.asm:asm:9.6'
implementation 'org.ow2.asm:asm-tree:9.6'
implementation 'org.ow2.asm:asm-commons:9.6'

implementation 'info.picocli:picocli:4.6.3'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,43 @@ private static void processIndy(ClassNode classNode, MethodNode methodNode,

InsnList bootstrapInstructions = new InsnList();
bootstrapInstructions.add(bootstrapStart); // 0


switch (platform) {
case STD_JAVA: {
Type[] bsmArguments = Type.getArgumentTypes(invokeDynamicInsnNode.bsm.getDesc());
int targetArgLength = bsmArguments.length - 3;
int originArgLength = invokeDynamicInsnNode.bsmArgs.length;

// process variable arguments for bsm like StringConcatFactory.makeConcatWithConstants(Lookup, String, MethodType, String, Object...)
// jvm will process variable argument automatically when using linkCallSite
// but if we want to use invokeWithArguments, we need to process variable argument manually
if (originArgLength < targetArgLength) {
Object[] newArgs = new Object[targetArgLength];
System.arraycopy(invokeDynamicInsnNode.bsmArgs, 0, newArgs, 0, originArgLength);

if (targetArgLength - originArgLength != 1)
throw new RuntimeException("Impossible BootstrapMethod Arguments Length");

if (bsmArguments[originArgLength + 3].getSort() == Type.ARRAY) {
newArgs[originArgLength] = new Object[0];
} else {
throw new RuntimeException("Last Argument of BootstrapMethod is NOT a Variable Argument");
}

invokeDynamicInsnNode.bsmArgs = newArgs;
} else if (originArgLength > targetArgLength || (bsmArguments[bsmArguments.length - 1].getSort() == Type.ARRAY && Type.getType(invokeDynamicInsnNode.bsmArgs[invokeDynamicInsnNode.bsmArgs.length - 1].getClass()).getSort() != Type.ARRAY)) {
Object[] newArgs = new Object[targetArgLength];
System.arraycopy(invokeDynamicInsnNode.bsmArgs, 0, newArgs, 0, targetArgLength - 1);

Object[] varArgs = new Object[originArgLength - targetArgLength + 1];
System.arraycopy(invokeDynamicInsnNode.bsmArgs, targetArgLength - 1, varArgs, 0, originArgLength - targetArgLength + 1);

newArgs[targetArgLength - 1] = varArgs;
invokeDynamicInsnNode.bsmArgs = newArgs;
}


if (bsmArguments.length < 3 || !bsmArguments[0].getDescriptor().equals("Ljava/lang/invoke/MethodHandles$Lookup;") ||
!bsmArguments[1].getDescriptor().equals("Ljava/lang/String;") ||
!bsmArguments[2].getDescriptor().equals("Ljava/lang/invoke/MethodType;")) {
Expand All @@ -43,32 +77,6 @@ private static void processIndy(ClassNode classNode, MethodNode methodNode,

Type[] arguments = Type.getArgumentTypes(invokeDynamicInsnNode.desc);

// compatibility with java 9+
// "aaa" + 1 + "bbb"
// -->
// iconst_1
//
// bsmArgs:
// "aaa\u0001bbb"
//
// invokedynamic StringConcatFactory.makeConcatWithConstants(Lookup, String, MethodType, String, Object...)CallSite

int targetArgLength = bsmArguments.length - 3;
int originArgLength = invokeDynamicInsnNode.bsmArgs.length;
if (originArgLength != targetArgLength) {
Object[] newArgs = new Object[targetArgLength];
System.arraycopy(invokeDynamicInsnNode.bsmArgs, 0, newArgs, 0, originArgLength);

for (int index = originArgLength; index < targetArgLength; index++) {
if (bsmArguments[index + 3].getSort() == Type.ARRAY) {
newArgs[index] = new Object[0];
} else {
throw new RuntimeException("Wrong argument type: " + bsmArguments[index + 3].getClass());
}
}

invokeDynamicInsnNode.bsmArgs = newArgs;
}

bootstrapInstructions.add(new LdcInsnNode(arguments.length)); // 1
bootstrapInstructions.add(new TypeInsnNode(Opcodes.ANEWARRAY, "java/lang/Object")); // 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,13 @@ public void postProcess(MethodContext context) {
context.method.instructions.clear();
if (Util.getFlag(context.clazz.access, Opcodes.ACC_INTERFACE)) {
InsnList list = new InsnList();

if (Util.getFlag(context.method.access, Opcodes.ACC_STATIC)) {
list.add(new LdcInsnNode(Type.getObjectType(context.clazz.name)));
}

int localVarsPosition = 0;

for (Type arg : context.argTypes) {
list.add(new VarInsnNode(arg.getOpcode(Opcodes.ILOAD), localVarsPosition));
localVarsPosition += arg.getSize();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package by.radioegor146;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.*;

import java.io.FileOutputStream;
import java.io.IOException;

public class StringConcatFactoryTest implements Opcodes {
public static void main(String[] args) throws IOException {
ClassNode classNode = new ClassNode();
classNode.version = V17;
classNode.access = ACC_PUBLIC | ACC_SUPER;
classNode.superName = "java/lang/Object";
classNode.name = "TestStringConcatFactory";
MethodNode method = new MethodNode(ACC_PUBLIC | ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);


method.instructions.add(new FieldInsnNode(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"));

method.instructions.add(new InsnNode(ICONST_1));
method.instructions.add(new InsnNode(ICONST_2));
method.instructions.add(new InsnNode(ICONST_3));

Handle handle = new Handle(H_INVOKESTATIC,
"java/lang/invoke/StringConcatFactory",
"makeConcatWithConstants",
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;",
false);
method.instructions.add(new InvokeDynamicInsnNode("makeConcatWithConstants",
"(III)Ljava/lang/String;",
handle,
"\u0001-\u0001-\u0001-\u0002-\u0002-\u0002",
3.14, 123, true));
method.instructions.add(new MethodInsnNode(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false));
method.instructions.add(new InsnNode(RETURN));
// System.out.println(String);
classNode.methods.add(method);

// read
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
classNode.accept(writer);

// save
FileOutputStream stream = new FileOutputStream("TestStringConcatFactory.class");
stream.write(writer.toByteArray());
stream.close();
}
}