You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by th...@apache.org on 2024/03/10 14:13:41 UTC

(tapestry-5) branch javax updated (f786355f2 -> 5da802b1e)

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

thiagohp pushed a change to branch javax
in repository https://gitbox.apache.org/repos/asf/tapestry-5.git


    from f786355f2 Removing unneeded code in SeleniumTestCase.openLinks()
     new e8564a9ff TAP5-2770: avoiding using FieldHandle when in multiple classloader mode
     new 4a226d063 TAP5-2770: avoiding using FieldHandle when in multiple classloader mode
     new b632f5e0a TAP5-2770: fixing JavaDoc errors
     new 5da802b1e TAP5-2770: fixing regression

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../internal/plastic/InstructionBuilderImpl.java   |  45 ++++-
 .../tapestry5/plastic/FieldValueProvider.java      |  64 +++++++
 .../tapestry5/plastic/InstructionBuilder.java      |  16 ++
 .../org/apache/tapestry5/plastic/PlasticUtils.java | 206 ++++++++++++++++++++-
 .../tapestry5/plastic/PropertyValueProvider.java   |  63 +++++++
 .../apache/tapestry5/plastic/PlasticUtilsTest.java | 115 ++++++++++++
 .../plastic/test/PlasticUtilsTestObject.java       | 105 +++++++++++
 ....java => PlasticUtilsTestObjectSuperclass.java} |  21 ++-
 .../tapestry5/internal/transform/CachedWorker.java |  57 +++++-
 .../tapestry5/internal/transform/ImportWorker.java |  85 +++++++--
 .../transform/PropertyValueProviderWorker.java     |  68 +++++++
 .../apache/tapestry5/modules/TapestryModule.java   |  13 +-
 .../app1/components/SubclassWithImport.java        |  14 +-
 .../app1/components/SuperclassWithImport.java      |  13 ++
 14 files changed, 842 insertions(+), 43 deletions(-)
 create mode 100644 plastic/src/main/java/org/apache/tapestry5/plastic/FieldValueProvider.java
 create mode 100644 plastic/src/main/java/org/apache/tapestry5/plastic/PropertyValueProvider.java
 create mode 100644 plastic/src/test/java/org/apache/tapestry5/plastic/PlasticUtilsTest.java
 create mode 100644 plastic/src/test/java/org/apache/tapestry5/plastic/test/PlasticUtilsTestObject.java
 copy plastic/src/test/java/org/apache/tapestry5/plastic/test/{NoopAdvice.java => PlasticUtilsTestObjectSuperclass.java} (62%)
 create mode 100644 tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/PropertyValueProviderWorker.java


(tapestry-5) 04/04: TAP5-2770: fixing regression

Posted by th...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

thiagohp pushed a commit to branch javax
in repository https://gitbox.apache.org/repos/asf/tapestry-5.git

commit 5da802b1e4b3a2ffd83064de83e86f1302ff6ace
Author: Thiago H. de Paula Figueiredo <th...@arsmachina.com.br>
AuthorDate: Wed Dec 20 22:46:57 2023 -0300

    TAP5-2770: fixing regression
---
 plastic/src/main/java/org/apache/tapestry5/plastic/PlasticUtils.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticUtils.java b/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticUtils.java
index e9d469c3b..8dd3c76f1 100644
--- a/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticUtils.java
+++ b/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticUtils.java
@@ -281,7 +281,7 @@ public class PlasticUtils
                 builder.loadArgument(0);
                 ifBuilder.invokeSpecial(
                         plasticClass.getSuperClassName(), 
-                        FIELD_VALUE_PROVIDER_METHOD_DESCRIPTION);
+                        PROPERTY_VALUE_PROVIDER_METHOD_DESCRIPTION);
                 ifBuilder.returnResult();
             });
             


(tapestry-5) 03/04: TAP5-2770: fixing JavaDoc errors

Posted by th...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

thiagohp pushed a commit to branch javax
in repository https://gitbox.apache.org/repos/asf/tapestry-5.git

commit b632f5e0a75d1b1f49aadb3746c261bf4b1d872a
Author: Thiago H. de Paula Figueiredo <th...@arsmachina.com.br>
AuthorDate: Wed Dec 20 21:42:51 2023 -0300

    TAP5-2770: fixing JavaDoc errors
---
 .../tapestry5/plastic/FieldValueProvider.java      |  2 +-
 .../org/apache/tapestry5/plastic/PlasticUtils.java | 29 ++++++++++++++++-----
 .../tapestry5/plastic/PropertyValueProvider.java   |  8 +++---
 .../apache/tapestry5/plastic/PlasticUtilsTest.java | 30 ++++++++++++++--------
 4 files changed, 46 insertions(+), 23 deletions(-)

