Author: hlship
Date: Fri Dec 23 18:55:06 2011
New Revision: 1222790

URL: http://svn.apache.org/viewvc?rev=1222790&view=rev
Log:
TAP5-1801: Allow non-public fields in instrumented classes

Support field instrumentation on accesses from inner classes

Added:
    
tapestry/tapestry5/trunk/plastic/src/test/java/testinterfaces/ValueGetter.java
Modified:
    
tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassImpl.java
    
tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassPool.java
    
tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/ClassType.java
    
tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldAccessTests.groovy
    
tapestry/tapestry5/trunk/plastic/src/test/java/testsubjects/ProtectedField.java
    
tapestry/tapestry5/trunk/plastic/src/test/java/testsubjects/ProtectedFieldCollaborator.java

Modified: 
tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassImpl.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassImpl.java?rev=1222790&r1=1222789&r2=1222790&view=diff
==============================================================================
--- 
tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassImpl.java
 (original)
+++ 
tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassImpl.java
 Fri Dec 23 18:55:06 2011
@@ -714,9 +714,6 @@ public class PlasticClassImpl extends Lo
 
             interceptFieldAccess(node);
         }
-
-        // Remove all added methods that aren't actually used.
-
     }
 
     /**

Modified: 
tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassPool.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassPool.java?rev=1222790&r1=1222789&r2=1222790&view=diff
==============================================================================
--- 
tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassPool.java
 (original)
+++ 
tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassPool.java
 Fri Dec 23 18:55:06 2011
@@ -17,8 +17,7 @@ package org.apache.tapestry5.internal.pl
 import org.apache.tapestry5.internal.plastic.asm.ClassReader;
 import org.apache.tapestry5.internal.plastic.asm.ClassWriter;
 import org.apache.tapestry5.internal.plastic.asm.Opcodes;
-import org.apache.tapestry5.internal.plastic.asm.tree.AnnotationNode;
-import org.apache.tapestry5.internal.plastic.asm.tree.ClassNode;
+import org.apache.tapestry5.internal.plastic.asm.tree.*;
 import org.apache.tapestry5.plastic.*;
 
 import java.lang.annotation.Annotation;
@@ -129,7 +128,7 @@ public class PlasticClassPool implements
 
     }
 
-    public Class realize(String primaryClassName, ClassType classType, final 
ClassNode classNode)
+    public Class realize(String primaryClassName, ClassType classType, 
ClassNode classNode)
     {
         synchronized (loader)
         {
@@ -315,7 +314,9 @@ public class PlasticClassPool implements
         // Inner classes are not transformed, but they are loaded by the same 
class loader.
 
         if (className.contains("$"))
+        {
             return loadInnerClass(className);
+        }
 
         // TODO: What about interfaces, enums, annotations, etc. ... they 
shouldn't be in the package, but
         // we should generate a reasonable error message.
@@ -360,11 +361,56 @@ public class PlasticClassPool implements
 
     private Class loadInnerClass(String className)
     {
-        byte[] bytecode = readBytecode(className);
+        ClassNode classNode = constructClassNodeFromBytecode(className);
 
-        return loader.defineClassWithBytecode(className, bytecode);
+        interceptFieldAccess(classNode);
+
+        return realize(className, ClassType.INNER, classNode);
     }
 
+    private void interceptFieldAccess(ClassNode classNode)
+    {
+        for (MethodNode method : (List<MethodNode>) classNode.methods) {
+            interceptFieldAccess(classNode.name, method);
+        }
+    }
+
+    private void interceptFieldAccess(String classInternalName, MethodNode 
method)
+    {
+        InsnList insns = method.instructions;
+
+        ListIterator it = insns.iterator();
+
+        while (it.hasNext())
+        {
+            AbstractInsnNode node = (AbstractInsnNode) it.next();
+
+            int opcode = node.getOpcode();
+
+            if (opcode != GETFIELD && opcode != PUTFIELD)
+            {
+                continue;
+            }
+
+            FieldInsnNode fnode = (FieldInsnNode) node;
+
+            String ownerInternalName = fnode.owner;
+
+            if (ownerInternalName.equals(classInternalName)) { continue; }
+
+            FieldInstrumentation instrumentation = 
getFieldInstrumentations(ownerInternalName).get(fnode.name, opcode == GETFIELD);
+
+            if (instrumentation == null) { continue; }
+
+            // Replace the field access node with the appropriate method 
invocation.
+
+            insns.insertBefore(fnode, new MethodInsnNode(INVOKEVIRTUAL, 
ownerInternalName, instrumentation.methodName, 
instrumentation.methodDescription));
+
+            it.remove();
+        }
+    }
+
+
     /**
      * For a fully-qualified class name of an <em>existing</em> class, loads 
the bytes for the class
      * and returns a PlasticClass instance.
@@ -388,8 +434,8 @@ public class PlasticClassPool implements
     /**
      *
      * @param baseClassName class from which the transformed class extends
-     * @param classNode node for the class
-     * @param proxy if true, the class is a new empty class; if false an 
existing class that's being transformed
+     * @param classNode     node for the class
+     * @param proxy         if true, the class is a new empty class; if false 
an existing class that's being transformed
      * @return
      * @throws ClassNotFoundException
      */

Modified: 
tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/ClassType.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/ClassType.java?rev=1222790&r1=1222789&r2=1222790&view=diff
==============================================================================
--- 
tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/ClassType.java
 (original)
+++ 
tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/ClassType.java
 Fri Dec 23 18:55:06 2011
@@ -33,5 +33,13 @@ public enum ClassType
      * An implementation of {@link MethodInvocation}, needed to handle advice 
added to
      * a method of a primary class.
      */
-    METHOD_INVOCATION
+    METHOD_INVOCATION,
+
+    /**
+     * An inner class within a controlled package, which may have field 
accesses (to non-private
+     * fields visible to it) instrumented.
+     *
+     * @since 5.4
+     */
+    INNER;
 }

Modified: 
tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldAccessTests.groovy
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldAccessTests.groovy?rev=1222790&r1=1222789&r2=1222790&view=diff
==============================================================================
--- 
tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldAccessTests.groovy
 (original)
+++ 
tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldAccessTests.groovy
 Fri Dec 23 18:55:06 2011
@@ -1,5 +1,8 @@
 package org.apache.tapestry5.plastic
 
+import testannotations.KindaInject
+import testannotations.SimpleAnnotation
+
 /**
  *  Tests related to access to non-private fields between transformed classes 
(a new feature in 5.4).
  */
@@ -38,4 +41,37 @@ class FieldAccessTests extends AbstractP
 
         collab.getProtectedValue() == "badoop"
     }
+
+    def "access protected field from inner class"()
+    {
+
+        FieldConduit fc = Mock()
+
+        def delegate
+
+        PlasticClassTransformer installFieldConduit = {     PlasticClass pc ->
+
+            pc.getFieldsWithAnnotation(SimpleAnnotation.class).each { f -> 
f.setConduit(fc) }
+
+        } as PlasticClassTransformer
+
+        PlasticClassTransformer handleInjection = { PlasticClass pc ->
+
+            pc.getFieldsWithAnnotation(KindaInject.class).each { f -> 
f.inject(delegate) }
+        } as PlasticClassTransformer
+
+        def mgr = createMgr(installFieldConduit, handleInjection)
+
+        delegate = 
mgr.getClassInstantiator("testsubjects.ProtectedField").newInstance()
+
+        def collab = 
mgr.getClassInstantiator("testsubjects.ProtectedFieldCollaborator").newInstance()
+
+        when:
+
+        fc.get(_, _) >> "gnip"
+
+        then:
+
+        collab.valueGetter.value == "gnip"
+    }
 }

Added: 
tapestry/tapestry5/trunk/plastic/src/test/java/testinterfaces/ValueGetter.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/test/java/testinterfaces/ValueGetter.java?rev=1222790&view=auto
==============================================================================
--- 
tapestry/tapestry5/trunk/plastic/src/test/java/testinterfaces/ValueGetter.java 
(added)
+++ 
tapestry/tapestry5/trunk/plastic/src/test/java/testinterfaces/ValueGetter.java 
Fri Dec 23 18:55:06 2011
@@ -0,0 +1,6 @@
+package testinterfaces;
+
+public interface ValueGetter
+{
+    String getValue();
+}

Modified: 
tapestry/tapestry5/trunk/plastic/src/test/java/testsubjects/ProtectedField.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/test/java/testsubjects/ProtectedField.java?rev=1222790&r1=1222789&r2=1222790&view=diff
==============================================================================
--- 
tapestry/tapestry5/trunk/plastic/src/test/java/testsubjects/ProtectedField.java 
(original)
+++ 
tapestry/tapestry5/trunk/plastic/src/test/java/testsubjects/ProtectedField.java 
Fri Dec 23 18:55:06 2011
@@ -1,9 +1,12 @@
 package testsubjects;
 
+import testannotations.SimpleAnnotation;
+
 /**
  * Used to test access to protected fields. Accessed from {@link 
ProtectedFieldCollaborator}.
  */
 public class ProtectedField
 {
+    @SimpleAnnotation
     protected String protectedValue;
 }

Modified: 
tapestry/tapestry5/trunk/plastic/src/test/java/testsubjects/ProtectedFieldCollaborator.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/test/java/testsubjects/ProtectedFieldCollaborator.java?rev=1222790&r1=1222789&r2=1222790&view=diff
==============================================================================
--- 
tapestry/tapestry5/trunk/plastic/src/test/java/testsubjects/ProtectedFieldCollaborator.java
 (original)
+++ 
tapestry/tapestry5/trunk/plastic/src/test/java/testsubjects/ProtectedFieldCollaborator.java
 Fri Dec 23 18:55:06 2011
@@ -1,8 +1,12 @@
 package testsubjects;
 
+import testannotations.KindaInject;
+import testinterfaces.ValueGetter;
+
 public class ProtectedFieldCollaborator
 {
-    private ProtectedField delegate;
+    @KindaInject
+    ProtectedField delegate;
 
     public String getProtectedValue()
     {
@@ -13,4 +17,15 @@ public class ProtectedFieldCollaborator
     {
         delegate.protectedValue = newValue;
     }
+
+    public ValueGetter getValueGetter()
+    {
+        return new ValueGetter()
+        {
+            public String getValue()
+            {
+                return delegate.protectedValue;
+            }
+        };
+    }
 }


Reply via email to