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 2026/03/29 19:54:35 UTC

(tapestry-5) 03/04: TAP5-2814: Upgrade ASM to 9.9

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

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

commit 5a0620feaa3371d62e7a30c06d4985bd7f288521
Author: Thiago H. de Paula Figueiredo <th...@arsmachina.com.br>
AuthorDate: Sat Oct 11 16:21:06 2025 -0300

    TAP5-2814: Upgrade ASM to 9.9
---
 .../internal/plastic/asm/AnnotationWriter.java     |   8 +-
 .../tapestry5/internal/plastic/asm/Attribute.java  | 139 +++++++++++++--
 .../internal/plastic/asm/ClassReader.java          | 123 +++++++------
 .../internal/plastic/asm/ClassWriter.java          |  32 +++-
 .../tapestry5/internal/plastic/asm/Constants.java  |   8 +-
 .../internal/plastic/asm/MethodVisitor.java        |  21 +--
 .../internal/plastic/asm/MethodWriter.java         |   8 +-
 .../internal/plastic/asm/ModuleVisitor.java        |  12 +-
 .../tapestry5/internal/plastic/asm/Opcodes.java    |   4 +
 .../tapestry5/internal/plastic/asm/Symbol.java     |   4 +-
 .../internal/plastic/asm/SymbolTable.java          |  48 +++--
 .../plastic/asm/commons/AdviceAdapter.java         |   1 -
 .../plastic/asm/commons/AnalyzerAdapter.java       |   1 -
 .../plastic/asm/commons/AnnotationRemapper.java    |   8 +-
 .../plastic/asm/commons/ClassRemapper.java         |   3 +-
 .../plastic/asm/commons/FieldRemapper.java         |   2 +-
 .../plastic/asm/commons/GeneratorAdapter.java      |   4 +-
 .../plastic/asm/commons/InstructionAdapter.java    |   3 +
 .../plastic/asm/commons/JSRInlinerAdapter.java     |   1 -
 .../internal/plastic/asm/commons/Method.java       |   1 -
 .../plastic/asm/commons/MethodRemapper.java        |  15 +-
 .../plastic/asm/commons/ModuleHashesAttribute.java |   1 -
 .../asm/commons/RecordComponentRemapper.java       |   2 +-
 .../internal/plastic/asm/commons/Remapper.java     |  77 +++++++-
 .../plastic/asm/commons/SerialVersionUIDAdder.java |   3 +-
 .../plastic/asm/commons/SignatureRemapper.java     |   1 -
 .../plastic/asm/commons/SimpleRemapper.java        |  66 ++++++-
 .../plastic/asm/commons/TryCatchBlockSorter.java   |   1 -
 .../plastic/asm/tree/AbstractInsnNode.java         |   1 -
 .../internal/plastic/asm/tree/AnnotationNode.java  |   1 -
 .../internal/plastic/asm/tree/ClassNode.java       |   5 +-
 .../internal/plastic/asm/tree/FieldInsnNode.java   |   1 -
 .../internal/plastic/asm/tree/FieldNode.java       |   1 -
 .../internal/plastic/asm/tree/FrameNode.java       |   1 -
 .../internal/plastic/asm/tree/IincInsnNode.java    |   1 -
 .../internal/plastic/asm/tree/InsnList.java        |   1 -
 .../internal/plastic/asm/tree/InsnNode.java        |   1 -
 .../internal/plastic/asm/tree/IntInsnNode.java     |   1 -
 .../plastic/asm/tree/InvokeDynamicInsnNode.java    |   1 -
 .../internal/plastic/asm/tree/JumpInsnNode.java    |   1 -
 .../internal/plastic/asm/tree/LabelNode.java       |   1 -
 .../internal/plastic/asm/tree/LdcInsnNode.java     |   1 -
 .../internal/plastic/asm/tree/LineNumberNode.java  |   1 -
 .../asm/tree/LocalVariableAnnotationNode.java      |   1 -
 .../plastic/asm/tree/LookupSwitchInsnNode.java     |   1 -
 .../internal/plastic/asm/tree/MethodInsnNode.java  |   1 -
 .../internal/plastic/asm/tree/MethodNode.java      |   3 +-
 .../plastic/asm/tree/ModuleExportNode.java         |   1 -
 .../internal/plastic/asm/tree/ModuleNode.java      |   1 -
 .../internal/plastic/asm/tree/ModuleOpenNode.java  |   1 -
 .../plastic/asm/tree/ModuleProvideNode.java        |   1 -
 .../plastic/asm/tree/MultiANewArrayInsnNode.java   |   1 -
 .../plastic/asm/tree/RecordComponentNode.java      |   1 -
 .../plastic/asm/tree/TableSwitchInsnNode.java      |   1 -
 .../plastic/asm/tree/TryCatchBlockNode.java        |   1 -
 .../internal/plastic/asm/tree/TypeInsnNode.java    |   1 -
 .../internal/plastic/asm/tree/VarInsnNode.java     |   1 -
 .../plastic/asm/tree/analysis/Analyzer.java        |  18 +-
 .../asm/tree/analysis/BasicInterpreter.java        |   1 -
 .../plastic/asm/tree/analysis/BasicVerifier.java   |   1 -
 .../internal/plastic/asm/tree/analysis/Frame.java  |  47 +++--
 .../plastic/asm/tree/analysis/Interpreter.java     |   1 -
 .../plastic/asm/tree/analysis/SimpleVerifier.java  | 196 ++++++++++++++-------
 .../asm/tree/analysis/SourceInterpreter.java       |  18 +-
 .../plastic/asm/tree/analysis/SourceValue.java     |   1 -
 .../plastic/asm/tree/analysis/Subroutine.java      |   1 -
 .../internal/plastic/asm/util/ASMifier.java        |  25 ++-
 .../internal/plastic/asm/util/ASMifierSupport.java |   1 -
 .../plastic/asm/util/CheckClassAdapter.java        |   9 +-
 .../plastic/asm/util/CheckFrameAnalyzer.java       |  21 ++-
 .../plastic/asm/util/CheckMethodAdapter.java       |  36 ++--
 .../plastic/asm/util/CheckModuleAdapter.java       |   1 -
 .../plastic/asm/util/CheckSignatureAdapter.java    |   1 -
 .../internal/plastic/asm/util/Printer.java         |   1 -
 .../internal/plastic/asm/util/Textifier.java       | 158 ++++++++++++-----
 .../plastic/asm/util/TextifierSupport.java         |   1 -
 .../plastic/asm/util/TraceClassVisitor.java        |   3 +-
 .../plastic/asm/util/TraceSignatureVisitor.java    |   1 -
 .../groovy/ioc/specs/GeneralIntegrationSpec.groovy |   5 +-
 79 files changed, 808 insertions(+), 371 deletions(-)

diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/AnnotationWriter.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/AnnotationWriter.java
index 736ea095a..313fcf351 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/AnnotationWriter.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/AnnotationWriter.java
@@ -144,7 +144,7 @@ final class AnnotationWriter extends AnnotationVisitor {
     // Write type_index and reserve space for num_element_value_pairs.
     annotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0);
     return new AnnotationWriter(
-        symbolTable, /* useNamedValues = */ true, annotation, previousAnnotation);
+        symbolTable, /* useNamedValues= */ true, annotation, previousAnnotation);
   }
 
   /**
@@ -179,7 +179,7 @@ final class AnnotationWriter extends AnnotationVisitor {
     // Write type_index and reserve space for num_element_value_pairs.
     typeAnnotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0);
     return new AnnotationWriter(
-        symbolTable, /* useNamedValues = */ true, typeAnnotation, previousAnnotation);
+        symbolTable, /* useNamedValues= */ true, typeAnnotation, previousAnnotation);
   }
 
   // -----------------------------------------------------------------------------------------------
@@ -284,7 +284,7 @@ final class AnnotationWriter extends AnnotationVisitor {
     }
     // Write tag and type_index, and reserve 2 bytes for num_element_value_pairs.
     annotation.put12('@', symbolTable.addConstantUtf8(descriptor)).putShort(0);
-    return new AnnotationWriter(symbolTable, /* useNamedValues = */ true, annotation, null);
+    return new AnnotationWriter(symbolTable, /* useNamedValues= */ true, annotation, null);
   }
 
   @Override
@@ -303,7 +303,7 @@ final class AnnotationWriter extends AnnotationVisitor {
     // visit the array elements. Its num_element_value_pairs will correspond to the number of array
     // elements and will be stored in what is in fact num_values.
     annotation.put12('[', 0);
-    return new AnnotationWriter(symbolTable, /* useNamedValues = */ false, annotation, null);
+    return new AnnotationWriter(symbolTable, /* useNamedValues= */ false, annotation, null);
   }
 
   @Override
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Attribute.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Attribute.java
index 1ed250c4c..f43940e23 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Attribute.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Attribute.java
@@ -44,11 +44,11 @@ public class Attribute {
   public final String type;
 
   /**
-   * The raw content of this attribute, only used for unknown attributes (see {@link #isUnknown()}).
-   * The 6 header bytes of the attribute (attribute_name_index and attribute_length) are <i>not</i>
-   * included.
+   * The raw content of this attribute, as returned by {@link
+   * #write(ClassWriter,byte[],int,int,int)}. The 6 header bytes of the attribute
+   * (attribute_name_index and attribute_length) are <i>not</i> included.
    */
-  private byte[] content;
+  private ByteVector cachedContent;
 
   /**
    * The next attribute in this attribute list (Attribute instances can be linked via this field to
@@ -93,7 +93,9 @@ public class Attribute {
    *
    * @return the labels corresponding to this attribute, or {@literal null} if this attribute is not
    *     a Code attribute that contains labels.
+   * @deprecated no longer used by ASM.
    */
+  @Deprecated
   protected Label[] getLabels() {
     return new Label[0];
   }
@@ -115,7 +117,9 @@ public class Attribute {
    *     attribute header bytes (attribute_name_index and attribute_length) are not taken into
    *     account here.
    * @param labels the labels of the method's code, or {@literal null} if the attribute to be read
-   *     is not a Code attribute.
+   *     is not a Code attribute. Labels defined in the attribute must be created and added to this
+   *     array, if not already present, by calling the {@link #readLabel} method (do not create
+   *     {@link Label} instances directly).
    * @return a <i>new</i> {@link Attribute} object corresponding to the specified bytes.
    */
   protected Attribute read(
@@ -126,16 +130,99 @@ public class Attribute {
       final int codeAttributeOffset,
       final Label[] labels) {
     Attribute attribute = new Attribute(type);
-    attribute.content = new byte[length];
-    System.arraycopy(classReader.classFileBuffer, offset, attribute.content, 0, length);
+    attribute.cachedContent = new ByteVector(classReader.readBytes(offset, length));
     return attribute;
   }
 
+  /**
+   * Reads an attribute with the same {@link #type} as the given attribute. This method returns a
+   * new {@link Attribute} object, corresponding to the 'length' bytes starting at 'offset', in the
+   * given ClassReader.
+   *
+   * @param attribute The attribute prototype that is used for reading.
+   * @param classReader the class that contains the attribute to be read.
+   * @param offset index of the first byte of the attribute's content in {@link ClassReader}. The 6
+   *     attribute header bytes (attribute_name_index and attribute_length) are not taken into
+   *     account here.
+   * @param length the length of the attribute's content (excluding the 6 attribute header bytes).
+   * @param charBuffer the buffer to be used to call the ClassReader methods requiring a
+   *     'charBuffer' parameter.
+   * @param codeAttributeOffset index of the first byte of content of the enclosing Code attribute
+   *     in {@link ClassReader}, or -1 if the attribute to be read is not a Code attribute. The 6
+   *     attribute header bytes (attribute_name_index and attribute_length) are not taken into
+   *     account here.
+   * @param labels the labels of the method's code, or {@literal null} if the attribute to be read
+   *     is not a Code attribute. Labels defined in the attribute are added to this array, if not
+   *     already present.
+   * @return a new {@link Attribute} object corresponding to the specified bytes.
+   */
+  public static Attribute read(
+      final Attribute attribute,
+      final ClassReader classReader,
+      final int offset,
+      final int length,
+      final char[] charBuffer,
+      final int codeAttributeOffset,
+      final Label[] labels) {
+    return attribute.read(classReader, offset, length, charBuffer, codeAttributeOffset, labels);
+  }
+
+  /**
+   * Returns the label corresponding to the given bytecode offset by calling {@link
+   * ClassReader#readLabel}. This creates and adds the label to the given array if it is not already
+   * present. Note that this created label may be a {@link Label} subclass instance, if the given
+   * ClassReader overrides {@link ClassReader#readLabel}. Hence {@link #read(ClassReader, int, int,
+   * char[], int, Label[])} must not manually create {@link Label} instances.
+   *
+   * @param bytecodeOffset a bytecode offset in a method.
+   * @param labels the already created labels, indexed by their offset. If a label already exists
+   *     for bytecodeOffset this method does not create a new one. Otherwise it stores the new label
+   *     in this array.
+   * @return a label for the given bytecode offset.
+   */
+  public static Label readLabel(
+      final ClassReader classReader, final int bytecodeOffset, final Label[] labels) {
+    return classReader.readLabel(bytecodeOffset, labels);
+  }
+
+  /**
+   * Calls {@link #write(ClassWriter,byte[],int,int,int)} if it has not already been called and
+   * returns its result or its (cached) previous result.
+   *
+   * @param classWriter the class to which this attribute must be added. This parameter can be used
+   *     to add the items that corresponds to this attribute to the constant pool of this class.
+   * @param code the bytecode of the method corresponding to this Code attribute, or {@literal null}
+   *     if this attribute is not a Code attribute. Corresponds to the 'code' field of the Code
+   *     attribute.
+   * @param codeLength the length of the bytecode of the method corresponding to this code
+   *     attribute, or 0 if this attribute is not a Code attribute. Corresponds to the 'code_length'
+   *     field of the Code attribute.
+   * @param maxStack the maximum stack size of the method corresponding to this Code attribute, or
+   *     -1 if this attribute is not a Code attribute.
+   * @param maxLocals the maximum number of local variables of the method corresponding to this code
+   *     attribute, or -1 if this attribute is not a Code attribute.
+   * @return the byte array form of this attribute.
+   */
+  private ByteVector maybeWrite(
+      final ClassWriter classWriter,
+      final byte[] code,
+      final int codeLength,
+      final int maxStack,
+      final int maxLocals) {
+    if (cachedContent == null) {
+      cachedContent = write(classWriter, code, codeLength, maxStack, maxLocals);
+    }
+    return cachedContent;
+  }
+
   /**
    * Returns the byte array form of the content of this attribute. The 6 header bytes
    * (attribute_name_index and attribute_length) must <i>not</i> be added in the returned
    * ByteVector.
    *
+   * <p>This method is only invoked once to compute the binary form of this attribute. Subsequent
+   * changes to the attribute after it was written for the first time will not be considered.
+   *
    * @param classWriter the class to which this attribute must be added. This parameter can be used
    *     to add the items that corresponds to this attribute to the constant pool of this class.
    * @param code the bytecode of the method corresponding to this Code attribute, or {@literal null}
@@ -156,7 +243,39 @@ public class Attribute {
       final int codeLength,
       final int maxStack,
       final int maxLocals) {
-    return new ByteVector(content);
+    return cachedContent;
+  }
+
+  /**
+   * Returns the byte array form of the content of the given attribute. The 6 header bytes
+   * (attribute_name_index and attribute_length) are <i>not</i> added in the returned byte array.
+   *
+   * @param attribute The attribute that should be written.
+   * @param classWriter the class to which this attribute must be added. This parameter can be used
+   *     to add the items that corresponds to this attribute to the constant pool of this class.
+   * @param code the bytecode of the method corresponding to this Code attribute, or {@literal null}
+   *     if this attribute is not a Code attribute. Corresponds to the 'code' field of the Code
+   *     attribute.
+   * @param codeLength the length of the bytecode of the method corresponding to this code
+   *     attribute, or 0 if this attribute is not a Code attribute. Corresponds to the 'code_length'
+   *     field of the Code attribute.
+   * @param maxStack the maximum stack size of the method corresponding to this Code attribute, or
+   *     -1 if this attribute is not a Code attribute.
+   * @param maxLocals the maximum number of local variables of the method corresponding to this code
+   *     attribute, or -1 if this attribute is not a Code attribute.
+   * @return the byte array form of this attribute.
+   */
+  public static byte[] write(
+      final Attribute attribute,
+      final ClassWriter classWriter,
+      final byte[] code,
+      final int codeLength,
+      final int maxStack,
+      final int maxLocals) {
+    ByteVector content = attribute.maybeWrite(classWriter, code, codeLength, maxStack, maxLocals);
+    byte[] result = new byte[content.length];
+    System.arraycopy(content.data, 0, result, 0, content.length);
+    return result;
   }
 
   /**
@@ -221,7 +340,7 @@ public class Attribute {
     Attribute attribute = this;
     while (attribute != null) {
       symbolTable.addConstantUtf8(attribute.type);
-      size += 6 + attribute.write(classWriter, code, codeLength, maxStack, maxLocals).length;
+      size += 6 + attribute.maybeWrite(classWriter, code, codeLength, maxStack, maxLocals).length;
       attribute = attribute.nextAttribute;
     }
     return size;
@@ -308,7 +427,7 @@ public class Attribute {
     Attribute attribute = this;
     while (attribute != null) {
       ByteVector attributeContent =
-          attribute.write(classWriter, code, codeLength, maxStack, maxLocals);
+          attribute.maybeWrite(classWriter, code, codeLength, maxStack, maxLocals);
       // Put attribute_name_index and attribute_length.
       output.putShort(symbolTable.addConstantUtf8(attribute.type)).putInt(attributeContent.length);
       output.putByteArray(attributeContent.data, 0, attributeContent.length);
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/ClassReader.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/ClassReader.java
index 7f81bc8f3..c6da5be30 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/ClassReader.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/ClassReader.java
@@ -177,7 +177,7 @@ public class ClassReader {
       final byte[] classFileBuffer,
       final int classFileOffset,
       final int classFileLength) { // NOPMD(UnusedFormalParameter) used for backward compatibility.
-    this(classFileBuffer, classFileOffset, /* checkClassVersion = */ true);
+    this(classFileBuffer, classFileOffset, /* checkClassVersion= */ true);
   }
 
   /**
@@ -195,7 +195,7 @@ public class ClassReader {
     this.b = classFileBuffer;
     // Check the class' major_version. This field is after the magic and minor_version fields, which
     // use 4 and 2 bytes respectively.
-    if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V22) {
+    if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V26) {
       throw new IllegalArgumentException(
           "Unsupported class file major version " + readShort(classFileOffset + 6));
     }
@@ -607,9 +607,9 @@ public class ClassReader {
         // Parse num_element_value_pairs and element_value_pairs and visit these values.
         currentAnnotationOffset =
             readElementValues(
-                classVisitor.visitAnnotation(annotationDescriptor, /* visible = */ true),
+                classVisitor.visitAnnotation(annotationDescriptor, /* visible= */ true),
                 currentAnnotationOffset,