diff --git a/plastic/src/main/java/org/apache/tapestry5/plastic/FieldValueProvider.java b/plastic/src/main/java/org/apache/tapestry5/plastic/FieldValueProvider.java
index 79087e8b8..c964530f5 100644
--- a/plastic/src/main/java/org/apache/tapestry5/plastic/FieldValueProvider.java
+++ b/plastic/src/main/java/org/apache/tapestry5/plastic/FieldValueProvider.java
@@ -19,7 +19,7 @@ package org.apache.tapestry5.plastic;
  * Interface that can be implemented to provide access to field values based on their name.
  * Usually implemented with {@linkplain PlasticUtils#implementFieldValueProvider(PlasticClass, java.util.Set)},
  * which doesn't use reflection.
- * <p/>
+ * </p>
  * <p>
  * The name of its abstract method is intended to avoid clashes with other existing methods
  * in the class.
diff --git a/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticUtils.java b/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticUtils.java
index 9c35d3428..e9d469c3b 100644
--- a/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticUtils.java
+++ b/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticUtils.java
@@ -39,10 +39,13 @@ public class PlasticUtils
     
     private static final MethodDescription PROPERTY_VALUE_PROVIDER_METHOD_DESCRIPTION;
     
+    private static final MethodDescription FIELD_VALUE_PROVIDER_METHOD_DESCRIPTION;
+    
     static
     {
         try {
             PROPERTY_VALUE_PROVIDER_METHOD_DESCRIPTION = new MethodDescription(PropertyValueProvider.class.getMethod("__propertyValueProvider__get", String.class));
+            FIELD_VALUE_PROVIDER_METHOD_DESCRIPTION = new MethodDescription(FieldValueProvider.class.getMethod("__fieldValueProvider__get", String.class));
         } catch (Exception e) {
             throw new ExceptionInInitializerError(e);
         }
@@ -185,10 +188,10 @@ public class PlasticUtils
      * an {@linkplain IllegalAccessError}.
      * 
      * @param plasticClass a {@linkplain PlasticClass} instance.
-     * @param fieldNames a {@linkplain Set} of {@linkplain String}s containing the field names.
+     * @param fieldInfos a {@linkplain Set} of {@linkplain String}s containing the field names.
      * @since 5.8.4
      */
-    public static void implementFieldValueProvider(PlasticClass plasticClass, Set<FieldInfo> fields)
+    public static void implementFieldValueProvider(PlasticClass plasticClass, Set<FieldInfo> fieldInfos)
     {
         
         final Set<PlasticMethod> methods = plasticClass.introduceInterface(FieldValueProvider.class);
@@ -199,7 +202,7 @@ public class PlasticUtils
             
             method.changeImplementation((builder) -> {
                 
-                for (FieldInfo field : fields) 
+                for (FieldInfo field : fieldInfos) 
                 {
                     builder.loadArgument(0);
                     builder.loadConstant(field.name);
@@ -212,6 +215,18 @@ public class PlasticUtils
                     });
                 }
                 
+                builder.loadThis();
+                builder.instanceOf(FieldValueProvider.class);
+                
+                builder.when(Condition.NON_ZERO, ifBuilder -> {
+                    builder.loadThis();
+                    builder.loadArgument(0);
+                    ifBuilder.invokeSpecial(
+                            plasticClass.getSuperClassName(), 
+                            FIELD_VALUE_PROVIDER_METHOD_DESCRIPTION);
+                    ifBuilder.returnResult();
+                });
+                
                 builder.throwException(RuntimeException.class, "Field not found or not supported");
                 
             });
@@ -227,17 +242,17 @@ public class PlasticUtils
      * of direct fields access.
      * 
      * @param plasticClass a {@linkplain PlasticClass} instance.
-     * @param fieldNames a {@linkplain Set} of {@linkplain String}s containing the filed (i.e. property) names.
+     * @param fieldInfos a {@linkplain Set} of {@linkplain String}s containing the filed (i.e. property) names.
      * @since 5.8.4
      */
-    public static void implementPropertyValueProvider(PlasticClass plasticClass, Set<FieldInfo> fields)
+    public static void implementPropertyValueProvider(PlasticClass plasticClass, Set<FieldInfo> fieldInfos)
     {
         
         final Set<PlasticMethod> methods = plasticClass.introduceInterface(PropertyValueProvider.class);
         
         final InstructionBuilderCallback callback = (builder) -> {
             
-            for (FieldInfo field : fields) 
+            for (FieldInfo field : fieldInfos) 
             {
                 builder.loadArgument(0);
                 builder.loadConstant(field.name);
@@ -266,7 +281,7 @@ public class PlasticUtils
                 builder.loadArgument(0);
                 ifBuilder.invokeSpecial(
                         plasticClass.getSuperClassName(), 
-                        PROPERTY_VALUE_PROVIDER_METHOD_DESCRIPTION);
+                        FIELD_VALUE_PROVIDER_METHOD_DESCRIPTION);
                 ifBuilder.returnResult();
             });
             
diff --git a/plastic/src/main/java/org/apache/tapestry5/plastic/PropertyValueProvider.java b/plastic/src/main/java/org/apache/tapestry5/plastic/PropertyValueProvider.java
index bbba55a3b..3f841b82d 100644
--- a/plastic/src/main/java/org/apache/tapestry5/plastic/PropertyValueProvider.java
+++ b/plastic/src/main/java/org/apache/tapestry5/plastic/PropertyValueProvider.java
@@ -14,18 +14,16 @@
 
 package org.apache.tapestry5.plastic;
 
-import java.lang.reflect.Method;
-
 /**
  * <p>
  * Interface that can be implemented to provide access to field values based on their name.
- * Usually implemented with {@linkplain} FieldValueProviderTransformation}.
- * <p/>
+ * Usually implemented with {@linkplain PlasticUtils#implementPropertyValueProvider(PlasticClass, java.util.Set)}.
+ * </p>
  * <p>
  * The name of its abstract method is intended to avoid clashes with other existing methods
  * in the class.
  * </p>
- * @see FieldValueProviderTransformation
+ * @see PlasticUtils#implementPropertyValueProvider(PlasticClass, java.util.Set)
  * @since 5.8.4
  */
 public interface PropertyValueProvider
diff --git a/plastic/src/test/java/org/apache/tapestry5/plastic/PlasticUtilsTest.java b/plastic/src/test/java/org/apache/tapestry5/plastic/PlasticUtilsTest.java
index 3d0e4f14f..40faac5d2 100644
--- a/plastic/src/test/java/org/apache/tapestry5/plastic/PlasticUtilsTest.java
+++ b/plastic/src/test/java/org/apache/tapestry5/plastic/PlasticUtilsTest.java
@@ -31,28 +31,38 @@ import org.junit.jupiter.api.Test;
 public class PlasticUtilsTest 
 {
     
-    public static void main(String[] args) throws ClassNotFoundException {
-        final PlasticUtilsTest plasticUtilsTest = new PlasticUtilsTest();
-        plasticUtilsTest.implement_field_value_pProvider();
-        plasticUtilsTest.implement_property_value_provider();
-    }
+//    public static void main(String[] args) throws ClassNotFoundException {
+//        final PlasticUtilsTest plasticUtilsTest = new PlasticUtilsTest();
+//        plasticUtilsTest.implement_field_value_provider();
+//        plasticUtilsTest.implement_property_value_provider();
+//    }
     
     @Test
-    public void implement_field_value_pProvider() throws ClassNotFoundException
+    public void implement_field_value_provider() throws ClassNotFoundException
     {
         
         Set<String> packages = new HashSet<>();
         packages.add(PlasticUtilsTestObject.class.getPackage().getName());
+
         PlasticManager plasticManager = PlasticManager.withContextClassLoader()
                 .packages(packages).create();
-        final PlasticClassTransformation<Object> transformation = plasticManager.getPlasticClass(PlasticUtilsTestObject.class.getName());
-        PlasticClass pc = transformation.getPlasticClass();
+        
         Set<PlasticUtils.FieldInfo> fieldInfos = new HashSet<PlasticUtils.FieldInfo>();
+
+        PlasticClassTransformation<Object> transformation2 = plasticManager.getPlasticClass(PlasticUtilsTestObjectSuperclass.class.getName());
+        PlasticClass pc2 = transformation2.getPlasticClass();
+        fieldInfos.clear();
+        fieldInfos.add(new PlasticUtils.FieldInfo("superString", "java.lang.String"));
+        PlasticUtils.implementFieldValueProvider(pc2, fieldInfos);
+
+        PlasticClassTransformation<Object> transformation = plasticManager.getPlasticClass(PlasticUtilsTestObject.class.getName());
+        PlasticClass pc = transformation.getPlasticClass();
         for (PlasticField field : pc.getAllFields()) {
             fieldInfos.add(PlasticUtils.toFieldInfo(field));
         }
-        fieldInfos.add(new PlasticUtils.FieldInfo("superString", "java.lang.String"));
         PlasticUtils.implementFieldValueProvider(pc, fieldInfos);
+
+        
         Object object = transformation.createInstantiator().newInstance();
         
         Class<?> original = PlasticUtilsTestObject.class;
@@ -66,7 +76,7 @@ public class PlasticUtilsTest
         assertEquals(PlasticUtilsTestObject.ENUMERATION.toString(), FieldValueProvider.get(object, "enumeration").toString());
         assertTrue(Arrays.equals(PlasticUtilsTestObject.INT_ARRAY, (int[]) FieldValueProvider.get(object, "intArray")));
         assertEquals(PlasticUtilsTestObject.TRUE_OF_FALSE, (Boolean) FieldValueProvider.get(object, "trueOrFalse"));
-        
+//        assertEquals(PlasticUtilsTestObjectSuperclass.SUPER, FieldValueProvider.get(object, "superString"));
     }
     
     @Test


(tapestry-5) 01/04: TAP5-2770: avoiding using FieldHandle when in multiple classloader mode

Posted by th...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

thiagohp pushed a commit to branch javax
in repository https://gitbox.apache.org/repos/asf/tapestry-5.git

commit e8564a9ff715d8a21c38b4f3b50cdb93293f126a
Author: Thiago H. de Paula Figueiredo <th...@arsmachina.com.br>
AuthorDate: Wed Dec 20 19:00:29 2023 -0300

    TAP5-2770: avoiding using FieldHandle when in multiple classloader mode
    
    For now, just in ImportWorker.
---
 .../internal/plastic/InstructionBuilderImpl.java   |  45 ++++-
 .../tapestry5/plastic/FieldValueProvider.java      |  64 +++++++
 .../tapestry5/plastic/InstructionBuilder.java      |  16 ++
 .../org/apache/tapestry5/plastic/PlasticUtils.java | 191 ++++++++++++++++++++-
 .../tapestry5/plastic/PropertyValueProvider.java   |  74 ++++++++
 .../apache/tapestry5/plastic/PlasticUtilsTest.java | 105 +++++++++++
 .../plastic/test/PlasticUtilsTestObject.java       | 105 +++++++++++
 .../test/PlasticUtilsTestObjectSuperclass.java     |  34 ++++
 .../tapestry5/internal/transform/ImportWorker.java |  80 +++++++--
 9 files changed, 689 insertions(+), 25 deletions(-)

diff --git a/plastic/src/main/java/org/apache/tapestry5/internal/plastic/InstructionBuilderImpl.java b/plastic/src/main/java/org/apache/tapestry5/internal/plastic/InstructionBuilderImpl.java
index dd4488349..a8be0b928 100644
--- a/plastic/src/main/java/org/apache/tapestry5/internal/plastic/InstructionBuilderImpl.java
+++ b/plastic/src/main/java/org/apache/tapestry5/internal/plastic/InstructionBuilderImpl.java
@@ -14,16 +14,28 @@
 
 package org.apache.tapestry5.internal.plastic;
 
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
 import org.apache.tapestry5.internal.plastic.InstructionBuilderState.LVInfo;
 import org.apache.tapestry5.internal.plastic.asm.Label;
 import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
 import org.apache.tapestry5.internal.plastic.asm.Opcodes;
 import org.apache.tapestry5.internal.plastic.asm.Type;
-import org.apache.tapestry5.plastic.*;
-
-import java.lang.reflect.Method;
-import java.util.HashMap;
-import java.util.Map;
+import org.apache.tapestry5.plastic.Condition;
+import org.apache.tapestry5.plastic.InstructionBuilder;
+import org.apache.tapestry5.plastic.InstructionBuilderCallback;
+import org.apache.tapestry5.plastic.LocalVariable;
+import org.apache.tapestry5.plastic.LocalVariableCallback;
+import org.apache.tapestry5.plastic.MethodDescription;
+import org.apache.tapestry5.plastic.PlasticField;
+import org.apache.tapestry5.plastic.PlasticMethod;
+import org.apache.tapestry5.plastic.PlasticUtils;
+import org.apache.tapestry5.plastic.SwitchCallback;
+import org.apache.tapestry5.plastic.TryCatchCallback;
+import org.apache.tapestry5.plastic.WhenCallback;
+import org.apache.tapestry5.plastic.WhileCallback;
 
 @SuppressWarnings("rawtypes")
 public class InstructionBuilderImpl extends Lockable implements Opcodes, InstructionBuilder
@@ -469,6 +481,29 @@ public class InstructionBuilderImpl extends Lockable implements Opcodes, Instruc
 
         return checkcast(cache.toTypeName(clazz));
     }
+    
+    @Override
+    public InstructionBuilder instanceOf(String className)
+    {
+        check();
+
+        // Found out the hard way that array names are handled differently; you cast to the descriptor, not the internal
+        // name.
+
+        String internalName = className.contains("[") ? cache.toDesc(className) : cache.toInternalName(className);
+
+        v.visitTypeInsn(INSTANCEOF, internalName);
+
+        return this;
+    }
+
+    @Override
+    public InstructionBuilder instanceOf(Class clazz)
+    {
+        check();
+
+        return instanceOf(cache.toTypeName(clazz));
+    }
 
     @Override
     public InstructionBuilder startTryCatch(TryCatchCallback callback)
diff --git a/plastic/src/main/java/org/apache/tapestry5/plastic/FieldValueProvider.java b/plastic/src/main/java/org/apache/tapestry5/plastic/FieldValueProvider.java
new file mode 100644
index 000000000..79087e8b8
--- /dev/null
+++ b/plastic/src/main/java/org/apache/tapestry5/plastic/FieldValueProvider.java
@@ -0,0 +1,64 @@
+// Copyright 2023 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;
+
+/**
+ * <p>
+ * Interface that can be implemented to provide access to field values based on their name.
+ * Usually implemented with {@linkplain PlasticUtils#implementFieldValueProvider(PlasticClass, java.util.Set)},
+ * which doesn't use reflection.
+ * <p/>
+ * <p>
+ * The name of its abstract method is intended to avoid clashes with other existing methods
+ * in the class.
+ * </p>
+ * @see PlasticUtils#implementFieldValueProvider(PlasticClass, java.util.Set)
+ * @since 5.8.4
+ */
+public interface FieldValueProvider
+{
+    /**
+     * Returns the value of a given field.
+     * @param fieldName the field name.
+     * @return the field value.
+     */
+    Object __fieldValueProvider__get(String fieldName);
+    
+    /**
+     * <p>
+     * Returns the value of a given field in a given object if it belongs to a class
+     * that implements {@linkplain FieldValueProvider}. Otherwise, it throws an exception.
+     * </p>
+     * <p>
+     * This is an utility method to avoid having to make casts very time you need to call
+     * {@linkplain #__fieldValueProvider__get(String)}.
+     * </p>
+     * @param object an object.
+     * @param fieldName the field name.
+     * @return the field value.
+     */
+    static Object get(Object object, String fieldName)
+    {
+        if (object instanceof FieldValueProvider)
+        {
+            return ((FieldValueProvider) object).__fieldValueProvider__get(fieldName);
+        }
+        else
+        {
+            throw new RuntimeException("Class " + object.getClass().getName() + " doesn't implement " + FieldValueProvider.class.getSimpleName());
+        }
+    }
+    
+}
diff --git a/plastic/src/main/java/org/apache/tapestry5/plastic/InstructionBuilder.java b/plastic/src/main/java/org/apache/tapestry5/plastic/InstructionBuilder.java
index 97a4f1768..7f14c860d 100644
--- a/plastic/src/main/java/org/apache/tapestry5/plastic/InstructionBuilder.java
+++ b/plastic/src/main/java/org/apache/tapestry5/plastic/InstructionBuilder.java
@@ -247,6 +247,22 @@ public interface InstructionBuilder
     @Opcodes("CHECKCAST")
     InstructionBuilder checkcast(Class clazz);
 
+    /**
+     * Adds a check that the object on top of the stack is assignable to the indicated class.
+     *
+     * @param className class to cast to
+     * @since 5.8.4
+     */
+    @Opcodes("CHECKCAST")
+    InstructionBuilder instanceOf(String className);
+
+    /**
+     * Adds a check that the object on top of the stack is assignable to the indicated class.
+     * @since 5.8.4
+     */
+    @Opcodes("CHECKCAST")
+    InstructionBuilder instanceOf(Class clazz);
+
     /**
      * Defines the start of a block that can have exception handlers and finally blocks applied.
      * Continue using this InstructionBuilder to define code inside the block, then call
diff --git a/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticUtils.java b/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticUtils.java
index 2fab09efd..9c35d3428 100644
--- a/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticUtils.java
+++ b/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticUtils.java
@@ -12,11 +12,14 @@
 
 package org.apache.tapestry5.plastic;
 
-import org.apache.tapestry5.internal.plastic.PrimitiveType;
-
 import java.lang.reflect.Method;
+import java.util.Objects;
+import java.util.Set;
 import java.util.concurrent.atomic.AtomicLong;
 
+import org.apache.tapestry5.internal.plastic.PlasticInternalUtils;
+import org.apache.tapestry5.internal.plastic.PrimitiveType;
+
 /**
  * Utilities for user code making use of Plastic.
  */
@@ -33,6 +36,18 @@ public class PlasticUtils
     public static final MethodDescription TO_STRING_DESCRIPTION = new MethodDescription(TO_STRING);
 
     private static final AtomicLong UID_GENERATOR = new AtomicLong(System.nanoTime());
+    
+    private static final MethodDescription PROPERTY_VALUE_PROVIDER_METHOD_DESCRIPTION;
+    
+    static
+    {
+        try {
+            PROPERTY_VALUE_PROVIDER_METHOD_DESCRIPTION = new MethodDescription(PropertyValueProvider.class.getMethod("__propertyValueProvider__get", String.class));
+        } catch (Exception e) {
+            throw new ExceptionInInitializerError(e);
+        }
+    }
+
 
     /**
      * Returns a string that can be used as part of a Java identifier and is unique
@@ -151,4 +166,176 @@ public class PlasticUtils
         int index = className.indexOf('$');
         return index <= 0 ? className : className.substring(0, index);
     }
+
+    /**
+     * Utility method for creating {@linkplain FieldInfo} instances.
+     * @param field a {@linkplain PlasticField}.
+     * @return a corresponding {@linkplain FieldInfo}.
+     * @since 5.8.4
+     */
+    public static FieldInfo toFieldInfo(PlasticField field)
+    {
+        return new FieldInfo(field.getName(), field.getTypeName());
+    }
+    
+    /**
+     * Transforms this {@linkplain PlasticClass} so it implements
+     * {@linkplain FieldValueProvider} for the given set of field names.
+     * Notice attempts to read a superclass' private field will result in 
+     * an {@linkplain IllegalAccessError}.
+     * 
+     * @param plasticClass a {@linkplain PlasticClass} instance.
+     * @param fieldNames a {@linkplain Set} of {@linkplain String}s containing the field names.
+     * @since 5.8.4
+     */
+    public static void implementFieldValueProvider(PlasticClass plasticClass, Set<FieldInfo> fields)
+    {
+        
+        final Set<PlasticMethod> methods = plasticClass.introduceInterface(FieldValueProvider.class);
+        
+        if (!methods.isEmpty())
+        {
+            final PlasticMethod method = methods.iterator().next();
+            
+            method.changeImplementation((builder) -> {
+                
+                for (FieldInfo field : fields) 
+                {
+                    builder.loadArgument(0);
+                    builder.loadConstant(field.name);
+                    builder.invokeVirtual(String.class.getName(), "boolean", "equals", Object.class.getName());
+                    builder.when(Condition.NON_ZERO, ifBuilder -> {
+                        ifBuilder.loadThis();
+                        ifBuilder.getField(plasticClass.getClassName(), field.name, field.type);
+                        ifBuilder.boxPrimitive(field.type);
+                        ifBuilder.returnResult();
+                    });
+                }
+                
+                builder.throwException(RuntimeException.class, "Field not found or not supported");
+                
+            });
+            
+        }
+        
+    }
+    
+    /**
+     * Transforms this {@linkplain PlasticClass} so it implements
+     * {@linkplain PropertyValueProvider} for the given set of field names.
+     * The implementation will use the fields' corresponding getters instead
+     * of direct fields access.
+     * 
+     * @param plasticClass a {@linkplain PlasticClass} instance.
+     * @param fieldNames a {@linkplain Set} of {@linkplain String}s containing the filed (i.e. property) names.
+     * @since 5.8.4
+     */
+    public static void implementPropertyValueProvider(PlasticClass plasticClass, Set<FieldInfo> fields)
+    {
+        
+        final Set<PlasticMethod> methods = plasticClass.introduceInterface(PropertyValueProvider.class);
+        
+        final InstructionBuilderCallback callback = (builder) -> {
+            
+            for (FieldInfo field : fields) 
+            {
+                builder.loadArgument(0);
+                builder.loadConstant(field.name);
+                builder.invokeVirtual(String.class.getName(), "boolean", "equals", Object.class.getName());
+                builder.when(Condition.NON_ZERO, ifBuilder -> 
+                {
+                    final String prefix = field.type.equals("boolean") ? "is" : "get";
+                    final String methodName = prefix + PlasticInternalUtils.capitalize(field.name);
+                    
+                    ifBuilder.loadThis();
+                    builder.invokeVirtual(
+                            plasticClass.getClassName(), 
+                            field.type, 
+                            methodName);
+                    ifBuilder.boxPrimitive(field.type);
+                    ifBuilder.returnResult();
+                });
+                
+            }
+            
+            builder.loadThis();
+            builder.instanceOf(PropertyValueProvider.class);
+            
+            builder.when(Condition.NON_ZERO, ifBuilder -> {
+                builder.loadThis();
+                builder.loadArgument(0);
+                ifBuilder.invokeSpecial(
+                        plasticClass.getSuperClassName(), 
+                        PROPERTY_VALUE_PROVIDER_METHOD_DESCRIPTION);
+                ifBuilder.returnResult();
+            });
+            
+            // Field/property not found, so let's try the superclass in case
+            // it also implement
+            
+            builder.throwException(RuntimeException.class, "Property not found or not supported");
+            
+        };
+        
+        final PlasticMethod method;
+        
+        // Superclass has already defined this method, so we need to override it so
+        // it can also find the subclasses' declared fields/properties.
+        if (methods.isEmpty())
+        {
+            method = plasticClass.introduceMethod(PROPERTY_VALUE_PROVIDER_METHOD_DESCRIPTION , callback);
+        }
+        else
+        {
+            method = methods.iterator().next();
+        }
+        
+        method.changeImplementation(callback);
+        
+    }
+
+    /**
+     * Class used to represent a field name and its type for 
+     * {@linkplain PlasticUtils#implementFieldValueProvider(PlasticClass, Set)}.
+     * It shouldn't be used directly. Use {@linkplain PlasticUtils#toFieldInfo(PlasticField)}
+     * instead.
+     * @see PlasticUtils#implementFieldValueProvider(PlasticClass, Set)
+     * @since 5.8.4
+     */
+    public static class FieldInfo {
+        final private String name;
+        final private String type;
+        public FieldInfo(String name, String type) 
+        {
+            super();
+            this.name = name;
+            this.type = type;
+        }
+        @Override
+        public int hashCode() 
+        {
+            return Objects.hash(name);
+        }
+        @Override
+        public boolean equals(Object obj) 
+        {
+            if (this == obj) 
+            {
+                return true;
+            }
+            if (!(obj instanceof FieldInfo)) 
+            {
+                return false;
+            }
+            FieldInfo other = (FieldInfo) obj;
+            return Objects.equals(name, other.name);
+        }
+        @Override
+        public String toString() 
+        {
+            return "FieldInfo [name=" + name + ", type=" + type + "]";
+        }
+        
+    }
+    
 }
