Prechádzať zdrojové kódy

draft: new: CGAdapter: @BindInternal
draft: new: CGAdapter: method binding
draft: new: Adapter: store generated classes in a map

Mariusz Sieroń 6 rokov pred
rodič
commit
bcdb804938

+ 12 - 6
assira.drafts/src/main/java/net/ranides/assira/reflection/Adapter.java

@@ -1,20 +1,26 @@
 package net.ranides.assira.reflection;
 
+import net.ranides.assira.collection.maps.HashMap;
+import net.ranides.assira.generic.Tuple;
 import net.ranides.assira.reflection.asm.ClassLoaders;
 import net.ranides.assira.reflection.asm.cg.CGAdapter;
 
+import java.util.Map;
+
 //import net.ranides.assira.reflection.asm.impl.VMNames;
 
 public class Adapter {
 
+    private static Map<Tuple, IClass<?>> GENERATED = new HashMap<>();
+
     public static <T> T cast(Class<T> type, Object that) {
         return cast(IClass.typeinfo(type), that);
     }
 
     public static <T> T cast(IClass<T> type, Object that) {
-        CGAdapter cg = new CGAdapter(type, IClass.typefor(that));
-        IClass<?> ic = ClassLoaders.load(cg);
-        return type.cast(ic.construct(that));
+        IClass<?> kc = IClass.typefor(that);
+        IClass<?> ac = GENERATED.computeIfAbsent(new Tuple(type, kc), k -> ClassLoaders.load(new CGAdapter(type, kc)));
+        return type.cast(ac.construct(that));
     }
 
     public static <T> T castUnsafe(Class<T> type, Object that) {
@@ -22,9 +28,9 @@ public class Adapter {
     }
 
     public static <T> T castUnsafe(IClass<T> type, Object that) {
-        CGAdapter cg = new CGAdapter(type, IClass.typefor(that));
-        IClass<?> ic = ClassLoaders.loadUnsafe(IClass.typefor(that), cg);
-        return type.cast(ic.construct(that));
+        IClass<?> kc = IClass.typefor(that);
+        IClass<?> ac = GENERATED.computeIfAbsent(new Tuple(type, kc), k -> ClassLoaders.loadUnsafe(kc, new CGAdapter(type, kc)));
+        return type.cast(ac.construct(that));
     }
 
 }

+ 17 - 0
assira.drafts/src/main/java/net/ranides/assira/reflection/asm/cg/BindInternal.java

@@ -0,0 +1,17 @@
+package net.ranides.assira.reflection.asm.cg;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Retention(RUNTIME)
+@Target(METHOD)
+@Documented
+public @interface BindInternal {
+
+    boolean reflective() default false;
+
+}

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

@@ -116,21 +116,7 @@ public class CGAdapter implements IClassGenerator {
         ///     return this.that.method_name(args...);
         /// }
 
-        // Find method with arguments:
-        //  1. identical
-        //  2. assignable
-        //  3. castable
-        CQuery<IMethod> query = that.methods().require(IAttribute.ANY).require(method.name()).stream().fetch();
-
-        IMethod target = query.filter(r -> r.declares(method.arguments())).first()
-                .or(() -> query.filter(r -> r.matches(method.arguments())).first())
-                .or(() -> query.filter(r -> method.matches(r.arguments())).first())
-                .or(() -> {
-                    List<IMethod> out = query.filter(r -> possible(method, r)).list();
-                    return Optional.ofNullable(out.size()==1 ? out.get(0) : null);
-                })
-                .orElseThrow(() -> new IllegalArgumentException("Not found method for: " + method));
-
+        IMethod target = find(method);
         List<IClass<?>> oargs = target.arguments().types().list();
         List<IClass<?>> iargs = method.arguments().types().list();
 
@@ -154,7 +140,61 @@ public class CGAdapter implements IClassGenerator {
         m.end();
     }
 
-    private boolean possible(IMethod m1, IMethod m2) {
+    private IMethod find(IMethod method) {
+        BindInternal bi = method.annotation(BindInternal.class).orElse(null);
+        if(bi != null && bi.reflective()) {
+            throw new LinkageError("No compatible binding to " + method, new UnsupportedOperationException("Reflective binding is not implemented"));
+        }
+        IAttribute scope = bi != null ? IAttribute.ANY : IAttribute.PUBLIC;
+
+        CQuery<IMethod> query = that.methods()
+                .require(scope)
+                .require(method.name())
+                .stream()
+                .fetch();
+
+        if(query.size() == 1) {
+            IMethod found = query.first().get();
+            if(!isPossible(found, found)) {
+                throw new LinkageError("No compatible binding to " + method);
+            }
+            return found;
+        }
+
+        Optional<IMethod> exact = query.filter(r -> r.declares(method.arguments())).first();
+
+        if(exact.isPresent()) {
+            return exact.get();
+        }
+
+        CQuery<IMethod> assignable = query.filter(r -> r.matches(method.arguments()));
+        if(assignable.size() == 1) {
+            return assignable.first().get();
+        }
+        if(assignable.size() > 1) {
+            throw new LinkageError("Ambiguous widening binding to " + method);
+        }
+
+        CQuery<IMethod> narrowing = query.filter(r -> method.matches(r.arguments()));
+        if(narrowing.size() == 1) {
+            return narrowing.first().get();
+        }
+        if(narrowing.size() > 1) {
+            throw new LinkageError("Ambiguous narrowing binding to " + method);
+        }
+
+        CQuery<IMethod> possible = query.filter(r -> isPossible(method, r));
+        if(possible.size() == 1) {
+            return possible.first().get();
+        }
+        if(possible.size() > 1) {
+            throw new LinkageError("Ambiguous possible binding to " + method);
+        }
+
+        throw new LinkageError("No compatible binding to " + method);
+    }
+
+    private boolean isPossible(IMethod m1, IMethod m2) {
         if(m1.arguments().count() != m2.arguments().count()) {
             return false;
         }

+ 2 - 0
assira.drafts/src/test/java/net/ranides/assira/reflection/AdapterTest.java

@@ -27,6 +27,8 @@ public class AdapterTest {
         Screen0 screen0 = new Screen0(640, 480);
 
         Screen1 screen1 = Adapter.cast(Screen1.class, screen0);
+        Screen1 screen1p = Adapter.cast(Screen1.class, screen0);
+        Screen1 screen1q = Adapter.cast(Screen1.class, screen0);
 
         assertEquals("640x480 false", screen1.width()+"x"+screen1.height()+" "+screen1.isWide());
 

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

@@ -1,6 +1,7 @@
 package net.ranides.assira.reflection.adapter.model1;
 
 import net.ranides.assira.reflection.adapter.model0.Image0;
+import net.ranides.assira.reflection.asm.cg.BindInternal;
 
 public interface Screen1 {
 
@@ -18,6 +19,7 @@ public interface Screen1 {
 
     String findId(int x, int y);
 
+    @BindInternal
     Image0 findImage(int x, int y);
 
     boolean isWide();

+ 3 - 0
assira.drafts/src/test/java/net/ranides/assira/reflection/adapter/model2/Screen2.java

@@ -1,5 +1,7 @@
 package net.ranides.assira.reflection.adapter.model2;
 
+import net.ranides.assira.reflection.asm.cg.BindInternal;
+
 public interface Screen2 {
 
     int width();
@@ -16,6 +18,7 @@ public interface Screen2 {
 
     String findId(int x, int y);
 
+    @BindInternal
     Object findImage(int x, int y);
 
     boolean isWide();

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

@@ -1,5 +1,7 @@
 package net.ranides.assira.reflection.adapter.model3;
 
+import net.ranides.assira.reflection.asm.cg.BindInternal;
+
 public interface Screen3 {
 
     int width();
@@ -16,6 +18,7 @@ public interface Screen3 {
 
     String findId(int x, int y);
 
+    @BindInternal
     Image3 findImage(int x, int y);
 
     boolean isWide();