-                /* named = */ true,
+                /* named= */ true,
                 charBuffer);
       }
     }
@@ -625,9 +625,9 @@ public class ClassReader {
         // Parse num_element_value_pairs and element_value_pairs and visit these values.
         currentAnnotationOffset =
             readElementValues(
-                classVisitor.visitAnnotation(annotationDescriptor, /* visible = */ false),
+                classVisitor.visitAnnotation(annotationDescriptor, /* visible= */ false),
                 currentAnnotationOffset,
-                /* named = */ true,
+                /* named= */ true,
                 charBuffer);
       }
     }
@@ -649,9 +649,9 @@ public class ClassReader {
                     context.currentTypeAnnotationTarget,
                     context.currentTypeAnnotationTargetPath,
                     annotationDescriptor,
-                    /* visible = */ true),
+                    /* visible= */ true),
                 currentAnnotationOffset,
-                /* named = */ true,
+                /* named= */ true,
                 charBuffer);
       }
     }
@@ -673,9 +673,9 @@ public class ClassReader {
                     context.currentTypeAnnotationTarget,
                     context.currentTypeAnnotationTargetPath,
                     annotationDescriptor,
-                    /* visible = */ false),
+                    /* visible= */ false),
                 currentAnnotationOffset,
-                /* named = */ true,
+                /* named= */ true,
                 charBuffer);
       }
     }
@@ -967,9 +967,9 @@ public class ClassReader {
         // Parse num_element_value_pairs and element_value_pairs and visit these values.
         currentAnnotationOffset =
             readElementValues(
-                recordComponentVisitor.visitAnnotation(annotationDescriptor, /* visible = */ true),
+                recordComponentVisitor.visitAnnotation(annotationDescriptor, /* visible= */ true),
                 currentAnnotationOffset,
-                /* named = */ true,
+                /* named= */ true,
                 charBuffer);
       }
     }
@@ -985,9 +985,9 @@ public class ClassReader {
         // Parse num_element_value_pairs and element_value_pairs and visit these values.
         currentAnnotationOffset =
             readElementValues(
-                recordComponentVisitor.visitAnnotation(annotationDescriptor, /* visible = */ false),
+                recordComponentVisitor.visitAnnotation(annotationDescriptor, /* visible= */ false),
                 currentAnnotationOffset,
-                /* named = */ true,
+                /* named= */ true,
                 charBuffer);
       }
     }
@@ -1009,9 +1009,9 @@ public class ClassReader {
                     context.currentTypeAnnotationTarget,
                     context.currentTypeAnnotationTargetPath,
                     annotationDescriptor,
-                    /* visible = */ true),
+                    /* visible= */ true),
                 currentAnnotationOffset,
-                /* named = */ true,
+                /* named= */ true,
                 charBuffer);
       }
     }
@@ -1033,9 +1033,9 @@ public class ClassReader {
                     context.currentTypeAnnotationTarget,
                     context.currentTypeAnnotationTargetPath,
                     annotationDescriptor,
-                    /* visible = */ false),
+                    /* visible= */ false),
                 currentAnnotationOffset,
-                /* named = */ true,
+                /* named= */ true,
                 charBuffer);
       }
     }
@@ -1151,9 +1151,9 @@ public class ClassReader {
         // Parse num_element_value_pairs and element_value_pairs and visit these values.
         currentAnnotationOffset =
             readElementValues(
-                fieldVisitor.visitAnnotation(annotationDescriptor, /* visible = */ true),
+                fieldVisitor.visitAnnotation(annotationDescriptor, /* visible= */ true),
                 currentAnnotationOffset,
-                /* named = */ true,
+                /* named= */ true,
                 charBuffer);
       }
     }
@@ -1169,9 +1169,9 @@ public class ClassReader {
         // Parse num_element_value_pairs and element_value_pairs and visit these values.
         currentAnnotationOffset =
             readElementValues(
-                fieldVisitor.visitAnnotation(annotationDescriptor, /* visible = */ false),
+                fieldVisitor.visitAnnotation(annotationDescriptor, /* visible= */ false),
                 currentAnnotationOffset,
-                /* named = */ true,
+                /* named= */ true,
                 charBuffer);
       }
     }
@@ -1193,9 +1193,9 @@ public class ClassReader {
                     context.currentTypeAnnotationTarget,
                     context.currentTypeAnnotationTargetPath,
                     annotationDescriptor,
-                    /* visible = */ true),
+                    /* visible= */ true),
                 currentAnnotationOffset,
-                /* named = */ true,
+                /* named= */ true,
                 charBuffer);
       }
     }
@@ -1217,9 +1217,9 @@ public class ClassReader {
                     context.currentTypeAnnotationTarget,
                     context.currentTypeAnnotationTargetPath,
                     annotationDescriptor,
-                    /* visible = */ false),
+                    /* visible= */ false),
                 currentAnnotationOffset,
-                /* named = */ true,
+                /* named= */ true,
                 charBuffer);
       }
     }
@@ -1412,9 +1412,9 @@ public class ClassReader {
         // Parse num_element_value_pairs and element_value_pairs and visit these values.
         currentAnnotationOffset =
             readElementValues(
-                methodVisitor.visitAnnotation(annotationDescriptor, /* visible = */ true),
+                methodVisitor.visitAnnotation(annotationDescriptor, /* visible= */ true),
                 currentAnnotationOffset,
-                /* named = */ true,
+                /* named= */ true,
                 charBuffer);
       }
     }
@@ -1430,9 +1430,9 @@ public class ClassReader {
         // Parse num_element_value_pairs and element_value_pairs and visit these values.
         currentAnnotationOffset =
             readElementValues(
-                methodVisitor.visitAnnotation(annotationDescriptor, /* visible = */ false),
+                methodVisitor.visitAnnotation(annotationDescriptor, /* visible= */ false),
                 currentAnnotationOffset,
-                /* named = */ true,
+                /* named= */ true,
                 charBuffer);
       }
     }
@@ -1454,9 +1454,9 @@ public class ClassReader {
                     context.currentTypeAnnotationTarget,
                     context.currentTypeAnnotationTargetPath,
                     annotationDescriptor,
-                    /* visible = */ true),
+                    /* visible= */ true),
                 currentAnnotationOffset,
-                /* named = */ true,
+                /* named= */ true,
                 charBuffer);
       }
     }
@@ -1478,9 +1478,9 @@ public class ClassReader {
                     context.currentTypeAnnotationTarget,
                     context.currentTypeAnnotationTargetPath,
                     annotationDescriptor,
-                    /* visible = */ false),
+                    /* visible= */ false),
                 currentAnnotationOffset,
-                /* named = */ true,
+                /* named= */ true,
                 charBuffer);
       }
     }
@@ -1488,16 +1488,13 @@ public class ClassReader {
     // Visit the RuntimeVisibleParameterAnnotations attribute.
     if (runtimeVisibleParameterAnnotationsOffset != 0) {
       readParameterAnnotations(
-          methodVisitor, context, runtimeVisibleParameterAnnotationsOffset, /* visible = */ true);
+          methodVisitor, context, runtimeVisibleParameterAnnotationsOffset, /* visible= */ true);
     }
 
     // Visit the RuntimeInvisibleParameterAnnotations attribute.
     if (runtimeInvisibleParameterAnnotationsOffset != 0) {
       readParameterAnnotations(
-          methodVisitor,
-          context,
-          runtimeInvisibleParameterAnnotationsOffset,
-          /* visible = */ false);
+          methodVisitor, context, runtimeInvisibleParameterAnnotationsOffset, /* visible= */ false);
     }
 
     // Visit the non standard attributes.
@@ -1926,7 +1923,7 @@ public class ClassReader {
         }
       } else if (Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
         visibleTypeAnnotationOffsets =
-            readTypeAnnotations(methodVisitor, context, currentOffset, /* visible = */ true);
+            readTypeAnnotations(methodVisitor, context, currentOffset, /* visible= */ true);
         // Here we do not extract the labels corresponding to the attribute content. This would
         // require a full parsing of the attribute, which would need to be repeated when parsing
         // the bytecode instructions (see below). Instead, the content of the attribute is read one
@@ -1935,7 +1932,7 @@ public class ClassReader {
         // time. This assumes that type annotations are ordered by increasing bytecode offset.
       } else if (Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
         invisibleTypeAnnotationOffsets =
-            readTypeAnnotations(methodVisitor, context, currentOffset, /* visible = */ false);
+            readTypeAnnotations(methodVisitor, context, currentOffset, /* visible= */ false);
         // Same comment as above for the RuntimeVisibleTypeAnnotations attribute.
       } else if (Constants.STACK_MAP_TABLE.equals(attributeName)) {
         if ((context.parsingOptions & SKIP_FRAMES) == 0) {
@@ -2517,9 +2514,9 @@ public class ClassReader {
                   context.currentTypeAnnotationTarget,
                   context.currentTypeAnnotationTargetPath,
                   annotationDescriptor,
-                  /* visible = */ true),
+                  /* visible= */ true),
               currentAnnotationOffset,
-              /* named = */ true,
+              /* named= */ true,
               charBuffer);
         }
         currentVisibleTypeAnnotationBytecodeOffset =
@@ -2545,9 +2542,9 @@ public class ClassReader {
                   context.currentTypeAnnotationTarget,
                   context.currentTypeAnnotationTargetPath,
                   annotationDescriptor,
-                  /* visible = */ false),
+                  /* visible= */ false),
               currentAnnotationOffset,
-              /* named = */ true,
+              /* named= */ true,
               charBuffer);
         }
         currentInvisibleTypeAnnotationBytecodeOffset =
@@ -2618,9 +2615,9 @@ public class ClassReader {
                   context.currentLocalVariableAnnotationRangeEnds,
                   context.currentLocalVariableAnnotationRangeIndices,
                   annotationDescriptor,
-                  /* visible = */ true),
+                  /* visible= */ true),
               currentOffset,
-              /* named = */ true,
+              /* named= */ true,
               charBuffer);
         }
       }
@@ -2646,9 +2643,9 @@ public class ClassReader {
                   context.currentLocalVariableAnnotationRangeEnds,
                   context.currentLocalVariableAnnotationRangeIndices,
                   annotationDescriptor,
-                  /* visible = */ false),
+                  /* visible= */ false),
               currentOffset,
-              /* named = */ true,
+              /* named= */ true,
               charBuffer);
         }
       }
@@ -2821,7 +2818,7 @@ public class ClassReader {
                 methodVisitor.visitTryCatchAnnotation(
                     targetType & 0xFFFFFF00, path, annotationDescriptor, visible),
                 currentOffset,
-                /* named = */ true,
+                /* named= */ true,
                 charBuffer);
       } else {
         // We don't want to visit the other target_type annotations, so we just skip them (which
@@ -2832,7 +2829,7 @@ public class ClassReader {
         // with a null AnnotationVisitor).
         currentOffset =
             readElementValues(
-                /* annotationVisitor = */ null, currentOffset, /* named = */ true, charBuffer);
+                /* annotationVisitor= */ null, currentOffset, /* named= */ true, charBuffer);
       }
     }
     return typeAnnotationsOffsets;
@@ -2972,7 +2969,7 @@ public class ClassReader {
             readElementValues(
                 methodVisitor.visitParameterAnnotation(i, annotationDescriptor, visible),
                 currentOffset,
-                /* named = */ true,
+                /* named= */ true,
                 charBuffer);
       }
     }
@@ -3042,9 +3039,9 @@ public class ClassReader {
         case 'e': // enum_const_value
           return currentOffset + 5;
         case '@': // annotation_value
-          return readElementValues(null, currentOffset + 3, /* named = */ true, charBuffer);
+          return readElementValues(null, currentOffset + 3, /* named= */ true, charBuffer);
         case '[': // array_value
-          return readElementValues(null, currentOffset + 1, /* named = */ false, charBuffer);
+          return readElementValues(null, currentOffset + 1, /* named= */ false, charBuffer);
         default:
           return currentOffset + 3;
       }
@@ -3112,7 +3109,7 @@ public class ClassReader {
           return readElementValues(
               annotationVisitor.visitArray(elementName),
               currentOffset - 2,
-              /* named = */ false,
+              /* named= */ false,
               charBuffer);
         }
         switch (classFileBuffer[currentOffset] & 0xFF) {
@@ -3189,7 +3186,7 @@ public class ClassReader {
                 readElementValues(
                     annotationVisitor.visitArray(elementName),
                     currentOffset - 2,
-                    /* named = */ false,
+                    /* named= */ false,
                     charBuffer);
             break;
         }
@@ -3600,6 +3597,20 @@ public class ClassReader {
     return classFileBuffer[offset] & 0xFF;
   }
 
+  /**
+   * Reads several bytes in this {@link ClassReader}. <i>This method is intended for {@link
+   * Attribute} sub classes, and is normally not needed by class generators or adapters.</i>
+   *
+   * @param offset the start offset of the bytes to be read in this {@link ClassReader}.
+   * @param length the number of bytes to read.
+   * @return the read bytes.
+   */
+  public byte[] readBytes(final int offset, final int length) {
+    byte[] result = new byte[length];
+    System.arraycopy(classFileBuffer, offset, result, 0, length);
+    return result;
+  }
+
   /**
    * Reads an unsigned short value in this {@link ClassReader}. <i>This method is intended for
    * {@link Attribute} sub classes, and is normally not needed by class generators or adapters.</i>
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/ClassWriter.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/ClassWriter.java
index f2df21680..7b9d94928 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/ClassWriter.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/ClassWriter.java
@@ -264,13 +264,7 @@ public class ClassWriter extends ClassVisitor {
     super(/* latest api = */ Opcodes.ASM9);
     this.flags = flags;
     symbolTable = classReader == null ? new SymbolTable(this) : new SymbolTable(this, classReader);
-    if ((flags & COMPUTE_FRAMES) != 0) {
-      compute = MethodWriter.COMPUTE_ALL_FRAMES;
-    } else if ((flags & COMPUTE_MAXS) != 0) {
-      compute = MethodWriter.COMPUTE_MAX_STACK_AND_LOCAL;
-    } else {
-      compute = MethodWriter.COMPUTE_NOTHING;
-    }
+    setFlags(flags);
   }
 
   // -----------------------------------------------------------------------------------------------
