Created
September 10, 2019 09:03
-
-
Save jponge/7174ba24a3793eecf38f670c5bd20288 to your computer and use it in GitHub Desktop.
Rewriting Vert.x JsonObject to make it JS-friendly in ES4X / GraalVM
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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