@Grapes([ @Grab(group = 'org.ow2.asm', module = 'asm', version = '9.4'), @Grab(group = 'org.ow2.asm', module = 'asm-commons', version = '9.4') ]) import org.objectweb.asm.ClassWriter import org.objectweb.asm.FieldVisitor import org.objectweb.asm.Handle import org.objectweb.asm.Label import org.objectweb.asm.MethodVisitor import org.objectweb.asm.Type import org.objectweb.asm.commons.GeneratorAdapter import org.objectweb.asm.commons.Method import java.lang.invoke.CallSite import java.lang.invoke.ConstantCallSite import java.lang.invoke.MethodHandle import java.lang.invoke.MethodHandles import java.lang.invoke.MethodType import static org.objectweb.asm.Opcodes.ACC_FINAL import static org.objectweb.asm.Opcodes.ACC_PRIVATE import static org.objectweb.asm.Opcodes.ACC_PUBLIC import static org.objectweb.asm.Opcodes.ALOAD import static org.objectweb.asm.Opcodes.H_INVOKESTATIC import static org.objectweb.asm.Opcodes.INVOKESPECIAL import static org.objectweb.asm.Opcodes.PUTFIELD import static org.objectweb.asm.Opcodes.RETURN import static org.objectweb.asm.Opcodes.V1_8
SimpleClassLoader simpleClassLoader = new SimpleClassLoader(Bootstrap.class.classLoader) Class<?> indyStringClass = simpleClassLoader.defineClass("IndyString", generateIndyStringClassBytes()) Object indyString = indyStringClass.getConstructor(String.class).newInstance("Hello")
String result = indyString.getClass().getMethod("concat", String.class).invoke(indyString, "World") assert 'HelloWorld' == result println result
class SimpleClassLoader extends ClassLoader { SimpleClassLoader(ClassLoader parent) { super(parent) }
Class<?> defineClass(final String name, final byte[] b) { return super.defineClass(name, b, 0, b.length) } }
static byte[] generateIndyStringClassBytes() throws Exception { ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES) FieldVisitor fv MethodVisitor mv
cw.visit(V1_8, ACC_PUBLIC | ACC_FINAL, "IndyString", null, "java/lang/Object") cw.visitSource("IndyString.java", null);
{ fv = cw.visitField(ACC_PRIVATE | ACC_FINAL, "str", "Ljava/lang/String;", null, null) fv.visitEnd() } { mv = cw.visitMethod(ACC_PUBLIC, "<init>", "(Ljava/lang/String;)V", null, null) mv.visitCode() Label l0 = new Label() mv.visitLabel(l0) mv.visitVarInsn(ALOAD, 0) mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false) Label l1 = new Label() mv.visitLabel(l1) mv.visitVarInsn(ALOAD, 0) mv.visitVarInsn(ALOAD, 1) mv.visitFieldInsn(PUTFIELD, "IndyString", "str", "Ljava/lang/String;") Label l2 = new Label() mv.visitLabel(l2) mv.visitInsn(RETURN) Label l3 = new Label() mv.visitLabel(l3) mv.visitMaxs(0, 0) mv.visitEnd() } { GeneratorAdapter ga = new GeneratorAdapter(ACC_PUBLIC, Method.getMethod("java.lang.String concat(java.lang.String)"), null, null, cw) ga.loadThis() ga.getField(Type.getType("LIndyString;"), "str", Type.getType(String.class)) ga.loadArg(0)
ga.invokeDynamic("concat", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", BOOTSTRAP_METHOD) ga.returnValue() ga.endMethod() } cw.visitEnd()
return cw.toByteArray() }
@groovy.transform.Field static final Handle BOOTSTRAP_METHOD = new Handle(H_INVOKESTATIC, "Bootstrap", "bootstrap", MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class) .toMethodDescriptorString(), false)
class Bootstrap {
static CallSite bootstrap(MethodHandles.Lookup caller, String name, MethodType type) { println "caller: ${caller}, name: ${name}, type: ${type}" MethodHandle methodHandle = "true" == System.getProperty("enableIndyStringPreview") ? MethodHandles.lookup().findStatic(Functions.class, "groovyConcat", type) : MethodHandles.lookup().findStatic(Functions.class, name, type) return new ConstantCallSite(methodHandle) } }
class Functions {
static String concat(String s1, String s2) { return s1 + s2 }
static String groovyConcat(String s1, String s2) { return "$s1$s2" }
private Functions() {} }
|