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 2011/05/03 23:12:49 UTC

svn commit: r1099254 - in /tapestry/tapestry5/trunk/plastic/src: main/java/org/apache/tapestry5/internal/plastic/ main/java/org/apache/tapestry5/plastic/ test/groovy/org/apache/tapestry5/plastic/ test/java/testsubjects/

Author: hlship
Date: Tue May  3 21:12:49 2011
New Revision: 1099254

URL: http://svn.apache.org/viewvc?rev=1099254&view=rev
Log:
TAP5-853: Add support for field write-behind (useful for debugging) when connecting a field to a FieldConduit

Added:
    tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/TransformationOption.java
    tapestry/tapestry5/trunk/plastic/src/test/java/testsubjects/IntWriteBehind.java
    tapestry/tapestry5/trunk/plastic/src/test/java/testsubjects/LongWriteBehind.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/PlasticField.java
    tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticManager.java
    tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldConduitTests.groovy
    tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/ObtainPlasticClass.groovy

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=1099254&r1=1099253&r2=1099254&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 Tue May  3 21:12:49 2011
@@ -64,6 +64,7 @@ import org.apache.tapestry5.plastic.Plas
 import org.apache.tapestry5.plastic.PropertyAccessType;
 import org.apache.tapestry5.plastic.SwitchBlock;
 import org.apache.tapestry5.plastic.SwitchCallback;
+import org.apache.tapestry5.plastic.TransformationOption;
 import org.apache.tapestry5.plastic.TryCatchBlock;
 import org.apache.tapestry5.plastic.TryCatchCallback;
 
@@ -837,6 +838,11 @@ public class PlasticClassImpl extends Lo
 
             builder.invoke(FieldConduit.class, void.class, "set", Object.class, InstanceContext.class, Object.class);
 
+            if (isWriteBehindEnabled())
+            {
+                builder.loadThis().loadArgument(0).putField(className, node.name, typeName);
+            }
+
             builder.returnResult();
 
             addMethod(setAccess);
@@ -846,6 +852,8 @@ public class PlasticClassImpl extends Lo
 
         private void replaceFieldReadAccess(String conduitFieldName)
         {
+            boolean writeBehindEnabled = isWriteBehindEnabled();
+
             String getAccessName = makeUnique(methodNames, "getfieldvalue_" + node.name);
 
             getAccess = new MethodNode(ACC_SYNTHETIC | ACC_FINAL, getAccessName, "()" + node.desc, null, null);
@@ -865,6 +873,28 @@ public class PlasticClassImpl extends Lo
             builder.invoke(FieldConduit.class, Object.class, "get", Object.class, InstanceContext.class).castOrUnbox(
                     typeName);
 
+            if (writeBehindEnabled)
+            {
+                // Dupe the value, then push this, then swap
+
+                if (isWide())
+                {
+                    // Dupe this under the wide value, then pop the wide value
+
+                    builder.dupeWide().loadThis().dupe(2).pop();
+                }
+                else
+                {
+                    builder.dupe().loadThis().swap();
+                }
+
+                // At which point the stack is the result value, this, the result value
+
+                builder.putField(className, node.name, typeName);
+
+                // And now it is just the result value
+            }
+
             builder.returnResult();
 
             addMethod(getAccess);
@@ -872,6 +902,18 @@ public class PlasticClassImpl extends Lo
             fieldToReadMethod.put(node.name, getAccess);
         }
 
