You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ws.apache.org by ve...@apache.org on 2021/11/28 09:20:39 UTC

[ws-axiom] branch master updated: Add support for singletons to the weaver

This is an automated email from the ASF dual-hosted git repository.

veithen pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ws-axiom.git


The following commit(s) were added to refs/heads/master by this push:
     new 6bbe3a1  Add support for singletons to the weaver
6bbe3a1 is described below

commit 6bbe3a154ef4ed6cacf778c8d89f75bef79a146b
Author: Andreas Veithen <an...@gmail.com>
AuthorDate: Sun Nov 28 09:19:42 2021 +0000

    Add support for singletons to the weaver
---
 .../apache/axiom/weaver/annotation/Singleton.java  | 17 +++++-----
 .../weaver/ImplementationClassDefinition.java      | 38 ++++++++++++++++++++--
 .../apache/axiom/weaver/ImplementationNode.java    |  1 +
 .../axiom/weaver/InterfaceClassVisitor.java}       | 29 ++++++++++++-----
 .../org/apache/axiom/weaver/InterfaceNode.java     |  8 ++++-
 .../main/java/org/apache/axiom/weaver/Weaver.java  |  4 ++-
 .../java/org/apache/axiom/weaver/tree/Factory.java |  2 ++
 .../org/apache/axiom/weaver/tree/TreeTest.java     | 10 +++---
 .../java/org/apache/axiom/core/NodeFactory2.java   |  2 ++
 .../org/apache/axiom/core/NodeFactoryImpl.java     |  2 +-
 10 files changed, 84 insertions(+), 29 deletions(-)

diff --git a/axiom-weaver/src/test/java/org/apache/axiom/weaver/tree/Factory.java b/axiom-weaver-annotations/src/main/java/org/apache/axiom/weaver/annotation/Singleton.java
similarity index 73%
copy from axiom-weaver/src/test/java/org/apache/axiom/weaver/tree/Factory.java
copy to axiom-weaver-annotations/src/main/java/org/apache/axiom/weaver/annotation/Singleton.java
index 00827d9..4c1861c 100644
--- a/axiom-weaver/src/test/java/org/apache/axiom/weaver/tree/Factory.java
+++ b/axiom-weaver-annotations/src/main/java/org/apache/axiom/weaver/annotation/Singleton.java
@@ -16,17 +16,16 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.axiom.weaver.tree;
+package org.apache.axiom.weaver.annotation;
 
-import org.apache.axiom.weaver.annotation.FactoryMethod;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.CLASS;
 
