Skip to content

Instantly share code, notes, and snippets.

@jponge
Created September 10, 2019 09:03
Show Gist options
  • Save jponge/7174ba24a3793eecf38f670c5bd20288 to your computer and use it in GitHub Desktop.
Save jponge/7174ba24a3793eecf38f670c5bd20288 to your computer and use it in GitHub Desktop.
Rewriting Vert.x JsonObject to make it JS-friendly in ES4X / GraalVM
import org.objectweb.asm.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import static org.objectweb.asm.ClassWriter.COMPUTE_FRAMES;
import static org.objectweb.asm.Opcodes.*;
public class RewriteJsonObject {
public static void main(String[] args) throws Throwable {
if (args.length != 2) {
System.out.println("Give me 2 arguments: the path to the original class file and the path to the rewritten one");
System.exit(1);
} else {
rewrite(new File(args[0]), new File(args[1]));
}
}
static void rewrite(File source, File target) throws IOException {
ClassReader classReader = new ClassReader(new FileInputStream(source));
ClassWriter classWriter = new ClassWriter(COMPUTE_FRAMES) {
@Override
protected String getCommonSuperClass(String type1, String type2) {
// Because we can't load dependent classes, this pleases the frame computation algorithm
return "java/lang/Object";
}
};
Enhancer enhancer = new Enhancer(ASM7, classWriter);
classReader.accept(enhancer, 0);
FileOutputStream out = new FileOutputStream(target);
out.write(classWriter.toByteArray());
out.close();
}
static class Enhancer extends ClassVisitor {
public Enhancer(int api, ClassVisitor classVisitor) {
super(api, classVisitor);
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
String[] newInterfaces = new String[interfaces.length + 1];
System.arraycopy(interfaces, 0, newInterfaces, 0, interfaces.length);
newInterfaces[interfaces.length] = "org/graalvm/polyglot/proxy/ProxyObject";
String newSignature = signature + "Lorg/graalvm/polyglot/proxy/ProxyObject;";
System.out.println("sig = " + signature);
System.out.println("sig' = " + newSignature);
System.out.println(Arrays.toString(interfaces));
System.out.println(Arrays.toString(newInterfaces));
super.visit(version, access, name, newSignature, superName, newInterfaces);
}
@Override
public void visitEnd() {
generateGetMember();
generateGetMemberKeys();
generateHasMember();
generatePutMember();
generateRemoveMember();
super.visitEnd();
}
private void generateGetMember() {
MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, "getMember", "(Ljava/lang/String;)Ljava/lang/Object;", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, "io/vertx/core/json/JsonObject", "getValue", "(Ljava/lang/String;)Ljava/lang/Object;", false);
mv.visitInsn(ARETURN);
mv.visitMaxs(2, 2);
mv.visitEnd();
}
private void generateGetMemberKeys() {
MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, "getMemberKeys", "()Ljava/lang/Object;", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEVIRTUAL, "io/vertx/core/json/JsonObject", "fieldNames", "()Ljava/util/Set;", false);
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Set", "toArray", "()[Ljava/lang/Object;", true);
mv.visitInsn(ARETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
private void generateHasMember() {
MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, "hasMember", "(Ljava/lang/String;)Z", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, "io/vertx/core/json/JsonObject", "containsKey", "(Ljava/lang/String;)Z", false);
mv.visitInsn(IRETURN);
mv.visitMaxs(2, 2);
mv.visitEnd();
}
private void generatePutMember() {
MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, "putMember", "(Ljava/lang/String;Lorg/graalvm/polyglot/Value;)V", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
mv.visitLdcInsn(Type.getType(Object.class));
mv.visitMethodInsn(INVOKEVIRTUAL, "org/graalvm/polyglot/Value", "as", "(Ljava/lang/Class;)Ljava/lang/Object;", false);
mv.visitMethodInsn(INVOKEVIRTUAL, "io/vertx/core/json/JsonObject", "put", "(Ljava/lang/String;Ljava/lang/Object;)Lio/vertx/core/json/JsonObject;", false);
mv.visitInsn(POP);
mv.visitInsn(RETURN);
mv.visitMaxs(3, 3);
mv.visitEnd();
}
private void generateRemoveMember() {
MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, "removeMember", "(Ljava/lang/String;)Z", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, "io/vertx/core/json/JsonObject", "remove", "(Ljava/lang/String;)Ljava/lang/Object;", false);
Label falseLabel = new Label();
mv.visitJumpInsn(IFNULL, falseLabel);
mv.visitLdcInsn(true);
mv.visitInsn(IRETURN);
mv.visitLabel(falseLabel);
mv.visitLdcInsn(false);
mv.visitInsn(IRETURN);
mv.visitMaxs(2, 2);
mv.visitEnd();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment