You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2012/04/13 00:20:08 UTC

svn commit: r1325552 - in /tapestry/tapestry5/branches/5.3/plastic/src: main/java/org/apache/tapestry5/internal/plastic/ test/groovy/org/apache/tapestry5/plastic/

Author: hlship
Date: Thu Apr 12 22:20:08 2012
New Revision: 1325552

URL: http://svn.apache.org/viewvc?rev=1325552&view=rev
Log:
TAP5-1860: Access to protected component fields does not always reflect in subclasses

Modified:
    tapestry/tapestry5/branches/5.3/plastic/src/main/java/org/apache/tapestry5/internal/plastic/FieldInstrumentations.java
    tapestry/tapestry5/branches/5.3/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassImpl.java
    tapestry/tapestry5/branches/5.3/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassPool.java
    tapestry/tapestry5/branches/5.3/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldConduitTests.groovy

Modified: tapestry/tapestry5/branches/5.3/plastic/src/main/java/org/apache/tapestry5/internal/plastic/FieldInstrumentations.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/branches/5.3/plastic/src/main/java/org/apache/tapestry5/internal/plastic/FieldInstrumentations.java?rev=1325552&r1=1325551&r2=1325552&view=diff
==============================================================================
--- tapestry/tapestry5/branches/5.3/plastic/src/main/java/org/apache/tapestry5/internal/plastic/FieldInstrumentations.java (original)
+++ tapestry/tapestry5/branches/5.3/plastic/src/main/java/org/apache/tapestry5/internal/plastic/FieldInstrumentations.java Thu Apr 12 22:20:08 2012
@@ -1,4 +1,4 @@
-// Copyright 2011 The Apache Software Foundation
+// Copyright 2011, 2012 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -24,6 +24,8 @@ import java.util.Map;
  */
 class FieldInstrumentations
 {
+    final String superClassInternalName;
+
     /**
      * Map field name to a read method.
      */
@@ -34,6 +36,11 @@ class FieldInstrumentations
      */
     final Map<String, FieldInstrumentation> write = PlasticInternalUtils.newMap();
 
+    FieldInstrumentations(String superClassInternalName)
+    {
+        this.superClassInternalName = superClassInternalName;
+    }
+
     FieldInstrumentation get(String fieldName, boolean forRead)
     {
         Map<String, FieldInstrumentation> map = forRead ? read : write;

Modified: tapestry/tapestry5/branches/5.3/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/branches/5.3/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassImpl.java?rev=1325552&r1=1325551&r2=1325552&view=diff
==============================================================================
--- tapestry/tapestry5/branches/5.3/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassImpl.java (original)
+++ tapestry/tapestry5/branches/5.3/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassImpl.java Thu Apr 12 22:20:08 2012
@@ -1,4 +1,4 @@
-// Copyright 2011 The Apache Software Foundation
+// Copyright 2011, 2012 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -138,7 +138,7 @@ public class PlasticClassImpl extends Lo
      * Tracks instrumentations of fields of this class, including private fields which are not published into the
      * {@link PlasticClassPool}.
      */
-    private final FieldInstrumentations fieldInstrumentations = new FieldInstrumentations();
+    private final FieldInstrumentations fieldInstrumentations;
 
     /**
      * This normal no-arguments constructor, or null. By the end of the transformation
@@ -186,6 +186,8 @@ public class PlasticClassImpl extends Lo
         className = PlasticInternalUtils.toClassName(classNode.name);
         superClassName = PlasticInternalUtils.toClassName(classNode.superName);
 
+        fieldInstrumentations = new FieldInstrumentations(classNode.superName);
+
         annotationAccess = new DelegatingAnnotationAccess(pool.createAnnotationAccess(classNode.visibleAnnotations),
                 pool.createAnnotationAccess(superClassName));
 
@@ -909,9 +911,12 @@ public class PlasticClassImpl extends Lo
 
             FieldInsnNode fnode = (FieldInsnNode) node;
 
-            FieldInstrumentation instrumentation = findInstrumentations(fnode).get(fnode.name, opcode == GETFIELD);
+            FieldInstrumentation instrumentation = findFieldNodeInstrumentation(fnode, opcode == GETFIELD);
 
-            if (instrumentation == null) { continue; }
+            if (instrumentation == null)
+            {
+                continue;
+            }
 
             // Replace the field access node with the appropriate method invocation.
 
@@ -921,15 +926,29 @@ public class PlasticClassImpl extends Lo
         }
     }
 
-    private FieldInstrumentations findInstrumentations(FieldInsnNode node)
+    private FieldInstrumentation findFieldNodeInstrumentation(FieldInsnNode node, boolean forRead)
     {
-        if (node.owner.equals(classNode.name))
+        // First look in the local fieldInstrumentations, which contains private field instrumentations
+        // (as well as non-private ones).
+
+        String searchStart = node.owner;
+
+        if (searchStart.equals(classNode.name))
         {
-            return fieldInstrumentations;
-        }
+            FieldInstrumentation result = fieldInstrumentations.get(node.name, forRead);
+
+            if (result != null)
+            {
+                return result;
+            }
+
+            // Slight optimization: start the search in the super-classes' fields, since we've already
+            // checked this classes fields.
 
-        return pool.getFieldInstrumentations(node.owner);
+            searchStart = classNode.superName;
+        }
 
+        return pool.getFieldInstrumentation(searchStart, node.name, forRead);
     }
 
     String getInstanceContextFieldName()

Modified: tapestry/tapestry5/branches/5.3/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassPool.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/branches/5.3/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassPool.java?rev=1325552&r1=1325551&r2=1325552&view=diff
==============================================================================
--- tapestry/tapestry5/branches/5.3/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassPool.java (original)
+++ tapestry/tapestry5/branches/5.3/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassPool.java Thu Apr 12 22:20:08 2012
@@ -1,4 +1,4 @@
-// Copyright 2011 The Apache Software Foundation
+// Copyright 2011, 2012 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -86,7 +86,7 @@ public class PlasticClassPool implements
 
     private final Map<String, FieldInstrumentations> instrumentations = PlasticInternalUtils.newMap();
 
-    private final FieldInstrumentations placeholder = new FieldInstrumentations();
+    private final FieldInstrumentations placeholder = new FieldInstrumentations(null);
 
 
     private final Set<TransformationOption> options;
@@ -370,7 +370,8 @@ public class PlasticClassPool implements
 
     private void interceptFieldAccess(ClassNode classNode)
     {
-        for (MethodNode method : (List<MethodNode>) classNode.methods) {
+        for (MethodNode method : (List<MethodNode>) classNode.methods)
+        {
             interceptFieldAccess(classNode.name, method);
         }
     }
@@ -396,11 +397,17 @@ public class PlasticClassPool implements
 
             String ownerInternalName = fnode.owner;
 
-            if (ownerInternalName.equals(classInternalName)) { continue; }
+            if (ownerInternalName.equals(classInternalName))
+            {
+                continue;
+            }
 
-            FieldInstrumentation instrumentation = getFieldInstrumentations(ownerInternalName).get(fnode.name, opcode == GETFIELD);
+            FieldInstrumentation instrumentation = getFieldInstrumentation(ownerInternalName, fnode.name, opcode == GETFIELD);
 
-            if (instrumentation == null) { continue; }
+            if (instrumentation == null)
+            {
+                continue;
+            }
 
             // Replace the field access node with the appropriate method invocation.
 
@@ -426,13 +433,12 @@ public class PlasticClassPool implements
 
         String baseClassName = PlasticInternalUtils.toClassName(classNode.superName);
 
-        instrumentations.put(classNode.name, new FieldInstrumentations());
+        instrumentations.put(classNode.name, new FieldInstrumentations(classNode.superName));
 
         return createTransformation(baseClassName, classNode, false);
     }
 
     /**
-     *
      * @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
@@ -582,7 +588,8 @@ public class PlasticClassPool implements
         instrumentations.get(classInternalName).read.put(fieldName, fi);
     }
 
-    FieldInstrumentations getFieldInstrumentations(String classInternalName)
+
+    private FieldInstrumentations getFieldInstrumentations(String classInternalName)
     {
         FieldInstrumentations result = instrumentations.get(classInternalName);
 
@@ -620,6 +627,32 @@ public class PlasticClassPool implements
         return result;
     }
 
+    FieldInstrumentation getFieldInstrumentation(String ownerClassInternalName, String fieldName, boolean forRead)
+    {
+        String currentName = ownerClassInternalName;
+
+        while (true)
+        {
+
+            if (currentName == null)
+            {
+                return null;
+            }
+
+            FieldInstrumentations instrumentations = getFieldInstrumentations(currentName);
+
+            FieldInstrumentation instrumentation = instrumentations.get(fieldName, forRead);
+
+            if (instrumentation != null)
+            {
+                return instrumentation;
+            }
+
+            currentName = instrumentations.superClassInternalName;
+        }
+    }
+
+
     void setFieldWriteInstrumentation(String classInternalName, String fieldName, FieldInstrumentation fi)
     {
         instrumentations.get(classInternalName).write.put(fieldName, fi);

Modified: tapestry/tapestry5/branches/5.3/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldConduitTests.groovy
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/branches/5.3/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldConduitTests.groovy?rev=1325552&r1=1325551&r2=1325552&view=diff
==============================================================================
--- tapestry/tapestry5/branches/5.3/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldConduitTests.groovy (original)
+++ tapestry/tapestry5/branches/5.3/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldConduitTests.groovy Thu Apr 12 22:20:08 2012
@@ -118,6 +118,37 @@ class FieldConduitTests extends Abstract
         1 * fc.get(o, _) >> "plastic"
     }
 
+    def "subclass access methods are reouted through field conduit"()
+    {
+        FieldConduit fc = Mock()
+
+        def mgr = PlasticManager.withContextClassLoader().delegate(
+                [
+                        transform: { PlasticClass pc ->
+                            pc.allFields.each { f -> f.setConduit(fc) }
+                        },
+                        configureInstantiator: { className, instantiator -> instantiator }
+                ] as PlasticManagerDelegate).packages(["testsubjects"]).create()
+
+        def o = mgr.getClassInstantiator(ProtectedFieldSubclass.class.name).newInstance()
+
+        when:
+
+        o.value = "wink"
+
+        then:
+
+        1 * fc.set(o, _, "wink")
+
+        when:
+
+        assert o.value == "bumble"
+
+        then:
+
+        1* fc.get(o, _) >> "bumble"
+    }
+
     def "verify writebehind on normal field"()
     {
         FieldConduit fc = Mock()