diff --git a/plastic/src/main/java/org/apache/tapestry5/plastic/PropertyValueProvider.java b/plastic/src/main/java/org/apache/tapestry5/plastic/PropertyValueProvider.java
new file mode 100644
index 000000000..e33301f48
--- /dev/null
+++ b/plastic/src/main/java/org/apache/tapestry5/plastic/PropertyValueProvider.java
@@ -0,0 +1,74 @@
+// Copyright 2023 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 java.lang.reflect.Method;
+
+/**
+ * <p>
+ * Interface that can be implemented to provide access to field values based on their name.
+ * Usually implemented with {@linkplain} FieldValueProviderTransformation}.
+ * <p/>
+ * <p>
+ * The name of its abstract method is intended to avoid clashes with other existing methods
+ * in the class.
+ * </p>
+ * @see FieldValueProviderTransformation
+ * @since 5.8.4
+ */
+public interface PropertyValueProvider
+{
+    /**
+     * Returns the value of a given field.
+     * @param fieldName the field name.
+     * @return the field value.
+     */
+    Object __propertyValueProvider__get(String fieldName);
+    
+    /**
+     * <p>
+     * Returns the value of a given field in a given object if it belongs to a class
+     * that implements {@linkplain PropertyValueProvider}. Otherwise, it throws an exception.
+     * </p>
+     * <p>
+     * This is an utility method to avoid having to make casts very time you need to call
+     * {@linkplain #__propertyValueProvider__get(String)}.
+     * </p>
+     * @param object an object.
+     * @param fieldName the field name.
+     * @return the field value.
+     */
+    static Object get(Object object, String fieldName)
+    {
+        if (object instanceof PropertyValueProvider)
+        {
+            try {
+            return ((PropertyValueProvider) object).__propertyValueProvider__get(fieldName);
+            }
+            catch (Exception e) {
+                final Method[] methods = object.getClass().getMethods();
+                for (Method method : methods) {
+                    System.out.println(method);
+                }
+                throw new RuntimeException(e.getMessage() + ": " + fieldName, e);
+            }
+        }
+        else
+        {
+            throw new RuntimeException("Class " + object.getClass().getName() + " doesn't implement " + PropertyValueProvider.class.getSimpleName());
+        }
+    }
+    
+}
diff --git a/plastic/src/test/java/org/apache/tapestry5/plastic/PlasticUtilsTest.java b/plastic/src/test/java/org/apache/tapestry5/plastic/PlasticUtilsTest.java
new file mode 100644
index 000000000..3d0e4f14f
--- /dev/null
+++ b/plastic/src/test/java/org/apache/tapestry5/plastic/PlasticUtilsTest.java
@@ -0,0 +1,105 @@
+// Copyright 2023 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 static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.tapestry5.plastic.test.PlasticUtilsTestObject;
+import org.apache.tapestry5.plastic.test.PlasticUtilsTestObjectSuperclass;
+import org.junit.jupiter.api.Test;
+
+// [Thiago] This is only here because I couldn't get Groovy tests to run on Eclipse
+// (and I admit to not liking Groovy anyway . . .).
+public class PlasticUtilsTest 
+{
+    
+    public static void main(String[] args) throws ClassNotFoundException {
+        final PlasticUtilsTest plasticUtilsTest = new PlasticUtilsTest();
+        plasticUtilsTest.implement_field_value_pProvider();
+        plasticUtilsTest.implement_property_value_provider();
+    }
+    
+    @Test
+    public void implement_field_value_pProvider() throws ClassNotFoundException
+    {
+        
+        Set<String> packages = new HashSet<>();
+        packages.add(PlasticUtilsTestObject.class.getPackage().getName());
+        PlasticManager plasticManager = PlasticManager.withContextClassLoader()
+                .packages(packages).create();
+        final PlasticClassTransformation<Object> transformation = plasticManager.getPlasticClass(PlasticUtilsTestObject.class.getName());
+        PlasticClass pc = transformation.getPlasticClass();
+        Set<PlasticUtils.FieldInfo> fieldInfos = new HashSet<PlasticUtils.FieldInfo>();
+        for (PlasticField field : pc.getAllFields()) {
+            fieldInfos.add(PlasticUtils.toFieldInfo(field));
+        }
+        fieldInfos.add(new PlasticUtils.FieldInfo("superString", "java.lang.String"));
+        PlasticUtils.implementFieldValueProvider(pc, fieldInfos);
+        Object object = transformation.createInstantiator().newInstance();
+        
+        Class<?> original = PlasticUtilsTestObject.class;
+        Class<?> transformed = object.getClass();
+        
+        assertNotEquals(original, transformed);
+        
+        assertEquals(PlasticUtilsTestObject.STRING, FieldValueProvider.get(object, "string"));
+        assertEquals(PlasticUtilsTestObject.OTHER_STRING, FieldValueProvider.get(object, "otherString"));
+        assertEquals(null, FieldValueProvider.get(object, "nullString"));
+        assertEquals(PlasticUtilsTestObject.ENUMERATION.toString(), FieldValueProvider.get(object, "enumeration").toString());
+        assertTrue(Arrays.equals(PlasticUtilsTestObject.INT_ARRAY, (int[]) FieldValueProvider.get(object, "intArray")));
+        assertEquals(PlasticUtilsTestObject.TRUE_OF_FALSE, (Boolean) FieldValueProvider.get(object, "trueOrFalse"));
+        
+    }
+    
+    @Test
+    public void implement_property_value_provider() throws ClassNotFoundException
+    {
+        
+        Set<String> packages = new HashSet<>();
+        packages.add(PlasticUtilsTestObject.class.getPackage().getName());
+        PlasticManager plasticManager = PlasticManager.withContextClassLoader()
+                .packages(packages).create();
+        final PlasticClassTransformation<Object> transformation = plasticManager.getPlasticClass(PlasticUtilsTestObject.class.getName());
+        PlasticClass pc = transformation.getPlasticClass();
+        Set<PlasticUtils.FieldInfo> fieldInfos = new HashSet<PlasticUtils.FieldInfo>();
+        for (PlasticField field : pc.getAllFields()) {
+            fieldInfos.add(PlasticUtils.toFieldInfo(field));
+        }
+        fieldInfos.add(new PlasticUtils.FieldInfo("superString", "java.lang.String"));
+        PlasticUtils.implementPropertyValueProvider(pc, fieldInfos);
+        Object object = transformation.createInstantiator().newInstance();
+        
+        Class<?> original = PlasticUtilsTestObject.class;
+        Class<?> transformed = object.getClass();
+        
+        assertNotEquals(original, transformed);
+        
+        assertEquals(PlasticUtilsTestObject.STRING, PropertyValueProvider.get(object, "string"));
+        assertEquals(PlasticUtilsTestObject.OTHER_STRING, PropertyValueProvider.get(object, "otherString"));
+        assertEquals(null, PropertyValueProvider.get(object, "nullString"));
+        assertEquals(PlasticUtilsTestObject.ENUMERATION.toString(), PropertyValueProvider.get(object, "enumeration").toString());
+        assertTrue(Arrays.equals(PlasticUtilsTestObject.INT_ARRAY, (int[]) PropertyValueProvider.get(object, "intArray")));
+        assertEquals(PlasticUtilsTestObject.TRUE_OF_FALSE, (Boolean) PropertyValueProvider.get(object, "trueOrFalse"));
+        assertEquals(PlasticUtilsTestObjectSuperclass.SUPER, PropertyValueProvider.get(object, "superString"));
+        
+    }
+    
+}
diff --git a/plastic/src/test/java/org/apache/tapestry5/plastic/test/PlasticUtilsTestObject.java b/plastic/src/test/java/org/apache/tapestry5/plastic/test/PlasticUtilsTestObject.java
new file mode 100644
index 000000000..bd34620ff
--- /dev/null
+++ b/plastic/src/test/java/org/apache/tapestry5/plastic/test/PlasticUtilsTestObject.java
@@ -0,0 +1,105 @@
+// Copyright 2023 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.test;
+
+public class PlasticUtilsTestObject extends PlasticUtilsTestObjectSuperclass
+{
+    
+    public static final String STRING = "A nice string";
+    public static final int[] INT_ARRAY = new int[] {1, 42};
+    public static final String OTHER_STRING = "Another nice string";
+    public static final Enumeration ENUMERATION = PlasticUtilsTestObject.Enumeration.FILE_NOT_FOUND;
+    public static final boolean TRUE_OF_FALSE = true;
+    
+    public static enum Enumeration
+    {
+        TRUE,
+        FALSE,
+        FILE_NOT_FOUND
+    }
+    
+    private String string = STRING;
+    
+    private String otherString = OTHER_STRING;
+    
+    private String nullString = null;
+    
+    private Enumeration enumeration = ENUMERATION;
+    
+    private int[] intArray = INT_ARRAY;
+    
+    private boolean trueOrFalse = TRUE_OF_FALSE;
+
+    public String getString() 
+    {
+        return string;
+    }
+    
+    public void setString(String string) 
+    {
+        this.string = string;
+    }
+
+    public String getOtherString() 
+    {
+        return otherString;
+    }
+    
+    public void setOtherString(String otherString) 
+    {
+        this.otherString = otherString;
+    }
+    
+    public String getNullString() 
+    {
+        return nullString;
+    }
+    
+    public void setNullString(String nullString) 
+    {
+        this.nullString = nullString;
+    }
+    
+    public Enumeration getEnumeration() 
+    {
+        return enumeration;
+    }
+    
+    public void setEnumeration(Enumeration enumeration) 
+    {
+        this.enumeration = enumeration;
+    }
+    
+    public int[] getIntArray() 
+    {
+        return intArray;
+    }
+    
+    public void setIntArray(int[] intArray) 
+    {
+        this.intArray = intArray;
+    }
+    
+    public boolean isTrueOrFalse() 
+    {
+        return trueOrFalse;
+    }
+    
+    public void setTrueOrFalse(boolean trueOrFalse) 
+    {
+        this.trueOrFalse = trueOrFalse;
+    }
+    
+}
diff --git a/plastic/src/test/java/org/apache/tapestry5/plastic/test/PlasticUtilsTestObjectSuperclass.java b/plastic/src/test/java/org/apache/tapestry5/plastic/test/PlasticUtilsTestObjectSuperclass.java
new file mode 100644
index 000000000..f82995f88
--- /dev/null
+++ b/plastic/src/test/java/org/apache/tapestry5/plastic/test/PlasticUtilsTestObjectSuperclass.java
@@ -0,0 +1,34 @@
+// Copyright 2023 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.test;
+
+public class PlasticUtilsTestObjectSuperclass 
+{
+    
+    public static final String SUPER = "Super!!!";
+    
+    private String superString = SUPER;
+
+    public String getSuperString() 
+    {
+        return superString;
+    }
+
+    public void setSuperString(String superString) 
+    {
+        this.superString = superString;
+    }
+    
+}
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ImportWorker.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ImportWorker.java
index e47553fbd..9e5b85967 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ImportWorker.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ImportWorker.java
@@ -24,6 +24,7 @@ import org.apache.tapestry5.internal.services.assets.ResourceChangeTracker;
 import org.apache.tapestry5.ioc.services.SymbolSource;
 import org.apache.tapestry5.model.MutableComponentModel;
 import org.apache.tapestry5.plastic.*;