-public interface Factory {
-    @FactoryMethod
-    Root createRoot();
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
 
-    @FactoryMethod
-    Directory createDirectory();
+@Retention(CLASS)
+@Target(TYPE)
+public @interface Singleton {
 
-    @FactoryMethod
-    Leaf createLeaf();
 }
diff --git a/axiom-weaver/src/main/java/org/apache/axiom/weaver/ImplementationClassDefinition.java b/axiom-weaver/src/main/java/org/apache/axiom/weaver/ImplementationClassDefinition.java
index a9823bb..c693821 100644
--- a/axiom-weaver/src/main/java/org/apache/axiom/weaver/ImplementationClassDefinition.java
+++ b/axiom-weaver/src/main/java/org/apache/axiom/weaver/ImplementationClassDefinition.java
@@ -40,6 +40,7 @@ final class ImplementationClassDefinition extends ClassDefinition {
     private final int access;
     private final String superName;
     private final String[] ifaceNames;
+    private final boolean singleton;
     private final Mixin[] mixins;
     private final MixinMethod[] methods;
     private final List<Named<InitializerMethod>> initializerMethods = new ArrayList<>();
@@ -51,6 +52,7 @@ final class ImplementationClassDefinition extends ClassDefinition {
             int access,
             String superName,
             String[] ifaceNames,
+            boolean singleton,
             Mixin[] mixins) {
         super(targetContext.getTargetClassName());
         this.targetContext = targetContext;
@@ -58,6 +60,7 @@ final class ImplementationClassDefinition extends ClassDefinition {
         this.access = access;
         this.superName = superName != null ? superName : Type.getInternalName(Object.class);
         this.ifaceNames = ifaceNames;
+        this.singleton = singleton;
         this.mixins = mixins;
         Map<String, MixinMethod> methodMap = new LinkedHashMap<>();
         UniqueNameGenerator methodNameGenerator = new UniqueNameGenerator();
@@ -97,7 +100,13 @@ final class ImplementationClassDefinition extends ClassDefinition {
     }
 
     private void generateConstructor(ClassVisitor cv) {
-        MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
+        MethodVisitor mv =
+                cv.visitMethod(
+                        singleton ? Opcodes.ACC_PRIVATE : Opcodes.ACC_PUBLIC,
+                        "<init>",
+                        "()V",
+                        null,
+                        null);
         mv.visitCode();
         mv.visitIntInsn(Opcodes.ALOAD, 0);
         mv.visitMethodInsn(Opcodes.INVOKESPECIAL, superName, "<init>", "()V", false);
@@ -111,7 +120,7 @@ final class ImplementationClassDefinition extends ClassDefinition {
     }
 
     private void generateStaticInitializer(ClassVisitor cv) {
-        if (staticInitializerMethods.isEmpty()) {
+        if (staticInitializerMethods.isEmpty() && !singleton) {
             return;
         }
         MethodVisitor mv =
@@ -121,14 +130,37 @@ final class ImplementationClassDefinition extends ClassDefinition {
         for (Named<StaticInitializerMethod> method : staticInitializerMethods) {
             mv.visitMethodInsn(Opcodes.INVOKESTATIC, className, method.getName(), "()V", false);
         }
+        if (singleton) {
+            mv.visitTypeInsn(Opcodes.NEW, targetContext.getTargetClassName());
+            mv.visitInsn(Opcodes.DUP);
+            mv.visitMethodInsn(
+                    Opcodes.INVOKESPECIAL,
+                    targetContext.getTargetClassName(),
+                    "<init>",
+                    "()V",
+                    false);
+            mv.visitFieldInsn(
+                    Opcodes.PUTSTATIC,
+                    targetContext.getTargetClassName(),
+                    "INSTANCE",
+                    "L" + targetContext.getTargetClassName() + ";");
+        }
         mv.visitInsn(Opcodes.RETURN);
-        mv.visitMaxs(0, 0);
+        mv.visitMaxs(singleton ? 2 : 0, 0);
         mv.visitEnd();
     }
 
     @Override
     public void accept(ClassVisitor cv) {
         cv.visit(version, access, className, null, superName, ifaceNames);
+        if (singleton) {
+            cv.visitField(
+                    Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL,
+                    "INSTANCE",
+                    "L" + targetContext.getTargetClassName() + ";",
+                    null,
+                    null);
+        }
         generateConstructor(cv);
         generateStaticInitializer(cv);
         for (Mixin mixin : mixins) {
diff --git a/axiom-weaver/src/main/java/org/apache/axiom/weaver/ImplementationNode.java b/axiom-weaver/src/main/java/org/apache/axiom/weaver/ImplementationNode.java
index 2b95690..d6fadbe 100644
--- a/axiom-weaver/src/main/java/org/apache/axiom/weaver/ImplementationNode.java
+++ b/axiom-weaver/src/main/java/org/apache/axiom/weaver/ImplementationNode.java
@@ -360,6 +360,7 @@ final class ImplementationNode {
                         access,
                         parents.isEmpty() ? null : parents.iterator().next().getClassName(),
                         ifaceNames.toArray(new String[ifaceNames.size()]),
+                        primaryInterface.isSingleton(),
                         mixins.toArray(new Mixin[mixins.size()])));
         return classDefinitions;
     }
diff --git a/axiom-weaver/src/test/java/org/apache/axiom/weaver/tree/Factory.java b/axiom-weaver/src/main/java/org/apache/axiom/weaver/InterfaceClassVisitor.java
similarity index 56%
copy from axiom-weaver/src/test/java/org/apache/axiom/weaver/tree/Factory.java
copy to axiom-weaver/src/main/java/org/apache/axiom/weaver/InterfaceClassVisitor.java
index 00827d9..e6c3b6e 100644
--- a/axiom-weaver/src/test/java/org/apache/axiom/weaver/tree/Factory.java
+++ b/axiom-weaver/src/main/java/org/apache/axiom/weaver/InterfaceClassVisitor.java
@@ -16,17 +16,28 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.axiom.weaver.tree;
+package org.apache.axiom.weaver;
 
-import org.apache.axiom.weaver.annotation.FactoryMethod;
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Opcodes;
 
-public interface Factory {
-    @FactoryMethod
-    Root createRoot();
+final class InterfaceClassVisitor extends ClassVisitor {
+    private boolean singleton;
 
-    @FactoryMethod
-    Directory createDirectory();
+    InterfaceClassVisitor() {
+        super(Opcodes.ASM9);
+    }
 
-    @FactoryMethod
-    Leaf createLeaf();
+    @Override
+    public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
+        if (descriptor.equals("Lorg/apache/axiom/weaver/annotation/Singleton;")) {
+            singleton = true;
+        }
+        return null;
+    }
+
+    boolean isSingleton() {
+        return singleton;
+    }
 }
diff --git a/axiom-weaver/src/main/java/org/apache/axiom/weaver/InterfaceNode.java b/axiom-weaver/src/main/java/org/apache/axiom/weaver/InterfaceNode.java
index c83bfbb..1f1bd5e 100644
--- a/axiom-weaver/src/main/java/org/apache/axiom/weaver/InterfaceNode.java
+++ b/axiom-weaver/src/main/java/org/apache/axiom/weaver/InterfaceNode.java
@@ -31,12 +31,14 @@ final class InterfaceNode {
 
     private final Class<?> iface;
     private final MutableReferences<InterfaceNode> parents = EXTENDS.newReferenceHolder(this);
+    private final boolean singleton;
     private final MutableReferences<ImplementationNode> implementations =
             Relations.IMPLEMENTS.getConverse().newReferenceHolder(this);
 
-    InterfaceNode(Class<?> iface, Set<InterfaceNode> parents) {
+    InterfaceNode(Class<?> iface, Set<InterfaceNode> parents, boolean singleton) {
         this.iface = iface;
         this.parents.addAll(parents);
+        this.singleton = singleton;
     }
 
     Class<?> getInterface() {
@@ -47,6 +49,10 @@ final class InterfaceNode {
         return parents.asSet();
     }
 
+    boolean isSingleton() {
+        return singleton;
+    }
+
     Set<ImplementationNode> getImplementations() {
         return implementations.asSet();
     }
diff --git a/axiom-weaver/src/main/java/org/apache/axiom/weaver/Weaver.java b/axiom-weaver/src/main/java/org/apache/axiom/weaver/Weaver.java
index 0c46698..3f6950d 100644
--- a/axiom-weaver/src/main/java/org/apache/axiom/weaver/Weaver.java
+++ b/axiom-weaver/src/main/java/org/apache/axiom/weaver/Weaver.java
@@ -78,7 +78,9 @@ public final class Weaver {
                 parentInterfaces.add(parentInterface);
                 parentImplementations.addAll(parentInterface.getImplementations());
             }
-            interfaceNode = new InterfaceNode(iface, parentInterfaces);
+            InterfaceClassVisitor cv = new InterfaceClassVisitor();
+            classFetcher.fetch(iface.getName(), cv);
+            interfaceNode = new InterfaceNode(iface, parentInterfaces, cv.isSingleton());
             interfaceNodes.put(iface, interfaceNode);
             Set<MixinNode> mixinNodes = new HashSet<>();
             Set<Mixin> mixins = mixinsByInterface.get(iface);
diff --git a/axiom-weaver/src/test/java/org/apache/axiom/weaver/tree/Factory.java b/axiom-weaver/src/test/java/org/apache/axiom/weaver/tree/Factory.java
index 00827d9..9913554 100644
--- a/axiom-weaver/src/test/java/org/apache/axiom/weaver/tree/Factory.java
+++ b/axiom-weaver/src/test/java/org/apache/axiom/weaver/tree/Factory.java
@@ -19,7 +19,9 @@
 package org.apache.axiom.weaver.tree;
 
 import org.apache.axiom.weaver.annotation.FactoryMethod;
+import org.apache.axiom.weaver.annotation.Singleton;
 
+@Singleton
 public interface Factory {
     @FactoryMethod
     Root createRoot();
diff --git a/axiom-weaver/src/test/java/org/apache/axiom/weaver/tree/TreeTest.java b/axiom-weaver/src/test/java/org/apache/axiom/weaver/tree/TreeTest.java
index 98cde1b..e5b0abb 100644
--- a/axiom-weaver/src/test/java/org/apache/axiom/weaver/tree/TreeTest.java
+++ b/axiom-weaver/src/test/java/org/apache/axiom/weaver/tree/TreeTest.java
@@ -32,11 +32,11 @@ public class TreeTest {
         weaver.addInterfaceToImplement(Leaf.class);
         weaver.addInterfaceToImplement(Factory.class);
         Factory factory =
-                weaver.toClassLoader(cl)
-                        .loadClass("impl.FactoryImpl")
-                        .asSubclass(Factory.class)
-                        .getConstructor()
-                        .newInstance();
+                (Factory)
+                        weaver.toClassLoader(cl)
+                                .loadClass("impl.FactoryImpl")
+                                .getField("INSTANCE")
+                                .get(null);
         factory.createRoot();
     }
 }
diff --git a/mixins/core-mixins/src/main/java/org/apache/axiom/core/NodeFactory2.java b/mixins/core-mixins/src/main/java/org/apache/axiom/core/NodeFactory2.java
index e8922eb..bbc8285 100644
--- a/mixins/core-mixins/src/main/java/org/apache/axiom/core/NodeFactory2.java
+++ b/mixins/core-mixins/src/main/java/org/apache/axiom/core/NodeFactory2.java
@@ -19,8 +19,10 @@
 package org.apache.axiom.core;
 
 import org.apache.axiom.weaver.annotation.FactoryMethod;
+import org.apache.axiom.weaver.annotation.Singleton;
 
 // TODO: this should be fused into NodeFactory
+@Singleton
 public interface NodeFactory2 {
     @FactoryMethod
     CoreDocument createDocument();
diff --git a/mixins/core-mixins/src/main/java/org/apache/axiom/core/NodeFactoryImpl.java b/mixins/core-mixins/src/main/java/org/apache/axiom/core/NodeFactoryImpl.java
index 8cc3c73..09839d8 100644
--- a/mixins/core-mixins/src/main/java/org/apache/axiom/core/NodeFactoryImpl.java
+++ b/mixins/core-mixins/src/main/java/org/apache/axiom/core/NodeFactoryImpl.java
@@ -39,7 +39,7 @@ public abstract class NodeFactoryImpl implements NodeFactory {
     
     public NodeFactoryImpl(ClassLoader cl, String factory2ClassName, String... packages) {
         try {
-            factory2 = cl.loadClass(factory2ClassName).asSubclass(NodeFactory2.class).getDeclaredConstructor().newInstance();
+            factory2 = (NodeFactory2)cl.loadClass(factory2ClassName).getDeclaredField("INSTANCE").get(null);
         } catch (ReflectiveOperationException ex) {
             throw new NodeFactoryException("Failed to instantiate NodeFactory2 implementation", ex);
         }