@@ -774,7 +768,7 @@ public class ClassWriter extends ClassVisitor {
     lastRecordComponent = null;
     firstAttribute = null;
     compute = hasFrames ? MethodWriter.COMPUTE_INSERTED_FRAMES : MethodWriter.COMPUTE_NOTHING;
-    new ClassReader(classFile, 0, /* checkClassVersion = */ false)
+    new ClassReader(classFile, 0, /* checkClassVersion= */ false)
         .accept(
             this,
             attributes,
@@ -1020,6 +1014,28 @@ public class ClassWriter extends ClassVisitor {
     return symbolTable.addConstantNameAndType(name, descriptor);
   }
 
+  /**
+   * Changes the computation strategy of method properties like max stack size, max number of local
+   * variables, and frames.
+   *
+   * <p><b>WARNING</b>: {@link #setFlags(int)} method changes the behavior of new method visitors
+   * returned from {@link #visitMethod(int, String, String, String, String[])}. The behavior will be
+   * changed only after the next method visitor is returned. All the previously returned method
+   * visitors keep their previous behavior.
+   *
+   * @param flags option flags that can be used to modify the default behavior of this class. Must
+   *     be zero or more of {@link #COMPUTE_MAXS} and {@link #COMPUTE_FRAMES}.
+   */
+  public final void setFlags(final int flags) {
+    if ((flags & ClassWriter.COMPUTE_FRAMES) != 0) {
+      compute = MethodWriter.COMPUTE_ALL_FRAMES;
+    } else if ((flags & ClassWriter.COMPUTE_MAXS) != 0) {
+      compute = MethodWriter.COMPUTE_MAX_STACK_AND_LOCAL;
+    } else {
+      compute = MethodWriter.COMPUTE_NOTHING;
+    }
+  }
+
   // -----------------------------------------------------------------------------------------------
   // Default method to compute common super classes when computing stack map frames
   // -----------------------------------------------------------------------------------------------
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Constants.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Constants.java
index 2c51aefc4..ee9ed838d 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Constants.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Constants.java
@@ -191,15 +191,15 @@ final class Constants {
   }
 
   static boolean isWhitelisted(final String internalName) {
-    if (!internalName.startsWith("org/apache/tapestry5/internal/plastic/asm/")) {
+    if (!internalName.startsWith("org/objectweb/asm/")) {
       return false;
     }
     String member = "(Annotation|Class|Field|Method|Module|RecordComponent|Signature)";
     return internalName.contains("Test$")
         || Pattern.matches(
-            "org/apache/tapestry5/internal/plastic/asm/util/Trace" + member + "Visitor(\\$.*)?", internalName)
+            "org/objectweb/asm/util/Trace" + member + "Visitor(\\$.*)?", internalName)
         || Pattern.matches(
-            "org/apache/tapestry5/internal/plastic/asm/util/Check" + member + "Adapter(\\$.*)?", internalName);
+            "org/objectweb/asm/util/Check" + member + "Adapter(\\$.*)?", internalName);
   }
 
   static void checkIsPreview(final InputStream classInputStream) {
@@ -215,7 +215,7 @@ final class Constants {
     }
     if (minorVersion != 0xFFFF) {
       throw new IllegalStateException(
-          "ASM9_EXPERIMENTAL can only be used by classes compiled with --enable-preview");
+          "ASM10_EXPERIMENTAL can only be used by classes compiled with --enable-preview");
     }
   }
 }
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/MethodVisitor.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/MethodVisitor.java
index 704676331..ae2bb34ee 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/MethodVisitor.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/MethodVisitor.java
@@ -34,15 +34,16 @@ package org.apache.tapestry5.internal.plastic.asm;
  * visitTypeAnnotation} | {@code visitAttribute} )* [ {@code visitCode} ( {@code visitFrame} |
  * {@code visit<i>X</i>Insn} | {@code visitLabel} | {@code visitInsnAnnotation} | {@code
  * visitTryCatchBlock} | {@code visitTryCatchAnnotation} | {@code visitLocalVariable} | {@code
- * visitLocalVariableAnnotation} | {@code visitLineNumber} )* {@code visitMaxs} ] {@code visitEnd}.
- * In addition, the {@code visit<i>X</i>Insn} and {@code visitLabel} methods must be called in the
- * sequential order of the bytecode instructions of the visited code, {@code visitInsnAnnotation}
- * must be called <i>after</i> the annotated instruction, {@code visitTryCatchBlock} must be called
- * <i>before</i> the labels passed as arguments have been visited, {@code
- * visitTryCatchBlockAnnotation} must be called <i>after</i> the corresponding try catch block has
- * been visited, and the {@code visitLocalVariable}, {@code visitLocalVariableAnnotation} and {@code
- * visitLineNumber} methods must be called <i>after</i> the labels passed as arguments have been
- * visited.
+ * visitLocalVariableAnnotation} | {@code visitLineNumber} | {@code visitAttribute} )* {@code
+ * visitMaxs} ] {@code visitEnd}. In addition, the {@code visit<i>X</i>Insn} and {@code visitLabel}
+ * methods must be called in the sequential order of the bytecode instructions of the visited code,
+ * {@code visitInsnAnnotation} must be called <i>after</i> the annotated instruction, {@code
+ * visitTryCatchBlock} must be called <i>before</i> the labels passed as arguments have been
+ * visited, {@code visitTryCatchBlockAnnotation} must be called <i>after</i> the corresponding try
+ * catch block has been visited, and the {@code visitLocalVariable}, {@code
+ * visitLocalVariableAnnotation} and {@code visitLineNumber} methods must be called <i>after</i> the
+ * labels passed as arguments have been visited. Finally, the {@code visitAttribute} method must be
+ * called before {@code visitCode} for non-code attributes, and after it for code attributes.
  *
  * @author Eric Bruneton
  */
@@ -595,7 +596,7 @@ public abstract class MethodVisitor {
    * Visits a LOOKUPSWITCH instruction.
    *
    * @param dflt beginning of the default handler block.
-   * @param keys the values of the keys.
+   * @param keys the values of the keys. Keys must be sorted in increasing order.
    * @param labels beginnings of the handler blocks. {@code labels[i]} is the beginning of the
    *     handler block for the {@code keys[i]} key.
    */
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/MethodWriter.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/MethodWriter.java
index 136998534..4d73ea80e 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/MethodWriter.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/MethodWriter.java
@@ -651,7 +651,7 @@ final class MethodWriter extends MethodVisitor {
   @Override
   public AnnotationVisitor visitAnnotationDefault() {
     defaultValue = new ByteVector();
-    return new AnnotationWriter(symbolTable, /* useNamedValues = */ false, defaultValue, null);
+    return new AnnotationWriter(symbolTable, /* useNamedValues= */ false, defaultValue, null);
   }
 
   @Override
@@ -1519,14 +1519,14 @@ final class MethodWriter extends MethodVisitor {
       return lastCodeRuntimeVisibleTypeAnnotation =
           new AnnotationWriter(
               symbolTable,
-              /* useNamedValues = */ true,
+              /* useNamedValues= */ true,
               typeAnnotation,
               lastCodeRuntimeVisibleTypeAnnotation);
     } else {
       return lastCodeRuntimeInvisibleTypeAnnotation =
           new AnnotationWriter(
               symbolTable,
-              /* useNamedValues = */ true,
+              /* useNamedValues= */ true,
               typeAnnotation,
               lastCodeRuntimeInvisibleTypeAnnotation);
     }
@@ -1642,7 +1642,7 @@ final class MethodWriter extends MethodVisitor {
           code.data[endOffset] = (byte) Opcodes.ATHROW;
           // Emit a frame for this unreachable block, with no local and a Throwable on the stack
           // (so that the ATHROW could consume this Throwable if it were reachable).
-          int frameIndex = visitFrameStart(startOffset, /* numLocal = */ 0, /* numStack = */ 1);
+          int frameIndex = visitFrameStart(startOffset, /* numLocal= */ 0, /* numStack= */ 1);
           currentFrame[frameIndex] =
               Frame.getAbstractTypeFromInternalName(symbolTable, "java/lang/Throwable");
           visitFrameEnd();
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/ModuleVisitor.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/ModuleVisitor.java
index ca731787f..bcba867a4 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/ModuleVisitor.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/ModuleVisitor.java
@@ -93,7 +93,7 @@ public abstract class ModuleVisitor {
   }
 
   /**
-   * Visit the main class of the current module.
+   * Visits the main class of the current module.
    *
    * @param mainClass the internal name of the main class of the current module (see {@link
    *     Type#getInternalName()}).
@@ -105,7 +105,7 @@ public abstract class ModuleVisitor {
   }
 
   /**
-   * Visit a package of the current module.
+   * Visits a package of the current module.
    *
    * @param packaze the internal name of a package (see {@link Type#getInternalName()}).
    */
@@ -130,7 +130,7 @@ public abstract class ModuleVisitor {
   }
 
   /**
-   * Visit an exported package of the current module.
+   * Visits an exported package of the current module.
    *
    * @param packaze the internal name of the exported package (see {@link Type#getInternalName()}).
    * @param access the access flag of the exported package, valid values are among {@code
@@ -145,7 +145,7 @@ public abstract class ModuleVisitor {
   }
 
   /**
-   * Visit an open package of the current module.
+   * Visits an open package of the current module.
    *
    * @param packaze the internal name of the opened package (see {@link Type#getInternalName()}).
    * @param access the access flag of the opened package, valid values are among {@code
@@ -160,7 +160,7 @@ public abstract class ModuleVisitor {
   }
 
   /**
-   * Visit a service used by the current module. The name must be the internal name of an interface
+   * Visits a service used by the current module. The name must be the internal name of an interface
    * or a class.
    *
    * @param service the internal name of the service (see {@link Type#getInternalName()}).
@@ -172,7 +172,7 @@ public abstract class ModuleVisitor {
   }
 
   /**
-   * Visit an implementation of a service.
+   * Visits an implementation of a service.
    *
    * @param service the internal name of the service (see {@link Type#getInternalName()}).
    * @param providers the internal names (see {@link Type#getInternalName()}) of the implementations
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Opcodes.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Opcodes.java
index da3c7f663..42cf12956 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Opcodes.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Opcodes.java
@@ -288,6 +288,10 @@ public interface Opcodes {
   int V20 = 0 << 16 | 64;
   int V21 = 0 << 16 | 65;
   int V22 = 0 << 16 | 66;
+  int V23 = 0 << 16 | 67;
+  int V24 = 0 << 16 | 68;
+  int V25 = 0 << 16 | 69;
+  int V26 = 0 << 16 | 70;
 
   /**
    * Version flag indicating that the class is using 'preview' features.
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Symbol.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Symbol.java
index 531c985cc..e5b165d47 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Symbol.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Symbol.java
@@ -178,7 +178,9 @@ abstract class Symbol {
    *   <li>the symbol's value for {@link #CONSTANT_INTEGER_TAG},{@link #CONSTANT_FLOAT_TAG}, {@link
    *       #CONSTANT_LONG_TAG}, {@link #CONSTANT_DOUBLE_TAG},
    *   <li>the CONSTANT_MethodHandle_info reference_kind field value for {@link
-   *       #CONSTANT_METHOD_HANDLE_TAG} symbols,
+   *       #CONSTANT_METHOD_HANDLE_TAG} symbols (or this value left shifted by 8 bits for
+   *       reference_kind values larger than or equal to H_INVOKEVIRTUAL and if the method owner is
+   *       an interface),
    *   <li>the CONSTANT_InvokeDynamic_info bootstrap_method_attr_index field value for {@link
    *       #CONSTANT_INVOKE_DYNAMIC_TAG} symbols,
    *   <li>the offset of a bootstrap method in the BootstrapMethods boostrap_methods array, for
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/SymbolTable.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/SymbolTable.java
index 8c0ffb95e..abc4bab20 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/SymbolTable.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/SymbolTable.java
@@ -221,7 +221,9 @@ final class SymbolTable {
               classReader.readByte(itemOffset),
               classReader.readClass(memberRefItemOffset, charBuffer),
               classReader.readUTF8(nameAndTypeItemOffset, charBuffer),
-              classReader.readUTF8(nameAndTypeItemOffset + 2, charBuffer));
+              classReader.readUTF8(nameAndTypeItemOffset + 2, charBuffer),
+              classReader.readByte(memberRefItemOffset - 1)
+                  == Symbol.CONSTANT_INTERFACE_METHODREF_TAG);
           break;
         case Symbol.CONSTANT_DYNAMIC_TAG:
         case Symbol.CONSTANT_INVOKE_DYNAMIC_TAG:
@@ -830,14 +832,15 @@ final class SymbolTable {
       final String descriptor,
       final boolean isInterface) {
     final int tag = Symbol.CONSTANT_METHOD_HANDLE_TAG;
+    final int data = getConstantMethodHandleSymbolData(referenceKind, isInterface);
     // Note that we don't need to include isInterface in the hash computation, because it is
     // redundant with owner (we can't have the same owner with different isInterface values).
-    int hashCode = hash(tag, owner, name, descriptor, referenceKind);
+    int hashCode = hash(tag, owner, name, descriptor, data);
     Entry entry = get(hashCode);
     while (entry != null) {
       if (entry.tag == tag
           && entry.hashCode == hashCode
-          && entry.data == referenceKind
+          && entry.data == data
           && entry.owner.equals(owner)
           && entry.name.equals(name)
           && entry.value.equals(descriptor)) {
@@ -851,8 +854,7 @@ final class SymbolTable {
       constantPool.put112(
           tag, referenceKind, addConstantMethodref(owner, name, descriptor, isInterface).index);
     }
-    return put(
-        new Entry(constantPoolCount++, tag, owner, name, descriptor, referenceKind, hashCode));
+    return put(new Entry(constantPoolCount++, tag, owner, name, descriptor, data, hashCode));
   }
 
   /**
@@ -866,16 +868,36 @@ final class SymbolTable {
    * @param owner the internal name of a class of interface.
    * @param name a field or method name.
    * @param descriptor a field or method descriptor.
+   * @param isInterface whether owner is an interface or not.
    */
   private void addConstantMethodHandle(
       final int index,
       final int referenceKind,
       final String owner,
       final String name,
-      final String descriptor) {
+      final String descriptor,
+      final boolean isInterface) {
     final int tag = Symbol.CONSTANT_METHOD_HANDLE_TAG;
-    int hashCode = hash(tag, owner, name, descriptor, referenceKind);
-    add(new Entry(index, tag, owner, name, descriptor, referenceKind, hashCode));
+    final int data = getConstantMethodHandleSymbolData(referenceKind, isInterface);
+    int hashCode = hash(tag, owner, name, descriptor, data);
+    add(new Entry(index, tag, owner, name, descriptor, data, hashCode));
+  }
+
+  /**
+   * Returns the {@link Symbol#data} field for a CONSTANT_MethodHandle_info Symbol.
+   *
+   * @param referenceKind one of {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC}, {@link
+   *     Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, {@link Opcodes#H_INVOKEVIRTUAL}, {@link
+   *     Opcodes#H_INVOKESTATIC}, {@link Opcodes#H_INVOKESPECIAL}, {@link
+   *     Opcodes#H_NEWINVOKESPECIAL} or {@link Opcodes#H_INVOKEINTERFACE}.
+   * @param isInterface whether owner is an interface or not.
+   */
+  private static int getConstantMethodHandleSymbolData(
+      final int referenceKind, final boolean isInterface) {
+    if (referenceKind > Opcodes.H_PUTSTATIC && isInterface) {
+      return referenceKind << 8;
+    }
+    return referenceKind;
   }
 
   /**
@@ -1414,23 +1436,23 @@ final class SymbolTable {
     }
 
     Entry(final int index, final int tag, final String value, final int hashCode) {
-      super(index, tag, /* owner = */ null, /* name = */ null, value, /* data = */ 0);
+      super(index, tag, /* owner= */ null, /* name= */ null, value, /* data= */ 0);
       this.hashCode = hashCode;
     }
 
     Entry(final int index, final int tag, final String value, final long data, final int hashCode) {
-      super(index, tag, /* owner = */ null, /* name = */ null, value, data);
+      super(index, tag, /* owner= */ null, /* name= */ null, value, data);
       this.hashCode = hashCode;
     }
 
     Entry(
         final int index, final int tag, final String name, final String value, final int hashCode) {
-      super(index, tag, /* owner = */ null, name, value, /* data = */ 0);
+      super(index, tag, /* owner= */ null, name, value, /* data= */ 0);
       this.hashCode = hashCode;
     }
 
     Entry(final int index, final int tag, final long data, final int hashCode) {
-      super(index, tag, /* owner = */ null, /* name = */ null, /* value = */ null, data);
+      super(index, tag, /* owner= */ null, /* name= */ null, /* value= */ null, data);
       this.hashCode = hashCode;
     }
   }
@@ -1451,7 +1473,7 @@ final class SymbolTable {
 
     /**
      * Another entry (and so on recursively) having the same hash code (modulo the size of {@link
-     * SymbolTable#labelEntries}}) as this one.
+     * SymbolTable#labelEntries}) as this one.
      */
     LabelEntry next;
 
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/AdviceAdapter.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/AdviceAdapter.java
index 8d841e5fe..f5c536742 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/AdviceAdapter.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/AdviceAdapter.java
@@ -31,7 +31,6 @@ import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-
 import org.apache.tapestry5.internal.plastic.asm.ConstantDynamic;
 import org.apache.tapestry5.internal.plastic.asm.Handle;
 import org.apache.tapestry5.internal.plastic.asm.Label;
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/AnalyzerAdapter.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/AnalyzerAdapter.java
index 5bdcf9a81..ae59173cd 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/AnalyzerAdapter.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/AnalyzerAdapter.java
@@ -31,7 +31,6 @@ import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-
 import org.apache.tapestry5.internal.plastic.asm.ConstantDynamic;
 import org.apache.tapestry5.internal.plastic.asm.Handle;
 import org.apache.tapestry5.internal.plastic.asm.Label;
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/AnnotationRemapper.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/AnnotationRemapper.java
index 3989e002a..b8eabc06f 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/AnnotationRemapper.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/AnnotationRemapper.java
@@ -57,7 +57,7 @@ public class AnnotationRemapper extends AnnotationVisitor {
    */
   @Deprecated
   public AnnotationRemapper(final AnnotationVisitor annotationVisitor, final Remapper remapper) {
-    this(/* descriptor = */ null, annotationVisitor, remapper);
+    this(/* descriptor= */ null, annotationVisitor, remapper);
   }
 
   /**
@@ -86,7 +86,7 @@ public class AnnotationRemapper extends AnnotationVisitor {
   @Deprecated
   protected AnnotationRemapper(
       final int api, final AnnotationVisitor annotationVisitor, final Remapper remapper) {
-    this(api, /* descriptor = */ null, annotationVisitor, remapper);
+    this(api, /* descriptor= */ null, annotationVisitor, remapper);
   }
 
   /**
@@ -139,7 +139,7 @@ public class AnnotationRemapper extends AnnotationVisitor {
     } else {
       return annotationVisitor == av
           ? this
-          : createAnnotationRemapper(/* descriptor = */ null, annotationVisitor);
+          : createAnnotationRemapper(/* descriptor= */ null, annotationVisitor);
     }
   }
 
