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;
+ }
+
+}