+import org.apache.tapestry5.plastic.PlasticUtils.FieldInfo;
 import org.apache.tapestry5.services.AssetSource;
 import org.apache.tapestry5.services.TransformConstants;
 import org.apache.tapestry5.services.javascript.Initialization;
@@ -32,7 +33,9 @@ import org.apache.tapestry5.services.transform.ComponentClassTransformWorker2;
 import org.apache.tapestry5.services.transform.TransformationSupport;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Implements the {@link Import} annotation, both at the class and at the method level.
@@ -41,6 +44,9 @@ import java.util.List;
  */
 public class ImportWorker implements ComponentClassTransformWorker2
 {
+    
+    private static final String FIELD_PREFIX = "importedAssets_";
+    
     private final JavaScriptSupport javascriptSupport;
 
     private final SymbolSource symbolSource;
@@ -90,17 +96,26 @@ public class ImportWorker implements ComponentClassTransformWorker2
     public void transform(PlasticClass componentClass, TransformationSupport support, MutableComponentModel model)
     {
         resourceChangeTracker.setCurrentClassName(model.getComponentClassName());
-        processClassAnnotationAtSetupRenderPhase(componentClass, model);
+        
+        Set<PlasticUtils.FieldInfo> fieldInfos = multipleClassLoaders ? new HashSet<>() : null;
+        processClassAnnotationAtSetupRenderPhase(componentClass, model, fieldInfos);
 
-        for (PlasticMethod m : componentClass.getMethodsWithAnnotation(Import.class))
+        final List<PlasticMethod> methods = componentClass.getMethodsWithAnnotation(Import.class);
+        for (PlasticMethod m : methods)
         {
-            decorateMethod(componentClass, model, m);
+            decorateMethod(componentClass, model, m, fieldInfos);
+        }
+        
+        if (multipleClassLoaders && !fieldInfos.isEmpty())
+        {
+            PlasticUtils.implementPropertyValueProvider(componentClass, fieldInfos);
         }
         
         resourceChangeTracker.clearCurrentClassName();
     }
 
-    private void processClassAnnotationAtSetupRenderPhase(PlasticClass componentClass, MutableComponentModel model)
+    private void processClassAnnotationAtSetupRenderPhase(PlasticClass componentClass, MutableComponentModel model,
+            Set<FieldInfo> fieldInfos)
     {
         Import annotation = componentClass.getAnnotation(Import.class);
 
@@ -108,27 +123,27 @@ public class ImportWorker implements ComponentClassTransformWorker2
         {
             PlasticMethod setupRender = componentClass.introduceMethod(TransformConstants.SETUP_RENDER_DESCRIPTION);
 
-            decorateMethod(componentClass, model, setupRender, annotation);
+            decorateMethod(componentClass, model, setupRender, annotation, fieldInfos);
 
             model.addRenderPhase(SetupRender.class);
         }
     }
 
-    private void decorateMethod(PlasticClass componentClass, MutableComponentModel model, PlasticMethod method)
+    private void decorateMethod(PlasticClass componentClass, MutableComponentModel model, PlasticMethod method, Set<FieldInfo> fieldInfos)
     {
         Import annotation = method.getAnnotation(Import.class);
 
-        decorateMethod(componentClass, model, method, annotation);
+        decorateMethod(componentClass, model, method, annotation, fieldInfos);
     }
 
     private void decorateMethod(PlasticClass componentClass, MutableComponentModel model, PlasticMethod method,
-                                Import annotation)
+                                Import annotation, Set<FieldInfo> fieldInfos)
     {
         importStacks(method, annotation.stack());
 
-        importLibraries(componentClass, model, method, annotation.library());
+        importLibraries(componentClass, model, method, annotation.library(), fieldInfos);
 
-        importStylesheets(componentClass, model, method, annotation.stylesheet());
+        importStylesheets(componentClass, model, method, annotation.stylesheet(), fieldInfos);
 
         importModules(method, annotation.module());
     }