+        private boolean isWriteBehindEnabled()
+        {
+            return pool.isEnabled(TransformationOption.FIELD_WRITEBEHIND);
+        }
+
+        private boolean isWide()
+        {
+            PrimitiveType pt = PrimitiveType.getByName(typeName);
+
+            return pt != null && pt.isWide();
+        }
+
         private void pushFieldConduitOntoStack(String conduitFileName, InstructionBuilder builder)
         {
             builder.loadThis();
@@ -2288,7 +2330,7 @@ public class PlasticClassImpl extends Lo
     {
         int index = staticContext.store(injectedFieldValue);
 
-        // Although it feels nicer to do the loadThis() later and then swap() that breaks
+        // Although it feels nicer to do the loadThis() later and then swap(), that breaks
         // on primitive longs and doubles, so its just easier to do the loadThis() first
         // so its at the right place on the stack for the putField().
 

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=1099254&r1=1099253&r2=1099254&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 Tue May  3 21:12:49 2011
@@ -37,6 +37,7 @@ import org.apache.tapestry5.plastic.Plas
 import org.apache.tapestry5.plastic.PlasticClassListenerHub;
 import org.apache.tapestry5.plastic.PlasticClassTransformation;
 import org.apache.tapestry5.plastic.PlasticManagerDelegate;
+import org.apache.tapestry5.plastic.TransformationOption;
 
 /**
  * Responsible for managing a class loader that allows ASM {@link ClassNode}s
@@ -86,6 +87,8 @@ public class PlasticClassPool implements
     /** Map from FQCN to BaseClassDef. */
     private final Map<String, BaseClassDef> baseClassDefs = new HashMap<String, PlasticClassPool.BaseClassDef>();
 
+    private final Set<TransformationOption> options;
+
     /**
      * Creates the pool with a set of controlled packages; all classes in the controlled packages are loaded by the
      * pool's class loader, and all top-level classes in the controlled packages are transformed via the delegate.
@@ -96,12 +99,16 @@ public class PlasticClassPool implements
      *            responsible for end stages of transforming top-level classes
      * @param controlledPackages
      *            set of package names (note: retained, not copied)
+     * @param options
+     *            used when transforming classes
      */
-    public PlasticClassPool(ClassLoader parentLoader, PlasticManagerDelegate delegate, Set<String> controlledPackages)
+    public PlasticClassPool(ClassLoader parentLoader, PlasticManagerDelegate delegate, Set<String> controlledPackages,
+            Set<TransformationOption> options)
     {
         loader = new PlasticClassLoader(parentLoader, this);
         this.delegate = delegate;
         this.controlledPackages = controlledPackages;
+        this.options = options;
     }
 
     public ClassLoader getClassLoader()
@@ -474,4 +481,8 @@ public class PlasticClassPool implements
         listeners.remove(listener);
     }
 
+    boolean isEnabled(TransformationOption option)
+    {
+        return options.contains(option);
+    }
 }

Modified: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticField.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticField.java?rev=1099254&r1=1099253&r2=1099254&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticField.java (original)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticField.java Tue May  3 21:12:49 2011
@@ -105,6 +105,9 @@ public interface PlasticField extends An
      * Intercepts all access to the field, replacing such access with calls on the conduit. Even access via
      * the FieldHandle will instead delegate to the conduit. Once a conduit is provided, it is not possible
      * to inject a value into the field.