@@ -153,7 +153,7 @@ public class AnnotationRemapper extends AnnotationVisitor {
    */
   @Deprecated
   protected AnnotationVisitor createAnnotationRemapper(final AnnotationVisitor annotationVisitor) {
-    return new AnnotationRemapper(api, /* descriptor = */ null, annotationVisitor, remapper);
+    return new AnnotationRemapper(api, /* descriptor= */ null, annotationVisitor, remapper);
   }
 
   /**
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/ClassRemapper.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/ClassRemapper.java
index d6dba3caa..ed3555736 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/ClassRemapper.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/ClassRemapper.java
@@ -29,7 +29,6 @@
 package org.apache.tapestry5.internal.plastic.asm.commons;
 
 import java.util.List;
-
 import org.apache.tapestry5.internal.plastic.asm.AnnotationVisitor;
 import org.apache.tapestry5.internal.plastic.asm.Attribute;
 import org.apache.tapestry5.internal.plastic.asm.ClassVisitor;
@@ -259,7 +258,7 @@ public class ClassRemapper extends ClassVisitor {
    */
   @Deprecated
   protected AnnotationVisitor createAnnotationRemapper(final AnnotationVisitor annotationVisitor) {
-    return new AnnotationRemapper(api, /* descriptor = */ null, annotationVisitor, remapper);
+    return new AnnotationRemapper(api, /* descriptor= */ null, annotationVisitor, remapper);
   }
 
   /**
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/FieldRemapper.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/FieldRemapper.java
index b60949f03..37065d76a 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/FieldRemapper.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/FieldRemapper.java
@@ -96,7 +96,7 @@ public class FieldRemapper extends FieldVisitor {
    */
   @Deprecated
   protected AnnotationVisitor createAnnotationRemapper(final AnnotationVisitor annotationVisitor) {
-    return new AnnotationRemapper(api, /* descriptor = */ null, annotationVisitor, remapper);
+    return new AnnotationRemapper(api, /* descriptor= */ null, annotationVisitor, remapper);
   }
 
   /**
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/GeneratorAdapter.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/GeneratorAdapter.java
index 866eb0c91..f398560e4 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/GeneratorAdapter.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/GeneratorAdapter.java
@@ -30,7 +30,6 @@ package org.apache.tapestry5.internal.plastic.asm.commons;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
-
 import org.apache.tapestry5.internal.plastic.asm.ClassVisitor;
 import org.apache.tapestry5.internal.plastic.asm.ConstantDynamic;
 import org.apache.tapestry5.internal.plastic.asm.Handle;
@@ -397,6 +396,9 @@ public class GeneratorAdapter extends LocalVariablesSorter {
       mv.visitInsn(Opcodes.ACONST_NULL);
     } else {
       switch (value.getSort()) {
+        case Type.VOID:
+          mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Void", "TYPE", CLASS_DESCRIPTOR);
+          break;
         case Type.BOOLEAN:
           mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Boolean", "TYPE", CLASS_DESCRIPTOR);
           break;
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/InstructionAdapter.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/InstructionAdapter.java
index 9e9631e00..88b0177ea 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/InstructionAdapter.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/InstructionAdapter.java
@@ -544,6 +544,9 @@ public class InstructionAdapter extends MethodVisitor {
       final String descriptor,
       final Handle bootstrapMethodHandle,
       final Object... bootstrapMethodArguments) {
+    if (api < Opcodes.ASM5) {
+      throw new UnsupportedOperationException("This feature requires ASM5");
+    }
     invokedynamic(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments);
   }
 
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/JSRInlinerAdapter.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/JSRInlinerAdapter.java
index a4290748d..cd7d93481 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/JSRInlinerAdapter.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/JSRInlinerAdapter.java
@@ -35,7 +35,6 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-
 import org.apache.tapestry5.internal.plastic.asm.Label;
 import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
 import org.apache.tapestry5.internal.plastic.asm.Opcodes;
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/Method.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/Method.java
index e97af1cda..44c02b6d0 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/Method.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/Method.java
@@ -29,7 +29,6 @@ package org.apache.tapestry5.internal.plastic.asm.commons;
 
 import java.util.HashMap;
 import java.util.Map;
-
 import org.apache.tapestry5.internal.plastic.asm.Type;
 
 /**
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/MethodRemapper.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/MethodRemapper.java
index 2dd14159c..3a4d20f23 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/MethodRemapper.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/MethodRemapper.java
@@ -75,7 +75,7 @@ public class MethodRemapper extends MethodVisitor {
     AnnotationVisitor annotationVisitor = super.visitAnnotationDefault();
     return annotationVisitor == null
         ? annotationVisitor
-        : createAnnotationRemapper(/* descriptor = */ null, annotationVisitor);
+        : createAnnotationRemapper(/* descriptor= */ null, annotationVisitor);
   }
 
   @Override
@@ -170,17 +170,26 @@ public class MethodRemapper extends MethodVisitor {
   }
 
   @Override