@@ -215,19 +230,20 @@ public class ImportWorker implements ComponentClassTransformWorker2
     }
 
     private void importLibraries(PlasticClass plasticClass, MutableComponentModel model, PlasticMethod method,
-                                 String[] paths)
+                                 String[] paths, Set<FieldInfo> fieldInfos)
     {
-        decorateMethodWithOperation(plasticClass, model, method, paths, importLibrary);
+        decorateMethodWithOperation(plasticClass, model, method, paths, importLibrary, fieldInfos);
     }
 
     private void importStylesheets(PlasticClass plasticClass, MutableComponentModel model, PlasticMethod method,
-                                   String[] paths)
+                                   String[] paths, Set<FieldInfo> fieldInfos)
     {
-        decorateMethodWithOperation(plasticClass, model, method, paths, importStylesheet);
+        decorateMethodWithOperation(plasticClass, model, method, paths, importStylesheet, fieldInfos);
     }
 
     private void decorateMethodWithOperation(PlasticClass componentClass, MutableComponentModel model,
-                                             PlasticMethod method, String[] paths, Worker<Asset> operation)
+                                             PlasticMethod method, String[] paths, Worker<Asset> operation,
+                                             Set<FieldInfo> fieldInfos)
     {
         if (paths.length == 0)
         {
@@ -236,14 +252,32 @@ public class ImportWorker implements ComponentClassTransformWorker2
 
         String[] expandedPaths = expandPaths(paths);
 
-        PlasticField assetListField = componentClass.introduceField(Asset[].class,
-                "importedAssets_" + method.getDescription().methodName);
+        final String fieldName = getFieldName(method);
+        PlasticField assetListField = componentClass.introduceField(Asset[].class, fieldName);
+        
+        if (multipleClassLoaders)
+        {
+            fieldInfos.add(PlasticUtils.toFieldInfo(assetListField));
+            assetListField.createAccessors(PropertyAccessType.READ_ONLY);
+        }
 
         initializeAssetsFromPaths(expandedPaths, assetListField, model.getLibraryName());
 
         addMethodAssetOperationAdvice(method, assetListField.getHandle(), operation);
     }
 
+    private String getFieldName(PlasticMethod method) 
+    {
+        final StringBuilder builder = new StringBuilder(FIELD_PREFIX);
+        builder.append(method.getDescription().methodName);
+        if (multipleClassLoaders)
+        {
+            builder.append("_");
+            builder.append(method.getPlasticClass().getClassName().replace('.', '_'));
+        }
+        return builder.toString();
+    }
+
     private String[] expandPaths(String[] paths)
     {
         return F.flow(paths).map(expandSymbols).toArray(String.class);
@@ -277,13 +311,17 @@ public class ImportWorker implements ComponentClassTransformWorker2
                                                final Worker<Asset> operation)
     {
         final String className = method.getPlasticClass().getClassName();
+        final String fieldName = getFieldName(method);
         method.addAdvice(new MethodAdvice()
         {
             public void advise(MethodInvocation invocation)
             {
                 invocation.proceed();
 
-                Asset[] assets = (Asset[]) access.get(invocation.getInstance());
+                final Object instance = invocation.getInstance();
+                Asset[] assets = (Asset[]) (multipleClassLoaders ?
+                        PropertyValueProvider.get(instance, fieldName) :
+                        access.get(instance));
 
                 if (multipleClassLoaders)
                 {
@@ -299,4 +337,10 @@ public class ImportWorker implements ComponentClassTransformWorker2
             }
         });
     }
+    
+    public static interface ImportWorkerDataProvider 
+    {
+        Asset[] get(int fieldNameHashcode);
+    }
+    
 }


(tapestry-5) 02/04: TAP5-2770: avoiding using FieldHandle when in multiple classloader mode

Posted by th...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

thiagohp pushed a commit to branch javax
in repository https://gitbox.apache.org/repos/asf/tapestry-5.git

commit 4a226d063914bdcce39d92ae64b7d33c783009ab
Author: Thiago H. de Paula Figueiredo <th...@arsmachina.com.br>
AuthorDate: Wed Dec 20 21:17:32 2023 -0300

    TAP5-2770: avoiding using FieldHandle when in multiple classloader mode
    
    Now, in CachedWorker too.
---
 .../tapestry5/plastic/PropertyValueProvider.java   |  9 ---
 .../tapestry5/internal/transform/CachedWorker.java | 57 +++++++++++++++---
 .../tapestry5/internal/transform/ImportWorker.java |  7 ++-
 .../transform/PropertyValueProviderWorker.java     | 68 ++++++++++++++++++++++
 .../apache/tapestry5/modules/TapestryModule.java   | 13 ++++-
 .../app1/components/SubclassWithImport.java        | 14 ++++-
 .../app1/components/SuperclassWithImport.java      | 13 +++++
 7 files changed, 161 insertions(+), 20 deletions(-)

diff --git a/plastic/src/main/java/org/apache/tapestry5/plastic/PropertyValueProvider.java b/plastic/src/main/java/org/apache/tapestry5/plastic/PropertyValueProvider.java
index e33301f48..bbba55a3b 100644
--- a/plastic/src/main/java/org/apache/tapestry5/plastic/PropertyValueProvider.java
+++ b/plastic/src/main/java/org/apache/tapestry5/plastic/PropertyValueProvider.java
@@ -54,16 +54,7 @@ public interface PropertyValueProvider
     {
         if (object instanceof PropertyValueProvider)
         {
-            try {
             return ((PropertyValueProvider) object).__propertyValueProvider__get(fieldName);
-            }
-            catch (Exception e) {
-                final Method[] methods = object.getClass().getMethods();
-                for (Method method : methods) {
-                    System.out.println(method);
-                }
-                throw new RuntimeException(e.getMessage() + ": " + fieldName, e);
-            }
         }
         else
         {
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/CachedWorker.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/CachedWorker.java
index e5eae27c6..7a7fbe38f 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/CachedWorker.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/CachedWorker.java
@@ -17,19 +17,25 @@ package org.apache.tapestry5.internal.transform;
 import org.apache.tapestry5.Binding;
 import org.apache.tapestry5.BindingConstants;
 import org.apache.tapestry5.ComponentResources;
+import org.apache.tapestry5.SymbolConstants;
 import org.apache.tapestry5.annotations.Cached;
 import org.apache.tapestry5.internal.TapestryInternalUtils;
+import org.apache.tapestry5.ioc.annotations.Inject;
+import org.apache.tapestry5.ioc.annotations.Symbol;
 import org.apache.tapestry5.ioc.services.PerThreadValue;
 import org.apache.tapestry5.ioc.services.PerthreadManager;
 import org.apache.tapestry5.model.MutableComponentModel;
 import org.apache.tapestry5.plastic.*;
+import org.apache.tapestry5.plastic.PlasticUtils.FieldInfo;
 import org.apache.tapestry5.runtime.PageLifecycleListener;
 import org.apache.tapestry5.services.BindingSource;
 import org.apache.tapestry5.services.TransformConstants;
 import org.apache.tapestry5.services.transform.ComponentClassTransformWorker2;
 import org.apache.tapestry5.services.transform.TransformationSupport;
 
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Caches method return values for methods annotated with {@link Cached}.
@@ -37,9 +43,15 @@ import java.util.List;
 @SuppressWarnings("all")
 public class CachedWorker implements ComponentClassTransformWorker2
 {
+    private static final String FIELD_PREFIX = "cache$";
+
     private final BindingSource bindingSource;
 
     private final PerthreadManager perThreadManager;
+    
+    private final PropertyValueProviderWorker propertyValueProviderWorker;
+    
+    private final boolean multipleClassLoaders;
 
     interface MethodResultCacheFactory
     {
@@ -117,32 +129,43 @@ public class CachedWorker implements ComponentClassTransformWorker2
         }
     }
 
-    public CachedWorker(BindingSource bindingSource, PerthreadManager perthreadManager)
+    public CachedWorker(BindingSource bindingSource, PerthreadManager perthreadManager,
+            PropertyValueProviderWorker propertyValueProviderWorker,
+            @Inject @Symbol(SymbolConstants.PRODUCTION_MODE) boolean productionMode,
+            @Inject @Symbol(SymbolConstants.MULTIPLE_CLASSLOADERS) boolean multipleClassloaders)
     {
         this.bindingSource = bindingSource;
         this.perThreadManager = perthreadManager;
+        this.propertyValueProviderWorker = propertyValueProviderWorker;
+        this.multipleClassLoaders = !productionMode && multipleClassloaders;
     }
 
 
     public void transform(PlasticClass plasticClass, TransformationSupport support, MutableComponentModel model)
     {
         List<PlasticMethod> methods = plasticClass.getMethodsWithAnnotation(Cached.class);
+        Set<PlasticUtils.FieldInfo> fieldInfos = multipleClassLoaders ? new HashSet<>() : null;
 
         for (PlasticMethod method : methods)
         {
             validateMethod(method);
 
-            adviseMethod(plasticClass, method);
+            adviseMethod(plasticClass, method, fieldInfos);
         }
+        
+        if (multipleClassLoaders && !fieldInfos.isEmpty())
+        {
+            this.propertyValueProviderWorker.add(plasticClass, fieldInfos);
+        }        
     }
 
-    private void adviseMethod(PlasticClass plasticClass, PlasticMethod method)
+    private void adviseMethod(PlasticClass plasticClass, PlasticMethod method, Set<FieldInfo> fieldInfos)
     {
-        // Every instance of the clas srequires its own per-thread value. This handles the case of multiple
+        // Every instance of the class requires its own per-thread value. This handles the case of multiple
         // pages containing the component, or the same page containing the component multiple times.
 
         PlasticField cacheField =
-                plasticClass.introduceField(PerThreadValue.class, "cache$" + method.getDescription().methodName);
+                plasticClass.introduceField(PerThreadValue.class, getFieldName(method));
 
         cacheField.injectComputed(new ComputedValue<PerThreadValue>()
         {
@@ -152,6 +175,12 @@ public class CachedWorker implements ComponentClassTransformWorker2
                 return perThreadManager.createValue();
             }
         });
+        
+        if (multipleClassLoaders)
+        {
+            fieldInfos.add(PlasticUtils.toFieldInfo(cacheField));
+            cacheField.createAccessors(PropertyAccessType.READ_ONLY);
+        }
 
         Cached annotation = method.getAnnotation(Cached.class);
 
@@ -162,11 +191,23 @@ public class CachedWorker implements ComponentClassTransformWorker2
         method.addAdvice(advice);
     }
 
+    private String getFieldName(PlasticMethod method) {
+        final StringBuilder builder = new StringBuilder(FIELD_PREFIX);
+        builder.append(method.getDescription().methodName);
+        if (multipleClassLoaders)
+        {
+            builder.append("_");
+            builder.append(method.getPlasticClass().getClassName().replace('.', '_'));
+        }
+        return builder.toString();
+    }
+
 
     private MethodAdvice createAdvice(PlasticField cacheField,
                                       final MethodResultCacheFactory factory)
     {
         final FieldHandle fieldHandle = cacheField.getHandle();
+        final String fieldName = multipleClassLoaders ? cacheField.getName() : null;
 
         return new MethodAdvice()
         {
@@ -194,8 +235,10 @@ public class CachedWorker implements ComponentClassTransformWorker2
 
                 // The PerThreadValue is created in the instance constructor.
 
-                PerThreadValue<MethodResultCache> value = (PerThreadValue<MethodResultCache>) fieldHandle
-                        .get(instance);
+                PerThreadValue<MethodResultCache> value = (PerThreadValue<MethodResultCache>) (
+                        multipleClassLoaders ?
+                        PropertyValueProvider.get(instance, fieldName) :
+                        fieldHandle.get(instance));
 
                 // But it will be empty when first created, or at the start of a new request.
                 if (value.exists())
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ImportWorker.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ImportWorker.java
index 9e5b85967..70bbe4464 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ImportWorker.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ImportWorker.java
@@ -56,6 +56,8 @@ public class ImportWorker implements ComponentClassTransformWorker2
     private final ResourceChangeTracker resourceChangeTracker;
     
     private final boolean multipleClassLoaders;
+    
+    private final PropertyValueProviderWorker propertyValueProviderWorker;
 
     private final Worker<Asset> importLibrary = new Worker<Asset>()
     {
@@ -82,12 +84,13 @@ public class ImportWorker implements ComponentClassTransformWorker2
     };
 
     public ImportWorker(JavaScriptSupport javascriptSupport, SymbolSource symbolSource, AssetSource assetSource,
-            ResourceChangeTracker resourceChangeTracker)
+            ResourceChangeTracker resourceChangeTracker, PropertyValueProviderWorker propertyValueProviderWorker)
     {
         this.javascriptSupport = javascriptSupport;
         this.symbolSource = symbolSource;
         this.assetSource = assetSource;
         this.resourceChangeTracker = resourceChangeTracker;
+        this.propertyValueProviderWorker = propertyValueProviderWorker;
         this.multipleClassLoaders = 
                 !Boolean.valueOf(symbolSource.valueForSymbol(SymbolConstants.PRODUCTION_MODE)) &&
                 Boolean.valueOf(symbolSource.valueForSymbol(SymbolConstants.MULTIPLE_CLASSLOADERS));
@@ -108,7 +111,7 @@ public class ImportWorker implements ComponentClassTransformWorker2
         
         if (multipleClassLoaders && !fieldInfos.isEmpty())
         {
-            PlasticUtils.implementPropertyValueProvider(componentClass, fieldInfos);
+            propertyValueProviderWorker.add(componentClass, fieldInfos);
         }
         
         resourceChangeTracker.clearCurrentClassName();
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/PropertyValueProviderWorker.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/PropertyValueProviderWorker.java
new file mode 100644
index 000000000..735368cbd
--- /dev/null
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/PropertyValueProviderWorker.java
@@ -0,0 +1,68 @@
+// Copyright 2023 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.internal.transform;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.tapestry5.ioc.services.PerThreadValue;
+import org.apache.tapestry5.ioc.services.PerthreadManager;
+import org.apache.tapestry5.model.MutableComponentModel;
+import org.apache.tapestry5.plastic.PlasticClass;
+import org.apache.tapestry5.plastic.PlasticUtils;
+import org.apache.tapestry5.plastic.PlasticUtils.FieldInfo;
+import org.apache.tapestry5.plastic.PropertyValueProvider;
+import org.apache.tapestry5.services.transform.ComponentClassTransformWorker2;
+import org.apache.tapestry5.services.transform.TransformationSupport;
+
+/**
+ * Worker used to gather {@linkplain FieldInfo} instances and implement
+ * {@linkplain PropertyValueProvider} for any class that has them.
+ * 
+ */
+public final class PropertyValueProviderWorker implements ComponentClassTransformWorker2
+{
+    private final PerThreadValue<Map<PlasticClass, Set<FieldInfo>>> perThreadMap;
+    
+    public PropertyValueProviderWorker(PerthreadManager perThreadManager)
+    {
+        perThreadMap = perThreadManager.createValue();
+    }
+    
+    public void add(PlasticClass plasticClass, Set<FieldInfo> fieldInfos)
+    {
+        final Map<PlasticClass, Set<FieldInfo>> map = perThreadMap.computeIfAbsent(HashMap::new);
+        if (!map.containsKey(plasticClass))
+        {
+            map.put(plasticClass, new HashSet<>(5));
+        }
+        map.get(plasticClass).addAll(fieldInfos);
+    }
+
+    public void transform(PlasticClass plasticClass, TransformationSupport support, MutableComponentModel model)
+    {
+        if (perThreadMap.exists())
+        {
+            final Set<FieldInfo> fieldInfos = perThreadMap.get().get(plasticClass);
+            if (fieldInfos != null && !fieldInfos.isEmpty())
+            {
+                PlasticUtils.implementPropertyValueProvider(plasticClass, fieldInfos);
+            }
+        }
+    }
+
+}
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java b/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java
index 57edc6bd4..b5a2cf557 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java
@@ -216,6 +216,7 @@ import org.apache.tapestry5.internal.transform.PageLifecycleAnnotationWorker;
 import org.apache.tapestry5.internal.transform.PageResetAnnotationWorker;
 import org.apache.tapestry5.internal.transform.ParameterWorker;
 import org.apache.tapestry5.internal.transform.PersistWorker;
+import org.apache.tapestry5.internal.transform.PropertyValueProviderWorker;
 import org.apache.tapestry5.internal.transform.PropertyWorker;
 import org.apache.tapestry5.internal.transform.RenderCommandWorker;
 import org.apache.tapestry5.internal.transform.RenderPhaseMethodWorker;
@@ -524,6 +525,7 @@ public final class TapestryModule
         binder.bind(Dispatcher.class, AssetDispatcher.class).withSimpleId();
         binder.bind(TranslatorAlternatesSource.class, TranslatorAlternatesSourceImpl.class);
         binder.bind(MetaWorker.class, MetaWorkerImpl.class);
+        binder.bind(PropertyValueProviderWorker.class);
         binder.bind(LinkTransformer.class, LinkTransformerImpl.class);
         binder.bind(SelectModelFactory.class, SelectModelFactoryImpl.class);
         binder.bind(DynamicTemplateParser.class, DynamicTemplateParserImpl.class);
@@ -680,7 +682,10 @@ public final class TapestryModule
             OrderedConfiguration<ComponentClassTransformWorker2> configuration,
             MetaWorker metaWorker,
             ComponentClassResolver resolver,
-            ComponentDependencyRegistry componentDependencyRegistry)
+            ComponentDependencyRegistry componentDependencyRegistry,
+            PropertyValueProviderWorker propertyValueProviderWorker,
+            @Symbol(SymbolConstants.PRODUCTION_MODE) boolean productionMode,
+            @Symbol(SymbolConstants.MULTIPLE_CLASSLOADERS) boolean multipleClassloaders)
     {
         configuration.add("Property", new PropertyWorker());
 
@@ -731,6 +736,12 @@ public final class TapestryModule
                 .addInstance("ActivationRequestParameter", ActivationRequestParameterWorker.class);
 
         configuration.addInstance("Cached", CachedWorker.class);
+        
+        if (!productionMode && multipleClassloaders)
+        {
+            configuration.add("PropertyValueProvider", propertyValueProviderWorker, 
+                    "after:Cached", "after:Import");
+        }
 
         configuration.addInstance("DiscardAfter", DiscardAfterWorker.class);
 
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/SubclassWithImport.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/SubclassWithImport.java
index f2ba5940a..9f3a743c4 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/SubclassWithImport.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/SubclassWithImport.java
@@ -1,8 +1,20 @@
 package org.apache.tapestry5.integration.app1.components;
 
+import org.apache.tapestry5.MarkupWriter;
+import org.apache.tapestry5.annotations.Cached;
 import org.apache.tapestry5.annotations.Import;
 
 @Import(stylesheet = "context:css/ie-only.css")
 public class SubclassWithImport extends SuperclassWithImport {
-
+    
+    @Cached
+    public int getInt() 
+    { 
+        return 2; 
+    }
+    
+    public void setupRender(MarkupWriter writer) {
+        writer.element("p").text("Int: " + getInt() + " : " + getInt());
+        writer.end();
+    }
 }
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/SuperclassWithImport.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/SuperclassWithImport.java
index 7909cc880..2de9d1793 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/SuperclassWithImport.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/SuperclassWithImport.java
@@ -1,8 +1,21 @@
 package org.apache.tapestry5.integration.app1.components;
 
+import org.apache.tapestry5.MarkupWriter;
+import org.apache.tapestry5.annotations.Cached;
 import org.apache.tapestry5.annotations.Import;
 
 @Import(stylesheet = "context:css/via-import.css")
 public class SuperclassWithImport {
+    
+    @Cached
+    public int getOther() 
+    { 
+        return 4; 
+    }
+    
+    public void cleanupRender(MarkupWriter writer) {
+        writer.element("p").text("Other: " + getOther() + " : " + getOther());
+        writer.end();
+    }
 
 }