+     * <p>
+     * Normally, once a conduit is in place, the field will never be actually read or written. This is problematic for
+     * debugging, so {@link TransformationOption#FIELD_WRITEBEHIND} is useful when operating in a non-production mode.
      * 
      * @return the field for further manipulation
      * @throws IllegalStateException

Modified: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticManager.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticManager.java?rev=1099254&r1=1099253&r2=1099254&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticManager.java (original)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticManager.java Tue May  3 21:12:49 2011
@@ -15,8 +15,10 @@
 package org.apache.tapestry5.plastic;
 
 import java.util.Collection;
+import java.util.EnumSet;
 import java.util.Set;
 
+import org.apache.tapestry5.internal.plastic.Lockable;
 import org.apache.tapestry5.internal.plastic.NoopDelegate;
 import org.apache.tapestry5.internal.plastic.PlasticClassPool;
 import org.apache.tapestry5.internal.plastic.PlasticInternalUtils;
@@ -25,7 +27,7 @@ import org.apache.tapestry5.internal.pla
  * Manages the internal class loaders and other logics necessary to load and transform existing classes,
  * or to create new classes dynamically at runtime. New instances are instantiates using
  * {@link #withClassLoader(ClassLoader)} or {@link #withContextClassLoader()}, then configuring
- * the returned options object before invoking {@link PlasticManagerOptions#create()}.
+ * the returned options object before invoking {@link PlasticManagerBuilder#create()}.
  */
 @SuppressWarnings("unchecked")
 public class PlasticManager implements PlasticClassListenerHub
@@ -34,10 +36,11 @@ public class PlasticManager implements P
 
     /**
      * A builder object for configuring the PlasticManager before instantiating it. Assumes a no-op
-     * {@link PlasticManagerDelegate} and an empty
-     * set of controlled packages.
+     * {@link PlasticManagerDelegate} and an empty set of controlled packages, which is appropriate
+     * when simply {@linkplain PlasticManager#createProxy(Class, PlasticClassTransformer) creating proxy objects).
+     * The builder object is internally mutable and uses a fluid API (each method returns the same instance).
      */
-    public static class PlasticManagerOptions
+    public static class PlasticManagerBuilder extends Lockable
     {
         private final ClassLoader loader;
 
@@ -45,7 +48,9 @@ public class PlasticManager implements P
 
         private final Set<String> packages = PlasticInternalUtils.newSet();
 
-        private PlasticManagerOptions(ClassLoader loader)
+        private final Set<TransformationOption> options = EnumSet.noneOf(TransformationOption.class);
+
+        private PlasticManagerBuilder(ClassLoader loader)
         {
             assert loader != null;
 
@@ -57,10 +62,12 @@ public class PlasticManager implements P
          * transforming classes loaded from controlled packages. The default delegate
          * does nothing.
          */
-        public PlasticManagerOptions delegate(PlasticManagerDelegate delegate)
+        public PlasticManagerBuilder delegate(PlasticManagerDelegate delegate)
         {
             assert delegate != null;
 
+            check();
+
             this.delegate = delegate;
 
             return this;
@@ -69,10 +76,21 @@ public class PlasticManager implements P
         /**
          * Adds additional controlled packages, in which classes are loaded and transformed.
          */
-        public PlasticManagerOptions packages(Collection<String> packageNames)
+        public PlasticManagerBuilder packages(Collection<String> packageNames)
         {
             packages.addAll(packageNames);
 
+            check();
+
+            return this;
+        }
+
+        public PlasticManagerBuilder enable(TransformationOption option)
+        {
+            options.add(option);
+
+            check();
+
             return this;
         }
 
@@ -83,22 +101,24 @@ public class PlasticManager implements P
          */
         public PlasticManager create()
         {
-            return new PlasticManager(loader, delegate, packages);
+            lock();
+
+            return new PlasticManager(loader, delegate, packages, options);
         }
     }
 
     /**
-     * Creates a new options using the thread's context class loader.
+     * Creates a new builder using the thread's context class loader.
      */
-    public static PlasticManagerOptions withContextClassLoader()
+    public static PlasticManagerBuilder withContextClassLoader()
     {
         return withClassLoader(Thread.currentThread().getContextClassLoader());
     }
 
-    /** Creates a new options using the specified class loader. */
-    public static PlasticManagerOptions withClassLoader(ClassLoader loader)
+    /** Creates a new builder using the specified class loader. */
+    public static PlasticManagerBuilder withClassLoader(ClassLoader loader)
     {
-        return new PlasticManagerOptions(loader);
+        return new PlasticManagerBuilder(loader);
     }
 
     /**
@@ -112,15 +132,17 @@ public class PlasticManager implements P
      * @param controlledPackageNames
      *            defines the packages that are to be transformed; top-classes in these packages
      *            (or sub-packages) will be passed to the delegate for transformation
+     * @param options
+     *            used when transforming classes
      */
     private PlasticManager(ClassLoader parentClassLoader, PlasticManagerDelegate delegate,
-            Set<String> controlledPackageNames)
+            Set<String> controlledPackageNames, Set<TransformationOption> options)
     {
         assert parentClassLoader != null;
         assert delegate != null;
         assert controlledPackageNames != null;
 
-        pool = new PlasticClassPool(parentClassLoader, delegate, controlledPackageNames);
+        pool = new PlasticClassPool(parentClassLoader, delegate, controlledPackageNames, options);
     }
 
     /**
@@ -195,7 +217,7 @@ public class PlasticManager implements P
     }
 
     /**
-     * Creates an entirely new class, extending from the provided base class.
+     * Creates an entirely new class. The class extends from Object and implements the provided interface.
      * 
      * @param interfaceType
      *            class to extend from, which must be a class, not an interface

Added: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/TransformationOption.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/TransformationOption.java?rev=1099254&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/TransformationOption.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/TransformationOption.java Tue May  3 21:12:49 2011
@@ -0,0 +1,33 @@
+// Copyright 2011 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.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.plastic;
+
+import org.apache.tapestry5.plastic.PlasticManager.PlasticManagerBuilder;
+
+/**
+ * Options used when transforming classes. By default, no options are enabled, they must be specifically enabled
+ * using {@link PlasticManagerBuilder#enable(TransformationOption)}.
+ */
+public enum TransformationOption
+{
+    /**
+     * When enabled, fields that have a {@link FieldConduit} will write the value sent to, or returned by, the conduit.
+     * This is essential for debugging of the transformed class.
+     * 
+     * @see PlasticField#setConduit(FieldConduit)
+     * 
+     */
+    FIELD_WRITEBEHIND;
+}

Modified: tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldConduitTests.groovy
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldConduitTests.groovy?rev=1099254&r1=1099253&r2=1099254&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldConduitTests.groovy (original)
+++ tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldConduitTests.groovy Tue May  3 21:12:49 2011
@@ -94,12 +94,13 @@ class FieldConduitTests extends Abstract
 
         FieldConduit fc = Mock()
 
-        def mgr = new PlasticManager (Thread.currentThread().contextClassLoader, [
+        def mgr = PlasticManager.withContextClassLoader().delegate(
+                [
                     transform: { PlasticClass pc ->
                         pc.allFields.first().setConduit(fc)
                     },
                     configureInstantiator: { className, instantiator -> instantiator }
-                ] as PlasticManagerDelegate , ["testsubjects"]as Set)
+                ] as PlasticManagerDelegate).packages(["testsubjects"]).create()
 
 
         def o = mgr.getClassInstantiator("testsubjects.AccessMethodsSubject").newInstance()
@@ -122,4 +123,72 @@ class FieldConduitTests extends Abstract
 
         1 * fc.get(o, _) >> "plastic"
     }
+
+    def "verify writebehind on normal field"() {
+        FieldConduit fc = Mock()
+
+        def mgr = PlasticManager.withContextClassLoader().enable(TransformationOption.FIELD_WRITEBEHIND).create()
+
+        def pc = mgr.getPlasticClass("testsubjects.IntWriteBehind")
+
+        pc.allFields.first().setConduit(fc)
+
+        def o = pc.createInstantiator().newInstance()
+
+        when:
+
+        o.value = 97
+
+        then:
+
+        1 * fc.set(o, _, 97)
+
+        o.m_value == 97
+
+        when:
+
+        def r = o.value
+
+        then:
+
+        1 * fc.get(o, _) >> 1097
+
+        r == 1097
+
+        o.m_value == 1097
+    }
+    
+    def "verify writebehind on wide field"() {
+        FieldConduit fc = Mock()
+
+        def mgr = PlasticManager.withContextClassLoader().enable(TransformationOption.FIELD_WRITEBEHIND).create()
+
+        def pc = mgr.getPlasticClass("testsubjects.LongWriteBehind")
+
+        pc.allFields.first().setConduit(fc)
+
+        def o = pc.createInstantiator().newInstance()
+
+        when:
+
+        o.value = 123456789L
+
+        then:
+
+        1 * fc.set(o, _, 123456789L)
+
+        o.m_value == 123456789L
+
+        when:
+
+        def r = o.value
+
+        then:
+
+        1 * fc.get(o, _) >> 987654321L
+
+        r == 987654321L
+
+        o.m_value == 987654321L
+    }
 }

Modified: tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/ObtainPlasticClass.groovy
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/ObtainPlasticClass.groovy?rev=1099254&r1=1099253&r2=1099254&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/ObtainPlasticClass.groovy (original)
+++ tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/ObtainPlasticClass.groovy Tue May  3 21:12:49 2011
@@ -119,10 +119,7 @@ class ObtainPlasticClass extends Abstrac
     def "original constructors now throw exceptions"() {
         setup:
 
-        def delegate = new NoopDelegate()
-        def packages = ["testsubjects"]as Set
-
-        def mgr = new PlasticManager(Thread.currentThread().contextClassLoader, delegate, packages)
+        def mgr = PlasticManager.withContextClassLoader().delegate(new NoopDelegate()).packages(["testsubjects"]).create()
 
         Class clazz = mgr.classLoader.loadClass("testsubjects.AlternateConstructor")
 

Added: tapestry/tapestry5/trunk/plastic/src/test/java/testsubjects/IntWriteBehind.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/test/java/testsubjects/IntWriteBehind.java?rev=1099254&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/test/java/testsubjects/IntWriteBehind.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/test/java/testsubjects/IntWriteBehind.java Tue May  3 21:12:49 2011
@@ -0,0 +1,16 @@
+package testsubjects;
+
+public class IntWriteBehind
+{
+    private int m_value;
+
+    public int getValue()
+    {
+        return m_value;
+    }
+
+    public void setValue(int value)
+    {
+        this.m_value = value;
+    }
+}

Added: tapestry/tapestry5/trunk/plastic/src/test/java/testsubjects/LongWriteBehind.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/test/java/testsubjects/LongWriteBehind.java?rev=1099254&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/test/java/testsubjects/LongWriteBehind.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/test/java/testsubjects/LongWriteBehind.java Tue May  3 21:12:49 2011
@@ -0,0 +1,17 @@
+package testsubjects;
+
+public class LongWriteBehind
+{
+    private long m_value;
+
+    public long getValue()
+    {
+        return m_value;
+    }
+
+    public void setValue(long value)
+    {
+        this.m_value = value;
+    }
+
+}