+  @SuppressWarnings("deprecation")
   public void visitInvokeDynamicInsn(
       final String name,
       final String descriptor,
       final Handle bootstrapMethodHandle,
       final Object... bootstrapMethodArguments) {
+    String remappedName;
+    if (remapper.api == 0) {
+      remappedName = remapper.mapInvokeDynamicMethodName(name, descriptor);
+    } else {
+      remappedName =
+          remapper.mapInvokeDynamicMethodName(
+              name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments);
+    }
     Object[] remappedBootstrapMethodArguments = new Object[bootstrapMethodArguments.length];
     for (int i = 0; i < bootstrapMethodArguments.length; ++i) {
       remappedBootstrapMethodArguments[i] = remapper.mapValue(bootstrapMethodArguments[i]);
     }
     super.visitInvokeDynamicInsn(
-        remapper.mapInvokeDynamicMethodName(name, descriptor),
+        remappedName,
         remapper.mapMethodDesc(descriptor),
         (Handle) remapper.mapValue(bootstrapMethodHandle),
         remappedBootstrapMethodArguments);
@@ -271,7 +280,7 @@ public class MethodRemapper extends MethodVisitor {
    */
   @Deprecated
   protected AnnotationVisitor createAnnotationRemapper(final AnnotationVisitor annotationVisitor) {
-    return new AnnotationRemapper(api, /* descriptor = */ null, annotationVisitor, remapper);
+    return new AnnotationRemapper(api, /* descriptor= */ null, annotationVisitor, remapper);
   }
 
   /**
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/ModuleHashesAttribute.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/ModuleHashesAttribute.java
index d672c87e4..532780456 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/ModuleHashesAttribute.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/ModuleHashesAttribute.java
@@ -30,7 +30,6 @@ package org.apache.tapestry5.internal.plastic.asm.commons;
 
 import java.util.ArrayList;
 import java.util.List;
-
 import org.apache.tapestry5.internal.plastic.asm.Attribute;
 import org.apache.tapestry5.internal.plastic.asm.ByteVector;
 import org.apache.tapestry5.internal.plastic.asm.ClassReader;
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/RecordComponentRemapper.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/RecordComponentRemapper.java
index 37e9210a0..d45b48103 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/RecordComponentRemapper.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/RecordComponentRemapper.java
@@ -99,7 +99,7 @@ public class RecordComponentRemapper extends RecordComponentVisitor {
    */
   @Deprecated
   protected AnnotationVisitor createAnnotationRemapper(final AnnotationVisitor annotationVisitor) {
-    return new AnnotationRemapper(api, /* descriptor = */ null, annotationVisitor, remapper);
+    return new AnnotationRemapper(api, /* descriptor= */ null, annotationVisitor, remapper);
   }
 
   /**
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/Remapper.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/Remapper.java
index b25d683cb..27c3fa880 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/Remapper.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/Remapper.java
@@ -43,6 +43,41 @@ import org.apache.tapestry5.internal.plastic.asm.signature.SignatureWriter;
  */
 public abstract class Remapper {
 
+  /**
+   * The ASM API version supported by this remapper, or 0 for instances created with the deprecated
+   * constructor.
+   */
+  final int api;
+
+  /**
+   * Creates a new {@link Remapper}.
+   *
+   * @deprecated use {@link #Remapper(int)} instead.
+   */
+  @Deprecated
+  public Remapper() {
+    this.api = 0;
+  }
+
+  /**
+   * Creates a new {@link Remapper}.
+   *
+   * @param api the ASM API version supported by this remapper. Must be one of the {@code
+   *     ASM}<i>x</i> values in {@link Opcodes}.
+   */
+  public Remapper(final int api) {
+    if (api != Opcodes.ASM9
+        && api != Opcodes.ASM8
+        && api != Opcodes.ASM7
+        && api != Opcodes.ASM6
+        && api != Opcodes.ASM5
+        && api != Opcodes.ASM4
+        && api != Opcodes.ASM10_EXPERIMENTAL) {
+      throw new IllegalArgumentException("Unsupported api " + api);
+    }
+    this.api = api;
+  }
+
   /**
    * Returns the given descriptor, remapped with {@link #map(String)}.
    *
@@ -177,17 +212,26 @@ public abstract class Remapper {
     }
     if (value instanceof ConstantDynamic) {
       ConstantDynamic constantDynamic = (ConstantDynamic) value;
+      String name = constantDynamic.getName();
+      String descriptor = constantDynamic.getDescriptor();
+      Handle bootstrapMethod = constantDynamic.getBootstrapMethod();
       int bootstrapMethodArgumentCount = constantDynamic.getBootstrapMethodArgumentCount();
+      Object[] bootstrapMethodArguments = new Object[bootstrapMethodArgumentCount];
       Object[] remappedBootstrapMethodArguments = new Object[bootstrapMethodArgumentCount];
       for (int i = 0; i < bootstrapMethodArgumentCount; ++i) {
-        remappedBootstrapMethodArguments[i] =
-            mapValue(constantDynamic.getBootstrapMethodArgument(i));
+        bootstrapMethodArguments[i] = constantDynamic.getBootstrapMethodArgument(i);
+        remappedBootstrapMethodArguments[i] = mapValue(bootstrapMethodArguments[i]);
+      }
+      if (api == 0) {
+        name = mapInvokeDynamicMethodName(name, descriptor);
+      } else {
+        name =
+            mapInvokeDynamicMethodName(name, descriptor, bootstrapMethod, bootstrapMethodArguments);
       }
-      String descriptor = constantDynamic.getDescriptor();
       return new ConstantDynamic(
-          mapInvokeDynamicMethodName(constantDynamic.getName(), descriptor),
+          name,
           mapDesc(descriptor),
-          (Handle) mapValue(constantDynamic.getBootstrapMethod()),
+          (Handle) mapValue(bootstrapMethod),
           remappedBootstrapMethodArguments);
     }
     return value;
@@ -316,11 +360,34 @@ public abstract class Remapper {
    * @param name the name of the method.
    * @param descriptor the descriptor of the method.
    * @return the new name of the method.
+   * @deprecated use {@link #mapInvokeDynamicMethodName(String, String, Handle, Object...)} instead.
    */
+  @Deprecated
   public String mapInvokeDynamicMethodName(final String name, final String descriptor) {
     return name;
   }
 
+  /**
+   * Maps an invokedynamic or a constant dynamic method name to its new name. The default
+   * implementation of this method returns the given name, unchanged. Subclasses can override.
+   *
+   * @param name the name of the method.
+   * @param descriptor the descriptor of the method.
+   * @param bootstrapMethodHandle the bootstrap method.
+   * @param bootstrapMethodArguments the bootstrap method constant arguments. Each argument must be
+   *     an {@link Integer}, {@link Float}, {@link Long}, {@link Double}, {@link String}, {@link
+   *     Type}, {@link Handle} or {@link ConstantDynamic} value. This method is allowed to modify
+   *     the content of the array so a caller should expect that this array may change.
+   * @return the new name of the method.
+   */
+  public String mapInvokeDynamicMethodName(
+      final String name,
+      final String descriptor,
+      final Handle bootstrapMethodHandle,
+      final Object... bootstrapMethodArguments) {
+    return name;
+  }
+
   /**
    * Maps a record component name to its new name. The default implementation of this method returns
    * the given name, unchanged. Subclasses can override.
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/SerialVersionUIDAdder.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/SerialVersionUIDAdder.java
index 8ed9468a9..19173b964 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/SerialVersionUIDAdder.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/SerialVersionUIDAdder.java
@@ -36,7 +36,6 @@ import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-
 import org.apache.tapestry5.internal.plastic.asm.ClassVisitor;
 import org.apache.tapestry5.internal.plastic.asm.FieldVisitor;
 import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
@@ -424,7 +423,7 @@ public class SerialVersionUIDAdder extends ClassVisitor {
   // DontCheck(AbbreviationAsWordInName): can't be renamed (for backward binary compatibility).
   protected byte[] computeSHAdigest(final byte[] value) {
     try {
-      return MessageDigest.getInstance("SHA").digest(value);
+      return MessageDigest.getInstance("SHA-1").digest(value);
     } catch (NoSuchAlgorithmException e) {
       throw new UnsupportedOperationException(e);
     }
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/SignatureRemapper.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/SignatureRemapper.java
index ddca2a433..a86395fea 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/SignatureRemapper.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/SignatureRemapper.java
@@ -29,7 +29,6 @@
 package org.apache.tapestry5.internal.plastic.asm.commons;
 
 import java.util.ArrayList;
-
 import org.apache.tapestry5.internal.plastic.asm.Opcodes;
 import org.apache.tapestry5.internal.plastic.asm.signature.SignatureVisitor;
 
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/SimpleRemapper.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/SimpleRemapper.java
index 46f2ac97c..a3fc44570 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/SimpleRemapper.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/SimpleRemapper.java
@@ -30,6 +30,8 @@ package org.apache.tapestry5.internal.plastic.asm.commons;
 
 import java.util.Collections;
 import java.util.Map;
+import org.apache.tapestry5.internal.plastic.asm.Handle;
+import org.apache.tapestry5.internal.plastic.asm.Opcodes;
 
 /**
  * A {@link Remapper} using a {@link Map} to define its mapping.
@@ -50,17 +52,48 @@ public class SimpleRemapper extends Remapper {
    *           name.
    *       <li>for invokedynamic method names, the key is the name and descriptor of the method (in
    *           the form .&lt;name&gt;&lt;descriptor&gt;), and the value is the new method name.
-   *       <li>for field and attribute names, the key is the owner and name of the field or
-   *           attribute (in the form &lt;owner&gt;.&lt;name&gt;), and the value is the new field
-   *           name.
+   *       <li>for field names, the key is the owner and name of the field or attribute (in the form
+   *           &lt;owner&gt;.&lt;name&gt;), and the value is the new field name.
+   *       <li>for attribute names, the key is the annotation descriptor and the name of the
+   *           attribute (in the form &lt;descriptor&gt;.&lt;name&gt;), and the value is the new
+   *           attribute name.
    *       <li>for internal names, the key is the old internal name, and the value is the new
    *           internal name (see {@link org.apache.tapestry5.internal.plastic.asm.Type#getInternalName()}).
    *     </ul>
+   *
+   * @deprecated use {@link #SimpleRemapper(int, Map)} instead.
    */
+  @Deprecated
   public SimpleRemapper(final Map<String, String> mapping) {
     this.mapping = mapping;
   }
 
+  /**
+   * Constructs a new {@link SimpleRemapper} with the given mapping.
+   *
+   * @param api the ASM API version supported by this remapper. Must be one of the {@code
+   *     ASM}<i>x</i> values in {@link Opcodes}.
+   * @param mapping a map specifying a remapping as follows:
+   *     <ul>
+   *       <li>for method names, the key is the owner, name and descriptor of the method (in the
+   *           form &lt;owner&gt;.&lt;name&gt;&lt;descriptor&gt;), and the value is the new method
+   *           name.
+   *       <li>for invokedynamic method names, the key is the name and descriptor of the method (in
+   *           the form .&lt;name&gt;&lt;descriptor&gt;), and the value is the new method name.
+   *       <li>for field names, the key is the owner and name of the field or attribute (in the form
+   *           &lt;owner&gt;.&lt;name&gt;), and the value is the new field name.
+   *       <li>for attribute names, the key is the annotation descriptor and the name of the
+   *           attribute (in the form &lt;descriptor&gt;.&lt;name&gt;), and the value is the new
+   *           attribute name.
+   *       <li>for internal names, the key is the old internal name, and the value is the new
+   *           internal name (see {@link org.apache.tapestry5.internal.plastic.asm.Type#getInternalName()}).
+   *     </ul>
+   */
+  public SimpleRemapper(final int api, final Map<String, String> mapping) {
+    super(api);
+    this.mapping = mapping;
+  }
+
   /**
    * Constructs a new {@link SimpleRemapper} with the given mapping.
    *
@@ -68,11 +101,28 @@ public class SimpleRemapper extends Remapper {
    *     #SimpleRemapper(Map)} for the format of these keys).
    * @param newName the new method, field or internal name (see {@link
    *     org.apache.tapestry5.internal.plastic.asm.Type#getInternalName()}).
+   * @deprecated use {@link #SimpleRemapper(int, String, String)} instead.
    */
+  @Deprecated
   public SimpleRemapper(final String oldName, final String newName) {
     this.mapping = Collections.singletonMap(oldName, newName);
   }
 
+  /**
+   * Constructs a new {@link SimpleRemapper} with the given mapping.
+   *
+   * @param api the ASM API version supported by this remapper. Must be one of the {@code
+   *     ASM}<i>x</i> values in {@link Opcodes}.
+   * @param oldName the key corresponding to a method, field or internal name (see {@link
+   *     #SimpleRemapper(Map)} for the format of these keys).
+   * @param newName the new method, field or internal name (see {@link
+   *     org.apache.tapestry5.internal.plastic.asm.Type#getInternalName()}).
+   */
+  public SimpleRemapper(final int api, final String oldName, final String newName) {
+    super(api);
+    this.mapping = Collections.singletonMap(oldName, newName);
+  }
+
   @Override
   public String mapMethodName(final String owner, final String name, final String descriptor) {
     String remappedName = map(owner + '.' + name + descriptor);
@@ -85,6 +135,16 @@ public class SimpleRemapper extends Remapper {
     return remappedName == null ? name : remappedName;
   }
 
+  @Override
+  public String mapInvokeDynamicMethodName(
+      final String name,
+      final String descriptor,
+      final Handle bootstrapMethodHandle,
+      final Object... bootstrapMethodArguments) {
+    String remappedName = map('.' + name + descriptor);
+    return remappedName == null ? name : remappedName;
+  }
+
   @Override
   public String mapAnnotationAttributeName(final String descriptor, final String name) {
     String remappedName = map(descriptor + '.' + name);
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/TryCatchBlockSorter.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/TryCatchBlockSorter.java
index 086716224..9e0e06dad 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/TryCatchBlockSorter.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/TryCatchBlockSorter.java
@@ -30,7 +30,6 @@ package org.apache.tapestry5.internal.plastic.asm.commons;
 
 import java.util.Collections;
 import java.util.Comparator;
-
 import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
 import org.apache.tapestry5.internal.plastic.asm.Opcodes;
 import org.apache.tapestry5.internal.plastic.asm.tree.MethodNode;
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/AbstractInsnNode.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/AbstractInsnNode.java
index 12b2b2bc7..c86e9c343 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/AbstractInsnNode.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/AbstractInsnNode.java
@@ -30,7 +30,6 @@ package org.apache.tapestry5.internal.plastic.asm.tree;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
-
 import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
 
 /**
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/AnnotationNode.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/AnnotationNode.java
index 3c37c46e9..e217ac396 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/AnnotationNode.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/AnnotationNode.java
@@ -29,7 +29,6 @@ package org.apache.tapestry5.internal.plastic.asm.tree;
 
 import java.util.ArrayList;
 import java.util.List;
-
 import org.apache.tapestry5.internal.plastic.asm.AnnotationVisitor;
 import org.apache.tapestry5.internal.plastic.asm.Opcodes;
 
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/ClassNode.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/ClassNode.java
index 21adf0eaf..863caadc1 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/ClassNode.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/ClassNode.java
@@ -29,7 +29,6 @@ package org.apache.tapestry5.internal.plastic.asm.tree;
 
 import java.util.ArrayList;
 import java.util.List;
-
 import org.apache.tapestry5.internal.plastic.asm.AnnotationVisitor;
 import org.apache.tapestry5.internal.plastic.asm.Attribute;
 import org.apache.tapestry5.internal.plastic.asm.ClassVisitor;
@@ -91,8 +90,8 @@ public class ClassNode extends ClassVisitor {
 
   /**
    * The internal name of the enclosing class of this class (see {@link
-   * org.apache.tapestry5.internal.plastic.asm.Type#getInternalName()}). Must be {@literal null} if this class has no
-   * enclosing class, or if it is a local or anonymous class.
+   * org.apache.tapestry5.internal.plastic.asm.Type#getInternalName()}). Must be {@literal null} if this class is not a
+   * local or anonymous class.
    */
   public String outerClass;
 
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/FieldInsnNode.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/FieldInsnNode.java
index 57afc1027..5aef46b0d 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/FieldInsnNode.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/FieldInsnNode.java
@@ -28,7 +28,6 @@
 package org.apache.tapestry5.internal.plastic.asm.tree;
 
 import java.util.Map;
-
 import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
 
 /**
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/FieldNode.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/FieldNode.java
index 2b3960c02..c6805139c 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/FieldNode.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/FieldNode.java
@@ -28,7 +28,6 @@
 package org.apache.tapestry5.internal.plastic.asm.tree;
 
 import java.util.List;
-
 import org.apache.tapestry5.internal.plastic.asm.AnnotationVisitor;
 import org.apache.tapestry5.internal.plastic.asm.Attribute;
 import org.apache.tapestry5.internal.plastic.asm.ClassVisitor;
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/FrameNode.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/FrameNode.java
index a0457e6f6..570678d84 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/FrameNode.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/FrameNode.java
@@ -30,7 +30,6 @@ package org.apache.tapestry5.internal.plastic.asm.tree;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
-
 import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
 import org.apache.tapestry5.internal.plastic.asm.Opcodes;
 
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/IincInsnNode.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/IincInsnNode.java
index 4370b1f2e..d5c8a8155 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/IincInsnNode.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/IincInsnNode.java
@@ -28,7 +28,6 @@
 package org.apache.tapestry5.internal.plastic.asm.tree;
 
 import java.util.Map;
-
 import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
 import org.apache.tapestry5.internal.plastic.asm.Opcodes;
 
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/InsnList.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/InsnList.java
index add510717..42e4d6afa 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/InsnList.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/InsnList.java
@@ -29,7 +29,6 @@ package org.apache.tapestry5.internal.plastic.asm.tree;
 
 import java.util.ListIterator;
 import java.util.NoSuchElementException;
-
 import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
 
 /**
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/InsnNode.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/InsnNode.java
index c6c1c4db2..2e0ea1b28 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/InsnNode.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/InsnNode.java
@@ -28,7 +28,6 @@
 package org.apache.tapestry5.internal.plastic.asm.tree;
 
 import java.util.Map;
-
 import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
 
 /**
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/IntInsnNode.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/IntInsnNode.java
index 7b530a23b..e0b091c87 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/IntInsnNode.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/IntInsnNode.java
@@ -28,7 +28,6 @@
 package org.apache.tapestry5.internal.plastic.asm.tree;
 
 import java.util.Map;
-
 import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
 
 /**
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/InvokeDynamicInsnNode.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/InvokeDynamicInsnNode.java
index a500bc737..c031c7e42 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/InvokeDynamicInsnNode.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/InvokeDynamicInsnNode.java
@@ -28,7 +28,6 @@
 package org.apache.tapestry5.internal.plastic.asm.tree;
 
 import java.util.Map;
-
 import org.apache.tapestry5.internal.plastic.asm.Handle;
 import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
 import org.apache.tapestry5.internal.plastic.asm.Opcodes;
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/JumpInsnNode.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/JumpInsnNode.java
index c76b3b016..e03387421 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/JumpInsnNode.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/JumpInsnNode.java
@@ -28,7 +28,6 @@
 package org.apache.tapestry5.internal.plastic.asm.tree;
 
 import java.util.Map;
-
 import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
 
 /**
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/LabelNode.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/LabelNode.java
index cbd3ad266..5711525a9 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/LabelNode.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/LabelNode.java
@@ -28,7 +28,6 @@
 package org.apache.tapestry5.internal.plastic.asm.tree;
 
 import java.util.Map;
-
 import org.apache.tapestry5.internal.plastic.asm.Label;
 import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
 
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/LdcInsnNode.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/LdcInsnNode.java
index 8209bfa64..5f40421e1 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/LdcInsnNode.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/LdcInsnNode.java
@@ -28,7 +28,6 @@
 package org.apache.tapestry5.internal.plastic.asm.tree;
 
 import java.util.Map;
-
 import org.apache.tapestry5.internal.plastic.asm.ConstantDynamic;
 import org.apache.tapestry5.internal.plastic.asm.Handle;
 import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/LineNumberNode.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/LineNumberNode.java
index 44afbe238..e02faac2b 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/LineNumberNode.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/LineNumberNode.java
@@ -28,7 +28,6 @@
 package org.apache.tapestry5.internal.plastic.asm.tree;
 
 import java.util.Map;
-
 import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
 
 /**
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/LocalVariableAnnotationNode.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/LocalVariableAnnotationNode.java
index 56acaa5ae..4164ebd05 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/LocalVariableAnnotationNode.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/LocalVariableAnnotationNode.java
@@ -29,7 +29,6 @@
 package org.apache.tapestry5.internal.plastic.asm.tree;
 
 import java.util.List;
-
 import org.apache.tapestry5.internal.plastic.asm.Label;
 import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
 import org.apache.tapestry5.internal.plastic.asm.Opcodes;
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/LookupSwitchInsnNode.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/LookupSwitchInsnNode.java
index eba6abe3f..f339b1771 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/LookupSwitchInsnNode.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/LookupSwitchInsnNode.java
@@ -29,7 +29,6 @@ package org.apache.tapestry5.internal.plastic.asm.tree;
 
 import java.util.List;
 import java.util.Map;
-
 import org.apache.tapestry5.internal.plastic.asm.Label;
 import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
 import org.apache.tapestry5.internal.plastic.asm.Opcodes;
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/MethodInsnNode.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/MethodInsnNode.java
index d5b4f8349..9e1e61381 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/MethodInsnNode.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/MethodInsnNode.java
@@ -28,7 +28,6 @@
 package org.apache.tapestry5.internal.plastic.asm.tree;
 
 import java.util.Map;
-
 import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
 import org.apache.tapestry5.internal.plastic.asm.Opcodes;
 
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/MethodNode.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/MethodNode.java
index efd6d9172..1dbe168df 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/MethodNode.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/MethodNode.java
@@ -29,7 +29,6 @@ package org.apache.tapestry5.internal.plastic.asm.tree;
 
 import java.util.ArrayList;
 import java.util.List;
-
 import org.apache.tapestry5.internal.plastic.asm.AnnotationVisitor;
 import org.apache.tapestry5.internal.plastic.asm.Attribute;
 import org.apache.tapestry5.internal.plastic.asm.ClassVisitor;
@@ -606,6 +605,8 @@ public class MethodNode extends MethodVisitor {
           if (isInterface != (insn.opcode == Opcodes.INVOKEINTERFACE)) {
             throw new UnsupportedClassVersionException();
           }
+        } else if (insn instanceof InvokeDynamicInsnNode) {
+          throw new UnsupportedClassVersionException();
         } else if (insn instanceof LdcInsnNode) {
           Object value = ((LdcInsnNode) insn).cst;
           if (value instanceof Handle
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/ModuleExportNode.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/ModuleExportNode.java
index 69e15dd0c..ee2857282 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/ModuleExportNode.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/ModuleExportNode.java
@@ -28,7 +28,6 @@
 package org.apache.tapestry5.internal.plastic.asm.tree;
 
 import java.util.List;
-
 import org.apache.tapestry5.internal.plastic.asm.ModuleVisitor;
 
 /**
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/ModuleNode.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/ModuleNode.java
index 809f33d39..a6d77e4c2 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/ModuleNode.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/ModuleNode.java
@@ -29,7 +29,6 @@ package org.apache.tapestry5.internal.plastic.asm.tree;
 
 import java.util.ArrayList;
 import java.util.List;
-
 import org.apache.tapestry5.internal.plastic.asm.ClassVisitor;
 import org.apache.tapestry5.internal.plastic.asm.ModuleVisitor;
 import org.apache.tapestry5.internal.plastic.asm.Opcodes;
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/ModuleOpenNode.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/ModuleOpenNode.java
index 39eabb013..109a2ee94 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/ModuleOpenNode.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/ModuleOpenNode.java
@@ -28,7 +28,6 @@
 package org.apache.tapestry5.internal.plastic.asm.tree;
 
 import java.util.List;
-
 import org.apache.tapestry5.internal.plastic.asm.ModuleVisitor;
 
 /**
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/ModuleProvideNode.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/ModuleProvideNode.java
index ddaec4b75..7a3dabd1d 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/ModuleProvideNode.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/ModuleProvideNode.java
@@ -28,7 +28,6 @@
 package org.apache.tapestry5.internal.plastic.asm.tree;
 
 import java.util.List;
-
 import org.apache.tapestry5.internal.plastic.asm.ModuleVisitor;
 
 /**
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/MultiANewArrayInsnNode.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/MultiANewArrayInsnNode.java
index 021d99396..6cb8d8ca7 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/MultiANewArrayInsnNode.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/MultiANewArrayInsnNode.java
@@ -28,7 +28,6 @@
 package org.apache.tapestry5.internal.plastic.asm.tree;
 
 import java.util.Map;
-
 import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
 import org.apache.tapestry5.internal.plastic.asm.Opcodes;
 
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/RecordComponentNode.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/RecordComponentNode.java
index 77429c0e6..101f1a26f 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/RecordComponentNode.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/RecordComponentNode.java
@@ -28,7 +28,6 @@
 package org.apache.tapestry5.internal.plastic.asm.tree;
 
 import java.util.List;
-
 import org.apache.tapestry5.internal.plastic.asm.AnnotationVisitor;
 import org.apache.tapestry5.internal.plastic.asm.Attribute;
 import org.apache.tapestry5.internal.plastic.asm.ClassVisitor;
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/TableSwitchInsnNode.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/TableSwitchInsnNode.java
index dd8d4c176..12e7e68b5 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/TableSwitchInsnNode.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/TableSwitchInsnNode.java
@@ -29,7 +29,6 @@ package org.apache.tapestry5.internal.plastic.asm.tree;
 
 import java.util.List;
 import java.util.Map;
-
 import org.apache.tapestry5.internal.plastic.asm.Label;
 import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
 import org.apache.tapestry5.internal.plastic.asm.Opcodes;
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/TryCatchBlockNode.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/TryCatchBlockNode.java
index 88c340968..694a1e8da 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/TryCatchBlockNode.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/TryCatchBlockNode.java
@@ -28,7 +28,6 @@
 package org.apache.tapestry5.internal.plastic.asm.tree;
 
 import java.util.List;
-
 import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
 
 /**
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/TypeInsnNode.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/TypeInsnNode.java
index b8fc47a19..7c4b77c77 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/TypeInsnNode.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/TypeInsnNode.java
@@ -28,7 +28,6 @@
 package org.apache.tapestry5.internal.plastic.asm.tree;
 
 import java.util.Map;
-
 import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
 
 /**
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/VarInsnNode.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/VarInsnNode.java
index 274fea92b..7df168fd8 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/VarInsnNode.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/VarInsnNode.java
@@ -28,7 +28,6 @@
 package org.apache.tapestry5.internal.plastic.asm.tree;
 
 import java.util.Map;
-
 import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
 
 /**
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/Analyzer.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/Analyzer.java
index 7f172f41c..493e15936 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/Analyzer.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/Analyzer.java
@@ -31,7 +31,6 @@ import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-
 import org.apache.tapestry5.internal.plastic.asm.Opcodes;
 import org.apache.tapestry5.internal.plastic.asm.Type;
 import org.apache.tapestry5.internal.plastic.asm.tree.AbstractInsnNode;
@@ -124,7 +123,7 @@ public class Analyzer<V extends Value> implements Opcodes {
       TryCatchBlockNode tryCatchBlock = method.tryCatchBlocks.get(i);
       int startIndex = insnList.indexOf(tryCatchBlock.start);
       int endIndex = insnList.indexOf(tryCatchBlock.end);
-      for (int j = startIndex; j <= endIndex; ++j) {
+      for (int j = startIndex; j < endIndex; ++j) {
         List<TryCatchBlockNode> insnHandlers = handlers[j];
         if (insnHandlers == null) {
           insnHandlers = new ArrayList<>();
@@ -166,6 +165,8 @@ public class Analyzer<V extends Value> implements Opcodes {
         if (insnType == AbstractInsnNode.LABEL
             || insnType == AbstractInsnNode.LINE
             || insnType == AbstractInsnNode.FRAME) {
+          // Update the current frame, so it can be used during processing for this instruction
+          currentFrame.init(oldFrame);
           merge(insnIndex + 1, oldFrame, subroutine);
           newControlFlowEdge(insnIndex, insnIndex + 1);
         } else {
@@ -175,7 +176,7 @@ public class Analyzer<V extends Value> implements Opcodes {
           if (insnNode instanceof JumpInsnNode) {
             JumpInsnNode jumpInsn = (JumpInsnNode) insnNode;
             if (insnOpcode != GOTO && insnOpcode != JSR) {
-              currentFrame.initJumpTarget(insnOpcode, /* target = */ null);
+              currentFrame.initJumpTarget(insnOpcode, /* target= */ null);
               merge(insnIndex + 1, currentFrame, subroutine);
               newControlFlowEdge(insnIndex, insnIndex + 1);
             }
@@ -264,9 +265,18 @@ public class Analyzer<V extends Value> implements Opcodes {
               catchType = Type.getObjectType(tryCatchBlock.type);
             }
             if (newControlFlowExceptionEdge(insnIndex, tryCatchBlock)) {
+              // Merge the frame *before* this instruction, with its stack cleared and an exception
+              // pushed, with the handler's frame.
               Frame<V> handler = newFrame(oldFrame);
               handler.clearStack();
-              handler.push(interpreter.newExceptionValue(tryCatchBlock, handler, catchType));
+              V exceptionValue = interpreter.newExceptionValue(tryCatchBlock, handler, catchType);
+              handler.push(exceptionValue);
+              merge(insnList.indexOf(tryCatchBlock.handler), handler, subroutine);
+              // Merge the frame *after* this instruction, with its stack cleared and an exception
+              // pushed, with the handler's frame.
+              handler = newFrame(currentFrame);
+              handler.clearStack();
+              handler.push(exceptionValue);
               merge(insnList.indexOf(tryCatchBlock.handler), handler, subroutine);
             }
           }
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/BasicInterpreter.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/BasicInterpreter.java
index 65bcba82f..99301657a 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/BasicInterpreter.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/BasicInterpreter.java
@@ -28,7 +28,6 @@
 package org.apache.tapestry5.internal.plastic.asm.tree.analysis;
 
 import java.util.List;
-
 import org.apache.tapestry5.internal.plastic.asm.ConstantDynamic;
 import org.apache.tapestry5.internal.plastic.asm.Handle;
 import org.apache.tapestry5.internal.plastic.asm.Opcodes;
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/BasicVerifier.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/BasicVerifier.java
index 07995cc9d..bfc416e7c 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/BasicVerifier.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/BasicVerifier.java
@@ -28,7 +28,6 @@
 package org.apache.tapestry5.internal.plastic.asm.tree.analysis;
 
 import java.util.List;
-
 import org.apache.tapestry5.internal.plastic.asm.Opcodes;
 import org.apache.tapestry5.internal.plastic.asm.Type;
 import org.apache.tapestry5.internal.plastic.asm.tree.AbstractInsnNode;
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/Frame.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/Frame.java
index acd6e9482..05f1bd265 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/Frame.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/Frame.java
@@ -29,7 +29,6 @@ package org.apache.tapestry5.internal.plastic.asm.tree.analysis;
 
 import java.util.ArrayList;
 import java.util.List;
-
 import org.apache.tapestry5.internal.plastic.asm.Opcodes;
 import org.apache.tapestry5.internal.plastic.asm.Type;
 import org.apache.tapestry5.internal.plastic.asm.tree.AbstractInsnNode;
@@ -372,7 +371,7 @@ public class Frame<V extends Value> {
         if (value1.getSize() != 1) {
           throw new AnalyzerException(insn, "Illegal use of DUP");
         }
-        push(value1);
+        push(interpreter.copyOperation(insn, value1));
         push(interpreter.copyOperation(insn, value1));
         break;
       case Opcodes.DUP_X1:
@@ -382,8 +381,8 @@ public class Frame<V extends Value> {
           throw new AnalyzerException(insn, "Illegal use of DUP_X1");
         }
         push(interpreter.copyOperation(insn, value1));
-        push(value2);
-        push(value1);
+        push(interpreter.copyOperation(insn, value2));
+        push(interpreter.copyOperation(insn, value1));
         break;
       case Opcodes.DUP_X2:
         value1 = pop();
@@ -396,14 +395,14 @@ public class Frame<V extends Value> {
         if (value1.getSize() == 1) {
           value2 = pop();
           if (value2.getSize() == 1) {
-            push(value2);
-            push(value1);
+            push(interpreter.copyOperation(insn, value2));
+            push(interpreter.copyOperation(insn, value1));
             push(interpreter.copyOperation(insn, value2));
             push(interpreter.copyOperation(insn, value1));
             break;
           }
         } else {
-          push(value1);
+          push(interpreter.copyOperation(insn, value1));
           push(interpreter.copyOperation(insn, value1));
           break;
         }
@@ -417,9 +416,9 @@ public class Frame<V extends Value> {
             if (value3.getSize() == 1) {
               push(interpreter.copyOperation(insn, value2));
               push(interpreter.copyOperation(insn, value1));
-              push(value3);
-              push(value2);
-              push(value1);
+              push(interpreter.copyOperation(insn, value3));
+              push(interpreter.copyOperation(insn, value2));
+              push(interpreter.copyOperation(insn, value1));
               break;
             }
           }
@@ -427,8 +426,8 @@ public class Frame<V extends Value> {
           value2 = pop();
           if (value2.getSize() == 1) {
             push(interpreter.copyOperation(insn, value1));
-            push(value2);
-            push(value1);
+            push(interpreter.copyOperation(insn, value2));
+            push(interpreter.copyOperation(insn, value1));
             break;
           }
         }
@@ -444,18 +443,18 @@ public class Frame<V extends Value> {
               if (value4.getSize() == 1) {
                 push(interpreter.copyOperation(insn, value2));
                 push(interpreter.copyOperation(insn, value1));
-                push(value4);
-                push(value3);
-                push(value2);
-                push(value1);
+                push(interpreter.copyOperation(insn, value4));
+                push(interpreter.copyOperation(insn, value3));
+                push(interpreter.copyOperation(insn, value2));
+                push(interpreter.copyOperation(insn, value1));
                 break;
               }
             } else {
               push(interpreter.copyOperation(insn, value2));
               push(interpreter.copyOperation(insn, value1));
-              push(value3);
-              push(value2);
-              push(value1);
+              push(interpreter.copyOperation(insn, value3));
+              push(interpreter.copyOperation(insn, value2));
+              push(interpreter.copyOperation(insn, value1));
               break;
             }
           }
@@ -655,15 +654,15 @@ public class Frame<V extends Value> {
       V value3 = pop();
       if (value3.getSize() == 1) {
         push(interpreter.copyOperation(insn, value1));
-        push(value3);
-        push(value2);
-        push(value1);
+        push(interpreter.copyOperation(insn, value3));
+        push(interpreter.copyOperation(insn, value2));
+        push(interpreter.copyOperation(insn, value1));
         return true;
       }
     } else {
       push(interpreter.copyOperation(insn, value1));
-      push(value2);
-      push(value1);
+      push(interpreter.copyOperation(insn, value2));
+      push(interpreter.copyOperation(insn, value1));
       return true;
     }
     return false;
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/Interpreter.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/Interpreter.java
index 01e4c9eed..323ff04b8 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/Interpreter.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/Interpreter.java
@@ -28,7 +28,6 @@
 package org.apache.tapestry5.internal.plastic.asm.tree.analysis;
 
 import java.util.List;
-
 import org.apache.tapestry5.internal.plastic.asm.Opcodes;
 import org.apache.tapestry5.internal.plastic.asm.Type;
 import org.apache.tapestry5.internal.plastic.asm.tree.AbstractInsnNode;
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/SimpleVerifier.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/SimpleVerifier.java
index 5cb00b1e9..6259c1c06 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/SimpleVerifier.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/SimpleVerifier.java
@@ -28,7 +28,6 @@
 package org.apache.tapestry5.internal.plastic.asm.tree.analysis;
 
 import java.util.List;
-
 import org.apache.tapestry5.internal.plastic.asm.Opcodes;
 import org.apache.tapestry5.internal.plastic.asm.Type;
 
@@ -42,6 +41,9 @@ import org.apache.tapestry5.internal.plastic.asm.Type;
  */
 public class SimpleVerifier extends BasicVerifier {
 
+  /** The type of the Object class. */
+  private static final Type OBJECT_TYPE = Type.getObjectType("java/lang/Object");
+
   /** The type of the class that is verified. */
   private final Type currentClass;
 
@@ -197,30 +199,70 @@ public class SimpleVerifier extends BasicVerifier {
 
   @Override
   protected boolean isSubTypeOf(final BasicValue value, final BasicValue expected) {
-    Type expectedType = expected.getType();
     Type type = value.getType();
+    Type expectedType = expected.getType();
+    // Null types correspond to BasicValue.UNINITIALIZED_VALUE.
+    if (type == null || expectedType == null) {
+      return type == null && expectedType == null;
+    }
+    if (type.equals(expectedType)) {
+      return true;
+    }
     switch (expectedType.getSort()) {
       case Type.INT:
       case Type.FLOAT:
       case Type.LONG:
       case Type.DOUBLE:
-        return type.equals(expectedType);
+        return false;
       case Type.ARRAY:
       case Type.OBJECT:
         if (type.equals(NULL_TYPE)) {
           return true;
-        } else if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
-          if (isAssignableFrom(expectedType, type)) {
-            return true;
-          } else if (getClass(expectedType).isInterface()) {
-            // The merge of class or interface types can only yield class types (because it is not
-            // possible in general to find an unambiguous common super interface, due to multiple
-            // inheritance). Because of this limitation, we need to relax the subtyping check here
-            // if 'value' is an interface.
-            return Object.class.isAssignableFrom(getClass(type));
-          } else {
+        }
+        // Convert 'type' to its element type and array dimension. Arrays of primitive values are
+        // seen as Object arrays with one dimension less. Hence the element type is always of
+        // Type.OBJECT sort.
+        int dim = 0;
+        if (type.getSort() == Type.ARRAY) {
+          dim = type.getDimensions();
+          type = type.getElementType();
+          if (type.getSort() != Type.OBJECT) {
+            dim = dim - 1;
+            type = OBJECT_TYPE;
+          }
+        }
+        // Do the same for expectedType.
+        int expectedDim = 0;
+        if (expectedType.getSort() == Type.ARRAY) {
+          expectedDim = expectedType.getDimensions();
+          expectedType = expectedType.getElementType();
+          if (expectedType.getSort() != Type.OBJECT) {
+            // If the expected type is an array of some primitive type, it does not have any subtype
+            // other than itself. And 'type' is different by hypothesis.
             return false;
           }
+        }
+        // A type with less dimensions than expected can't be a subtype of the expected type.
+        if (dim < expectedDim) {
+          return false;
+        }
+        // A type with more dimensions than expected is seen as an array with the expected
+        // dimensions but with an Object element type. For instance an array of arrays of Integer is
+        // seen as an array of Object if the expected type is an array of Serializable.
+        if (dim > expectedDim) {
+          type = OBJECT_TYPE;
+        }
+        // type and expectedType have a Type.OBJECT sort by construction (see above),
+        // as expected by isAssignableFrom.
+        if (isAssignableFrom(expectedType, type)) {
+          return true;
+        }
+        if (getClass(expectedType).isInterface()) {
+          // The merge of class or interface types can only yield class types (because it is not
+          // possible in general to find an unambiguous common super interface, due to multiple
+          // inheritance). Because of this limitation, we need to relax the subtyping check here
+          // if 'value' is an interface.
+          return Object.class.isAssignableFrom(getClass(type));
         } else {
           return false;
         }
@@ -231,48 +273,71 @@ public class SimpleVerifier extends BasicVerifier {
 
   @Override
   public BasicValue merge(final BasicValue value1, final BasicValue value2) {
-    if (!value1.equals(value2)) {
-      Type type1 = value1.getType();
-      Type type2 = value2.getType();
-      if (type1 != null
-          && (type1.getSort() == Type.OBJECT || type1.getSort() == Type.ARRAY)
-          && type2 != null
-          && (type2.getSort() == Type.OBJECT || type2.getSort() == Type.ARRAY)) {
-        if (type1.equals(NULL_TYPE)) {
-          return value2;
-        }
-        if (type2.equals(NULL_TYPE)) {
-          return value1;
-        }
+    Type type1 = value1.getType();
+    Type type2 = value2.getType();
+    // Null types correspond to BasicValue.UNINITIALIZED_VALUE.
+    if (type1 == null || type2 == null) {
+      return BasicValue.UNINITIALIZED_VALUE;
+    }
+    if (type1.equals(type2)) {
+      return value1;
+    }
+    // The merge of a primitive type with a different type is the type of uninitialized values.
+    if (type1.getSort() != Type.OBJECT && type1.getSort() != Type.ARRAY) {
+      return BasicValue.UNINITIALIZED_VALUE;
+    }
+    if (type2.getSort() != Type.OBJECT && type2.getSort() != Type.ARRAY) {
+      return BasicValue.UNINITIALIZED_VALUE;
+    }
+    // Special case for the type of the "null" literal.
+    if (type1.equals(NULL_TYPE)) {
+      return value2;
+    }
+    if (type2.equals(NULL_TYPE)) {
+      return value1;
+    }
+    // Convert type1 to its element type and array dimension. Arrays of primitive values are seen as
+    // Object arrays with one dimension less. Hence the element type is always of Type.OBJECT sort.
+    int dim1 = 0;
+    if (type1.getSort() == Type.ARRAY) {
+      dim1 = type1.getDimensions();
+      type1 = type1.getElementType();
+      if (type1.getSort() != Type.OBJECT) {
+        dim1 = dim1 - 1;
+        type1 = OBJECT_TYPE;
+      }
+    }
+    // Do the same for type2.
+    int dim2 = 0;
+    if (type2.getSort() == Type.ARRAY) {
+      dim2 = type2.getDimensions();
+      type2 = type2.getElementType();
+      if (type2.getSort() != Type.OBJECT) {
+        dim2 = dim2 - 1;
+        type2 = OBJECT_TYPE;
+      }
+    }
+    // The merge of array types of different dimensions is an Object array type.
+    if (dim1 != dim2) {
+      return newArrayValue(OBJECT_TYPE, Math.min(dim1, dim2));
+    }
+    // Type1 and type2 have a Type.OBJECT sort by construction (see above),
+    // as expected by isAssignableFrom.
+    if (isAssignableFrom(type1, type2)) {
+      return newArrayValue(type1, dim1);
+    }
+    if (isAssignableFrom(type2, type1)) {
+      return newArrayValue(type2, dim1);
+    }
+    if (!isInterface(type1)) {
+      while (!type1.equals(OBJECT_TYPE)) {
+        type1 = getSuperClass(type1);
         if (isAssignableFrom(type1, type2)) {
-          return value1;
-        }
-        if (isAssignableFrom(type2, type1)) {
-          return value2;
-        }
-        int numDimensions = 0;
-        if (type1.getSort() == Type.ARRAY
-            && type2.getSort() == Type.ARRAY
-            && type1.getDimensions() == type2.getDimensions()
-            && type1.getElementType().getSort() == Type.OBJECT
-            && type2.getElementType().getSort() == Type.OBJECT) {
-          numDimensions = type1.getDimensions();
-          type1 = type1.getElementType();
-          type2 = type2.getElementType();
-        }
-        while (true) {
-          if (type1 == null || isInterface(type1)) {
-            return newArrayValue(Type.getObjectType("java/lang/Object"), numDimensions);
-          }
-          type1 = getSuperClass(type1);
-          if (isAssignableFrom(type1, type2)) {
-            return newArrayValue(type1, numDimensions);
-          }
+          return newArrayValue(type1, dim1);
         }
       }
-      return BasicValue.UNINITIALIZED_VALUE;
     }
-    return value1;
+    return newArrayValue(OBJECT_TYPE, dim1);
   }
 
   private BasicValue newArrayValue(final Type type, final int dimensions) {
@@ -293,7 +358,7 @@ public class SimpleVerifier extends BasicVerifier {
    * implementation of this method loads the class and uses the reflection API to return its result
    * (unless the given type corresponds to the class being verified).
    *
-   * @param type a type.
+   * @param type an object reference type (i.e., with Type.OBJECT sort).
    * @return whether 'type' corresponds to an interface.
    */
   protected boolean isInterface(final Type type) {
@@ -308,8 +373,9 @@ public class SimpleVerifier extends BasicVerifier {
    * of this method loads the class and uses the reflection API to return its result (unless the
    * given type corresponds to the class being verified).
    *
-   * @param type a type.
-   * @return the type corresponding to the super class of 'type'.
+   * @param type an object reference type (i.e., with Type.OBJECT sort).
+   * @return the type corresponding to the super class of 'type', or {@literal null} if 'type' is
+   *     the type of the Object class.
    */
   protected Type getSuperClass(final Type type) {
     if (currentClass != null && currentClass.equals(type)) {
@@ -326,8 +392,8 @@ public class SimpleVerifier extends BasicVerifier {
    * result (unless the result can be computed from the class being verified, and the types of its
    * super classes and implemented interfaces).
    *
-   * @param type1 a type.
-   * @param type2 another type.
+   * @param type1 an object reference type (i.e., with Type.OBJECT sort).
+   * @param type2 another object reference type (i.e., with Type.OBJECT sort).
    * @return whether the class corresponding to 'type1' is either the same as, or is a superclass or
    *     superinterface of the class corresponding to 'type2'.
    */
@@ -336,14 +402,16 @@ public class SimpleVerifier extends BasicVerifier {
       return true;
     }
     if (currentClass != null && currentClass.equals(type1)) {
-      if (getSuperClass(type2) == null) {
+      Type superType2 = getSuperClass(type2);
+      if (superType2 == null) {
         return false;
-      } else {
-        if (isInterface) {
-          return type2.getSort() == Type.OBJECT || type2.getSort() == Type.ARRAY;
-        }
-        return isAssignableFrom(type1, getSuperClass(type2));
       }
+      if (isInterface) {
+        // This should always be true, given the preconditions of this method, but is kept for
+        // backward compatibility.
+        return type2.getSort() == Type.OBJECT || type2.getSort() == Type.ARRAY;
+      }
+      return isAssignableFrom(type1, superType2);
     }
     if (currentClass != null && currentClass.equals(type2)) {
       if (isAssignableFrom(type1, currentSuperClass)) {
@@ -366,12 +434,14 @@ public class SimpleVerifier extends BasicVerifier {
    * specified with {@link #setClassLoader}, or with the class loader of this class if no class
    * loader was specified.
    *
-   * @param type a type.
+   * @param type an object reference type (i.e., with Type.OBJECT sort).
    * @return the class corresponding to 'type'.
    */
   protected Class<?> getClass(final Type type) {
     try {
       if (type.getSort() == Type.ARRAY) {
+        // This should never happen, given the preconditions of this method, but is kept for
+        // backward compatibility.
         return Class.forName(type.getDescriptor().replace('/', '.'), false, loader);
       }
       return Class.forName(type.getClassName(), false, loader);
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/SourceInterpreter.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/SourceInterpreter.java
index 10e654371..15c864ed4 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/SourceInterpreter.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/SourceInterpreter.java
@@ -30,7 +30,7 @@ package org.apache.tapestry5.internal.plastic.asm.tree.analysis;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
-
+import org.apache.tapestry5.internal.plastic.asm.ConstantDynamic;
 import org.apache.tapestry5.internal.plastic.asm.Opcodes;
 import org.apache.tapestry5.internal.plastic.asm.Type;
 import org.apache.tapestry5.internal.plastic.asm.tree.AbstractInsnNode;
@@ -87,8 +87,22 @@ public class SourceInterpreter extends Interpreter<SourceValue> implements Opcod
         size = 2;
         break;
       case LDC:
+        // Values able to be pushed by LDC:
+        //   - int, float, string (object), type (Class, object), type (MethodType, object),
+        //       handle (MethodHandle, object): one word
+        //   - long, double, ConstantDynamic (can produce either single word values, or double word
+        //       values): (up to) two words
         Object value = ((LdcInsnNode) insn).cst;
-        size = value instanceof Long || value instanceof Double ? 2 : 1;
+        if (value instanceof Long || value instanceof Double) {
+          // two words guaranteed
+          size = 2;
+        } else if (value instanceof ConstantDynamic) {
+          // might yield two words
+          size = ((ConstantDynamic) value).getSize();
+        } else {
+          // one word guaranteed
+          size = 1;
+        }
         break;
       case GETSTATIC:
         size = Type.getType(((FieldInsnNode) insn).desc).getSize();
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/SourceValue.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/SourceValue.java
index fa078d82e..72bb1b3c5 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/SourceValue.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/SourceValue.java
@@ -28,7 +28,6 @@
 package org.apache.tapestry5.internal.plastic.asm.tree.analysis;
 
 import java.util.Set;
-
 import org.apache.tapestry5.internal.plastic.asm.tree.AbstractInsnNode;
 
 /**
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/Subroutine.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/Subroutine.java
index 29ba6bbb6..2e9cecc75 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/Subroutine.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/Subroutine.java
@@ -29,7 +29,6 @@ package org.apache.tapestry5.internal.plastic.asm.tree.analysis;
 
 import java.util.ArrayList;
 import java.util.List;
-
 import org.apache.tapestry5.internal.plastic.asm.tree.JumpInsnNode;
 import org.apache.tapestry5.internal.plastic.asm.tree.LabelNode;
 
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/ASMifier.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/ASMifier.java
index edbcb6997..f0d0a3157 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/ASMifier.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/ASMifier.java
@@ -34,7 +34,6 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-
 import org.apache.tapestry5.internal.plastic.asm.Attribute;
 import org.apache.tapestry5.internal.plastic.asm.ConstantDynamic;
 import org.apache.tapestry5.internal.plastic.asm.Handle;
@@ -113,6 +112,10 @@ public class ASMifier extends Printer {
     classVersions.put(Opcodes.V20, "V20");
     classVersions.put(Opcodes.V21, "V21");
     classVersions.put(Opcodes.V22, "V22");
+    classVersions.put(Opcodes.V23, "V23");
+    classVersions.put(Opcodes.V24, "V24");
+    classVersions.put(Opcodes.V25, "V25");
+    classVersions.put(Opcodes.V26, "V26");
     CLASS_VERSIONS = Collections.unmodifiableMap(classVersions);
   }
 
@@ -671,7 +674,11 @@ public class ASMifier extends Printer {
   public void visitParameter(final String parameterName, final int access) {
     stringBuilder.setLength(0);
     stringBuilder.append(name).append(".visitParameter(");
-    appendString(stringBuilder, parameterName);
+    if (parameterName == null) {
+      stringBuilder.append("null");
+    } else {
+      appendString(stringBuilder, parameterName);
+    }
     stringBuilder.append(", ");
     appendAccessFlags(access);
     text.add(stringBuilder.append(");\n").toString());
@@ -1469,24 +1476,24 @@ public class ASMifier extends Printer {
       }
       stringBuilder.append("})");
     } else if (value instanceof Byte) {
-      stringBuilder.append("new Byte((byte)").append(value).append(')');
+      stringBuilder.append("Byte.valueOf((byte)").append(value).append(')');
     } else if (value instanceof Boolean) {
       stringBuilder.append(((Boolean) value).booleanValue() ? "Boolean.TRUE" : "Boolean.FALSE");
     } else if (value instanceof Short) {
-      stringBuilder.append("new Short((short)").append(value).append(')');
+      stringBuilder.append("Short.valueOf((short)").append(value).append(')');
     } else if (value instanceof Character) {
       stringBuilder
-          .append("new Character((char)")
+          .append("Character.valueOf((char)")
           .append((int) ((Character) value).charValue())
           .append(')');
     } else if (value instanceof Integer) {
-      stringBuilder.append("new Integer(").append(value).append(')');
+      stringBuilder.append("Integer.valueOf(").append(value).append(')');
     } else if (value instanceof Float) {
-      stringBuilder.append("new Float(\"").append(value).append("\")");
+      stringBuilder.append("Float.valueOf(\"").append(value).append("\")");
     } else if (value instanceof Long) {
-      stringBuilder.append("new Long(").append(value).append("L)");
+      stringBuilder.append("Long.valueOf(").append(value).append("L)");
     } else if (value instanceof Double) {
-      stringBuilder.append("new Double(\"").append(value).append("\")");
+      stringBuilder.append("Double.valueOf(\"").append(value).append("\")");
     } else if (value instanceof byte[]) {
       byte[] byteArray = (byte[]) value;
       stringBuilder.append("new byte[] {");
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/ASMifierSupport.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/ASMifierSupport.java
index 2e21d7ab7..4c5859d15 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/ASMifierSupport.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/ASMifierSupport.java
@@ -22,7 +22,6 @@
 package org.apache.tapestry5.internal.plastic.asm.util;
 
 import java.util.Map;
-
 import org.apache.tapestry5.internal.plastic.asm.Label;
 
 /**
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckClassAdapter.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckClassAdapter.java
index f01bc00d8..18909b7da 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckClassAdapter.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckClassAdapter.java
@@ -35,7 +35,6 @@ import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-
 import org.apache.tapestry5.internal.plastic.asm.AnnotationVisitor;
 import org.apache.tapestry5.internal.plastic.asm.Attribute;
 import org.apache.tapestry5.internal.plastic.asm.ClassReader;
@@ -163,7 +162,7 @@ public class CheckClassAdapter extends ClassVisitor {
    * @param classVisitor the class visitor to which this adapter must delegate calls.
    */
   public CheckClassAdapter(final ClassVisitor classVisitor) {
-    this(classVisitor, /* checkDataFlow = */ true);
+    this(classVisitor, /* checkDataFlow= */ true);
   }
 
   /**
@@ -381,7 +380,7 @@ public class CheckClassAdapter extends ClassVisitor {
       final String name, final String descriptor, final String signature) {
     checkState();
     CheckMethodAdapter.checkUnqualifiedName(version, name, "record component name");
-    CheckMethodAdapter.checkDescriptor(version, descriptor, /* canBeVoid = */ false);
+    CheckMethodAdapter.checkDescriptor(version, descriptor, /* canBeVoid= */ false);
     if (signature != null) {
       checkFieldSignature(signature);
     }
@@ -411,7 +410,7 @@ public class CheckClassAdapter extends ClassVisitor {
             | Opcodes.ACC_MANDATED
             | Opcodes.ACC_DEPRECATED);
     CheckMethodAdapter.checkUnqualifiedName(version, name, "field name");
-    CheckMethodAdapter.checkDescriptor(version, descriptor, /* canBeVoid = */ false);
+    CheckMethodAdapter.checkDescriptor(version, descriptor, /* canBeVoid= */ false);
     if (signature != null) {
       checkFieldSignature(signature);
     }
@@ -1049,7 +1048,7 @@ public class CheckClassAdapter extends ClassVisitor {
       final PrintWriter printWriter) {
     ClassNode classNode = new ClassNode();
     classReader.accept(
-        new CheckClassAdapter(/*latest*/ Opcodes.ASM10_EXPERIMENTAL, classNode, false) {},
+        new CheckClassAdapter(/*latest*/ Opcodes.ASM9, classNode, false) {},
         ClassReader.SKIP_DEBUG);
 
     Type syperType = classNode.superName == null ? null : Type.getObjectType(classNode.superName);
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckFrameAnalyzer.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckFrameAnalyzer.java
index 5f1487c79..4ea4c8754 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckFrameAnalyzer.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckFrameAnalyzer.java
@@ -29,7 +29,6 @@ package org.apache.tapestry5.internal.plastic.asm.util;
 
 import java.util.Collections;
 import java.util.List;
-
 import org.apache.tapestry5.internal.plastic.asm.Opcodes;
 import org.apache.tapestry5.internal.plastic.asm.Type;
 import org.apache.tapestry5.internal.plastic.asm.tree.AbstractInsnNode;
@@ -124,7 +123,7 @@ class CheckFrameAnalyzer<V extends Value> extends Analyzer<V> {
     }
 
     Frame<V>[] frames = getFrames();
-    Frame<V> currentFrame = frames[0];
+    Frame<V> currentFrame = newFrame(frames[0]);
     expandFrames(owner, method, currentFrame);
     for (int insnIndex = 0; insnIndex < insnList.size(); ++insnIndex) {
       Frame<V> oldFrame = frames[insnIndex];
@@ -139,7 +138,7 @@ class CheckFrameAnalyzer<V extends Value> extends Analyzer<V> {
         if (insnType == AbstractInsnNode.LABEL
             || insnType == AbstractInsnNode.LINE
             || insnType == AbstractInsnNode.FRAME) {
-          checkFrame(insnIndex + 1, oldFrame, /* requireFrame = */ false);
+          checkFrame(insnIndex + 1, oldFrame, /* requireFrame= */ false);
         } else {
           currentFrame.init(oldFrame).execute(insnNode, interpreter);
 
@@ -149,40 +148,40 @@ class CheckFrameAnalyzer<V extends Value> extends Analyzer<V> {
             }
             JumpInsnNode jumpInsn = (JumpInsnNode) insnNode;
             int targetInsnIndex = insnList.indexOf(jumpInsn.label);
-            checkFrame(targetInsnIndex, currentFrame, /* requireFrame = */ true);
+            checkFrame(targetInsnIndex, currentFrame, /* requireFrame= */ true);
             if (insnOpcode == GOTO) {
               endControlFlow(insnIndex);
             } else {
-              checkFrame(insnIndex + 1, currentFrame, /* requireFrame = */ false);
+              checkFrame(insnIndex + 1, currentFrame, /* requireFrame= */ false);
             }
           } else if (insnNode instanceof LookupSwitchInsnNode) {
             LookupSwitchInsnNode lookupSwitchInsn = (LookupSwitchInsnNode) insnNode;
             int targetInsnIndex = insnList.indexOf(lookupSwitchInsn.dflt);
-            checkFrame(targetInsnIndex, currentFrame, /* requireFrame = */ true);
+            checkFrame(targetInsnIndex, currentFrame, /* requireFrame= */ true);
             for (int i = 0; i < lookupSwitchInsn.labels.size(); ++i) {
               LabelNode label = lookupSwitchInsn.labels.get(i);
               targetInsnIndex = insnList.indexOf(label);
               currentFrame.initJumpTarget(insnOpcode, label);
-              checkFrame(targetInsnIndex, currentFrame, /* requireFrame = */ true);
+              checkFrame(targetInsnIndex, currentFrame, /* requireFrame= */ true);
             }
             endControlFlow(insnIndex);
           } else if (insnNode instanceof TableSwitchInsnNode) {
             TableSwitchInsnNode tableSwitchInsn = (TableSwitchInsnNode) insnNode;
             int targetInsnIndex = insnList.indexOf(tableSwitchInsn.dflt);
             currentFrame.initJumpTarget(insnOpcode, tableSwitchInsn.dflt);
-            checkFrame(targetInsnIndex, currentFrame, /* requireFrame = */ true);
+            checkFrame(targetInsnIndex, currentFrame, /* requireFrame= */ true);
             newControlFlowEdge(insnIndex, targetInsnIndex);
             for (int i = 0; i < tableSwitchInsn.labels.size(); ++i) {
               LabelNode label = tableSwitchInsn.labels.get(i);
               currentFrame.initJumpTarget(insnOpcode, label);
               targetInsnIndex = insnList.indexOf(label);
-              checkFrame(targetInsnIndex, currentFrame, /* requireFrame = */ true);
+              checkFrame(targetInsnIndex, currentFrame, /* requireFrame= */ true);
             }
             endControlFlow(insnIndex);
           } else if (insnOpcode == RET) {
             throw new AnalyzerException(insnNode, "RET instructions are unsupported");
           } else if (insnOpcode != ATHROW && (insnOpcode < IRETURN || insnOpcode > RETURN)) {
-            checkFrame(insnIndex + 1, currentFrame, /* requireFrame = */ false);
+            checkFrame(insnIndex + 1, currentFrame, /* requireFrame= */ false);
           } else {
             endControlFlow(insnIndex);
           }
@@ -200,7 +199,7 @@ class CheckFrameAnalyzer<V extends Value> extends Analyzer<V> {
             Frame<V> handler = newFrame(oldFrame);
             handler.clearStack();
             handler.push(interpreter.newExceptionValue(tryCatchBlock, handler, catchType));
-            checkFrame(insnList.indexOf(tryCatchBlock.handler), handler, /* requireFrame = */ true);
+            checkFrame(insnList.indexOf(tryCatchBlock.handler), handler, /* requireFrame= */ true);
           }
         }
 
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckMethodAdapter.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckMethodAdapter.java
index dcdaca70b..5fa02108b 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckMethodAdapter.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckMethodAdapter.java
@@ -35,7 +35,6 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-
 import org.apache.tapestry5.internal.plastic.asm.AnnotationVisitor;
 import org.apache.tapestry5.internal.plastic.asm.Attribute;
 import org.apache.tapestry5.internal.plastic.asm.ClassWriter;
@@ -788,7 +787,7 @@ public class CheckMethodAdapter extends MethodVisitor {
     checkVisitCodeCalled();
     checkVisitMaxsNotCalled();
     checkOpcodeMethod(opcode, Method.VISIT_JUMP_INSN);
-    checkLabel(label, /* checkVisited = */ false, "label");
+    checkLabel(label, /* checkVisited= */ false, "label");
     super.visitJumpInsn(opcode, label);
     ++insnCount;
   }
@@ -797,7 +796,7 @@ public class CheckMethodAdapter extends MethodVisitor {
   public void visitLabel(final Label label) {
     checkVisitCodeCalled();
     checkVisitMaxsNotCalled();
-    checkLabel(label, /* checkVisited = */ false, "label");
+    checkLabel(label, /* checkVisited= */ false, "label");
     if (labelInsnIndices.get(label) != null) {
       throw new IllegalStateException("Already visited label");
     }
@@ -833,12 +832,12 @@ public class CheckMethodAdapter extends MethodVisitor {
       throw new IllegalArgumentException(
           "Max = " + max + " must be greater than or equal to min = " + min);
     }
-    checkLabel(dflt, /* checkVisited = */ false, "default label");
+    checkLabel(dflt, /* checkVisited= */ false, "default label");
     if (labels == null || labels.length != max - min + 1) {
       throw new IllegalArgumentException("There must be max - min + 1 labels");
     }
     for (int i = 0; i < labels.length; ++i) {
-      checkLabel(labels[i], /* checkVisited = */ false, "label at index " + i);
+      checkLabel(labels[i], /* checkVisited= */ false, "label at index " + i);
     }
     super.visitTableSwitchInsn(min, max, dflt, labels);
     ++insnCount;
@@ -848,12 +847,17 @@ public class CheckMethodAdapter extends MethodVisitor {
   public void visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels) {
     checkVisitMaxsNotCalled();
     checkVisitCodeCalled();
-    checkLabel(dflt, /* checkVisited = */ false, "default label");
+    checkLabel(dflt, /* checkVisited= */ false, "default label");
     if (keys == null || labels == null || keys.length != labels.length) {
       throw new IllegalArgumentException("There must be the same number of keys and labels");
     }
+    for (int i = 1; i < keys.length; ++i) {
+      if (keys[i] < keys[i - 1]) {
+        throw new IllegalArgumentException("The keys must be sorted in increasing order");
+      }
+    }
     for (int i = 0; i < labels.length; ++i) {
-      checkLabel(labels[i], /* checkVisited = */ false, "label at index " + i);
+      checkLabel(labels[i], /* checkVisited= */ false, "label at index " + i);
     }
     super.visitLookupSwitchInsn(dflt, keys, labels);
     ++insnCount;
@@ -909,9 +913,9 @@ public class CheckMethodAdapter extends MethodVisitor {
       final Label start, final Label end, final Label handler, final String type) {
     checkVisitCodeCalled();
     checkVisitMaxsNotCalled();
-    checkLabel(start, /* checkVisited = */ false, START_LABEL);
-    checkLabel(end, /* checkVisited = */ false, END_LABEL);
-    checkLabel(handler, /* checkVisited = */ false, "handler label");
+    checkLabel(start, /* checkVisited= */ false, START_LABEL);
+    checkLabel(end, /* checkVisited= */ false, END_LABEL);
+    checkLabel(handler, /* checkVisited= */ false, "handler label");
     if (labelInsnIndices.get(start) != null
         || labelInsnIndices.get(end) != null
         || labelInsnIndices.get(handler) != null) {
@@ -955,8 +959,8 @@ public class CheckMethodAdapter extends MethodVisitor {
     if (signature != null) {
       CheckClassAdapter.checkFieldSignature(signature);
     }
-    checkLabel(start, /* checkVisited = */ true, START_LABEL);
-    checkLabel(end, /* checkVisited = */ true, END_LABEL);
+    checkLabel(start, /* checkVisited= */ true, START_LABEL);
+    checkLabel(end, /* checkVisited= */ true, END_LABEL);
     checkUnsignedShort(index, INVALID_LOCAL_VARIABLE_INDEX);
     int startInsnIndex = labelInsnIndices.get(start).intValue();
     int endInsnIndex = labelInsnIndices.get(end).intValue();
@@ -993,8 +997,8 @@ public class CheckMethodAdapter extends MethodVisitor {
           "Invalid start, end and index arrays (must be non null and of identical length");
     }
     for (int i = 0; i < start.length; ++i) {
-      checkLabel(start[i], /* checkVisited = */ true, START_LABEL);
-      checkLabel(end[i], /* checkVisited = */ true, END_LABEL);
+      checkLabel(start[i], /* checkVisited= */ true, START_LABEL);
+      checkLabel(end[i], /* checkVisited= */ true, END_LABEL);
       checkUnsignedShort(index[i], INVALID_LOCAL_VARIABLE_INDEX);
       int startInsnIndex = labelInsnIndices.get(start[i]).intValue();
       int endInsnIndex = labelInsnIndices.get(end[i]).intValue();
@@ -1012,7 +1016,7 @@ public class CheckMethodAdapter extends MethodVisitor {
     checkVisitCodeCalled();
     checkVisitMaxsNotCalled();
     checkUnsignedShort(line, "Invalid line number");
-    checkLabel(start, /* checkVisited = */ true, START_LABEL);
+    checkLabel(start, /* checkVisited= */ true, START_LABEL);
     super.visitLineNumber(line, start);
   }
 
@@ -1089,7 +1093,7 @@ public class CheckMethodAdapter extends MethodVisitor {
     if (value instanceof String) {
       checkInternalName(version, (String) value, "Invalid stack frame value");
     } else if (value instanceof Label) {
-      checkLabel((Label) value, /* checkVisited = */ false, "label");
+      checkLabel((Label) value, /* checkVisited= */ false, "label");
     } else {
       throw new IllegalArgumentException("Invalid stack frame value: " + value);
     }
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckModuleAdapter.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckModuleAdapter.java
index d5ec707b2..379bd7b26 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckModuleAdapter.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckModuleAdapter.java
@@ -28,7 +28,6 @@
 package org.apache.tapestry5.internal.plastic.asm.util;
 
 import java.util.HashSet;
-
 import org.apache.tapestry5.internal.plastic.asm.ModuleVisitor;
 import org.apache.tapestry5.internal.plastic.asm.Opcodes;
 
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckSignatureAdapter.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckSignatureAdapter.java
index 56b43c194..b251a0db3 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckSignatureAdapter.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckSignatureAdapter.java
@@ -28,7 +28,6 @@
 package org.apache.tapestry5.internal.plastic.asm.util;
 
 import java.util.EnumSet;
-
 import org.apache.tapestry5.internal.plastic.asm.Opcodes;
 import org.apache.tapestry5.internal.plastic.asm.signature.SignatureVisitor;
 
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/Printer.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/Printer.java
index 7f2085a7b..e6fe7f0ab 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/Printer.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/Printer.java
@@ -33,7 +33,6 @@ import java.io.InputStream;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
-
 import org.apache.tapestry5.internal.plastic.asm.Attribute;
 import org.apache.tapestry5.internal.plastic.asm.ClassReader;
 import org.apache.tapestry5.internal.plastic.asm.ConstantDynamic;
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/Textifier.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/Textifier.java
index ff185ad27..b592901cd 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/Textifier.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/Textifier.java
@@ -34,8 +34,8 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-
 import org.apache.tapestry5.internal.plastic.asm.Attribute;
+import org.apache.tapestry5.internal.plastic.asm.ConstantDynamic;
 import org.apache.tapestry5.internal.plastic.asm.Handle;
 import org.apache.tapestry5.internal.plastic.asm.Label;
 import org.apache.tapestry5.internal.plastic.asm.Opcodes;
@@ -322,7 +322,7 @@ public class Textifier extends Printer {
       final String name, final String outerName, final String innerName, final int access) {
     stringBuilder.setLength(0);
     stringBuilder.append(tab);
-    appendRawAccess(access & ~Opcodes.ACC_SUPER);
+    appendRawAccess(access);
     stringBuilder.append(tab);
     appendAccess(access);
     stringBuilder.append("INNERCLASS ");
@@ -385,11 +385,7 @@ public class Textifier extends Printer {
     stringBuilder.append(' ').append(name);
     if (value != null) {
       stringBuilder.append(" = ");
-      if (value instanceof String) {
-        stringBuilder.append('\"').append(value).append('\"');
-      } else {
-        stringBuilder.append(value);
-      }
+      appendConstant(value);
     }
 
     stringBuilder.append('\n');
@@ -438,10 +434,10 @@ public class Textifier extends Printer {
     stringBuilder.append(name);
     appendDescriptor(METHOD_DESCRIPTOR, descriptor);
     if (exceptions != null && exceptions.length > 0) {
-      stringBuilder.append(" throws ");
+      stringBuilder.append(" throws");
       for (String exception : exceptions) {
-        appendDescriptor(INTERNAL_NAME, exception);
         stringBuilder.append(' ');
+        appendDescriptor(INTERNAL_NAME, exception);
       }
     }
 
@@ -949,33 +945,9 @@ public class Textifier extends Printer {
     stringBuilder.append(" [");
     stringBuilder.append('\n');
     stringBuilder.append(tab3);
-    appendHandle(bootstrapMethodHandle);
-    stringBuilder.append('\n');
-    stringBuilder.append(tab3).append("// arguments:");
-    if (bootstrapMethodArguments.length == 0) {
-      stringBuilder.append(" none");
-    } else {
-      stringBuilder.append('\n');
-      for (Object value : bootstrapMethodArguments) {
-        stringBuilder.append(tab3);
-        if (value instanceof String) {
-          Printer.appendString(stringBuilder, (String) value);
-        } else if (value instanceof Type) {
-          Type type = (Type) value;
-          if (type.getSort() == Type.METHOD) {
-            appendDescriptor(METHOD_DESCRIPTOR, type.getDescriptor());
-          } else {
-            visitType(type);
-          }
-        } else if (value instanceof Handle) {
-          appendHandle((Handle) value);
-        } else {
-          stringBuilder.append(value);
-        }
-        stringBuilder.append(", \n");
-      }
-      stringBuilder.setLength(stringBuilder.length() - 3);
-    }
+    appendHandle(bootstrapMethodHandle, tab3);
+    stringBuilder.append('\n').append(tab3);
+    appendBoostrapMethodArgs(bootstrapMethodArguments, tab3);
     stringBuilder.append('\n');
     stringBuilder.append(tab2).append("]\n");
     text.add(stringBuilder.toString());
@@ -1002,13 +974,15 @@ public class Textifier extends Printer {
   @Override
   public void visitLdcInsn(final Object value) {
     stringBuilder.setLength(0);
-    stringBuilder.append(tab2).append("LDC ");
-    if (value instanceof String) {
-      Printer.appendString(stringBuilder, (String) value);
-    } else if (value instanceof Type) {
-      stringBuilder.append(((Type) value).getDescriptor()).append(CLASS_SUFFIX);
+    if (value instanceof ConstantDynamic) {
+      stringBuilder.append(tab2).append("LDC ");
+      appendConstantDynamic((ConstantDynamic) value, tab2);
+    } else if (value instanceof Handle) {
+      stringBuilder.append(tab2);
+      appendHandle((Handle) value, tab2 + "LDC ");
     } else {
-      stringBuilder.append(value);
+      stringBuilder.append(tab2).append("LDC ");
+      appendConstant(value);
     }
     stringBuilder.append('\n');
     text.add(stringBuilder.toString());
@@ -1308,6 +1282,96 @@ public class Textifier extends Printer {
     }
   }
 
+  /**
+   * Appends a constant value. This method can be used for {@link Integer}, {@link Float}, {@link
+   * Long}, {@link Double}, {@link Boolean}, {@link String}, and {@link Type}. Attempting to use any
+   * other type will result in its {@link Object#toString()} representation.
+   *
+   * @param constant the constant to be appended.
+   */
+  private void appendConstant(final Object constant) {
+    if (constant instanceof Number) {
+      if (constant instanceof Double) {
+        stringBuilder.append(constant).append('D');
+      } else if (constant instanceof Float) {
+        stringBuilder.append(constant).append('F');
+      } else if (constant instanceof Long) {
+        stringBuilder.append(constant).append('L');
+      } else {
+        // Integer (or other "unsupported" Number subclass)
+        stringBuilder.append(constant);
+      }
+    } else {
+      if (constant instanceof Type) {
+        stringBuilder.append(((Type) constant).getDescriptor()).append(CLASS_SUFFIX);
+      } else if (constant instanceof String) {
+        Printer.appendString(stringBuilder, constant.toString());
+      } else {
+        // Boolean or other "unsupported" constant
+        stringBuilder.append(constant);
+      }
+    }
+  }
+
+  /**
+   * Append the contents of a {@link ConstantDynamic}.
+   *
+   * @param condy the constant dynamic to append
+   * @param condyIndent the indent to use for newlines.
+   */
+  private void appendConstantDynamic(final ConstantDynamic condy, final String condyIndent) {
+    stringBuilder
+        .append(condy.getName())
+        .append(" : ")
+        .append(condy.getDescriptor())
+        .append(" [\n");
+    stringBuilder.append(condyIndent).append(tab);
+    appendHandle(condy.getBootstrapMethod(), condyIndent + tab);
+    stringBuilder.append('\n').append(condyIndent).append(tab);
+    Object[] bsmArgs = new Object[condy.getBootstrapMethodArgumentCount()];
+    for (int i = 0; i < bsmArgs.length; i++) {
+      bsmArgs[i] = condy.getBootstrapMethodArgument(i);
+    }
+    appendBoostrapMethodArgs(bsmArgs, condyIndent + tab);
+    stringBuilder.append('\n').append(condyIndent).append(']');
+  }
+
+  /**
+   * Appends bootstrap method args for {@link ConstantDynamic} and {@link #visitInvokeDynamicInsn}.
+   *
+   * @param bsmArgs the bootstrap method arguments.
+   * @param argIndent the indent to use for newlines.
+   */
+  private void appendBoostrapMethodArgs(final Object[] bsmArgs, final String argIndent) {
+    stringBuilder.append("// arguments:");
+    if (bsmArgs.length == 0) {
+      stringBuilder.append(" none");
+    } else {
+      for (int i = 0; i < bsmArgs.length; i++) {
+        Object arg = bsmArgs[i];
+        if (i != 0) {
+          stringBuilder.append(", ");
+        }
+        stringBuilder.append('\n').append(argIndent);
+        if (arg instanceof Type) {
+          Type type = (Type) arg;
+          if (type.getSort() == Type.METHOD) {
+            appendDescriptor(METHOD_DESCRIPTOR, type.getDescriptor());
+          } else {
+            visitType(type);
+          }
+        } else if (arg instanceof Handle) {
+          appendHandle((Handle) arg, argIndent);
+        } else if (arg instanceof ConstantDynamic) {
+          stringBuilder.append("// constant dynamic: ").append('\n').append(argIndent);
+          appendConstantDynamic((ConstantDynamic) arg, argIndent);
+        } else {
+          appendConstant(arg);
+        }
+      }
+    }
+  }
+
   /**
    * Appends the hexadecimal value of the given access flags to {@link #stringBuilder}.
    *
@@ -1379,12 +1443,18 @@ public class Textifier extends Printer {
     stringBuilder.append(name);
   }
 
+  @Deprecated
+  protected void appendHandle(final Handle handle) {
+    appendHandle(handle, tab3);
+  }
+
   /**
    * Appends a string representation of the given handle to {@link #stringBuilder}.
    *
    * @param handle a handle.
+   * @param afterComment this is the prefix of the line after the handle kind.
    */
-  protected void appendHandle(final Handle handle) {
+  protected void appendHandle(final Handle handle, final String afterComment) {
     int tag = handle.getTag();
     stringBuilder.append("// handle kind 0x").append(Integer.toHexString(tag)).append(" : ");
     boolean isMethodHandle = false;
@@ -1425,7 +1495,7 @@ public class Textifier extends Printer {
         throw new IllegalArgumentException();
     }
     stringBuilder.append('\n');
-    stringBuilder.append(tab3);
+    stringBuilder.append(afterComment);
     appendDescriptor(INTERNAL_NAME, handle.getOwner());
     stringBuilder.append('.');
     stringBuilder.append(handle.getName());
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TextifierSupport.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TextifierSupport.java
index f3ba1154d..457c93565 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TextifierSupport.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TextifierSupport.java
@@ -22,7 +22,6 @@
 package org.apache.tapestry5.internal.plastic.asm.util;
 
 import java.util.Map;
-
 import org.apache.tapestry5.internal.plastic.asm.Label;
 
 /**
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceClassVisitor.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceClassVisitor.java
index cdad62ee8..963709746 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceClassVisitor.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceClassVisitor.java
@@ -28,7 +28,6 @@
 package org.apache.tapestry5.internal.plastic.asm.util;
 
 import java.io.PrintWriter;
-
 import org.apache.tapestry5.internal.plastic.asm.AnnotationVisitor;
 import org.apache.tapestry5.internal.plastic.asm.Attribute;
 import org.apache.tapestry5.internal.plastic.asm.ClassVisitor;
@@ -120,7 +119,7 @@ public final class TraceClassVisitor extends ClassVisitor {
    */
   public TraceClassVisitor(
       final ClassVisitor classVisitor, final Printer printer, final PrintWriter printWriter) {
-    super(/* latest api = */ Opcodes.ASM10_EXPERIMENTAL, classVisitor);
+    super(/* latest api = */ Opcodes.ASM9, classVisitor);
     this.printWriter = printWriter;
     this.p = printer;
   }
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceSignatureVisitor.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceSignatureVisitor.java
index d227bfea0..94b7153fc 100644
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceSignatureVisitor.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceSignatureVisitor.java
@@ -30,7 +30,6 @@ package org.apache.tapestry5.internal.plastic.asm.util;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
-
 import org.apache.tapestry5.internal.plastic.asm.Opcodes;
 import org.apache.tapestry5.internal.plastic.asm.signature.SignatureVisitor;
 
diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/GeneralIntegrationSpec.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/GeneralIntegrationSpec.groovy
index ea3a16a23..611366e7c 100644
--- a/tapestry-ioc/src/test/groovy/ioc/specs/GeneralIntegrationSpec.groovy
+++ b/tapestry-ioc/src/test/groovy/ioc/specs/GeneralIntegrationSpec.groovy
@@ -53,13 +53,10 @@ class GeneralIntegrationSpec extends AbstractSharedRegistrySpecification {
   
   // TAP5-2667
   def "Default methods in a service should be skipped while introducing methods and advising methods"() {
-      when:
       Registry registry = RegistryBuilder.buildAndStartupRegistry(TestModule.class);
       InterfaceWithDefaultMethod service = registry.getService(InterfaceWithDefaultMethod.class)
-      then:
-      service.staticMethod() == InterfaceWithDefaultMethod.STATIC_METHOD_RETURN_VALUE;
+      service.method(); // blows up without fix.
   }
-
   
   public static class DefaultMethodServiceImpl implements DefaultMethodService 
   {