Przeglądaj źródła

change: adapter can wrap returns & arguments

Mariusz Sieroń 6 lat temu
rodzic
commit
b2e4dc2a83

+ 12 - 0
assira.drafts/src/main/java/net/ranides/assira/reflection/asm/cg/Adapt.java

@@ -0,0 +1,12 @@
+package net.ranides.assira.reflection.asm.cg;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Retention(RUNTIME)
+@Target({TYPE})
+public @interface Adapt {
+}

+ 136 - 56
assira.drafts/src/main/java/net/ranides/assira/reflection/asm/cg/CGAdapter.java

@@ -1,5 +1,6 @@
 package net.ranides.assira.reflection.asm.cg;
 
+import lombok.Data;
 import lombok.extern.slf4j.Slf4j;
 import net.ranides.assira.reflection.*;
 import net.ranides.assira.reflection.IClassLoader;
@@ -8,6 +9,7 @@ import net.ranides.assira.reflection.asm.AsmMethodBuilder;
 import net.ranides.assira.reflection.asm.AsmSupport;
 
 import java.util.*;
+import java.util.function.Function;
 
 @Slf4j
 public class CGAdapter {
@@ -18,6 +20,14 @@ public class CGAdapter {
 
     private static final String F_METHODS = "method_";
 
+    private static final IAttributes A_METHODS = IAttributes.of(IAttribute.PUBLIC, IAttribute.SYNTHETIC, IAttribute.STATIC);
+
+    private static final String F_ADAPTERS = "adapter_";
+
+    private static final IAttributes A_ADAPTERS = IAttributes.of(IAttribute.PUBLIC, IAttribute.SYNTHETIC, IAttribute.STATIC);
+
+    private static final IMethod M_THAT = IClass.typeinfo(CGAdapterWrapper.class).method("net_ranides_assira_cg_that").get();
+
     private final IClass<?> adapter;
 
     private final IClass<?> that;
@@ -32,14 +42,17 @@ public class CGAdapter {
 
     private final List<IMethod> reflective;
 
+    private final List<AdapterMeta> adapters;
+
     public CGAdapter(IClass<?> adapter, IClass<?> that) {
         this.adapter = adapter;
         this.that = that;
         this.name = that.name() + "$asm:" + AsmSupport.escape(adapter.name());
         this.reflective = new ArrayList<>();
+        this.adapters = new ArrayList<>();
         this.ci = new CGAdapterReflect(adapter, that);
 
-        writer = new AsmClassBuilder(IAttributes.of(IAttribute.PUBLIC), name, IClass.OBJECT, Arrays.asList(adapter));
+        writer = new AsmClassBuilder(IAttributes.of(IAttribute.PUBLIC), name, IClass.OBJECT, Arrays.asList(adapter, IClass.typeinfo(CGAdapterWrapper.class)));
 
         for(IClass<?> c : ci.inner()) {
             writer.inner(c);
@@ -51,12 +64,17 @@ public class CGAdapter {
 
         putToString();
 
+        putWrapperThat();
+
         for(IMethod m : adapter.methods()) {
-            putMethod(m);
+            new MethodLink(m).link();
         }
 
         for(int i=0, n=reflective.size(); i<n; i++) {
-            writer.field(IAttributes.of(IAttribute.PUBLIC, IAttribute.SYNTHETIC, IAttribute.STATIC), IClass.typeinfo(IMethod.class), F_METHODS+i);
+            writer.field(A_METHODS, IClass.typeinfo(IMethod.class), F_METHODS+i);
+        }
+        for(int i=0, n=adapters.size(); i<n; i++) {
+            writer.field(A_ADAPTERS, IClass.typeinfo(Function.class), F_ADAPTERS + i);
         }
         this.bytecode = writer.bytecode();
     }
@@ -65,6 +83,10 @@ public class CGAdapter {
         for(int i=0, n=reflective.size(); i<n; i++) {
             ic.field(F_METHODS+i).get().set(null, reflective.get(i));
         }
+        for(int i=0, n=adapters.size(); i<n; i++) {
+            AdapterMeta meta = adapters.get(i);
+            ic.field(F_ADAPTERS+i).get().set(null, Adapter.adapter(meta.target, meta.source));
+        }
     }
 
     public IClass<?> load(IClassLoader loader) {
@@ -131,80 +153,138 @@ public class CGAdapter {
         m.end();
     }
 
-    private void putMethod(IMethod method) {
-        IMethod target = ci.find(method);
-        if(!target.attribute(IAttribute.PUBLIC)) {
-            putMethodReflectiveCall(method, target);
-        } else {
-            putMethodCall(method, target);
-        }
+    private void putWrapperThat() {
+        /// public Object net_ranides_assira_cg_that() {
+        ///     return this.that;
+        /// }
+
+        AsmMethodBuilder m = writer.method(IAttributes.of(IAttribute.PUBLIC), M_THAT.name(), M_THAT);
+
+        m.load(IClass.OBJECT, 0);
+        m.getField(writer.type(), that, F_THAT);
+        m.returns(IClass.OBJECT);
+
+        m.end();
     }
 
-    private void putMethodReflectiveCall(IMethod method, IMethod target) {
-        int index = reflective.size();
-        reflective.add(target.accessible());
+    private class MethodLink {
 
-        List<IClass<?>> oargs = target.arguments().types().list();
-        List<IClass<?>> iargs = method.arguments().types().list();
+        private final IMethod method;
+        private final IMethod target;
 
-        AsmMethodBuilder m = writer.method(IAttributes.of(IAttribute.PUBLIC), method.name(), method);
+        private final List<IClass<?>> oargs;
+        private final List<IClass<?>> iargs;
 
-        m.getStatic(writer.type(), IClass.typeinfo(IMethod.class), F_METHODS+index);
-        m.load(IClass.OBJECT, 0);
-        m.getField(writer.type(), that, F_THAT);
+        private final int iadapter;
+        private final int ireflective;
 
-        m.push(oargs.size());
-        m.newObject(IClass.typeinfo(Object[].class));
+        private final AsmMethodBuilder m;
 
-        for(int i=0, n=iargs.size(); i<n; i++) {
-            m.dup();
-            m.push(i);
-            m.load(iargs.get(i), i+1);
-            if(iargs.get(i).attribute(IAttribute.PRIMITIVE)) {
-                m.box(iargs.get(i));
-            }
-            m.storeArray(IClass.OBJECT);
+        public MethodLink(IMethod method) {
+            this.method = method;
+            this.target = ci.find(method);
+
+            this.oargs = target.arguments().types().list();
+            this.iargs = method.arguments().types().list();
+
+            this.iadapter = initResultAdapter();
+            this.ireflective = initReflective();
+
+            this.m = writer.method(IAttributes.of(IAttribute.PUBLIC), method.name(), method);
         }
 
-        m.invokeVirtual(IClass.typeinfo(IMethod.class), "invoke", Arrays.asList(IClass.OBJECT, IClass.typeinfo(Object[].class)), IClass.OBJECT);
-        m.checkCast(method.returns());
-        m.returns(method.returns());
+        private int initResultAdapter() {
+            if(!method.returns().annotation(Adapt.class).isPresent()) {
+                return -1;
+            }
+            AdapterMeta meta = new AdapterMeta(target.returns(), method.returns());
 
-        m.var(writer.type(), F_THIS);
-        m.var(method.arguments());
+            int found = adapters.indexOf(meta);
+            if(found>=0) {
+                return found;
+            }
 
-        m.end();
-    }
+            adapters.add(meta);
+            return adapters.size() - 1;
+        }
 
-    private void putMethodCall(IMethod method, IMethod target) {
-        /// T method_name(args...) {
-        ///     return this.that.method_name(args...);
-        /// }
+        private int initReflective() {
+            if(target.attribute(IAttribute.PUBLIC)) {
+                return -1;
+            }
+            reflective.add(target.accessible());
+            return reflective.size() - 1;
+        }
 
-        List<IClass<?>> oargs = target.arguments().types().list();
-        List<IClass<?>> iargs = method.arguments().types().list();
+        private void link() {
+            if(iadapter != -1) {
+                m.getStatic(writer.type(), IClass.typeinfo(Function.class), F_ADAPTERS+ iadapter);
+            }
+            if(ireflective != -1) {
+                m.getStatic(writer.type(), IClass.typeinfo(IMethod.class), F_METHODS + ireflective);
+            }
 
-        AsmMethodBuilder m = writer.method(IAttributes.of(IAttribute.PUBLIC), method.name(), method);
+            m.load(IClass.OBJECT, 0);
+            m.getField(writer.type(), that, F_THAT);
 
-        m.load(IClass.OBJECT, 0);
-        m.getField(writer.type(), that, F_THAT);
+            if(ireflective != -1) {
+                invokeReflective();
+            } else {
+                invokeDirect();
+            }
+            if (iadapter != -1) {
+                m.invokeVirtual(IClass.typeinfo(Function.class), "apply", Arrays.asList(IClass.OBJECT), IClass.OBJECT);
+            }
+            if (!method.returns().attribute(IAttribute.VOID) && (iadapter != -1 || ireflective != -1 || !target.returns().isSubclass(method.returns()))) {
+                m.checkCast(method.returns());
+            }
+
+            m.returns(method.returns());
+            m.var(writer.type(), F_THIS);
+            m.var(method.arguments());
+            m.end();
+        }
 
-        for(int i=0, n=iargs.size(); i<n; i++) {
-            m.load(iargs.get(i), i+1);
-            if(!iargs.get(i).isSubclass(oargs.get(i))) {
-                m.checkCast(oargs.get(i));
+        private void invokeReflective() {
+            m.push(oargs.size());
+            m.newObject(IClass.typeinfo(Object[].class));
+
+            for (int i = 0, n = iargs.size(); i < n; i++) {
+                m.dup();
+                m.push(i);
+                m.load(iargs.get(i), i + 1);
+                if(iargs.get(i).annotation(Adapt.class).isPresent()) {
+                    m.invokeVirtual(M_THAT);
+                }
+                if (iargs.get(i).attribute(IAttribute.PRIMITIVE)) {
+                    m.box(iargs.get(i));
+                }
+                m.storeArray(IClass.OBJECT);
             }
+            m.invokeVirtual(IClass.typeinfo(IMethod.class), "invoke", Arrays.asList(IClass.OBJECT, IClass.typeinfo(Object[].class)), IClass.OBJECT);
         }
 
-        m.invokeVirtual(that, target);
-        if(!target.returns().isSubclass(method.returns())) {
-            m.checkCast(method.returns());
+        private void invokeDirect() {
+            for(int i=0, n=iargs.size(); i<n; i++) {
+                m.load(iargs.get(i), i+1);
+                if(iargs.get(i).annotation(Adapt.class).isPresent()) {
+                    m.invokeVirtual(M_THAT);
+                }
+                if(!iargs.get(i).isSubclass(oargs.get(i))) {
+                    m.checkCast(oargs.get(i));
+                }
+            }
+            m.invokeVirtual(that, target);
         }
-        m.returns(method.returns());
 
-        m.var(writer.type(), F_THIS);
-        m.var(method.arguments());
-        m.end();
+    }
+
+    @Data
+    private static class AdapterMeta {
+
+        private final IClass<?> source;
+        private final IClass<?> target;
+
     }
 
 }

+ 7 - 0
assira.drafts/src/main/java/net/ranides/assira/reflection/asm/cg/CGAdapterWrapper.java

@@ -0,0 +1,7 @@
+package net.ranides.assira.reflection.asm.cg;
+
+public interface CGAdapterWrapper {
+
+    Object net_ranides_assira_cg_that();
+
+}

+ 23 - 4
assira.drafts/src/test/java/net/ranides/assira/example/IRecord4Proxy.java

@@ -1,23 +1,30 @@
 package net.ranides.assira.example;
 
-import net.ranides.assira.reflection.IArguments;
-import net.ranides.assira.reflection.IAttribute;
 import net.ranides.assira.reflection.IClass;
 import net.ranides.assira.reflection.IMethod;
 import net.ranides.assira.reflection.adapter.example.AbstractRecord;
 import net.ranides.assira.reflection.adapter.example.InnerDefs;
+import net.ranides.assira.reflection.asm.cg.CGAdapterWrapper;
 
 import java.util.List;
 import java.util.Map;
+import java.util.function.Function;
 
-public class IRecord4Proxy implements InnerDefs.IRecord3 {
+public class IRecord4Proxy implements InnerDefs.IRecord3, CGAdapterWrapper {
 
-    private final InnerDefs.MutableRecord that;
+    public final InnerDefs.MutableRecord that;
+
+    public final Function adapter1 = null;
 
     public IRecord4Proxy(InnerDefs.MutableRecord that) {
         this.that = that;
     }
 
+    @Override
+    public Object net_ranides_assira_cg_that() {
+        return that;
+    }
+
     @Override
     public int getX() {
         return that.getX();
@@ -90,6 +97,18 @@ public class IRecord4Proxy implements InnerDefs.IRecord3 {
         return that.complex(list, map);
     }
 
+    public String complex_adapt(List<String> list, Map<String, Number> map) throws NumberFormatException, IllegalStateException {
+        return (String)adapter1.apply(that.complex(list, map));
+    }
+
+    public void doSomething(String a, int b, IRecord4Proxy c, List<String> d) {
+        that.doSomething(a, b, c, d);
+    }
+
+    public void doSomething_unwrap(String a, int b, IRecord4Proxy c, List<String> d) {
+        that.doSomething(a, b, c.net_ranides_assira_cg_that(), d);
+    }
+
     @Override
     public String toString() {
         String v = that.toString();

+ 133 - 29
assira.drafts/src/test/java/net/ranides/assira/example/IRecord4ProxyDump.java

@@ -21,7 +21,7 @@ public class IRecord4ProxyDump implements Opcodes {
         MethodVisitor methodVisitor;
         AnnotationVisitor annotationVisitor0;
 
-        classWriter.visit(V1_8, ACC_PUBLIC | ACC_SUPER, "net/ranides/assira/example/IRecord4Proxy", null, "java/lang/Object", new String[] { "net/ranides/assira/reflection/adapter/example/InnerDefs$IRecord3" });
+        classWriter.visit(V1_8, ACC_PUBLIC | ACC_SUPER, "net/ranides/assira/example/IRecord4Proxy", null, "java/lang/Object", new String[] { "net/ranides/assira/reflection/adapter/example/InnerDefs$IRecord3", "net/ranides/assira/reflection/asm/cg/CGAdapterWrapper" });
 
         classWriter.visitSource("IRecord4Proxy.java", null);
 
@@ -34,7 +34,11 @@ public class IRecord4ProxyDump implements Opcodes {
         classWriter.visitInnerClass("net/ranides/assira/reflection/adapter/example/InnerDefs$IRecord3", "net/ranides/assira/reflection/adapter/example/InnerDefs", "IRecord3", ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
 
         {
-            fieldVisitor = classWriter.visitField(ACC_PRIVATE | ACC_FINAL, "that", "Lnet/ranides/assira/reflection/adapter/example/InnerDefs$MutableRecord;", null, null);
+            fieldVisitor = classWriter.visitField(ACC_PUBLIC | ACC_FINAL, "that", "Lnet/ranides/assira/reflection/adapter/example/InnerDefs$MutableRecord;", null, null);
+            fieldVisitor.visitEnd();
+        }
+        {
+            fieldVisitor = classWriter.visitField(ACC_PUBLIC | ACC_FINAL, "adapter1", "Ljava/util/function/Function;", null, null);
             fieldVisitor.visitEnd();
         }
         {
@@ -50,32 +54,53 @@ public class IRecord4ProxyDump implements Opcodes {
             methodVisitor.visitCode();
             Label label0 = new Label();
             methodVisitor.visitLabel(label0);
-            methodVisitor.visitLineNumber(17, label0);
+            methodVisitor.visitLineNumber(19, label0);
             methodVisitor.visitVarInsn(ALOAD, 0);
             methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
             Label label1 = new Label();
             methodVisitor.visitLabel(label1);
-            methodVisitor.visitLineNumber(18, label1);
+            methodVisitor.visitLineNumber(17, label1);
             methodVisitor.visitVarInsn(ALOAD, 0);
-            methodVisitor.visitVarInsn(ALOAD, 1);
-            methodVisitor.visitFieldInsn(PUTFIELD, "net/ranides/assira/example/IRecord4Proxy", "that", "Lnet/ranides/assira/reflection/adapter/example/InnerDefs$MutableRecord;");
+            methodVisitor.visitInsn(ACONST_NULL);
+            methodVisitor.visitFieldInsn(PUTFIELD, "net/ranides/assira/example/IRecord4Proxy", "adapter1", "Ljava/util/function/Function;");
             Label label2 = new Label();
             methodVisitor.visitLabel(label2);
-            methodVisitor.visitLineNumber(19, label2);
-            methodVisitor.visitInsn(RETURN);
+            methodVisitor.visitLineNumber(20, label2);
+            methodVisitor.visitVarInsn(ALOAD, 0);
+            methodVisitor.visitVarInsn(ALOAD, 1);
+            methodVisitor.visitFieldInsn(PUTFIELD, "net/ranides/assira/example/IRecord4Proxy", "that", "Lnet/ranides/assira/reflection/adapter/example/InnerDefs$MutableRecord;");
             Label label3 = new Label();
             methodVisitor.visitLabel(label3);
-            methodVisitor.visitLocalVariable("this", "Lnet/ranides/assira/example/IRecord4Proxy;", null, label0, label3, 0);
-            methodVisitor.visitLocalVariable("that", "Lnet/ranides/assira/reflection/adapter/example/InnerDefs$MutableRecord;", null, label0, label3, 1);
+            methodVisitor.visitLineNumber(21, label3);
+            methodVisitor.visitInsn(RETURN);
+            Label label4 = new Label();
+            methodVisitor.visitLabel(label4);
+            methodVisitor.visitLocalVariable("this", "Lnet/ranides/assira/example/IRecord4Proxy;", null, label0, label4, 0);
+            methodVisitor.visitLocalVariable("that", "Lnet/ranides/assira/reflection/adapter/example/InnerDefs$MutableRecord;", null, label0, label4, 1);
             methodVisitor.visitMaxs(2, 2);
             methodVisitor.visitEnd();
         }
+        {
+            methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "net_ranides_assira_cg_that", "()Ljava/lang/Object;", null, null);
+            methodVisitor.visitCode();
+            Label label0 = new Label();
+            methodVisitor.visitLabel(label0);
+            methodVisitor.visitLineNumber(25, label0);
+            methodVisitor.visitVarInsn(ALOAD, 0);
+            methodVisitor.visitFieldInsn(GETFIELD, "net/ranides/assira/example/IRecord4Proxy", "that", "Lnet/ranides/assira/reflection/adapter/example/InnerDefs$MutableRecord;");
+            methodVisitor.visitInsn(ARETURN);
+            Label label1 = new Label();
+            methodVisitor.visitLabel(label1);
+            methodVisitor.visitLocalVariable("this", "Lnet/ranides/assira/example/IRecord4Proxy;", null, label0, label1, 0);
+            methodVisitor.visitMaxs(1, 1);
+            methodVisitor.visitEnd();
+        }
         {
             methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "getX", "()I", null, null);
             methodVisitor.visitCode();
             Label label0 = new Label();
             methodVisitor.visitLabel(label0);
-            methodVisitor.visitLineNumber(23, label0);
+            methodVisitor.visitLineNumber(30, label0);
             methodVisitor.visitVarInsn(ALOAD, 0);
             methodVisitor.visitFieldInsn(GETFIELD, "net/ranides/assira/example/IRecord4Proxy", "that", "Lnet/ranides/assira/reflection/adapter/example/InnerDefs$MutableRecord;");
             methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "net/ranides/assira/reflection/adapter/example/InnerDefs$MutableRecord", "getX", "()I", false);
@@ -91,7 +116,7 @@ public class IRecord4ProxyDump implements Opcodes {
             methodVisitor.visitCode();
             Label label0 = new Label();
             methodVisitor.visitLabel(label0);
-            methodVisitor.visitLineNumber(28, label0);
+            methodVisitor.visitLineNumber(35, label0);
             methodVisitor.visitVarInsn(ALOAD, 0);
             methodVisitor.visitFieldInsn(GETFIELD, "net/ranides/assira/example/IRecord4Proxy", "that", "Lnet/ranides/assira/reflection/adapter/example/InnerDefs$MutableRecord;");
             methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "net/ranides/assira/reflection/adapter/example/InnerDefs$MutableRecord", "getY", "()I", false);
@@ -107,7 +132,7 @@ public class IRecord4ProxyDump implements Opcodes {
             methodVisitor.visitCode();
             Label label0 = new Label();
             methodVisitor.visitLabel(label0);
-            methodVisitor.visitLineNumber(33, label0);
+            methodVisitor.visitLineNumber(40, label0);
             methodVisitor.visitVarInsn(ALOAD, 0);
             methodVisitor.visitFieldInsn(GETFIELD, "net/ranides/assira/example/IRecord4Proxy", "that", "Lnet/ranides/assira/reflection/adapter/example/InnerDefs$MutableRecord;");
             methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "net/ranides/assira/reflection/adapter/example/InnerDefs$MutableRecord", "area", "()I", false);
@@ -123,7 +148,7 @@ public class IRecord4ProxyDump implements Opcodes {
             methodVisitor.visitCode();
             Label label0 = new Label();
             methodVisitor.visitLabel(label0);
-            methodVisitor.visitLineNumber(38, label0);
+            methodVisitor.visitLineNumber(45, label0);
             methodVisitor.visitVarInsn(ALOAD, 0);
             methodVisitor.visitFieldInsn(GETFIELD, "net/ranides/assira/example/IRecord4Proxy", "that", "Lnet/ranides/assira/reflection/adapter/example/InnerDefs$MutableRecord;");
             methodVisitor.visitVarInsn(ILOAD, 1);
@@ -141,7 +166,7 @@ public class IRecord4ProxyDump implements Opcodes {
             methodVisitor.visitCode();
             Label label0 = new Label();
             methodVisitor.visitLabel(label0);
-            methodVisitor.visitLineNumber(43, label0);
+            methodVisitor.visitLineNumber(50, label0);
             methodVisitor.visitVarInsn(ALOAD, 0);
             methodVisitor.visitFieldInsn(GETFIELD, "net/ranides/assira/example/IRecord4Proxy", "that", "Lnet/ranides/assira/reflection/adapter/example/InnerDefs$MutableRecord;");
             methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "net/ranides/assira/reflection/adapter/example/InnerDefs$MutableRecord", "stats", "()Lnet/ranides/assira/reflection/adapter/example/InnerDefs$RecordStats;", false);
@@ -157,7 +182,7 @@ public class IRecord4ProxyDump implements Opcodes {
             methodVisitor.visitCode();
             Label label0 = new Label();
             methodVisitor.visitLabel(label0);
-            methodVisitor.visitLineNumber(48, label0);
+            methodVisitor.visitLineNumber(55, label0);
             methodVisitor.visitVarInsn(ALOAD, 0);
             methodVisitor.visitFieldInsn(GETFIELD, "net/ranides/assira/example/IRecord4Proxy", "that", "Lnet/ranides/assira/reflection/adapter/example/InnerDefs$MutableRecord;");
             methodVisitor.visitVarInsn(ALOAD, 1);
@@ -179,7 +204,7 @@ public class IRecord4ProxyDump implements Opcodes {
             methodVisitor.visitCode();
             Label label0 = new Label();
             methodVisitor.visitLabel(label0);
-            methodVisitor.visitLineNumber(52, label0);
+            methodVisitor.visitLineNumber(59, label0);
             methodVisitor.visitVarInsn(ALOAD, 0);
             methodVisitor.visitFieldInsn(GETFIELD, "net/ranides/assira/example/IRecord4Proxy", "that", "Lnet/ranides/assira/reflection/adapter/example/InnerDefs$MutableRecord;");
             methodVisitor.visitVarInsn(ALOAD, 1);
@@ -206,7 +231,7 @@ public class IRecord4ProxyDump implements Opcodes {
             methodVisitor.visitCode();
             Label label0 = new Label();
             methodVisitor.visitLabel(label0);
-            methodVisitor.visitLineNumber(56, label0);
+            methodVisitor.visitLineNumber(63, label0);
             methodVisitor.visitVarInsn(ALOAD, 0);
             methodVisitor.visitFieldInsn(GETFIELD, "net/ranides/assira/example/IRecord4Proxy", "that", "Lnet/ranides/assira/reflection/adapter/example/InnerDefs$MutableRecord;");
             methodVisitor.visitVarInsn(ALOAD, 0);
@@ -234,7 +259,7 @@ public class IRecord4ProxyDump implements Opcodes {
             methodVisitor.visitCode();
             Label label0 = new Label();
             methodVisitor.visitLabel(label0);
-            methodVisitor.visitLineNumber(60, label0);
+            methodVisitor.visitLineNumber(67, label0);
             methodVisitor.visitInsn(ICONST_0);
             methodVisitor.visitIntInsn(NEWARRAY, T_INT);
             methodVisitor.visitInsn(ARETURN);
@@ -250,7 +275,7 @@ public class IRecord4ProxyDump implements Opcodes {
             methodVisitor.visitCode();
             Label label0 = new Label();
             methodVisitor.visitLabel(label0);
-            methodVisitor.visitLineNumber(64, label0);
+            methodVisitor.visitLineNumber(71, label0);
             methodVisitor.visitInsn(ICONST_0);
             methodVisitor.visitTypeInsn(ANEWARRAY, "java/lang/Float");
             methodVisitor.visitInsn(ARETURN);
@@ -266,7 +291,7 @@ public class IRecord4ProxyDump implements Opcodes {
             methodVisitor.visitCode();
             Label label0 = new Label();
             methodVisitor.visitLabel(label0);
-            methodVisitor.visitLineNumber(68, label0);
+            methodVisitor.visitLineNumber(75, label0);
             methodVisitor.visitIntInsn(BIPUSH, 33);
             methodVisitor.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false);
             methodVisitor.visitInsn(ARETURN);
@@ -282,7 +307,7 @@ public class IRecord4ProxyDump implements Opcodes {
             methodVisitor.visitCode();
             Label label0 = new Label();
             methodVisitor.visitLabel(label0);
-            methodVisitor.visitLineNumber(76, label0);
+            methodVisitor.visitLineNumber(83, label0);
             methodVisitor.visitFieldInsn(GETSTATIC, "net/ranides/assira/example/IRecord4Proxy", "ARGS1", "Lnet/ranides/assira/reflection/IMethod;");
             methodVisitor.visitVarInsn(ALOAD, 0);
             methodVisitor.visitFieldInsn(GETFIELD, "net/ranides/assira/example/IRecord4Proxy", "that", "Lnet/ranides/assira/reflection/adapter/example/InnerDefs$MutableRecord;");
@@ -317,7 +342,7 @@ public class IRecord4ProxyDump implements Opcodes {
             methodVisitor.visitCode();
             Label label0 = new Label();
             methodVisitor.visitLabel(label0);
-            methodVisitor.visitLineNumber(81, label0);
+            methodVisitor.visitLineNumber(88, label0);
             methodVisitor.visitFieldInsn(GETSTATIC, "net/ranides/assira/example/ExampleSupport", "METHODS", "[Lnet/ranides/assira/reflection/IMethod;");
             methodVisitor.visitIntInsn(SIPUSH, 192);
             methodVisitor.visitInsn(AALOAD);
@@ -354,7 +379,7 @@ public class IRecord4ProxyDump implements Opcodes {
             methodVisitor.visitCode();
             Label label0 = new Label();
             methodVisitor.visitLabel(label0);
-            methodVisitor.visitLineNumber(85, label0);
+            methodVisitor.visitLineNumber(92, label0);
             methodVisitor.visitFieldInsn(GETSTATIC, "net/ranides/assira/example/ExampleSupport", "METHODS", "[Lnet/ranides/assira/reflection/IMethod;");
             methodVisitor.visitIntInsn(SIPUSH, 192);
             methodVisitor.visitInsn(AALOAD);
@@ -393,7 +418,7 @@ public class IRecord4ProxyDump implements Opcodes {
             methodVisitor.visitCode();
             Label label0 = new Label();
             methodVisitor.visitLabel(label0);
-            methodVisitor.visitLineNumber(90, label0);
+            methodVisitor.visitLineNumber(97, label0);
             methodVisitor.visitVarInsn(ALOAD, 0);
             methodVisitor.visitFieldInsn(GETFIELD, "net/ranides/assira/example/IRecord4Proxy", "that", "Lnet/ranides/assira/reflection/adapter/example/InnerDefs$MutableRecord;");
             methodVisitor.visitVarInsn(ALOAD, 1);
@@ -408,19 +433,98 @@ public class IRecord4ProxyDump implements Opcodes {
             methodVisitor.visitMaxs(3, 3);
             methodVisitor.visitEnd();
         }
+        {
+            methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "complex_adapt", "(Ljava/util/List;Ljava/util/Map;)Ljava/lang/String;", "(Ljava/util/List<Ljava/lang/String;>;Ljava/util/Map<Ljava/lang/String;Ljava/lang/Number;>;)Ljava/lang/String;", new String[] { "java/lang/NumberFormatException", "java/lang/IllegalStateException" });
+            methodVisitor.visitCode();
+            Label label0 = new Label();
+            methodVisitor.visitLabel(label0);
+            methodVisitor.visitLineNumber(101, label0);
+            methodVisitor.visitVarInsn(ALOAD, 0);
+            methodVisitor.visitFieldInsn(GETFIELD, "net/ranides/assira/example/IRecord4Proxy", "adapter1", "Ljava/util/function/Function;");
+            methodVisitor.visitVarInsn(ALOAD, 0);
+            methodVisitor.visitFieldInsn(GETFIELD, "net/ranides/assira/example/IRecord4Proxy", "that", "Lnet/ranides/assira/reflection/adapter/example/InnerDefs$MutableRecord;");
+            methodVisitor.visitVarInsn(ALOAD, 1);
+            methodVisitor.visitVarInsn(ALOAD, 2);
+            methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "net/ranides/assira/reflection/adapter/example/InnerDefs$MutableRecord", "complex", "(Ljava/util/List;Ljava/util/Map;)Ljava/lang/String;", false);
+            methodVisitor.visitMethodInsn(INVOKEINTERFACE, "java/util/function/Function", "apply", "(Ljava/lang/Object;)Ljava/lang/Object;", true);
+            methodVisitor.visitTypeInsn(CHECKCAST, "java/lang/String");
+            methodVisitor.visitInsn(ARETURN);
+            Label label1 = new Label();
+            methodVisitor.visitLabel(label1);
+            methodVisitor.visitLocalVariable("this", "Lnet/ranides/assira/example/IRecord4Proxy;", null, label0, label1, 0);
+            methodVisitor.visitLocalVariable("list", "Ljava/util/List;", "Ljava/util/List<Ljava/lang/String;>;", label0, label1, 1);
+            methodVisitor.visitLocalVariable("map", "Ljava/util/Map;", "Ljava/util/Map<Ljava/lang/String;Ljava/lang/Number;>;", label0, label1, 2);
+            methodVisitor.visitMaxs(4, 3);
+            methodVisitor.visitEnd();
+        }
+        {
+            methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "doSomething", "(Ljava/lang/String;ILnet/ranides/assira/example/IRecord4Proxy;Ljava/util/List;)V", "(Ljava/lang/String;ILnet/ranides/assira/example/IRecord4Proxy;Ljava/util/List<Ljava/lang/String;>;)V", null);
+            methodVisitor.visitCode();
+            Label label0 = new Label();
+            methodVisitor.visitLabel(label0);
+            methodVisitor.visitLineNumber(105, label0);
+            methodVisitor.visitVarInsn(ALOAD, 0);
+            methodVisitor.visitFieldInsn(GETFIELD, "net/ranides/assira/example/IRecord4Proxy", "that", "Lnet/ranides/assira/reflection/adapter/example/InnerDefs$MutableRecord;");
+            methodVisitor.visitVarInsn(ALOAD, 1);
+            methodVisitor.visitVarInsn(ILOAD, 2);
+            methodVisitor.visitVarInsn(ALOAD, 3);
+            methodVisitor.visitVarInsn(ALOAD, 4);
+            methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "net/ranides/assira/reflection/adapter/example/InnerDefs$MutableRecord", "doSomething", "(Ljava/lang/String;ILjava/lang/Object;Ljava/util/List;)V", false);
+            Label label1 = new Label();
+            methodVisitor.visitLabel(label1);
+            methodVisitor.visitLineNumber(106, label1);
+            methodVisitor.visitInsn(RETURN);
+            Label label2 = new Label();
+            methodVisitor.visitLabel(label2);
+            methodVisitor.visitLocalVariable("this", "Lnet/ranides/assira/example/IRecord4Proxy;", null, label0, label2, 0);
+            methodVisitor.visitLocalVariable("a", "Ljava/lang/String;", null, label0, label2, 1);
+            methodVisitor.visitLocalVariable("b", "I", null, label0, label2, 2);
+            methodVisitor.visitLocalVariable("c", "Lnet/ranides/assira/example/IRecord4Proxy;", null, label0, label2, 3);
+            methodVisitor.visitLocalVariable("d", "Ljava/util/List;", "Ljava/util/List<Ljava/lang/String;>;", label0, label2, 4);
+            methodVisitor.visitMaxs(5, 5);
+            methodVisitor.visitEnd();
+        }
+        {
+            methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "doSomething_unwrap", "(Ljava/lang/String;ILnet/ranides/assira/example/IRecord4Proxy;Ljava/util/List;)V", "(Ljava/lang/String;ILnet/ranides/assira/example/IRecord4Proxy;Ljava/util/List<Ljava/lang/String;>;)V", null);
+            methodVisitor.visitCode();
+            Label label0 = new Label();
+            methodVisitor.visitLabel(label0);
+            methodVisitor.visitLineNumber(109, label0);
+            methodVisitor.visitVarInsn(ALOAD, 0);
+            methodVisitor.visitFieldInsn(GETFIELD, "net/ranides/assira/example/IRecord4Proxy", "that", "Lnet/ranides/assira/reflection/adapter/example/InnerDefs$MutableRecord;");
+            methodVisitor.visitVarInsn(ALOAD, 1);
+            methodVisitor.visitVarInsn(ILOAD, 2);
+            methodVisitor.visitVarInsn(ALOAD, 3);
+            methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "net/ranides/assira/example/IRecord4Proxy", "net_ranides_assira_cg_that", "()Ljava/lang/Object;", false);
+            methodVisitor.visitVarInsn(ALOAD, 4);
+            methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "net/ranides/assira/reflection/adapter/example/InnerDefs$MutableRecord", "doSomething", "(Ljava/lang/String;ILjava/lang/Object;Ljava/util/List;)V", false);
+            Label label1 = new Label();
+            methodVisitor.visitLabel(label1);
+            methodVisitor.visitLineNumber(110, label1);
+            methodVisitor.visitInsn(RETURN);
+            Label label2 = new Label();
+            methodVisitor.visitLabel(label2);
+            methodVisitor.visitLocalVariable("this", "Lnet/ranides/assira/example/IRecord4Proxy;", null, label0, label2, 0);
+            methodVisitor.visitLocalVariable("a", "Ljava/lang/String;", null, label0, label2, 1);
+            methodVisitor.visitLocalVariable("b", "I", null, label0, label2, 2);
+            methodVisitor.visitLocalVariable("c", "Lnet/ranides/assira/example/IRecord4Proxy;", null, label0, label2, 3);
+            methodVisitor.visitLocalVariable("d", "Ljava/util/List;", "Ljava/util/List<Ljava/lang/String;>;", label0, label2, 4);
+            methodVisitor.visitMaxs(5, 5);
+            methodVisitor.visitEnd();
+        }
         {
             methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null);
             methodVisitor.visitCode();
             Label label0 = new Label();
             methodVisitor.visitLabel(label0);
-            methodVisitor.visitLineNumber(95, label0);
+            methodVisitor.visitLineNumber(114, label0);
             methodVisitor.visitVarInsn(ALOAD, 0);
             methodVisitor.visitFieldInsn(GETFIELD, "net/ranides/assira/example/IRecord4Proxy", "that", "Lnet/ranides/assira/reflection/adapter/example/InnerDefs$MutableRecord;");
             methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "net/ranides/assira/reflection/adapter/example/InnerDefs$MutableRecord", "toString", "()Ljava/lang/String;", false);
             methodVisitor.visitVarInsn(ASTORE, 1);
             Label label1 = new Label();
             methodVisitor.visitLabel(label1);
-            methodVisitor.visitLineNumber(96, label1);
+            methodVisitor.visitLineNumber(115, label1);
             methodVisitor.visitTypeInsn(NEW, "java/lang/StringBuilder");
             methodVisitor.visitInsn(DUP);
             methodVisitor.visitIntInsn(BIPUSH, 24);
@@ -448,13 +552,13 @@ public class IRecord4ProxyDump implements Opcodes {
             methodVisitor.visitCode();
             Label label0 = new Label();
             methodVisitor.visitLabel(label0);
-            methodVisitor.visitLineNumber(71, label0);
+            methodVisitor.visitLineNumber(78, label0);
             methodVisitor.visitLdcInsn(Type.getType("Lnet/ranides/assira/reflection/adapter/example/InnerDefs$Record;"));
             methodVisitor.visitMethodInsn(INVOKESTATIC, "net/ranides/assira/reflection/IClass", "typeinfo", "(Ljava/lang/Class;)Lnet/ranides/assira/reflection/IClass;", true);
             methodVisitor.visitFieldInsn(PUTSTATIC, "net/ranides/assira/example/IRecord4Proxy", "I_CLASS", "Lnet/ranides/assira/reflection/IClass;");
             Label label1 = new Label();
             methodVisitor.visitLabel(label1);
-            methodVisitor.visitLineNumber(73, label1);
+            methodVisitor.visitLineNumber(80, label1);
             methodVisitor.visitFieldInsn(GETSTATIC, "net/ranides/assira/example/IRecord4Proxy", "I_CLASS", "Lnet/ranides/assira/reflection/IClass;");
             methodVisitor.visitLdcInsn("args1");
             methodVisitor.visitIntInsn(BIPUSH, 11);

+ 22 - 19
assira.drafts/src/test/java/net/ranides/assira/reflection/AdapterTest.java

@@ -63,6 +63,8 @@ public class AdapterTest {
         assertEquals(new Image0("second", 7, 8), screen1.findImage(7,8));
         assertEquals(new Image0("last", 2, 3), screen1.findImage(2,3));
 
+        assertTrue(screen1.contains(screen1.findImage(2,3)));
+
         assertEquals("Screen0{960x720}", screen1.resize(1.5f).toString());
     }
 
@@ -85,40 +87,41 @@ public class AdapterTest {
         }
 
         screen2.add("far",420,150);
-//        screen2.add(new Image0("last",2,3)); // declaration requires more specific object
         screen2.add(new Image2("last",2,3));
 
         assertEquals("first", screen2.findId(4,5));
         assertEquals("far", screen2.findId(420,150));
     }
 
-    @Ignore("simple adapter shouldn't support any mappings")
     @Test
     public void castM3() {
-        Screen0 screen0 = new Screen0(640, 480);
-        Screen3 screen3 = Adapter.cast(Screen3.class, screen0);
+        Screen3 buffer = Adapter.cast(Screen3.class, new Screen0(32,32));
+        Screen3 screen = Adapter.cast(Screen3.class, new Screen0(640, 480));
+
+        assertEquals("640x480 false", screen.width()+"x"+screen.height()+" "+screen.isWide());
+
+        Image3 a = screen.put("first",4,5);
+        Image3 b = buffer.put("buffd",7,9);
+        Image3 c = buffer.put("foreign",21,22);
 
-        assertEquals("640x480 false", screen3.width()+"x"+screen3.height()+" "+screen3.isWide());
+        assertTrue(a instanceof Image3);
 
-        Object a = screen3.put("first",4,5);
-        Image0 b = (Image0)screen3.put("first",4,5);
-        Image3 c = screen3.put("first",4,5);
+        assertEquals(4, a.getX());
 
-        // nawet tutaj jakąś metodę można wywołać
-        System.out.println(c.getClass());
+        screen.add("far",420,150);
+        screen.add(c);
 
-        // dopiero tutaj się wywala błędem dość ekstrawaganckim
-        // IncompatibleClassChangeError: Image0 does not implement the requested interface Image3
-        System.out.println(c.getX());
+        assertEquals("first", screen.findId(4,5));
+        assertEquals("far", screen.findId(420,150));
 
-//        assertEquals(new Image0("second", 7, 8), screen3.put(new Image3("second",7,8)));
+        assertTrue(screen.contains(a));
+        assertFalse(screen.contains(b));
 
-        screen3.add("far",420,150);
-//        screen3.add(new Image0("last",2,3));
-//        screen3.add(new Image0("last",2,3));
+        assertFalse(buffer.contains(a));
+        assertTrue(buffer.contains(b));
 
-        assertEquals("first", screen3.findId(4,5));
-        assertEquals("far", screen3.findId(420,150));
+        assertEquals("Adapter{4:5:first => Image3}", screen.findImage(4,5).toString());
+        assertEquals("Adapter{21:22:foreign => Image3}", screen.findImage(21,22).toString());
     }
 
     @Test

+ 4 - 0
assira.drafts/src/test/java/net/ranides/assira/reflection/adapter/example/InnerDefs.java

@@ -150,5 +150,9 @@ public class InnerDefs {
             return this;
         }
 
+        public void doSomething(String a, int b, Object c, List<String> d) {
+            // do nothing
+        }
+
     }
 }

+ 4 - 0
assira.drafts/src/test/java/net/ranides/assira/reflection/adapter/model0/Screen0.java

@@ -46,6 +46,10 @@ public class Screen0 extends AbstractScreen0 {
         put(image);
     }
 
+    public boolean contains(Image0 image) {
+        return images.stream().anyMatch(image::equals);
+    }
+
     public String findId(int x, int y) {
         return images.stream().filter(m -> m.atLocation(x,y)).findFirst().map(m -> m.getId()).orElse("?");
     }

+ 2 - 1
assira.drafts/src/test/java/net/ranides/assira/reflection/adapter/model1/Screen1.java

@@ -1,6 +1,5 @@
 package net.ranides.assira.reflection.adapter.model1;
 
-import javafx.stage.Screen;
 import net.ranides.assira.reflection.adapter.model0.Image0;
 import net.ranides.assira.reflection.adapter.model0.Screen0;
 
@@ -22,6 +21,8 @@ public interface Screen1 {
 
     Image0 findImage(int x, int y);
 
+    boolean contains(Image0 image);
+
     Screen0 resize(double f);
 
     boolean isWide();

+ 3 - 0
assira.drafts/src/test/java/net/ranides/assira/reflection/adapter/model3/Image3.java

@@ -1,5 +1,8 @@
 package net.ranides.assira.reflection.adapter.model3;
 
+import net.ranides.assira.reflection.asm.cg.Adapt;
+
+@Adapt
 public interface Image3 {
 
     String getId();

+ 5 - 0
assira.drafts/src/test/java/net/ranides/assira/reflection/adapter/model3/Screen3.java

@@ -1,5 +1,8 @@
 package net.ranides.assira.reflection.adapter.model3;
 
+import net.ranides.assira.reflection.asm.cg.Adapt;
+
+@Adapt
 public interface Screen3 {
 
     int width();
@@ -18,6 +21,8 @@ public interface Screen3 {
 
     Image3 findImage(int x, int y);
 
+    boolean contains(Image3 image);
+
     boolean isWide();
 
 }

BIN
doc/vm.ods