TAP5-2624: upgrade embedded ASM to 8.0.1
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/AnnotationVisitor.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/AnnotationVisitor.java
old mode 100755
new mode 100644
index a0c6726..703faf5
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/AnnotationVisitor.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/AnnotationVisitor.java
@@ -43,7 +43,10 @@
    */
   protected final int api;
 
-  /** The annotation visitor to which this visitor must delegate method calls. May be null. */
+  /**
+   * The annotation visitor to which this visitor must delegate method calls. May be {@literal
+   * null}.
+   */
   protected AnnotationVisitor av;
 
   /**
@@ -62,11 +65,19 @@
    * @param api the ASM API version implemented by this visitor. Must be one of {@link
    *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
    * @param annotationVisitor the annotation visitor to which this visitor must delegate method
-   *     calls. May be null.
+   *     calls. May be {@literal null}.
    */
   public AnnotationVisitor(final int api, final AnnotationVisitor annotationVisitor) {
-    if (api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4 && api != Opcodes.ASM7) {
-      throw new IllegalArgumentException();
+    if (api != Opcodes.ASM8
+        && api != Opcodes.ASM7
+        && api != Opcodes.ASM6
+        && api != Opcodes.ASM5
+        && api != Opcodes.ASM4
+        && api != Opcodes.ASM9_EXPERIMENTAL) {
+      throw new IllegalArgumentException("Unsupported api " + api);
+    }
+    if (api == Opcodes.ASM9_EXPERIMENTAL) {
+      Constants.checkAsmExperimental(this);
     }
     this.api = api;
     this.av = annotationVisitor;
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
old mode 100755
new mode 100644
index c55f36a..68d97c6
--- 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
@@ -91,7 +91,7 @@
   private AnnotationWriter nextAnnotation;
 
   // -----------------------------------------------------------------------------------------------
-  // Constructors
+  // Constructors and factories
   // -----------------------------------------------------------------------------------------------
 
   /**
@@ -104,15 +104,15 @@
    *     the visited content must be stored. This ByteVector must already contain all the fields of
    *     the structure except the last one (the element_value_pairs array).
    * @param previousAnnotation the previously visited annotation of the
-   *     Runtime[In]Visible[Type]Annotations attribute to which this annotation belongs, or null in
-   *     other cases (e.g. nested or array annotations).
+   *     Runtime[In]Visible[Type]Annotations attribute to which this annotation belongs, or
+   *     {@literal null} in other cases (e.g. nested or array annotations).
    */
   AnnotationWriter(
       final SymbolTable symbolTable,
       final boolean useNamedValues,
       final ByteVector annotation,
       final AnnotationWriter previousAnnotation) {
-    super(Opcodes.ASM7);
+    super(/* latest api = */ Opcodes.ASM8);
     this.symbolTable = symbolTable;
     this.useNamedValues = useNamedValues;
     this.annotation = annotation;
@@ -125,21 +125,61 @@
   }
 
   /**
-   * Constructs a new {@link AnnotationWriter} using named values.
+   * Creates a new {@link AnnotationWriter} using named values.
    *
    * @param symbolTable where the constants used in this AnnotationWriter must be stored.
-   * @param annotation where the 'annotation' or 'type_annotation' JVMS structure corresponding to
-   *     the visited content must be stored. This ByteVector must already contain all the fields of
-   *     the structure except the last one (the element_value_pairs array).
+   * @param descriptor the class descriptor of the annotation class.
    * @param previousAnnotation the previously visited annotation of the
-   *     Runtime[In]Visible[Type]Annotations attribute to which this annotation belongs, or null in
-   *     other cases (e.g. nested or array annotations).
+   *     Runtime[In]Visible[Type]Annotations attribute to which this annotation belongs, or
+   *     {@literal null} in other cases (e.g. nested or array annotations).
+   * @return a new {@link AnnotationWriter} for the given annotation descriptor.
    */
-  AnnotationWriter(
+  static AnnotationWriter create(
       final SymbolTable symbolTable,
-      final ByteVector annotation,
+      final String descriptor,
       final AnnotationWriter previousAnnotation) {
-    this(symbolTable, /* useNamedValues = */ true, annotation, previousAnnotation);
+    // Create a ByteVector to hold an 'annotation' JVMS structure.
+    // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.
+    ByteVector annotation = new ByteVector();
+    // 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);
+  }
+
+  /**
+   * Creates a new {@link AnnotationWriter} using named values.
+   *
+   * @param symbolTable where the constants used in this AnnotationWriter must be stored.
+   * @param typeRef a reference to the annotated type. The sort of this type reference must be
+   *     {@link TypeReference#CLASS_TYPE_PARAMETER}, {@link
+   *     TypeReference#CLASS_TYPE_PARAMETER_BOUND} or {@link TypeReference#CLASS_EXTENDS}. See
+   *     {@link TypeReference}.
+   * @param typePath the path to the annotated type argument, wildcard bound, array element type, or
+   *     static inner type within 'typeRef'. May be {@literal null} if the annotation targets
+   *     'typeRef' as a whole.
+   * @param descriptor the class descriptor of the annotation class.
+   * @param previousAnnotation the previously visited annotation of the
+   *     Runtime[In]Visible[Type]Annotations attribute to which this annotation belongs, or
+   *     {@literal null} in other cases (e.g. nested or array annotations).
+   * @return a new {@link AnnotationWriter} for the given type annotation reference and descriptor.
+   */
+  static AnnotationWriter create(
+      final SymbolTable symbolTable,
+      final int typeRef,
+      final TypePath typePath,
+      final String descriptor,
+      final AnnotationWriter previousAnnotation) {
+    // Create a ByteVector to hold a 'type_annotation' JVMS structure.
+    // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20.
+    ByteVector typeAnnotation = new ByteVector();
+    // Write target_type, target_info, and target_path.
+    TypeReference.putTarget(typeRef, typeAnnotation);
+    TypePath.put(typePath, typeAnnotation);
+    // 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);
   }
 
   // -----------------------------------------------------------------------------------------------
@@ -244,7 +284,7 @@
     }
     // 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, annotation, null);
+    return new AnnotationWriter(symbolTable, /* useNamedValues = */ true, annotation, null);
   }
 
   @Override
@@ -284,7 +324,7 @@
    * and all its <i>predecessors</i> (see {@link #previousAnnotation}. Also adds the attribute name
    * to the constant pool of the class (if not null).
    *
-   * @param attributeName one of "Runtime[In]Visible[Type]Annotations", or null.
+   * @param attributeName one of "Runtime[In]Visible[Type]Annotations", or {@literal null}.
    * @return the size in bytes of a Runtime[In]Visible[Type]Annotations attribute containing this
    *     annotation and all its predecessors. This includes the size of the attribute_name_index and
    *     attribute_length fields.
@@ -304,6 +344,56 @@
   }
 
   /**
+   * Returns the size of the Runtime[In]Visible[Type]Annotations attributes containing the given
+   * annotations and all their <i>predecessors</i> (see {@link #previousAnnotation}. Also adds the
+   * attribute names to the constant pool of the class (if not null).
+   *
+   * @param lastRuntimeVisibleAnnotation The last runtime visible annotation of a field, method or
+   *     class. The previous ones can be accessed with the {@link #previousAnnotation} field. May be
+   *     {@literal null}.
+   * @param lastRuntimeInvisibleAnnotation The last runtime invisible annotation of this a field,
+   *     method or class. The previous ones can be accessed with the {@link #previousAnnotation}
+   *     field. May be {@literal null}.
+   * @param lastRuntimeVisibleTypeAnnotation The last runtime visible type annotation of this a
+   *     field, method or class. The previous ones can be accessed with the {@link
+   *     #previousAnnotation} field. May be {@literal null}.
+   * @param lastRuntimeInvisibleTypeAnnotation The last runtime invisible type annotation of a
+   *     field, method or class field. The previous ones can be accessed with the {@link
+   *     #previousAnnotation} field. May be {@literal null}.
+   * @return the size in bytes of a Runtime[In]Visible[Type]Annotations attribute containing the
+   *     given annotations and all their predecessors. This includes the size of the
+   *     attribute_name_index and attribute_length fields.
+   */
+  static int computeAnnotationsSize(
+      final AnnotationWriter lastRuntimeVisibleAnnotation,
+      final AnnotationWriter lastRuntimeInvisibleAnnotation,
+      final AnnotationWriter lastRuntimeVisibleTypeAnnotation,
+      final AnnotationWriter lastRuntimeInvisibleTypeAnnotation) {
+    int size = 0;
+    if (lastRuntimeVisibleAnnotation != null) {
+      size +=
+          lastRuntimeVisibleAnnotation.computeAnnotationsSize(
+              Constants.RUNTIME_VISIBLE_ANNOTATIONS);
+    }
+    if (lastRuntimeInvisibleAnnotation != null) {
+      size +=
+          lastRuntimeInvisibleAnnotation.computeAnnotationsSize(
+              Constants.RUNTIME_INVISIBLE_ANNOTATIONS);
+    }
+    if (lastRuntimeVisibleTypeAnnotation != null) {
+      size +=
+          lastRuntimeVisibleTypeAnnotation.computeAnnotationsSize(
+              Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS);
+    }
+    if (lastRuntimeInvisibleTypeAnnotation != null) {
+      size +=
+          lastRuntimeInvisibleTypeAnnotation.computeAnnotationsSize(
+              Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS);
+    }
+    return size;
+  }
+
+  /**
    * Puts a Runtime[In]Visible[Type]Annotations attribute containing this annotations and all its
    * <i>predecessors</i> (see {@link #previousAnnotation} in the given ByteVector. Annotations are
    * put in the same order they have been visited.
@@ -336,6 +426,51 @@
   }
 
   /**
+   * Puts the Runtime[In]Visible[Type]Annotations attributes containing the given annotations and
+   * all their <i>predecessors</i> (see {@link #previousAnnotation} in the given ByteVector.
+   * Annotations are put in the same order they have been visited.
+   *
+   * @param symbolTable where the constants used in the AnnotationWriter instances are stored.
+   * @param lastRuntimeVisibleAnnotation The last runtime visible annotation of a field, method or
+   *     class. The previous ones can be accessed with the {@link #previousAnnotation} field. May be
+   *     {@literal null}.
+   * @param lastRuntimeInvisibleAnnotation The last runtime invisible annotation of this a field,
+   *     method or class. The previous ones can be accessed with the {@link #previousAnnotation}
+   *     field. May be {@literal null}.
+   * @param lastRuntimeVisibleTypeAnnotation The last runtime visible type annotation of this a
+   *     field, method or class. The previous ones can be accessed with the {@link
+   *     #previousAnnotation} field. May be {@literal null}.
+   * @param lastRuntimeInvisibleTypeAnnotation The last runtime invisible type annotation of a
+   *     field, method or class field. The previous ones can be accessed with the {@link
+   *     #previousAnnotation} field. May be {@literal null}.
+   * @param output where the attributes must be put.
+   */
+  static void putAnnotations(
+      final SymbolTable symbolTable,
+      final AnnotationWriter lastRuntimeVisibleAnnotation,
+      final AnnotationWriter lastRuntimeInvisibleAnnotation,
+      final AnnotationWriter lastRuntimeVisibleTypeAnnotation,
+      final AnnotationWriter lastRuntimeInvisibleTypeAnnotation,
+      final ByteVector output) {
+    if (lastRuntimeVisibleAnnotation != null) {
+      lastRuntimeVisibleAnnotation.putAnnotations(
+          symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_ANNOTATIONS), output);
+    }
+    if (lastRuntimeInvisibleAnnotation != null) {
+      lastRuntimeInvisibleAnnotation.putAnnotations(
+          symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_ANNOTATIONS), output);
+    }
+    if (lastRuntimeVisibleTypeAnnotation != null) {
+      lastRuntimeVisibleTypeAnnotation.putAnnotations(
+          symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS), output);
+    }
+    if (lastRuntimeInvisibleTypeAnnotation != null) {
+      lastRuntimeInvisibleTypeAnnotation.putAnnotations(
+          symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS), output);
+    }
+  }
+
+  /**
    * Returns the size of a Runtime[In]VisibleParameterAnnotations attribute containing all the
    * annotation lists from the given AnnotationWriter sub-array. Also adds the attribute name to the
    * constant pool of the class.
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
old mode 100755
new mode 100644
index c062ade..1ed250c
--- 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
@@ -28,7 +28,7 @@
 package org.apache.tapestry5.internal.plastic.asm;
 
 /**
- * A non standard class, field, method or code attribute, as defined in the Java Virtual Machine
+ * A non standard class, field, method or Code attribute, as defined in the Java Virtual Machine
  * Specification (JVMS).
  *
  * @see <a href= "https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7">JVMS
@@ -52,7 +52,7 @@
 
   /**
    * The next attribute in this attribute list (Attribute instances can be linked via this field to
-   * store a list of class, field, method or code attributes). May be {@literal null}.
+   * store a list of class, field, method or Code attributes). May be {@literal null}.
    */
   Attribute nextAttribute;
 
@@ -80,9 +80,9 @@
   }
 
   /**
-   * Returns {@literal true} if this type of attribute is a code attribute.
+   * Returns {@literal true} if this type of attribute is a Code attribute.
    *
-   * @return {@literal true} if this type of attribute is a code attribute.
+   * @return {@literal true} if this type of attribute is a Code attribute.
    */
   public boolean isCodeAttribute() {
     return false;
@@ -92,7 +92,7 @@
    * Returns the labels corresponding to this attribute.
    *
    * @return the labels corresponding to this attribute, or {@literal null} if this attribute is not
-   *     a code attribute that contains labels.
+   *     a Code attribute that contains labels.
    */
   protected Label[] getLabels() {
     return new Label[0];
@@ -104,18 +104,18 @@
    * ClassReader.
    *
    * @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#b}. The
-   *     6 attribute header bytes (attribute_name_index and attribute_length) are not taken into
+   * @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#b}, or -1 if the attribute to be read is not a code attribute. The 6
+   *     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.
+   *     is not a Code attribute.
    * @return a <i>new</i> {@link Attribute} object corresponding to the specified bytes.
    */
   protected Attribute read(
@@ -127,7 +127,7 @@
       final Label[] labels) {
     Attribute attribute = new Attribute(type);
     attribute.content = new byte[length];
-    System.arraycopy(classReader.b, offset, attribute.content, 0, length);
+    System.arraycopy(classReader.classFileBuffer, offset, attribute.content, 0, length);
     return attribute;
   }
 
@@ -138,16 +138,16 @@
    *
    * @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
+   * @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'
+   *     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 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.
+   *     attribute, or -1 if this attribute is not a Code attribute.
    * @return the byte array form of this attribute.
    */
   protected ByteVector write(
@@ -197,16 +197,16 @@
    * attribute_length) per attribute. Also adds the attribute type names to the constant pool.
    *
    * @param symbolTable where the constants used in the attributes must be stored.
-   * @param code the bytecode of the method corresponding to these code attributes, or {@literal
-   *     null} if they are not code attributes. Corresponds to the 'code' field of the Code
+   * @param code the bytecode of the method corresponding to these Code attributes, or {@literal
+   *     null} if they are not Code attributes. Corresponds to the 'code' field of the Code
    *     attribute.
    * @param codeLength the length of the bytecode of the method corresponding to these code
-   *     attributes, or 0 if they are not code attributes. Corresponds to the 'code_length' field of
+   *     attributes, or 0 if they are not Code attributes. Corresponds to the 'code_length' field of
    *     the Code attribute.
-   * @param maxStack the maximum stack size of the method corresponding to these code attributes, or
-   *     -1 if they are not code attributes.
+   * @param maxStack the maximum stack size of the method corresponding to these Code attributes, or
+   *     -1 if they are not Code attributes.
    * @param maxLocals the maximum number of local variables of the method corresponding to these
-   *     code attributes, or -1 if they are not code attribute.
+   *     Code attributes, or -1 if they are not Code attribute.
    * @return the size of all the attributes in this attribute list. This size includes the size of
    *     the attribute headers.
    */
@@ -228,6 +228,42 @@
   }
 
   /**
+   * Returns the total size in bytes of all the attributes that correspond to the given field,
+   * method or class access flags and signature. This size includes the 6 header bytes
+   * (attribute_name_index and attribute_length) per attribute. Also adds the attribute type names
+   * to the constant pool.
+   *
+   * @param symbolTable where the constants used in the attributes must be stored.
+   * @param accessFlags some field, method or class access flags.
+   * @param signatureIndex the constant pool index of a field, method of class signature.
+   * @return the size of all the attributes in bytes. This size includes the size of the attribute
+   *     headers.
+   */
+  static int computeAttributesSize(
+      final SymbolTable symbolTable, final int accessFlags, final int signatureIndex) {
+    int size = 0;
+    // Before Java 1.5, synthetic fields are represented with a Synthetic attribute.
+    if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0
+        && symbolTable.getMajorVersion() < Opcodes.V1_5) {
+      // Synthetic attributes always use 6 bytes.
+      symbolTable.addConstantUtf8(Constants.SYNTHETIC);
+      size += 6;
+    }
+    if (signatureIndex != 0) {
+      // Signature attributes always use 8 bytes.
+      symbolTable.addConstantUtf8(Constants.SIGNATURE);
+      size += 8;
+    }
+    // ACC_DEPRECATED is ASM specific, the ClassFile format uses a Deprecated attribute instead.
+    if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) {
+      // Deprecated attributes always use 6 bytes.
+      symbolTable.addConstantUtf8(Constants.DEPRECATED);
+      size += 6;
+    }
+    return size;
+  }
+
+  /**
    * Puts all the attributes of the attribute list that begins with this attribute, in the given
    * byte vector. This includes the 6 header bytes (attribute_name_index and attribute_length) per
    * attribute.
@@ -249,16 +285,16 @@
    * attribute.
    *
    * @param symbolTable where the constants used in the attributes must be stored.
-   * @param code the bytecode of the method corresponding to these code attributes, or {@literal
-   *     null} if they are not code attributes. Corresponds to the 'code' field of the Code
+   * @param code the bytecode of the method corresponding to these Code attributes, or {@literal
+   *     null} if they are not Code attributes. Corresponds to the 'code' field of the Code
    *     attribute.
    * @param codeLength the length of the bytecode of the method corresponding to these code
-   *     attributes, or 0 if they are not code attributes. Corresponds to the 'code_length' field of
+   *     attributes, or 0 if they are not Code attributes. Corresponds to the 'code_length' field of
    *     the Code attribute.
-   * @param maxStack the maximum stack size of the method corresponding to these code attributes, or
-   *     -1 if they are not code attributes.
+   * @param maxStack the maximum stack size of the method corresponding to these Code attributes, or
+   *     -1 if they are not Code attributes.
    * @param maxLocals the maximum number of local variables of the method corresponding to these
-   *     code attributes, or -1 if they are not code attribute.
+   *     Code attributes, or -1 if they are not Code attribute.
    * @param output where the attributes must be written.
    */
   final void putAttributes(
@@ -280,6 +316,37 @@
     }
   }
 
+  /**
+   * Puts all the attributes that correspond to the given field, method or class access flags and
+   * signature, in the given byte vector. This includes the 6 header bytes (attribute_name_index and
+   * attribute_length) per attribute.
+   *
+   * @param symbolTable where the constants used in the attributes must be stored.
+   * @param accessFlags some field, method or class access flags.
+   * @param signatureIndex the constant pool index of a field, method of class signature.
+   * @param output where the attributes must be written.
+   */
+  static void putAttributes(
+      final SymbolTable symbolTable,
+      final int accessFlags,
+      final int signatureIndex,
+      final ByteVector output) {
+    // Before Java 1.5, synthetic fields are represented with a Synthetic attribute.
+    if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0
+        && symbolTable.getMajorVersion() < Opcodes.V1_5) {
+      output.putShort(symbolTable.addConstantUtf8(Constants.SYNTHETIC)).putInt(0);
+    }
+    if (signatureIndex != 0) {
+      output
+          .putShort(symbolTable.addConstantUtf8(Constants.SIGNATURE))
+          .putInt(2)
+          .putShort(signatureIndex);
+    }
+    if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) {
+      output.putShort(symbolTable.addConstantUtf8(Constants.DEPRECATED)).putInt(0);
+    }
+  }
+
   /** A set of attribute prototypes (attributes with the same type are considered equal). */
   static final class Set {
 
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/ByteVector.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/ByteVector.java
old mode 100755
new mode 100644
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
old mode 100755
new mode 100644
index 41b6de2..0a940f0
--- 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
@@ -50,10 +50,11 @@
   public static final int SKIP_CODE = 1;
 
   /**
-   * A flag to skip the SourceFile, SourceDebugExtension, LocalVariableTable, LocalVariableTypeTable
-   * and LineNumberTable attributes. If this flag is set these attributes are neither parsed nor
-   * visited (i.e. {@link ClassVisitor#visitSource}, {@link MethodVisitor#visitLocalVariable} and
-   * {@link MethodVisitor#visitLineNumber} are not called).
+   * A flag to skip the SourceFile, SourceDebugExtension, LocalVariableTable,
+   * LocalVariableTypeTable, LineNumberTable and MethodParameters attributes. If this flag is set
+   * these attributes are neither parsed nor visited (i.e. {@link ClassVisitor#visitSource}, {@link
+   * MethodVisitor#visitLocalVariable}, {@link MethodVisitor#visitLineNumber} and {@link
+   * MethodVisitor#visitParameter} are not called).
    */
   public static final int SKIP_DEBUG = 2;
 
@@ -91,6 +92,16 @@
   private static final int INPUT_STREAM_DATA_CHUNK_SIZE = 4096;
 
   /**
+   * A byte array containing the JVMS ClassFile structure to be parsed.
+   *
+   * @deprecated Use {@link #readByte(int)} and the other read methods instead. This field will
+   *     eventually be deleted.
+   */
+  @Deprecated
+  // DontCheck(MemberName): can't be renamed (for backward binary compatibility).
+  public final byte[] b;
+
+  /**
    * A byte array containing the JVMS ClassFile structure to be parsed. <i>The content of this array
    * must not be modified. This field is intended for {@link Attribute} sub classes, and is normally
    * not needed by class visitors.</i>
@@ -99,13 +110,13 @@
    * necessarily start at offset 0. Use {@link #getItem} and {@link #header} to get correct
    * ClassFile element offsets within this byte array.
    */
-  // DontCheck(MemberName): can't be renamed (for backward binary compatibility).
-  public final byte[] b;
+  final byte[] classFileBuffer;
 
   /**
-   * The offset in bytes, in {@link #b}, of each cp_info entry of the ClassFile's constant_pool
-   * array, <i>plus one</i>. In other words, the offset of constant pool entry i is given by
-   * cpInfoOffsets[i] - 1, i.e. its cp_info's tag field is given by b[cpInfoOffsets[i] - 1].
+   * The offset in bytes, in {@link #classFileBuffer}, of each cp_info entry of the ClassFile's
+   * constant_pool array, <i>plus one</i>. In other words, the offset of constant pool entry i is
+   * given by cpInfoOffsets[i] - 1, i.e. its cp_info's tag field is given by b[cpInfoOffsets[i] -
+   * 1].
    */
   private final int[] cpInfoOffsets;
 
@@ -122,8 +133,8 @@
   private final ConstantDynamic[] constantDynamicValues;
 
   /**
-   * The start offsets in {@link #b} of each element of the bootstrap_methods array (in the
-   * BootstrapMethods attribute).
+   * The start offsets in {@link #classFileBuffer} of each element of the bootstrap_methods array
+   * (in the BootstrapMethods attribute).
    *
    * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.23">JVMS
    *     4.7.23</a>
@@ -136,7 +147,7 @@
    */
   private final int maxStringLength;
 
-  /** The offset in bytes, in {@link #b}, of the ClassFile's access_flags field. */
+  /** The offset in bytes of the ClassFile's access_flags field. */
   public final int header;
 
   // -----------------------------------------------------------------------------------------------
@@ -176,10 +187,11 @@
    */
   ClassReader(
       final byte[] classFileBuffer, final int classFileOffset, final boolean checkClassVersion) {
-    b = classFileBuffer;
+    this.classFileBuffer = classFileBuffer;
+    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.V12) {
+    if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V15) {
       throw new IllegalArgumentException(
           "Unsupported class file major version " + readShort(classFileOffset + 6));
     }
@@ -195,8 +207,8 @@
     int currentCpInfoIndex = 1;
     int currentCpInfoOffset = classFileOffset + 10;
     int currentMaxStringLength = 0;
+    boolean hasBootstrapMethods = false;
     boolean hasConstantDynamic = false;
-    boolean hasConstantInvokeDynamic = false;
     // The offset of the other entries depend on the total size of all the previous entries.
     while (currentCpInfoIndex < constantPoolCount) {
       cpInfoOffsets[currentCpInfoIndex++] = currentCpInfoOffset + 1;
@@ -212,11 +224,12 @@
           break;
         case Symbol.CONSTANT_DYNAMIC_TAG:
           cpInfoSize = 5;
+          hasBootstrapMethods = true;
           hasConstantDynamic = true;
           break;
         case Symbol.CONSTANT_INVOKE_DYNAMIC_TAG:
           cpInfoSize = 5;
-          hasConstantInvokeDynamic = true;
+          hasBootstrapMethods = true;
           break;
         case Symbol.CONSTANT_LONG_TAG:
         case Symbol.CONSTANT_DOUBLE_TAG:
@@ -256,9 +269,7 @@
 
     // Read the BootstrapMethods attribute, if any (only get the offset of each method).
     bootstrapMethodOffsets =
-        (hasConstantDynamic | hasConstantInvokeDynamic)
-            ? readBootstrapMethodsAttribute(currentMaxStringLength)
-            : null;
+        hasBootstrapMethods ? readBootstrapMethodsAttribute(currentMaxStringLength) : null;
   }
 
   /**
@@ -299,8 +310,7 @@
     if (inputStream == null) {
       throw new IOException("Class not found");
     }
-    try {
-      ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+    try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
       byte[] data = new byte[INPUT_STREAM_DATA_CHUNK_SIZE];
       int bytesRead;
       while ((bytesRead = inputStream.read(data, 0, data.length)) != -1) {
@@ -457,6 +467,10 @@
     String nestHostClass = null;
     // - The offset of the NestMembers attribute, or 0.
     int nestMembersOffset = 0;
+    // - The offset of the PermittedSubtypes attribute, or 0
+    int permittedSubtypesOffset = 0;
+    // - The offset of the Record attribute, or 0.
+    int recordOffset = 0;
     // - The non standard attributes (linked with their {@link Attribute#nextAttribute} field).
     //   This list in the <i>reverse order</i> or their order in the ClassFile structure.
     Attribute attributes = null;
@@ -479,6 +493,8 @@
         nestHostClass = readClass(currentAttributeOffset, charBuffer);
       } else if (Constants.NEST_MEMBERS.equals(attributeName)) {
         nestMembersOffset = currentAttributeOffset;
+      } else if (Constants.PERMITTED_SUBTYPES.equals(attributeName)) {
+        permittedSubtypesOffset = currentAttributeOffset;
       } else if (Constants.SIGNATURE.equals(attributeName)) {
         signature = readUTF8(currentAttributeOffset, charBuffer);
       } else if (Constants.RUNTIME_VISIBLE_ANNOTATIONS.equals(attributeName)) {
@@ -496,6 +512,9 @@
         runtimeInvisibleAnnotationsOffset = currentAttributeOffset;
       } else if (Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
         runtimeInvisibleTypeAnnotationsOffset = currentAttributeOffset;
+      } else if (Constants.RECORD.equals(attributeName)) {
+        recordOffset = currentAttributeOffset;
+        accessFlags |= Opcodes.ACC_RECORD;
       } else if (Constants.MODULE.equals(attributeName)) {
         moduleOffset = currentAttributeOffset;
       } else if (Constants.MODULE_MAIN_CLASS.equals(attributeName)) {
@@ -653,6 +672,17 @@
       }
     }
 
+    // Visit the PermittedSubtypes attribute.
+    if (permittedSubtypesOffset != 0) {
+      int numberOfPermittedSubtypes = readUnsignedShort(permittedSubtypesOffset);
+      int currentPermittedSubtypeOffset = permittedSubtypesOffset + 2;
+      while (numberOfPermittedSubtypes-- > 0) {
+        classVisitor.visitPermittedSubtypeExperimental(
+            readClass(currentPermittedSubtypeOffset, charBuffer));
+        currentPermittedSubtypeOffset += 2;
+      }
+    }
+
     // Visit the InnerClasses attribute.
     if (innerClassesOffset != 0) {
       int numberOfClasses = readUnsignedShort(innerClassesOffset);
@@ -667,6 +697,15 @@
       }
     }
 
+    // Visit Record components.
+    if (recordOffset != 0) {
+      int recordComponentsCount = readUnsignedShort(recordOffset);
+      recordOffset += 2;
+      while (recordComponentsCount-- > 0) {
+        recordOffset = readRecordComponent(classVisitor, context, recordOffset);
+      }
+    }
+
     // Visit the fields and methods.
     int fieldsCount = readUnsignedShort(currentOffset);
     currentOffset += 2;
@@ -696,7 +735,8 @@
    *     attribute_name_index and attribute_length fields).
    * @param modulePackagesOffset the offset of the ModulePackages attribute (excluding the
    *     attribute_info's attribute_name_index and attribute_length fields), or 0.
-   * @param moduleMainClass the string corresponding to the ModuleMainClass attribute, or null.
+   * @param moduleMainClass the string corresponding to the ModuleMainClass attribute, or {@literal
+   *     null}.
    */
   private void readModuleAttributes(
       final ClassVisitor classVisitor,
@@ -814,6 +854,180 @@
   }
 
   /**
+   * Reads a record component and visit it.
+   *
+   * @param classVisitor the current class visitor
+   * @param context information about the class being parsed.
+   * @param recordComponentOffset the offset of the current record component.
+   * @return the offset of the first byte following the record component.
+   */
+  private int readRecordComponent(
+      final ClassVisitor classVisitor, final Context context, final int recordComponentOffset) {
+    char[] charBuffer = context.charBuffer;
+
+    int currentOffset = recordComponentOffset;
+    String name = readUTF8(currentOffset, charBuffer);
+    String descriptor = readUTF8(currentOffset + 2, charBuffer);
+    currentOffset += 4;
+
+    // Read the record component attributes (the variables are ordered as in Section 4.7 of the
+    // JVMS).
+
+    // Attribute offsets exclude the attribute_name_index and attribute_length fields.
+    // - The string corresponding to the Signature attribute, or null.
+    String signature = null;
+    // - The offset of the RuntimeVisibleAnnotations attribute, or 0.
+    int runtimeVisibleAnnotationsOffset = 0;
+    // - The offset of the RuntimeInvisibleAnnotations attribute, or 0.
+    int runtimeInvisibleAnnotationsOffset = 0;
+    // - The offset of the RuntimeVisibleTypeAnnotations attribute, or 0.
+    int runtimeVisibleTypeAnnotationsOffset = 0;
+    // - The offset of the RuntimeInvisibleTypeAnnotations attribute, or 0.
+    int runtimeInvisibleTypeAnnotationsOffset = 0;
+    // - The non standard attributes (linked with their {@link Attribute#nextAttribute} field).
+    //   This list in the <i>reverse order</i> or their order in the ClassFile structure.
+    Attribute attributes = null;
+
+    int attributesCount = readUnsignedShort(currentOffset);
+    currentOffset += 2;
+    while (attributesCount-- > 0) {
+      // Read the attribute_info's attribute_name and attribute_length fields.
+      String attributeName = readUTF8(currentOffset, charBuffer);
+      int attributeLength = readInt(currentOffset + 2);
+      currentOffset += 6;
+      // The tests are sorted in decreasing frequency order (based on frequencies observed on
+      // typical classes).
+      if (Constants.SIGNATURE.equals(attributeName)) {
+        signature = readUTF8(currentOffset, charBuffer);
+      } else if (Constants.RUNTIME_VISIBLE_ANNOTATIONS.equals(attributeName)) {
+        runtimeVisibleAnnotationsOffset = currentOffset;
+      } else if (Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
+        runtimeVisibleTypeAnnotationsOffset = currentOffset;
+      } else if (Constants.RUNTIME_INVISIBLE_ANNOTATIONS.equals(attributeName)) {
+        runtimeInvisibleAnnotationsOffset = currentOffset;
+      } else if (Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
+        runtimeInvisibleTypeAnnotationsOffset = currentOffset;
+      } else {
+        Attribute attribute =
+            readAttribute(
+                context.attributePrototypes,
+                attributeName,
+                currentOffset,
+                attributeLength,
+                charBuffer,
+                -1,
+                null);
+        attribute.nextAttribute = attributes;
+        attributes = attribute;
+      }
+      currentOffset += attributeLength;
+    }
+
+    RecordComponentVisitor recordComponentVisitor =
+        classVisitor.visitRecordComponent(name, descriptor, signature);
+    if (recordComponentVisitor == null) {
+      return currentOffset;
+    }
+
+    // Visit the RuntimeVisibleAnnotations attribute.
+    if (runtimeVisibleAnnotationsOffset != 0) {
+      int numAnnotations = readUnsignedShort(runtimeVisibleAnnotationsOffset);
+      int currentAnnotationOffset = runtimeVisibleAnnotationsOffset + 2;
+      while (numAnnotations-- > 0) {
+        // Parse the type_index field.
+        String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
+        currentAnnotationOffset += 2;
+        // Parse num_element_value_pairs and element_value_pairs and visit these values.
+        currentAnnotationOffset =
+            readElementValues(
+                recordComponentVisitor.visitAnnotation(annotationDescriptor, /* visible = */ true),
+                currentAnnotationOffset,
+                /* named = */ true,
+                charBuffer);
+      }
+    }
+
+    // Visit the RuntimeInvisibleAnnotations attribute.
+    if (runtimeInvisibleAnnotationsOffset != 0) {
+      int numAnnotations = readUnsignedShort(runtimeInvisibleAnnotationsOffset);
+      int currentAnnotationOffset = runtimeInvisibleAnnotationsOffset + 2;
+      while (numAnnotations-- > 0) {
+        // Parse the type_index field.
+        String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
+        currentAnnotationOffset += 2;
+        // Parse num_element_value_pairs and element_value_pairs and visit these values.
+        currentAnnotationOffset =
+            readElementValues(
+                recordComponentVisitor.visitAnnotation(annotationDescriptor, /* visible = */ false),
+                currentAnnotationOffset,
+                /* named = */ true,
+                charBuffer);
+      }
+    }
+
+    // Visit the RuntimeVisibleTypeAnnotations attribute.
+    if (runtimeVisibleTypeAnnotationsOffset != 0) {
+      int numAnnotations = readUnsignedShort(runtimeVisibleTypeAnnotationsOffset);
+      int currentAnnotationOffset = runtimeVisibleTypeAnnotationsOffset + 2;
+      while (numAnnotations-- > 0) {
+        // Parse the target_type, target_info and target_path fields.
+        currentAnnotationOffset = readTypeAnnotationTarget(context, currentAnnotationOffset);
+        // Parse the type_index field.
+        String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
+        currentAnnotationOffset += 2;
+        // Parse num_element_value_pairs and element_value_pairs and visit these values.
+        currentAnnotationOffset =
+            readElementValues(
+                recordComponentVisitor.visitTypeAnnotation(
+                    context.currentTypeAnnotationTarget,
+                    context.currentTypeAnnotationTargetPath,
+                    annotationDescriptor,
+                    /* visible = */ true),
+                currentAnnotationOffset,
+                /* named = */ true,
+                charBuffer);
+      }
+    }
+
+    // Visit the RuntimeInvisibleTypeAnnotations attribute.
+    if (runtimeInvisibleTypeAnnotationsOffset != 0) {
+      int numAnnotations = readUnsignedShort(runtimeInvisibleTypeAnnotationsOffset);
+      int currentAnnotationOffset = runtimeInvisibleTypeAnnotationsOffset + 2;
+      while (numAnnotations-- > 0) {
+        // Parse the target_type, target_info and target_path fields.
+        currentAnnotationOffset = readTypeAnnotationTarget(context, currentAnnotationOffset);
+        // Parse the type_index field.
+        String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
+        currentAnnotationOffset += 2;
+        // Parse num_element_value_pairs and element_value_pairs and visit these values.
+        currentAnnotationOffset =
+            readElementValues(
+                recordComponentVisitor.visitTypeAnnotation(
+                    context.currentTypeAnnotationTarget,
+                    context.currentTypeAnnotationTargetPath,
+                    annotationDescriptor,
+                    /* visible = */ false),
+                currentAnnotationOffset,
+                /* named = */ true,
+                charBuffer);
+      }
+    }
+
+    // Visit the non standard attributes.
+    while (attributes != null) {
+      // Copy and reset the nextAttribute field so that it can also be used in FieldWriter.
+      Attribute nextAttribute = attributes.nextAttribute;
+      attributes.nextAttribute = null;
+      recordComponentVisitor.visitAttribute(attributes);
+      attributes = nextAttribute;
+    }
+
+    // Visit the end of the field.
+    recordComponentVisitor.visitEnd();
+    return currentOffset;
+  }
+
+  /**
    * Reads a JVMS field_info structure and makes the given visitor visit it.
    *
    * @param classVisitor the visitor that must visit the field.
@@ -1128,19 +1342,18 @@
       MethodWriter methodWriter = (MethodWriter) methodVisitor;
       if (methodWriter.canCopyMethodAttributes(
           this,
-          methodInfoOffset,
-          currentOffset - methodInfoOffset,
           synthetic,
           (context.currentMethodAccessFlags & Opcodes.ACC_DEPRECATED) != 0,
           readUnsignedShort(methodInfoOffset + 4),
           signatureIndex,
           exceptionsOffset)) {
+        methodWriter.setMethodAttributesSource(methodInfoOffset, currentOffset - methodInfoOffset);
         return currentOffset;
       }
     }
 
     // Visit the MethodParameters attribute.
-    if (methodParametersOffset != 0) {
+    if (methodParametersOffset != 0 && (context.parsingOptions & SKIP_DEBUG) == 0) {
       int parametersCount = readByte(methodParametersOffset);
       int currentParameterOffset = methodParametersOffset + 1;
       while (parametersCount-- > 0) {
@@ -1289,15 +1502,15 @@
    *
    * @param methodVisitor the visitor that must visit the Code attribute.
    * @param context information about the class being parsed.
-   * @param codeOffset the start offset in {@link #b} of the Code attribute, excluding its
-   *     attribute_name_index and attribute_length fields.
+   * @param codeOffset the start offset in {@link #classFileBuffer} of the Code attribute, excluding
+   *     its attribute_name_index and attribute_length fields.
    */
   private void readCode(
       final MethodVisitor methodVisitor, final Context context, final int codeOffset) {
     int currentOffset = codeOffset;
 
     // Read the max_stack, max_locals and code_length fields.
-    final byte[] classFileBuffer = b;
+    final byte[] classBuffer = classFileBuffer;
     final char[] charBuffer = context.charBuffer;
     final int maxStack = readUnsignedShort(currentOffset);
     final int maxLocals = readUnsignedShort(currentOffset + 2);
@@ -1310,115 +1523,115 @@
     final Label[] labels = context.currentMethodLabels = new Label[codeLength + 1];
     while (currentOffset < bytecodeEndOffset) {
       final int bytecodeOffset = currentOffset - bytecodeStartOffset;
-      final int opcode = classFileBuffer[currentOffset] & 0xFF;
+      final int opcode = classBuffer[currentOffset] & 0xFF;
       switch (opcode) {
-        case Constants.NOP:
-        case Constants.ACONST_NULL:
-        case Constants.ICONST_M1:
-        case Constants.ICONST_0:
-        case Constants.ICONST_1:
-        case Constants.ICONST_2:
-        case Constants.ICONST_3:
-        case Constants.ICONST_4:
-        case Constants.ICONST_5:
-        case Constants.LCONST_0:
-        case Constants.LCONST_1:
-        case Constants.FCONST_0:
-        case Constants.FCONST_1:
-        case Constants.FCONST_2:
-        case Constants.DCONST_0:
-        case Constants.DCONST_1:
-        case Constants.IALOAD:
-        case Constants.LALOAD:
-        case Constants.FALOAD:
-        case Constants.DALOAD:
-        case Constants.AALOAD:
-        case Constants.BALOAD:
-        case Constants.CALOAD:
-        case Constants.SALOAD:
-        case Constants.IASTORE:
-        case Constants.LASTORE:
-        case Constants.FASTORE:
-        case Constants.DASTORE:
-        case Constants.AASTORE:
-        case Constants.BASTORE:
-        case Constants.CASTORE:
-        case Constants.SASTORE:
-        case Constants.POP:
-        case Constants.POP2:
-        case Constants.DUP:
-        case Constants.DUP_X1:
-        case Constants.DUP_X2:
-        case Constants.DUP2:
-        case Constants.DUP2_X1:
-        case Constants.DUP2_X2:
-        case Constants.SWAP:
-        case Constants.IADD:
-        case Constants.LADD:
-        case Constants.FADD:
-        case Constants.DADD:
-        case Constants.ISUB:
-        case Constants.LSUB:
-        case Constants.FSUB:
-        case Constants.DSUB:
-        case Constants.IMUL:
-        case Constants.LMUL:
-        case Constants.FMUL:
-        case Constants.DMUL:
-        case Constants.IDIV:
-        case Constants.LDIV:
-        case Constants.FDIV:
-        case Constants.DDIV:
-        case Constants.IREM:
-        case Constants.LREM:
-        case Constants.FREM:
-        case Constants.DREM:
-        case Constants.INEG:
-        case Constants.LNEG:
-        case Constants.FNEG:
-        case Constants.DNEG:
-        case Constants.ISHL:
-        case Constants.LSHL:
-        case Constants.ISHR:
-        case Constants.LSHR:
-        case Constants.IUSHR:
-        case Constants.LUSHR:
-        case Constants.IAND:
-        case Constants.LAND:
-        case Constants.IOR:
-        case Constants.LOR:
-        case Constants.IXOR:
-        case Constants.LXOR:
-        case Constants.I2L:
-        case Constants.I2F:
-        case Constants.I2D:
-        case Constants.L2I:
-        case Constants.L2F:
-        case Constants.L2D:
-        case Constants.F2I:
-        case Constants.F2L:
-        case Constants.F2D:
-        case Constants.D2I:
-        case Constants.D2L:
-        case Constants.D2F:
-        case Constants.I2B:
-        case Constants.I2C:
-        case Constants.I2S:
-        case Constants.LCMP:
-        case Constants.FCMPL:
-        case Constants.FCMPG:
-        case Constants.DCMPL:
-        case Constants.DCMPG:
-        case Constants.IRETURN:
-        case Constants.LRETURN:
-        case Constants.FRETURN:
-        case Constants.DRETURN:
-        case Constants.ARETURN:
-        case Constants.RETURN:
-        case Constants.ARRAYLENGTH:
-        case Constants.ATHROW:
-        case Constants.MONITORENTER:
-        case Constants.MONITOREXIT:
+        case Opcodes.NOP:
+        case Opcodes.ACONST_NULL:
+        case Opcodes.ICONST_M1:
+        case Opcodes.ICONST_0:
+        case Opcodes.ICONST_1:
+        case Opcodes.ICONST_2:
+        case Opcodes.ICONST_3:
+        case Opcodes.ICONST_4:
+        case Opcodes.ICONST_5:
+        case Opcodes.LCONST_0:
+        case Opcodes.LCONST_1:
+        case Opcodes.FCONST_0:
+        case Opcodes.FCONST_1:
+        case Opcodes.FCONST_2:
+        case Opcodes.DCONST_0:
+        case Opcodes.DCONST_1:
+        case Opcodes.IALOAD:
+        case Opcodes.LALOAD:
+        case Opcodes.FALOAD:
+        case Opcodes.DALOAD:
+        case Opcodes.AALOAD:
+        case Opcodes.BALOAD:
+        case Opcodes.CALOAD:
+        case Opcodes.SALOAD:
+        case Opcodes.IASTORE:
+        case Opcodes.LASTORE:
+        case Opcodes.FASTORE:
+        case Opcodes.DASTORE:
+        case Opcodes.AASTORE:
+        case Opcodes.BASTORE:
+        case Opcodes.CASTORE:
+        case Opcodes.SASTORE:
+        case Opcodes.POP:
+        case Opcodes.POP2:
+        case Opcodes.DUP:
+        case Opcodes.DUP_X1:
+        case Opcodes.DUP_X2:
+        case Opcodes.DUP2:
+        case Opcodes.DUP2_X1:
+        case Opcodes.DUP2_X2:
+        case Opcodes.SWAP:
+        case Opcodes.IADD:
+        case Opcodes.LADD:
+        case Opcodes.FADD:
+        case Opcodes.DADD:
+        case Opcodes.ISUB:
+        case Opcodes.LSUB:
+        case Opcodes.FSUB:
+        case Opcodes.DSUB:
+        case Opcodes.IMUL:
+        case Opcodes.LMUL:
+        case Opcodes.FMUL:
+        case Opcodes.DMUL:
+        case Opcodes.IDIV:
+        case Opcodes.LDIV:
+        case Opcodes.FDIV:
+        case Opcodes.DDIV:
+        case Opcodes.IREM:
+        case Opcodes.LREM:
+        case Opcodes.FREM:
+        case Opcodes.DREM:
+        case Opcodes.INEG:
+        case Opcodes.LNEG:
+        case Opcodes.FNEG:
+        case Opcodes.DNEG:
+        case Opcodes.ISHL:
+        case Opcodes.LSHL:
+        case Opcodes.ISHR:
+        case Opcodes.LSHR:
+        case Opcodes.IUSHR:
+        case Opcodes.LUSHR:
+        case Opcodes.IAND:
+        case Opcodes.LAND:
+        case Opcodes.IOR:
+        case Opcodes.LOR:
+        case Opcodes.IXOR:
+        case Opcodes.LXOR:
+        case Opcodes.I2L:
+        case Opcodes.I2F:
+        case Opcodes.I2D:
+        case Opcodes.L2I:
+        case Opcodes.L2F:
+        case Opcodes.L2D:
+        case Opcodes.F2I:
+        case Opcodes.F2L:
+        case Opcodes.F2D:
+        case Opcodes.D2I:
+        case Opcodes.D2L:
+        case Opcodes.D2F:
+        case Opcodes.I2B:
+        case Opcodes.I2C:
+        case Opcodes.I2S:
+        case Opcodes.LCMP:
+        case Opcodes.FCMPL:
+        case Opcodes.FCMPG:
+        case Opcodes.DCMPL:
+        case Opcodes.DCMPG:
+        case Opcodes.IRETURN:
+        case Opcodes.LRETURN:
+        case Opcodes.FRETURN:
+        case Opcodes.DRETURN:
+        case Opcodes.ARETURN:
+        case Opcodes.RETURN:
+        case Opcodes.ARRAYLENGTH:
+        case Opcodes.ATHROW:
+        case Opcodes.MONITORENTER:
+        case Opcodes.MONITOREXIT:
         case Constants.ILOAD_0:
         case Constants.ILOAD_1:
         case Constants.ILOAD_2:
@@ -1461,24 +1674,24 @@
         case Constants.ASTORE_3:
           currentOffset += 1;
           break;
-        case Constants.IFEQ:
-        case Constants.IFNE:
-        case Constants.IFLT:
-        case Constants.IFGE:
-        case Constants.IFGT:
-        case Constants.IFLE:
-        case Constants.IF_ICMPEQ:
-        case Constants.IF_ICMPNE:
-        case Constants.IF_ICMPLT:
-        case Constants.IF_ICMPGE:
-        case Constants.IF_ICMPGT:
-        case Constants.IF_ICMPLE:
-        case Constants.IF_ACMPEQ:
-        case Constants.IF_ACMPNE:
-        case Constants.GOTO:
-        case Constants.JSR:
-        case Constants.IFNULL:
-        case Constants.IFNONNULL:
+        case Opcodes.IFEQ:
+        case Opcodes.IFNE:
+        case Opcodes.IFLT:
+        case Opcodes.IFGE:
+        case Opcodes.IFGT:
+        case Opcodes.IFLE:
+        case Opcodes.IF_ICMPEQ:
+        case Opcodes.IF_ICMPNE:
+        case Opcodes.IF_ICMPLT:
+        case Opcodes.IF_ICMPGE:
+        case Opcodes.IF_ICMPGT:
+        case Opcodes.IF_ICMPLE:
+        case Opcodes.IF_ACMPEQ:
+        case Opcodes.IF_ACMPNE:
+        case Opcodes.GOTO:
+        case Opcodes.JSR:
+        case Opcodes.IFNULL:
+        case Opcodes.IFNONNULL:
           createLabel(bytecodeOffset + readShort(currentOffset + 1), labels);
           currentOffset += 3;
           break;
@@ -1510,28 +1723,28 @@
           currentOffset += 5;
           break;
         case Constants.WIDE:
-          switch (classFileBuffer[currentOffset + 1] & 0xFF) {
-            case Constants.ILOAD:
-            case Constants.FLOAD:
-            case Constants.ALOAD:
-            case Constants.LLOAD:
-            case Constants.DLOAD:
-            case Constants.ISTORE:
-            case Constants.FSTORE:
-            case Constants.ASTORE:
-            case Constants.LSTORE:
-            case Constants.DSTORE:
-            case Constants.RET:
+          switch (classBuffer[currentOffset + 1] & 0xFF) {
+            case Opcodes.ILOAD:
+            case Opcodes.FLOAD:
+            case Opcodes.ALOAD:
+            case Opcodes.LLOAD:
+            case Opcodes.DLOAD:
+            case Opcodes.ISTORE:
+            case Opcodes.FSTORE:
+            case Opcodes.ASTORE:
+            case Opcodes.LSTORE:
+            case Opcodes.DSTORE:
+            case Opcodes.RET:
               currentOffset += 4;
               break;
-            case Constants.IINC:
+            case Opcodes.IINC:
               currentOffset += 6;
               break;
             default:
               throw new IllegalArgumentException();
           }
           break;
-        case Constants.TABLESWITCH:
+        case Opcodes.TABLESWITCH:
           // Skip 0 to 3 padding bytes.
           currentOffset += 4 - (bytecodeOffset & 3);
           // Read the default label and the number of table entries.
@@ -1544,7 +1757,7 @@
             currentOffset += 4;
           }
           break;
-        case Constants.LOOKUPSWITCH:
+        case Opcodes.LOOKUPSWITCH:
           // Skip 0 to 3 padding bytes.
           currentOffset += 4 - (bytecodeOffset & 3);
           // Read the default label and the number of switch cases.
@@ -1557,44 +1770,44 @@
             currentOffset += 8;
           }
           break;
-        case Constants.ILOAD:
-        case Constants.LLOAD:
-        case Constants.FLOAD:
-        case Constants.DLOAD:
-        case Constants.ALOAD:
-        case Constants.ISTORE:
-        case Constants.LSTORE:
-        case Constants.FSTORE:
-        case Constants.DSTORE:
-        case Constants.ASTORE:
-        case Constants.RET:
-        case Constants.BIPUSH:
-        case Constants.NEWARRAY:
-        case Constants.LDC:
+        case Opcodes.ILOAD:
+        case Opcodes.LLOAD:
+        case Opcodes.FLOAD:
+        case Opcodes.DLOAD:
+        case Opcodes.ALOAD:
+        case Opcodes.ISTORE:
+        case Opcodes.LSTORE:
+        case Opcodes.FSTORE:
+        case Opcodes.DSTORE:
+        case Opcodes.ASTORE:
+        case Opcodes.RET:
+        case Opcodes.BIPUSH:
+        case Opcodes.NEWARRAY:
+        case Opcodes.LDC:
           currentOffset += 2;
           break;
-        case Constants.SIPUSH:
+        case Opcodes.SIPUSH:
         case Constants.LDC_W:
         case Constants.LDC2_W:
-        case Constants.GETSTATIC:
-        case Constants.PUTSTATIC:
-        case Constants.GETFIELD:
-        case Constants.PUTFIELD:
-        case Constants.INVOKEVIRTUAL:
-        case Constants.INVOKESPECIAL:
-        case Constants.INVOKESTATIC:
-        case Constants.NEW:
-        case Constants.ANEWARRAY:
-        case Constants.CHECKCAST:
-        case Constants.INSTANCEOF:
-        case Constants.IINC:
+        case Opcodes.GETSTATIC:
+        case Opcodes.PUTSTATIC:
+        case Opcodes.GETFIELD:
+        case Opcodes.PUTFIELD:
+        case Opcodes.INVOKEVIRTUAL:
+        case Opcodes.INVOKESPECIAL:
+        case Opcodes.INVOKESTATIC:
+        case Opcodes.NEW:
+        case Opcodes.ANEWARRAY:
+        case Opcodes.CHECKCAST:
+        case Opcodes.INSTANCEOF:
+        case Opcodes.IINC:
           currentOffset += 3;
           break;
-        case Constants.INVOKEINTERFACE:
-        case Constants.INVOKEDYNAMIC:
+        case Opcodes.INVOKEINTERFACE:
+        case Opcodes.INVOKEDYNAMIC:
           currentOffset += 5;
           break;
-        case Constants.MULTIANEWARRAY:
+        case Opcodes.MULTIANEWARRAY:
           currentOffset += 4;
           break;
         default:
@@ -1759,11 +1972,11 @@
       // creating a label for each NEW instruction, and faster than fully decoding the whole stack
       // map table.
       for (int offset = stackMapFrameOffset; offset < stackMapTableEndOffset - 2; ++offset) {
-        if (classFileBuffer[offset] == Frame.ITEM_UNINITIALIZED) {
+        if (classBuffer[offset] == Frame.ITEM_UNINITIALIZED) {
           int potentialBytecodeOffset = readUnsignedShort(offset + 1);
           if (potentialBytecodeOffset >= 0
               && potentialBytecodeOffset < codeLength
-              && (classFileBuffer[bytecodeStartOffset + potentialBytecodeOffset] & 0xFF)
+              && (classBuffer[bytecodeStartOffset + potentialBytecodeOffset] & 0xFF)
                   == Opcodes.NEW) {
             createLabel(potentialBytecodeOffset, labels);
           }
@@ -1859,115 +2072,115 @@
       }
 
       // Visit the instruction at this bytecode offset.
-      int opcode = classFileBuffer[currentOffset] & 0xFF;
+      int opcode = classBuffer[currentOffset] & 0xFF;
       switch (opcode) {
-        case Constants.NOP:
-        case Constants.ACONST_NULL:
-        case Constants.ICONST_M1:
-        case Constants.ICONST_0:
-        case Constants.ICONST_1:
-        case Constants.ICONST_2:
-        case Constants.ICONST_3:
-        case Constants.ICONST_4:
-        case Constants.ICONST_5:
-        case Constants.LCONST_0:
-        case Constants.LCONST_1:
-        case Constants.FCONST_0:
-        case Constants.FCONST_1:
-        case Constants.FCONST_2:
-        case Constants.DCONST_0:
-        case Constants.DCONST_1:
-        case Constants.IALOAD:
-        case Constants.LALOAD:
-        case Constants.FALOAD:
-        case Constants.DALOAD:
-        case Constants.AALOAD:
-        case Constants.BALOAD:
-        case Constants.CALOAD:
-        case Constants.SALOAD:
-        case Constants.IASTORE:
-        case Constants.LASTORE:
-        case Constants.FASTORE:
-        case Constants.DASTORE:
-        case Constants.AASTORE:
-        case Constants.BASTORE:
-        case Constants.CASTORE:
-        case Constants.SASTORE:
-        case Constants.POP:
-        case Constants.POP2:
-        case Constants.DUP:
-        case Constants.DUP_X1:
-        case Constants.DUP_X2:
-        case Constants.DUP2:
-        case Constants.DUP2_X1:
-        case Constants.DUP2_X2:
-        case Constants.SWAP:
-        case Constants.IADD:
-        case Constants.LADD:
-        case Constants.FADD:
-        case Constants.DADD:
-        case Constants.ISUB:
-        case Constants.LSUB:
-        case Constants.FSUB:
-        case Constants.DSUB:
-        case Constants.IMUL:
-        case Constants.LMUL:
-        case Constants.FMUL:
-        case Constants.DMUL:
-        case Constants.IDIV:
-        case Constants.LDIV:
-        case Constants.FDIV:
-        case Constants.DDIV:
-        case Constants.IREM:
-        case Constants.LREM:
-        case Constants.FREM:
-        case Constants.DREM:
-        case Constants.INEG:
-        case Constants.LNEG:
-        case Constants.FNEG:
-        case Constants.DNEG:
-        case Constants.ISHL:
-        case Constants.LSHL:
-        case Constants.ISHR:
-        case Constants.LSHR:
-        case Constants.IUSHR:
-        case Constants.LUSHR:
-        case Constants.IAND:
-        case Constants.LAND:
-        case Constants.IOR:
-        case Constants.LOR:
-        case Constants.IXOR:
-        case Constants.LXOR:
-        case Constants.I2L:
-        case Constants.I2F:
-        case Constants.I2D:
-        case Constants.L2I:
-        case Constants.L2F:
-        case Constants.L2D:
-        case Constants.F2I:
-        case Constants.F2L:
-        case Constants.F2D:
-        case Constants.D2I:
-        case Constants.D2L:
-        case Constants.D2F:
-        case Constants.I2B:
-        case Constants.I2C:
-        case Constants.I2S:
-        case Constants.LCMP:
-        case Constants.FCMPL:
-        case Constants.FCMPG:
-        case Constants.DCMPL:
-        case Constants.DCMPG:
-        case Constants.IRETURN:
-        case Constants.LRETURN:
-        case Constants.FRETURN:
-        case Constants.DRETURN:
-        case Constants.ARETURN:
-        case Constants.RETURN:
-        case Constants.ARRAYLENGTH:
-        case Constants.ATHROW:
-        case Constants.MONITORENTER:
-        case Constants.MONITOREXIT:
+        case Opcodes.NOP:
+        case Opcodes.ACONST_NULL:
+        case Opcodes.ICONST_M1:
+        case Opcodes.ICONST_0:
+        case Opcodes.ICONST_1:
+        case Opcodes.ICONST_2:
+        case Opcodes.ICONST_3:
+        case Opcodes.ICONST_4:
+        case Opcodes.ICONST_5:
+        case Opcodes.LCONST_0:
+        case Opcodes.LCONST_1:
+        case Opcodes.FCONST_0:
+        case Opcodes.FCONST_1:
+        case Opcodes.FCONST_2:
+        case Opcodes.DCONST_0:
+        case Opcodes.DCONST_1:
+        case Opcodes.IALOAD:
+        case Opcodes.LALOAD:
+        case Opcodes.FALOAD:
+        case Opcodes.DALOAD:
+        case Opcodes.AALOAD:
+        case Opcodes.BALOAD:
+        case Opcodes.CALOAD:
+        case Opcodes.SALOAD:
+        case Opcodes.IASTORE:
+        case Opcodes.LASTORE:
+        case Opcodes.FASTORE:
+        case Opcodes.DASTORE:
+        case Opcodes.AASTORE:
+        case Opcodes.BASTORE:
+        case Opcodes.CASTORE:
+        case Opcodes.SASTORE:
+        case Opcodes.POP:
+        case Opcodes.POP2:
+        case Opcodes.DUP:
+        case Opcodes.DUP_X1:
+        case Opcodes.DUP_X2:
+        case Opcodes.DUP2:
+        case Opcodes.DUP2_X1:
+        case Opcodes.DUP2_X2:
+        case Opcodes.SWAP:
+        case Opcodes.IADD:
+        case Opcodes.LADD:
+        case Opcodes.FADD:
+        case Opcodes.DADD:
+        case Opcodes.ISUB:
+        case Opcodes.LSUB:
+        case Opcodes.FSUB:
+        case Opcodes.DSUB:
+        case Opcodes.IMUL:
+        case Opcodes.LMUL:
+        case Opcodes.FMUL:
+        case Opcodes.DMUL:
+        case Opcodes.IDIV:
+        case Opcodes.LDIV:
+        case Opcodes.FDIV:
+        case Opcodes.DDIV:
+        case Opcodes.IREM:
+        case Opcodes.LREM:
+        case Opcodes.FREM:
+        case Opcodes.DREM:
+        case Opcodes.INEG:
+        case Opcodes.LNEG:
+        case Opcodes.FNEG:
+        case Opcodes.DNEG:
+        case Opcodes.ISHL:
+        case Opcodes.LSHL:
+        case Opcodes.ISHR:
+        case Opcodes.LSHR:
+        case Opcodes.IUSHR:
+        case Opcodes.LUSHR:
+        case Opcodes.IAND:
+        case Opcodes.LAND:
+        case Opcodes.IOR:
+        case Opcodes.LOR:
+        case Opcodes.IXOR:
+        case Opcodes.LXOR:
+        case Opcodes.I2L:
+        case Opcodes.I2F:
+        case Opcodes.I2D:
+        case Opcodes.L2I:
+        case Opcodes.L2F:
+        case Opcodes.L2D:
+        case Opcodes.F2I:
+        case Opcodes.F2L:
+        case Opcodes.F2D:
+        case Opcodes.D2I:
+        case Opcodes.D2L:
+        case Opcodes.D2F:
+        case Opcodes.I2B:
+        case Opcodes.I2C:
+        case Opcodes.I2S:
+        case Opcodes.LCMP:
+        case Opcodes.FCMPL:
+        case Opcodes.FCMPG:
+        case Opcodes.DCMPL:
+        case Opcodes.DCMPG:
+        case Opcodes.IRETURN:
+        case Opcodes.LRETURN:
+        case Opcodes.FRETURN:
+        case Opcodes.DRETURN:
+        case Opcodes.ARETURN:
+        case Opcodes.RETURN:
+        case Opcodes.ARRAYLENGTH:
+        case Opcodes.ATHROW:
+        case Opcodes.MONITORENTER:
+        case Opcodes.MONITOREXIT:
           methodVisitor.visitInsn(opcode);
           currentOffset += 1;
           break;
@@ -2019,24 +2232,24 @@
           methodVisitor.visitVarInsn(Opcodes.ISTORE + (opcode >> 2), opcode & 0x3);
           currentOffset += 1;
           break;
-        case Constants.IFEQ:
-        case Constants.IFNE:
-        case Constants.IFLT:
-        case Constants.IFGE:
-        case Constants.IFGT:
-        case Constants.IFLE:
-        case Constants.IF_ICMPEQ:
-        case Constants.IF_ICMPNE:
-        case Constants.IF_ICMPLT:
-        case Constants.IF_ICMPGE:
-        case Constants.IF_ICMPGT:
-        case Constants.IF_ICMPLE:
-        case Constants.IF_ACMPEQ:
-        case Constants.IF_ACMPNE:
-        case Constants.GOTO:
-        case Constants.JSR:
-        case Constants.IFNULL:
-        case Constants.IFNONNULL:
+        case Opcodes.IFEQ:
+        case Opcodes.IFNE:
+        case Opcodes.IFLT:
+        case Opcodes.IFGE:
+        case Opcodes.IFGT:
+        case Opcodes.IFLE:
+        case Opcodes.IF_ICMPEQ:
+        case Opcodes.IF_ICMPNE:
+        case Opcodes.IF_ICMPLT:
+        case Opcodes.IF_ICMPGE:
+        case Opcodes.IF_ICMPGT:
+        case Opcodes.IF_ICMPLE:
+        case Opcodes.IF_ACMPEQ:
+        case Opcodes.IF_ACMPNE:
+        case Opcodes.GOTO:
+        case Opcodes.JSR:
+        case Opcodes.IFNULL:
+        case Opcodes.IFNONNULL:
           methodVisitor.visitJumpInsn(
               opcode, labels[currentBytecodeOffset + readShort(currentOffset + 1)]);
           currentOffset += 3;
@@ -2097,19 +2310,17 @@
             break;
           }
         case Constants.ASM_GOTO_W:
-          {
-            // Replace ASM_GOTO_W with GOTO_W.
-            methodVisitor.visitJumpInsn(
-                Constants.GOTO_W, labels[currentBytecodeOffset + readInt(currentOffset + 1)]);
-            // The instruction just after is a jump target (because ASM_GOTO_W is used in patterns
-            // IFNOTxxx <L> ASM_GOTO_W <l> L:..., see MethodWriter), so we need to insert a frame
-            // here.
-            insertFrame = true;
-            currentOffset += 5;
-            break;
-          }
+          // Replace ASM_GOTO_W with GOTO_W.
+          methodVisitor.visitJumpInsn(
+              Constants.GOTO_W, labels[currentBytecodeOffset + readInt(currentOffset + 1)]);
+          // The instruction just after is a jump target (because ASM_GOTO_W is used in patterns
+          // IFNOTxxx <L> ASM_GOTO_W <l> L:..., see MethodWriter), so we need to insert a frame
+          // here.
+          insertFrame = true;
+          currentOffset += 5;
+          break;
         case Constants.WIDE:
-          opcode = classFileBuffer[currentOffset + 1] & 0xFF;
+          opcode = classBuffer[currentOffset + 1] & 0xFF;
           if (opcode == Opcodes.IINC) {
             methodVisitor.visitIincInsn(
                 readUnsignedShort(currentOffset + 2), readShort(currentOffset + 4));
@@ -2119,7 +2330,7 @@
             currentOffset += 4;
           }
           break;
-        case Constants.TABLESWITCH:
+        case Opcodes.TABLESWITCH:
           {
             // Skip 0 to 3 padding bytes.
             currentOffset += 4 - (currentBytecodeOffset & 3);
@@ -2136,7 +2347,7 @@
             methodVisitor.visitTableSwitchInsn(low, high, defaultLabel, table);
             break;
           }
-        case Constants.LOOKUPSWITCH:
+        case Opcodes.LOOKUPSWITCH:
           {
             // Skip 0 to 3 padding bytes.
             currentOffset += 4 - (currentBytecodeOffset & 3);
@@ -2154,32 +2365,31 @@
             methodVisitor.visitLookupSwitchInsn(defaultLabel, keys, values);
             break;
           }
-        case Constants.ILOAD:
-        case Constants.LLOAD:
-        case Constants.FLOAD:
-        case Constants.DLOAD:
-        case Constants.ALOAD:
-        case Constants.ISTORE:
-        case Constants.LSTORE:
-        case Constants.FSTORE:
-        case Constants.DSTORE:
-        case Constants.ASTORE:
-        case Constants.RET:
-          methodVisitor.visitVarInsn(opcode, classFileBuffer[currentOffset + 1] & 0xFF);
+        case Opcodes.ILOAD:
+        case Opcodes.LLOAD:
+        case Opcodes.FLOAD:
+        case Opcodes.DLOAD:
+        case Opcodes.ALOAD:
+        case Opcodes.ISTORE:
+        case Opcodes.LSTORE:
+        case Opcodes.FSTORE:
+        case Opcodes.DSTORE:
+        case Opcodes.ASTORE:
+        case Opcodes.RET:
+          methodVisitor.visitVarInsn(opcode, classBuffer[currentOffset + 1] & 0xFF);
           currentOffset += 2;
           break;
-        case Constants.BIPUSH:
-        case Constants.NEWARRAY:
-          methodVisitor.visitIntInsn(opcode, classFileBuffer[currentOffset + 1]);
+        case Opcodes.BIPUSH:
+        case Opcodes.NEWARRAY:
+          methodVisitor.visitIntInsn(opcode, classBuffer[currentOffset + 1]);
           currentOffset += 2;
           break;
-        case Constants.SIPUSH:
+        case Opcodes.SIPUSH:
           methodVisitor.visitIntInsn(opcode, readShort(currentOffset + 1));
           currentOffset += 3;
           break;
-        case Constants.LDC:
-          methodVisitor.visitLdcInsn(
-              readConst(classFileBuffer[currentOffset + 1] & 0xFF, charBuffer));
+        case Opcodes.LDC:
+          methodVisitor.visitLdcInsn(readConst(classBuffer[currentOffset + 1] & 0xFF, charBuffer));
           currentOffset += 2;
           break;
         case Constants.LDC_W:
@@ -2187,14 +2397,14 @@
           methodVisitor.visitLdcInsn(readConst(readUnsignedShort(currentOffset + 1), charBuffer));
           currentOffset += 3;
           break;
-        case Constants.GETSTATIC:
-        case Constants.PUTSTATIC:
-        case Constants.GETFIELD:
-        case Constants.PUTFIELD:
-        case Constants.INVOKEVIRTUAL:
-        case Constants.INVOKESPECIAL:
-        case Constants.INVOKESTATIC:
-        case Constants.INVOKEINTERFACE:
+        case Opcodes.GETSTATIC:
+        case Opcodes.PUTSTATIC:
+        case Opcodes.GETFIELD:
+        case Opcodes.PUTFIELD:
+        case Opcodes.INVOKEVIRTUAL:
+        case Opcodes.INVOKESPECIAL:
+        case Opcodes.INVOKESTATIC:
+        case Opcodes.INVOKEINTERFACE:
           {
             int cpInfoOffset = cpInfoOffsets[readUnsignedShort(currentOffset + 1)];
             int nameAndTypeCpInfoOffset = cpInfoOffsets[readUnsignedShort(cpInfoOffset + 2)];
@@ -2205,7 +2415,7 @@
               methodVisitor.visitFieldInsn(opcode, owner, name, descriptor);
             } else {
               boolean isInterface =
-                  classFileBuffer[cpInfoOffset - 1] == Symbol.CONSTANT_INTERFACE_METHODREF_TAG;
+                  classBuffer[cpInfoOffset - 1] == Symbol.CONSTANT_INTERFACE_METHODREF_TAG;
               methodVisitor.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
             }
             if (opcode == Opcodes.INVOKEINTERFACE) {
@@ -2215,7 +2425,7 @@
             }
             break;
           }
-        case Constants.INVOKEDYNAMIC:
+        case Opcodes.INVOKEDYNAMIC:
           {
             int cpInfoOffset = cpInfoOffsets[readUnsignedShort(currentOffset + 1)];
             int nameAndTypeCpInfoOffset = cpInfoOffsets[readUnsignedShort(cpInfoOffset + 2)];
@@ -2237,21 +2447,21 @@
             currentOffset += 5;
             break;
           }
-        case Constants.NEW:
-        case Constants.ANEWARRAY:
-        case Constants.CHECKCAST:
-        case Constants.INSTANCEOF:
+        case Opcodes.NEW:
+        case Opcodes.ANEWARRAY:
+        case Opcodes.CHECKCAST:
+        case Opcodes.INSTANCEOF:
           methodVisitor.visitTypeInsn(opcode, readClass(currentOffset + 1, charBuffer));
           currentOffset += 3;
           break;
-        case Constants.IINC:
+        case Opcodes.IINC:
           methodVisitor.visitIincInsn(
-              classFileBuffer[currentOffset + 1] & 0xFF, classFileBuffer[currentOffset + 2]);
+              classBuffer[currentOffset + 1] & 0xFF, classBuffer[currentOffset + 2]);
           currentOffset += 3;
           break;
-        case Constants.MULTIANEWARRAY:
+        case Opcodes.MULTIANEWARRAY:
           methodVisitor.visitMultiANewArrayInsn(
-              readClass(currentOffset + 1, charBuffer), classFileBuffer[currentOffset + 3] & 0xFF);
+              readClass(currentOffset + 1, charBuffer), classBuffer[currentOffset + 3] & 0xFF);
           currentOffset += 4;
           break;
         default:
@@ -2555,7 +2765,7 @@
       int pathLength = readByte(currentOffset);
       if ((targetType >>> 24) == TypeReference.EXCEPTION_PARAMETER) {
         // Parse the target_path structure and create a corresponding TypePath.
-        TypePath path = pathLength == 0 ? null : new TypePath(b, currentOffset);
+        TypePath path = pathLength == 0 ? null : new TypePath(classFileBuffer, currentOffset);
         currentOffset += 1 + 2 * pathLength;
         // Parse the type_index field.
         String annotationDescriptor = readUTF8(currentOffset, charBuffer);
@@ -2588,7 +2798,7 @@
    * -1 if there is no such type_annotation of if it does not have a bytecode offset.
    *
    * @param typeAnnotationOffsets the offset of each 'type_annotation' entry in a
-   *     Runtime[In]VisibleTypeAnnotations attribute, or null.
+   *     Runtime[In]VisibleTypeAnnotations attribute, or {@literal null}.
    * @param typeAnnotationIndex the index a 'type_annotation' entry in typeAnnotationOffsets.
    * @return bytecode offset corresponding to the specified JVMS 'type_annotation' structure, or -1
    *     if there is no such type_annotation of if it does not have a bytecode offset.
@@ -2680,7 +2890,7 @@
     // Parse and store the target_path structure.
     int pathLength = readByte(currentOffset);
     context.currentTypeAnnotationTargetPath =
-        pathLength == 0 ? null : new TypePath(b, currentOffset);
+        pathLength == 0 ? null : new TypePath(classFileBuffer, currentOffset);
     // Return the start offset of the rest of the type_annotation structure.
     return currentOffset + 1 + 2 * pathLength;
   }
@@ -2702,7 +2912,7 @@
       final int runtimeParameterAnnotationsOffset,
       final boolean visible) {
     int currentOffset = runtimeParameterAnnotationsOffset;
-    int numParameters = b[currentOffset++] & 0xFF;
+    int numParameters = classFileBuffer[currentOffset++] & 0xFF;
     methodVisitor.visitAnnotableParameterCount(numParameters, visible);
     char[] charBuffer = context.charBuffer;
     for (int i = 0; i < numParameters; ++i) {
@@ -2770,8 +2980,8 @@
    * Reads a JVMS 'element_value' structure and makes the given visitor visit it.
    *
    * @param annotationVisitor the visitor that must visit the element_value structure.
-   * @param elementValueOffset the start offset in {@link #b} of the element_value structure to be
-   *     read.
+   * @param elementValueOffset the start offset in {@link #classFileBuffer} of the element_value
+   *     structure to be read.
    * @param elementName the name of the element_value structure to be read, or {@literal null}.
    * @param charBuffer the buffer used to read strings in the constant pool.
    * @return the end offset of the JVMS 'element_value' structure.
@@ -2783,7 +2993,7 @@
       final char[] charBuffer) {
     int currentOffset = elementValueOffset;
     if (annotationVisitor == null) {
-      switch (b[currentOffset] & 0xFF) {
+      switch (classFileBuffer[currentOffset] & 0xFF) {
         case 'e': // enum_const_value
           return currentOffset + 5;
         case '@': // annotation_value
@@ -2794,7 +3004,7 @@
           return currentOffset + 3;
       }
     }
-    switch (b[currentOffset++] & 0xFF) {
+    switch (classFileBuffer[currentOffset++] & 0xFF) {
       case 'B': // const_value_index, CONSTANT_Integer
         annotationVisitor.visit(
             elementName, (byte) readInt(cpInfoOffsets[readUnsignedShort(currentOffset)]));
@@ -2860,7 +3070,7 @@
               /* named = */ false,
               charBuffer);
         }
-        switch (b[currentOffset] & 0xFF) {
+        switch (classFileBuffer[currentOffset] & 0xFF) {
           case 'B':
             byte[] byteValues = new byte[numValues];
             for (int i = 0; i < numValues; i++) {
@@ -3022,9 +3232,9 @@
    * object. This method can also be used to read a full_frame structure, excluding its frame_type
    * field (this is used to parse the legacy StackMap attributes).
    *
-   * @param stackMapFrameOffset the start offset in {@link #b} of the stack_map_frame_value
-   *     structure to be read, or the start offset of a full_frame structure (excluding its
-   *     frame_type field).
+   * @param stackMapFrameOffset the start offset in {@link #classFileBuffer} of the
+   *     stack_map_frame_value structure to be read, or the start offset of a full_frame structure
+   *     (excluding its frame_type field).
    * @param compressed true to read a 'stack_map_frame' structure, false to read a 'full_frame'
    *     structure without its frame_type field.
    * @param expand if the stack map frame must be expanded. See {@link #EXPAND_FRAMES}.
@@ -3042,7 +3252,7 @@
     int frameType;
     if (compressed) {
       // Read the frame_type field.
-      frameType = b[currentOffset++] & 0xFF;
+      frameType = classFileBuffer[currentOffset++] & 0xFF;
     } else {
       frameType = Frame.FULL_FRAME;
       context.currentFrameOffset = -1;
@@ -3137,7 +3347,7 @@
       final char[] charBuffer,
       final Label[] labels) {
     int currentOffset = verificationTypeInfoOffset;
-    int tag = b[currentOffset++] & 0xFF;
+    int tag = classFileBuffer[currentOffset++] & 0xFF;
     switch (tag) {
       case Frame.ITEM_TOP:
         frame[index] = Opcodes.TOP;
@@ -3179,9 +3389,11 @@
   // ----------------------------------------------------------------------------------------------
 
   /**
-   * Returns the offset in {@link #b} of the first ClassFile's 'attributes' array field entry.
+   * Returns the offset in {@link #classFileBuffer} of the first ClassFile's 'attributes' array
+   * field entry.
    *
-   * @return the offset in {@link #b} of the first ClassFile's 'attributes' array field entry.
+   * @return the offset in {@link #classFileBuffer} of the first ClassFile's 'attributes' array
+   *     field entry.
    */
   final int getFirstAttributeOffset() {
     // Skip the access_flags, this_class, super_class, and interfaces_count fields (using 2 bytes
@@ -3228,7 +3440,7 @@
    *
    * @param maxStringLength a conservative estimate of the maximum length of the strings contained
    *     in the constant pool of the class.
-   * @return the offsets of the bootstrap methods or null.
+   * @return the offsets of the bootstrap methods.
    */
   private int[] readBootstrapMethodsAttribute(final int maxStringLength) {
     char[] charBuffer = new char[maxStringLength];
@@ -3255,23 +3467,25 @@
       }
       currentAttributeOffset += attributeLength;
     }
-    return null;
+    throw new IllegalArgumentException();
   }
 
   /**
-   * Reads a non standard JVMS 'attribute' structure in {@link #b}.
+   * Reads a non standard JVMS 'attribute' structure in {@link #classFileBuffer}.
    *
    * @param attributePrototypes prototypes of the attributes that must be parsed during the visit of
    *     the class. Any attribute whose type is not equal to the type of one the prototypes will not
    *     be parsed: its byte array value will be passed unchanged to the ClassWriter.
    * @param type the type of the attribute.
-   * @param offset the start offset of the JVMS 'attribute' structure in {@link #b}. The 6 attribute
-   *     header bytes (attribute_name_index and attribute_length) are not taken into account here.
+   * @param offset the start offset of the JVMS 'attribute' structure in {@link #classFileBuffer}.
+   *     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 read strings in the constant pool.
-   * @param codeAttributeOffset the start offset of the enclosing Code attribute in {@link #b}, 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 codeAttributeOffset the start offset of the enclosing Code attribute in {@link
+   *     #classFileBuffer}, 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.
    * @return the attribute that has been read.
@@ -3307,13 +3521,14 @@
   }
 
   /**
-   * Returns the start offset in {@link #b} of a JVMS 'cp_info' structure (i.e. a constant pool
-   * entry), plus one. <i>This method is intended for {@link Attribute} sub classes, and is normally
-   * not needed by class generators or adapters.</i>
+   * Returns the start offset in this {@link ClassReader} of a JVMS 'cp_info' structure (i.e. a
+   * constant pool entry), plus one. <i>This method is intended for {@link Attribute} sub classes,
+   * and is normally not needed by class generators or adapters.</i>
    *
    * @param constantPoolEntryIndex the index a constant pool entry in the class's constant pool
    *     table.
-   * @return the start offset in {@link #b} of the corresponding JVMS 'cp_info' structure, plus one.
+   * @return the start offset in this {@link ClassReader} of the corresponding JVMS 'cp_info'
+   *     structure, plus one.
    */
   public int getItem(final int constantPoolEntryIndex) {
     return cpInfoOffsets[constantPoolEntryIndex];
@@ -3331,60 +3546,60 @@
   }
 
   /**
-   * Reads a byte value in {@link #b}. <i>This method is intended for {@link Attribute} sub classes,
-   * and is normally not needed by class generators or adapters.</i>
+   * Reads a byte 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>
    *
-   * @param offset the start offset of the value to be read in {@link #b}.
+   * @param offset the start offset of the value to be read in this {@link ClassReader}.
    * @return the read value.
    */
   public int readByte(final int offset) {
-    return b[offset] & 0xFF;
+    return classFileBuffer[offset] & 0xFF;
   }
 
   /**
-   * Reads an unsigned short value in {@link #b}. <i>This method is intended for {@link Attribute}
-   * sub classes, and is normally not needed by class generators or adapters.</i>
+   * 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>
    *
-   * @param offset the start index of the value to be read in {@link #b}.
+   * @param offset the start index of the value to be read in this {@link ClassReader}.
    * @return the read value.
    */
   public int readUnsignedShort(final int offset) {
-    byte[] classFileBuffer = b;
-    return ((classFileBuffer[offset] & 0xFF) << 8) | (classFileBuffer[offset + 1] & 0xFF);
+    byte[] classBuffer = classFileBuffer;
+    return ((classBuffer[offset] & 0xFF) << 8) | (classBuffer[offset + 1] & 0xFF);
   }
 
   /**
-   * Reads a signed short value in {@link #b}. <i>This method is intended for {@link Attribute} sub
-   * classes, and is normally not needed by class generators or adapters.</i>
+   * Reads a signed 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>
    *
-   * @param offset the start offset of the value to be read in {@link #b}.
+   * @param offset the start offset of the value to be read in this {@link ClassReader}.
    * @return the read value.
    */
   public short readShort(final int offset) {
-    byte[] classFileBuffer = b;
-    return (short) (((classFileBuffer[offset] & 0xFF) << 8) | (classFileBuffer[offset + 1] & 0xFF));
+    byte[] classBuffer = classFileBuffer;
+    return (short) (((classBuffer[offset] & 0xFF) << 8) | (classBuffer[offset + 1] & 0xFF));
   }
 
   /**
-   * Reads a signed int value in {@link #b}. <i>This method is intended for {@link Attribute} sub
-   * classes, and is normally not needed by class generators or adapters.</i>
+   * Reads a signed int 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>
    *
-   * @param offset the start offset of the value to be read in {@link #b}.
+   * @param offset the start offset of the value to be read in this {@link ClassReader}.
    * @return the read value.
    */
   public int readInt(final int offset) {
-    byte[] classFileBuffer = b;
-    return ((classFileBuffer[offset] & 0xFF) << 24)
-        | ((classFileBuffer[offset + 1] & 0xFF) << 16)
-        | ((classFileBuffer[offset + 2] & 0xFF) << 8)
-        | (classFileBuffer[offset + 3] & 0xFF);
+    byte[] classBuffer = classFileBuffer;
+    return ((classBuffer[offset] & 0xFF) << 24)
+        | ((classBuffer[offset + 1] & 0xFF) << 16)
+        | ((classBuffer[offset + 2] & 0xFF) << 8)
+        | (classBuffer[offset + 3] & 0xFF);
   }
 
   /**
-   * Reads a signed long value in {@link #b}. <i>This method is intended for {@link Attribute} sub
-   * classes, and is normally not needed by class generators or adapters.</i>
+   * Reads a signed long 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>
    *
-   * @param offset the start offset of the value to be read in {@link #b}.
+   * @param offset the start offset of the value to be read in this {@link ClassReader}.
    * @return the read value.
    */
   public long readLong(final int offset) {
@@ -3394,11 +3609,12 @@
   }
 
   /**
-   * Reads a CONSTANT_Utf8 constant pool entry in {@link #b}. <i>This method is intended for {@link
-   * Attribute} sub classes, and is normally not needed by class generators or adapters.</i>
+   * Reads a CONSTANT_Utf8 constant pool entry 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 an unsigned short value in {@link #b}, whose value is the
-   *     index of a CONSTANT_Utf8 entry in the class's constant pool table.
+   * @param offset the start offset of an unsigned short value in this {@link ClassReader}, whose
+   *     value is the index of a CONSTANT_Utf8 entry in the class's constant pool table.
    * @param charBuffer the buffer to be used to read the string. This buffer must be sufficiently
    *     large. It is not automatically resized.
    * @return the String corresponding to the specified CONSTANT_Utf8 entry.
@@ -3413,7 +3629,7 @@
   }
 
   /**
-   * Reads a CONSTANT_Utf8 constant pool entry in {@link #b}.
+   * Reads a CONSTANT_Utf8 constant pool entry in {@link #classFileBuffer}.
    *
    * @param constantPoolEntryIndex the index of a CONSTANT_Utf8 entry in the class's constant pool
    *     table.
@@ -3432,7 +3648,7 @@
   }
 
   /**
-   * Reads an UTF8 string in {@link #b}.
+   * Reads an UTF8 string in {@link #classFileBuffer}.
    *
    * @param utfOffset the start offset of the UTF8 string to be read.
    * @param utfLength the length of the UTF8 string to be read.
@@ -3444,20 +3660,20 @@
     int currentOffset = utfOffset;
     int endOffset = currentOffset + utfLength;
     int strLength = 0;
-    byte[] classFileBuffer = b;
+    byte[] classBuffer = classFileBuffer;
     while (currentOffset < endOffset) {
-      int currentByte = classFileBuffer[currentOffset++];
+      int currentByte = classBuffer[currentOffset++];
       if ((currentByte & 0x80) == 0) {
         charBuffer[strLength++] = (char) (currentByte & 0x7F);
       } else if ((currentByte & 0xE0) == 0xC0) {
         charBuffer[strLength++] =
-            (char) (((currentByte & 0x1F) << 6) + (classFileBuffer[currentOffset++] & 0x3F));
+            (char) (((currentByte & 0x1F) << 6) + (classBuffer[currentOffset++] & 0x3F));
       } else {
         charBuffer[strLength++] =
             (char)
                 (((currentByte & 0xF) << 12)
-                    + ((classFileBuffer[currentOffset++] & 0x3F) << 6)
-                    + (classFileBuffer[currentOffset++] & 0x3F));
+                    + ((classBuffer[currentOffset++] & 0x3F) << 6)
+                    + (classBuffer[currentOffset++] & 0x3F));
       }
     }
     return new String(charBuffer, 0, strLength);
@@ -3465,12 +3681,13 @@
 
   /**
    * Reads a CONSTANT_Class, CONSTANT_String, CONSTANT_MethodType, CONSTANT_Module or
-   * CONSTANT_Package constant pool entry in {@link #b}. <i>This method is intended for {@link
-   * Attribute} sub classes, and is normally not needed by class generators or adapters.</i>
+   * CONSTANT_Package constant pool entry in {@link #classFileBuffer}. <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 an unsigned short value in {@link #b}, whose value is the
-   *     index of a CONSTANT_Class, CONSTANT_String, CONSTANT_MethodType, CONSTANT_Module or
-   *     CONSTANT_Package entry in class's constant pool table.
+   * @param offset the start offset of an unsigned short value in {@link #classFileBuffer}, whose
+   *     value is the index of a CONSTANT_Class, CONSTANT_String, CONSTANT_MethodType,
+   *     CONSTANT_Module or CONSTANT_Package entry in class's constant pool table.
    * @param charBuffer the buffer to be used to read the item. This buffer must be sufficiently
    *     large. It is not automatically resized.
    * @return the String corresponding to the specified constant pool entry.
@@ -3482,11 +3699,12 @@
   }
 
   /**
-   * Reads a CONSTANT_Class constant pool entry in {@link #b}. <i>This method is intended for {@link
-   * Attribute} sub classes, and is normally not needed by class generators or adapters.</i>
+   * Reads a CONSTANT_Class constant pool entry 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 an unsigned short value in {@link #b}, whose value is the
-   *     index of a CONSTANT_Class entry in class's constant pool table.
+   * @param offset the start offset of an unsigned short value in this {@link ClassReader}, whose
+   *     value is the index of a CONSTANT_Class entry in class's constant pool table.
    * @param charBuffer the buffer to be used to read the item. This buffer must be sufficiently
    *     large. It is not automatically resized.
    * @return the String corresponding to the specified CONSTANT_Class entry.
@@ -3496,11 +3714,12 @@
   }
 
   /**
-   * Reads a CONSTANT_Module constant pool entry in {@link #b}. <i>This method is intended for
-   * {@link Attribute} sub classes, and is normally not needed by class generators or adapters.</i>
+   * Reads a CONSTANT_Module constant pool entry 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 an unsigned short value in {@link #b}, whose value is the
-   *     index of a CONSTANT_Module entry in class's constant pool table.
+   * @param offset the start offset of an unsigned short value in this {@link ClassReader}, whose
+   *     value is the index of a CONSTANT_Module entry in class's constant pool table.
    * @param charBuffer the buffer to be used to read the item. This buffer must be sufficiently
    *     large. It is not automatically resized.
    * @return the String corresponding to the specified CONSTANT_Module entry.
@@ -3510,11 +3729,12 @@
   }
 
   /**
-   * Reads a CONSTANT_Package constant pool entry in {@link #b}. <i>This method is intended for
-   * {@link Attribute} sub classes, and is normally not needed by class generators or adapters.</i>
+   * Reads a CONSTANT_Package constant pool entry 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 an unsigned short value in {@link #b}, whose value is the
-   *     index of a CONSTANT_Package entry in class's constant pool table.
+   * @param offset the start offset of an unsigned short value in this {@link ClassReader}, whose
+   *     value is the index of a CONSTANT_Package entry in class's constant pool table.
    * @param charBuffer the buffer to be used to read the item. This buffer must be sufficiently
    *     large. It is not automatically resized.
    * @return the String corresponding to the specified CONSTANT_Package entry.
@@ -3524,7 +3744,7 @@
   }
 
   /**
-   * Reads a CONSTANT_Dynamic constant pool entry in {@link #b}.
+   * Reads a CONSTANT_Dynamic constant pool entry in {@link #classFileBuffer}.
    *
    * @param constantPoolEntryIndex the index of a CONSTANT_Dynamic entry in the class's constant
    *     pool table.
@@ -3555,8 +3775,9 @@
   }
 
   /**
-   * Reads a numeric or string constant pool entry in {@link #b}. <i>This method is intended for
-   * {@link Attribute} sub classes, and is normally not needed by class generators or adapters.</i>
+   * Reads a numeric or string constant pool entry 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 constantPoolEntryIndex the index of a CONSTANT_Integer, CONSTANT_Float, CONSTANT_Long,
    *     CONSTANT_Double, CONSTANT_Class, CONSTANT_String, CONSTANT_MethodType,
@@ -3569,7 +3790,7 @@
    */
   public Object readConst(final int constantPoolEntryIndex, final char[] charBuffer) {
     int cpInfoOffset = cpInfoOffsets[constantPoolEntryIndex];
-    switch (b[cpInfoOffset - 1]) {
+    switch (classFileBuffer[cpInfoOffset - 1]) {
       case Symbol.CONSTANT_INTEGER_TAG:
         return readInt(cpInfoOffset);
       case Symbol.CONSTANT_FLOAT_TAG:
@@ -3592,7 +3813,7 @@
         String name = readUTF8(nameAndTypeCpInfoOffset, charBuffer);
         String descriptor = readUTF8(nameAndTypeCpInfoOffset + 2, charBuffer);
         boolean isInterface =
-            b[referenceCpInfoOffset - 1] == Symbol.CONSTANT_INTERFACE_METHODREF_TAG;
+            classFileBuffer[referenceCpInfoOffset - 1] == Symbol.CONSTANT_INTERFACE_METHODREF_TAG;
         return new Handle(referenceKind, owner, name, descriptor, isInterface);
       case Symbol.CONSTANT_DYNAMIC_TAG:
         return readConstantDynamic(constantPoolEntryIndex, charBuffer);
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/ClassTooLargeException.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/ClassTooLargeException.java
old mode 100755
new mode 100644
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/ClassVisitor.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/ClassVisitor.java
old mode 100755
new mode 100644
index 8631280f..235ef95
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/ClassVisitor.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/ClassVisitor.java
@@ -30,9 +30,9 @@
 /**
  * A visitor to visit a Java class. The methods of this class must be called in the following order:
  * {@code visit} [ {@code visitSource} ] [ {@code visitModule} ][ {@code visitNestHost} ][ {@code
- * visitOuterClass} ] ( {@code visitAnnotation} | {@code visitTypeAnnotation} | {@code
- * visitAttribute} )* ( {@code visitNestMember} | {@code visitInnerClass} | {@code visitField} |
- * {@code visitMethod} )* {@code visitEnd}.
+ * visitPermittedSubtype} ][ {@code visitOuterClass} ] ( {@code visitAnnotation} | {@code
+ * visitTypeAnnotation} | {@code visitAttribute} )* ( {@code visitNestMember} | {@code
+ * visitInnerClass} | {@code visitField} | {@code visitMethod} )* {@code visitEnd}.
  *
  * @author Eric Bruneton
  */
@@ -44,7 +44,7 @@
    */
   protected final int api;
 
-  /** The class visitor to which this visitor must delegate method calls. May be null. */
+  /** The class visitor to which this visitor must delegate method calls. May be {@literal null}. */
   protected ClassVisitor cv;
 
   /**
@@ -61,13 +61,22 @@
    * Constructs a new {@link ClassVisitor}.
    *
    * @param api the ASM API version implemented by this visitor. Must be one of {@link
-   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
+   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7} or {@link
+   *     Opcodes#ASM8}.
    * @param classVisitor the class visitor to which this visitor must delegate method calls. May be
    *     null.
    */
   public ClassVisitor(final int api, final ClassVisitor classVisitor) {
-    if (api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4 && api != Opcodes.ASM7) {
-      throw new IllegalArgumentException();
+    if (api != Opcodes.ASM8
+        && api != Opcodes.ASM7
+        && api != Opcodes.ASM6
+        && api != Opcodes.ASM5
+        && api != Opcodes.ASM4
+        && api != Opcodes.ASM9_EXPERIMENTAL) {
+      throw new IllegalArgumentException("Unsupported api " + api);
+    }
+    if (api == Opcodes.ASM9_EXPERIMENTAL) {
+      Constants.checkAsmExperimental(this);
     }
     this.api = api;
     this.cv = classVisitor;
@@ -79,7 +88,8 @@
    * @param version the class version. The minor version is stored in the 16 most significant bits,
    *     and the major version in the 16 least significant bits.
    * @param access the class's access flags (see {@link Opcodes}). This parameter also indicates if
-   *     the class is deprecated.
+   *     the class is deprecated {@link Opcodes#ACC_DEPRECATED} or a record {@link
+   *     Opcodes#ACC_RECORD}.
    * @param name the internal name of the class (see {@link Type#getInternalName()}).
    * @param signature the signature of this class. May be {@literal null} if the class is not a
    *     generic one, and does not extend or implement generic classes or interfaces.
@@ -96,6 +106,9 @@
       final String signature,
       final String superName,
       final String[] interfaces) {
+    if (api < Opcodes.ASM8 && (access & Opcodes.ACC_RECORD) != 0) {
+      throw new UnsupportedOperationException("Records requires ASM8");
+    }
     if (cv != null) {
       cv.visit(version, access, name, signature, superName, interfaces);
     }
@@ -241,6 +254,24 @@
   }
 
   /**
+   * <b>Experimental, use at your own risk. This method will be renamed when it becomes stable, this
+   * will break existing code using it</b>. Visits a permitted subtypes. A permitted subtypes is one
+   * of the allowed subtypes of the current class.
+   *
+   * @param permittedSubtype the internal name of a permitted subtype.
+   * @deprecated this API is experimental.
+   */
+  @Deprecated
+  public void visitPermittedSubtypeExperimental(final String permittedSubtype) {
+    if (api != Opcodes.ASM9_EXPERIMENTAL) {
+      throw new UnsupportedOperationException("This feature requires ASM9_EXPERIMENTAL");
+    }
+    if (cv != null) {
+      cv.visitPermittedSubtypeExperimental(permittedSubtype);
+    }
+  }
+
+  /**
    * Visits information about an inner class. This inner class is not necessarily a member of the
    * class being visited.
    *
@@ -260,6 +291,27 @@
   }
 
   /**
+   * Visits a record component of the class.
+   *
+   * @param name the record component name.
+   * @param descriptor the record component descriptor (see {@link Type}).
+   * @param signature the record component signature. May be {@literal null} if the record component
+   *     type does not use generic types.
+   * @return a visitor to visit this record component annotations and attributes, or {@literal null}
+   *     if this class visitor is not interested in visiting these annotations and attributes.
+   */
+  public RecordComponentVisitor visitRecordComponent(
+      final String name, final String descriptor, final String signature) {
+    if (api < Opcodes.ASM8) {
+      throw new UnsupportedOperationException("This feature requires ASM8");
+    }
+    if (cv != null) {
+      return cv.visitRecordComponent(name, descriptor, signature);
+    }
+    return null;
+  }
+
+  /**
    * Visits a field of the class.
    *
    * @param access the field's access flags (see {@link Opcodes}). This parameter also indicates if
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
old mode 100755
new mode 100644
index 2416289..35faa16
--- 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
@@ -79,8 +79,8 @@
 
   /**
    * The access_flags field of the JVMS ClassFile structure. This field can contain ASM specific
-   * access flags, such as {@link Opcodes#ACC_DEPRECATED}, which are removed when generating the
-   * ClassFile structure.
+   * access flags, such as {@link Opcodes#ACC_DEPRECATED} or {}@link Opcodes#ACC_RECORD}, which are
+   * removed when generating the ClassFile structure.
    */
   private int accessFlags;
 
@@ -177,6 +177,26 @@
   /** The 'classes' array of the NestMembers attribute, or {@literal null}. */
   private ByteVector nestMemberClasses;
 
+  /** The number_of_classes field of the PermittedSubtypes attribute, or 0. */
+  private int numberOfPermittedSubtypeClasses;
+
+  /** The 'classes' array of the PermittedSubtypes attribute, or {@literal null}. */
+  private ByteVector permittedSubtypeClasses;
+
+  /**
+   * The record components of this class, stored in a linked list of {@link RecordComponentWriter}
+   * linked via their {@link RecordComponentWriter#delegate} field. This field stores the first
+   * element of this list.
+   */
+  private RecordComponentWriter firstRecordComponent;
+
+  /**
+   * The record components of this class, stored in a linked list of {@link RecordComponentWriter}
+   * linked via their {@link RecordComponentWriter#delegate} field. This field stores the last
+   * element of this list.
+   */
+  private RecordComponentWriter lastRecordComponent;
+
   /**
    * The first non standard attribute of this class. The next ones can be accessed with the {@link
    * Attribute#nextAttribute} field. May be {@literal null}.
@@ -234,7 +254,7 @@
    *     maximum stack size nor the stack frames will be computed for these methods</i>.
    */
   public ClassWriter(final ClassReader classReader, final int flags) {
-    super(Opcodes.ASM7);
+    super(/* latest api = */ Opcodes.ASM8);
     symbolTable = classReader == null ? new SymbolTable(this) : new SymbolTable(this, classReader);
     if ((flags & COMPUTE_FRAMES) != 0) {
       this.compute = MethodWriter.COMPUTE_ALL_FRAMES;
@@ -298,7 +318,7 @@
   }
 
   @Override
-  public void visitNestHost(final String nestHost) {
+  public final void visitNestHost(final String nestHost) {
     nestHostClassIndex = symbolTable.addConstantClass(nestHost).index;
   }
 
@@ -313,37 +333,26 @@
 
   @Override
   public final AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
-    // Create a ByteVector to hold an 'annotation' JVMS structure.
-    // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.
-    ByteVector annotation = new ByteVector();
-    // Write type_index and reserve space for num_element_value_pairs.
-    annotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0);
     if (visible) {
       return lastRuntimeVisibleAnnotation =
-          new AnnotationWriter(symbolTable, annotation, lastRuntimeVisibleAnnotation);
+          AnnotationWriter.create(symbolTable, descriptor, lastRuntimeVisibleAnnotation);
     } else {
       return lastRuntimeInvisibleAnnotation =
-          new AnnotationWriter(symbolTable, annotation, lastRuntimeInvisibleAnnotation);
+          AnnotationWriter.create(symbolTable, descriptor, lastRuntimeInvisibleAnnotation);
     }
   }
 
   @Override
   public final AnnotationVisitor visitTypeAnnotation(
       final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
-    // Create a ByteVector to hold a 'type_annotation' JVMS structure.
-    // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20.
-    ByteVector typeAnnotation = new ByteVector();
-    // Write target_type, target_info, and target_path.
-    TypeReference.putTarget(typeRef, typeAnnotation);
-    TypePath.put(typePath, typeAnnotation);
-    // Write type_index and reserve space for num_element_value_pairs.
-    typeAnnotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0);
     if (visible) {
       return lastRuntimeVisibleTypeAnnotation =
-          new AnnotationWriter(symbolTable, typeAnnotation, lastRuntimeVisibleTypeAnnotation);
+          AnnotationWriter.create(
+              symbolTable, typeRef, typePath, descriptor, lastRuntimeVisibleTypeAnnotation);
     } else {
       return lastRuntimeInvisibleTypeAnnotation =
-          new AnnotationWriter(symbolTable, typeAnnotation, lastRuntimeInvisibleTypeAnnotation);
+          AnnotationWriter.create(
+              symbolTable, typeRef, typePath, descriptor, lastRuntimeInvisibleTypeAnnotation);
     }
   }
 
@@ -355,7 +364,7 @@
   }
 
   @Override
-  public void visitNestMember(final String nestMember) {
+  public final void visitNestMember(final String nestMember) {
     if (nestMemberClasses == null) {
       nestMemberClasses = new ByteVector();
     }
@@ -363,6 +372,22 @@
     nestMemberClasses.putShort(symbolTable.addConstantClass(nestMember).index);
   }
 
+  /**
+   * <b>Experimental, use at your own risk.</b>
+   *
+   * @param permittedSubtype the internal name of a permitted subtype.
+   * @deprecated this API is experimental.
+   */
+  @Override
+  @Deprecated
+  public final void visitPermittedSubtypeExperimental(final String permittedSubtype) {
+    if (permittedSubtypeClasses == null) {
+      permittedSubtypeClasses = new ByteVector();
+    }
+    ++numberOfPermittedSubtypeClasses;
+    permittedSubtypeClasses.putShort(symbolTable.addConstantClass(permittedSubtype).index);
+  }
+
   @Override
   public final void visitInnerClass(
       final String name, final String outerName, final String innerName, final int access) {
@@ -389,6 +414,19 @@
   }
 
   @Override
+  public final RecordComponentVisitor visitRecordComponent(
+      final String name, final String descriptor, final String signature) {
+    RecordComponentWriter recordComponentWriter =
+        new RecordComponentWriter(symbolTable, name, descriptor, signature);
+    if (firstRecordComponent == null) {
+      firstRecordComponent = recordComponentWriter;
+    } else {
+      lastRecordComponent.delegate = recordComponentWriter;
+    }
+    return lastRecordComponent = recordComponentWriter;
+  }
+
+  @Override
   public final FieldVisitor visitField(
       final int access,
       final String name,
@@ -438,7 +476,7 @@
    * @throws ClassTooLargeException if the constant pool of the class is too large.
    * @throws MethodTooLargeException if the Code attribute of a method is too large.
    */
-  public byte[] toByteArray() throws ClassTooLargeException, MethodTooLargeException {
+  public byte[] toByteArray() {
     // First step: compute the size in bytes of the ClassFile structure.
     // The magic field uses 4 bytes, 10 mandatory fields (minor_version, major_version,
     // constant_pool_count, access_flags, this_class, super_class, interfaces_count, fields_count,
@@ -458,6 +496,7 @@
       size += methodWriter.computeMethodInfoSize();
       methodWriter = (MethodWriter) methodWriter.mv;
     }
+
     // For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS.
     int attributesCount = 0;
     if (innerClasses != null) {
@@ -537,6 +576,24 @@
       size += 8 + nestMemberClasses.length;
       symbolTable.addConstantUtf8(Constants.NEST_MEMBERS);
     }
+    if (permittedSubtypeClasses != null) {
+      ++attributesCount;
+      size += 8 + permittedSubtypeClasses.length;
+      symbolTable.addConstantUtf8(Constants.PERMITTED_SUBTYPES);
+    }
+    int recordComponentCount = 0;
+    int recordSize = 0;
+    if ((accessFlags & Opcodes.ACC_RECORD) != 0 || firstRecordComponent != null) {
+      RecordComponentWriter recordComponentWriter = firstRecordComponent;
+      while (recordComponentWriter != null) {
+        ++recordComponentCount;
+        recordSize += recordComponentWriter.computeRecordComponentInfoSize();
+        recordComponentWriter = (RecordComponentWriter) recordComponentWriter.delegate;
+      }
+      ++attributesCount;
+      size += 8 + recordSize;
+      symbolTable.addConstantUtf8(Constants.RECORD);
+    }
     if (firstAttribute != null) {
       attributesCount += firstAttribute.getAttributeCount();
       size += firstAttribute.computeAttributesSize(symbolTable);
@@ -617,22 +674,13 @@
     if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) {
       result.putShort(symbolTable.addConstantUtf8(Constants.DEPRECATED)).putInt(0);
     }
-    if (lastRuntimeVisibleAnnotation != null) {
-      lastRuntimeVisibleAnnotation.putAnnotations(
-          symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_ANNOTATIONS), result);
-    }
-    if (lastRuntimeInvisibleAnnotation != null) {
-      lastRuntimeInvisibleAnnotation.putAnnotations(
-          symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_ANNOTATIONS), result);
-    }
-    if (lastRuntimeVisibleTypeAnnotation != null) {
-      lastRuntimeVisibleTypeAnnotation.putAnnotations(
-          symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS), result);
-    }
-    if (lastRuntimeInvisibleTypeAnnotation != null) {
-      lastRuntimeInvisibleTypeAnnotation.putAnnotations(
-          symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS), result);
-    }
+    AnnotationWriter.putAnnotations(
+        symbolTable,
+        lastRuntimeVisibleAnnotation,
+        lastRuntimeInvisibleAnnotation,
+        lastRuntimeVisibleTypeAnnotation,
+        lastRuntimeInvisibleTypeAnnotation,
+        result);
     symbolTable.putBootstrapMethods(result);
     if (moduleWriter != null) {
       moduleWriter.putAttributes(result);
@@ -650,6 +698,24 @@
           .putShort(numberOfNestMemberClasses)
           .putByteArray(nestMemberClasses.data, 0, nestMemberClasses.length);
     }
+    if (permittedSubtypeClasses != null) {
+      result
+          .putShort(symbolTable.addConstantUtf8(Constants.PERMITTED_SUBTYPES))
+          .putInt(permittedSubtypeClasses.length + 2)
+          .putShort(numberOfPermittedSubtypeClasses)
+          .putByteArray(permittedSubtypeClasses.data, 0, permittedSubtypeClasses.length);
+    }
+    if ((accessFlags & Opcodes.ACC_RECORD) != 0 || firstRecordComponent != null) {
+      result
+          .putShort(symbolTable.addConstantUtf8(Constants.RECORD))
+          .putInt(recordSize + 2)
+          .putShort(recordComponentCount);
+      RecordComponentWriter recordComponentWriter = firstRecordComponent;
+      while (recordComponentWriter != null) {
+        recordComponentWriter.putRecordComponentInfo(result);
+        recordComponentWriter = (RecordComponentWriter) recordComponentWriter.delegate;
+      }
+    }
     if (firstAttribute != null) {
       firstAttribute.putAttributes(symbolTable, result);
     }
@@ -686,6 +752,10 @@
     nestHostClassIndex = 0;
     numberOfNestMemberClasses = 0;
     nestMemberClasses = null;
+    numberOfPermittedSubtypeClasses = 0;
+    permittedSubtypeClasses = null;
+    firstRecordComponent = null;
+    lastRecordComponent = null;
     firstAttribute = null;
     compute = hasFrames ? MethodWriter.COMPUTE_INSERTED_FRAMES : MethodWriter.COMPUTE_NOTHING;
     new ClassReader(classFile, 0, /* checkClassVersion = */ false)
@@ -714,6 +784,11 @@
       methodWriter.collectAttributePrototypes(attributePrototypes);
       methodWriter = (MethodWriter) methodWriter.mv;
     }
+    RecordComponentWriter recordComponentWriter = firstRecordComponent;
+    while (recordComponentWriter != null) {
+      recordComponentWriter.collectAttributePrototypes(attributePrototypes);
+      recordComponentWriter = (RecordComponentWriter) recordComponentWriter.delegate;
+    }
     return attributePrototypes.toArray();
   }
 
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/ConstantDynamic.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/ConstantDynamic.java
old mode 100755
new mode 100644
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
old mode 100755
new mode 100644
index 7d99004..81edcb9
--- 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
@@ -27,6 +27,11 @@
 // THE POSSIBILITY OF SUCH DAMAGE.
 package org.apache.tapestry5.internal.plastic.asm;
 
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.regex.Pattern;
+
 /**
  * Defines additional JVM opcodes, access flags and constants which are not part of the ASM public
  * API.
@@ -34,7 +39,7 @@
  * @see <a href="https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-6.html">JVMS 6</a>
  * @author Eric Bruneton
  */
-final class Constants implements Opcodes {
+final class Constants {
 
   // The ClassFile attribute names, in the order they are defined in
   // https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.7-300.
@@ -68,6 +73,8 @@
   static final String MODULE_MAIN_CLASS = "ModuleMainClass";
   static final String NEST_HOST = "NestHost";
   static final String NEST_MEMBERS = "NestMembers";
+  static final String PERMITTED_SUBTYPES = "PermittedSubtypes";
+  static final String RECORD = "Record";
 
   // ASM specific access flags.
   // WARNING: the 16 least significant bits must NOT be used, to avoid conflicts with standard
@@ -140,7 +147,7 @@
   // Constants to convert between normal and wide jump instructions.
 
   // The delta between the GOTO_W and JSR_W opcodes and GOTO and JUMP.
-  static final int WIDE_JUMP_OPCODE_DELTA = GOTO_W - GOTO;
+  static final int WIDE_JUMP_OPCODE_DELTA = GOTO_W - Opcodes.GOTO;
 
   // Constants to convert JVM opcodes to the equivalent ASM specific opcodes, and vice versa.
 
@@ -153,25 +160,62 @@
 
   // ASM specific opcodes, used for long forward jump instructions.
 
-  static final int ASM_IFEQ = IFEQ + ASM_OPCODE_DELTA;
-  static final int ASM_IFNE = IFNE + ASM_OPCODE_DELTA;
-  static final int ASM_IFLT = IFLT + ASM_OPCODE_DELTA;
-  static final int ASM_IFGE = IFGE + ASM_OPCODE_DELTA;
-  static final int ASM_IFGT = IFGT + ASM_OPCODE_DELTA;
-  static final int ASM_IFLE = IFLE + ASM_OPCODE_DELTA;
-  static final int ASM_IF_ICMPEQ = IF_ICMPEQ + ASM_OPCODE_DELTA;
-  static final int ASM_IF_ICMPNE = IF_ICMPNE + ASM_OPCODE_DELTA;
-  static final int ASM_IF_ICMPLT = IF_ICMPLT + ASM_OPCODE_DELTA;
-  static final int ASM_IF_ICMPGE = IF_ICMPGE + ASM_OPCODE_DELTA;
-  static final int ASM_IF_ICMPGT = IF_ICMPGT + ASM_OPCODE_DELTA;
-  static final int ASM_IF_ICMPLE = IF_ICMPLE + ASM_OPCODE_DELTA;
-  static final int ASM_IF_ACMPEQ = IF_ACMPEQ + ASM_OPCODE_DELTA;
-  static final int ASM_IF_ACMPNE = IF_ACMPNE + ASM_OPCODE_DELTA;
-  static final int ASM_GOTO = GOTO + ASM_OPCODE_DELTA;
-  static final int ASM_JSR = JSR + ASM_OPCODE_DELTA;
-  static final int ASM_IFNULL = IFNULL + ASM_IFNULL_OPCODE_DELTA;
-  static final int ASM_IFNONNULL = IFNONNULL + ASM_IFNULL_OPCODE_DELTA;
+  static final int ASM_IFEQ = Opcodes.IFEQ + ASM_OPCODE_DELTA;
+  static final int ASM_IFNE = Opcodes.IFNE + ASM_OPCODE_DELTA;
+  static final int ASM_IFLT = Opcodes.IFLT + ASM_OPCODE_DELTA;
+  static final int ASM_IFGE = Opcodes.IFGE + ASM_OPCODE_DELTA;
+  static final int ASM_IFGT = Opcodes.IFGT + ASM_OPCODE_DELTA;
+  static final int ASM_IFLE = Opcodes.IFLE + ASM_OPCODE_DELTA;
+  static final int ASM_IF_ICMPEQ = Opcodes.IF_ICMPEQ + ASM_OPCODE_DELTA;
+  static final int ASM_IF_ICMPNE = Opcodes.IF_ICMPNE + ASM_OPCODE_DELTA;
+  static final int ASM_IF_ICMPLT = Opcodes.IF_ICMPLT + ASM_OPCODE_DELTA;
+  static final int ASM_IF_ICMPGE = Opcodes.IF_ICMPGE + ASM_OPCODE_DELTA;
+  static final int ASM_IF_ICMPGT = Opcodes.IF_ICMPGT + ASM_OPCODE_DELTA;
+  static final int ASM_IF_ICMPLE = Opcodes.IF_ICMPLE + ASM_OPCODE_DELTA;
+  static final int ASM_IF_ACMPEQ = Opcodes.IF_ACMPEQ + ASM_OPCODE_DELTA;
+  static final int ASM_IF_ACMPNE = Opcodes.IF_ACMPNE + ASM_OPCODE_DELTA;
+  static final int ASM_GOTO = Opcodes.GOTO + ASM_OPCODE_DELTA;
+  static final int ASM_JSR = Opcodes.JSR + ASM_OPCODE_DELTA;
+  static final int ASM_IFNULL = Opcodes.IFNULL + ASM_IFNULL_OPCODE_DELTA;
+  static final int ASM_IFNONNULL = Opcodes.IFNONNULL + ASM_IFNULL_OPCODE_DELTA;
   static final int ASM_GOTO_W = 220;
 
   private Constants() {}
+
+  static void checkAsmExperimental(final Object caller) {
+    Class<?> callerClass = caller.getClass();
+    String internalName = callerClass.getName().replace('.', '/');
+    if (!isWhitelisted(internalName)) {
+      checkIsPreview(callerClass.getClassLoader().getResourceAsStream(internalName + ".class"));
+    }
+  }
+
+  static boolean isWhitelisted(final String internalName) {
+    if (!internalName.startsWith("org/objectweb/asm/")) {
+      return false;
+    }
+    String member = "(Annotation|Class|Field|Method|Module|RecordComponent|Signature)";
+    return internalName.contains("Test$")
+        || Pattern.matches(
+            "org/objectweb/asm/util/Trace" + member + "Visitor(\\$.*)?", internalName)
+        || Pattern.matches(
+            "org/objectweb/asm/util/Check" + member + "Adapter(\\$.*)?", internalName);
+  }
+
+  static void checkIsPreview(final InputStream classInputStream) {
+    if (classInputStream == null) {
+      throw new IllegalStateException("Bytecode not available, can't check class version");
+    }
+    int minorVersion;
+    try (DataInputStream callerClassStream = new DataInputStream(classInputStream); ) {
+      callerClassStream.readInt();
+      minorVersion = callerClassStream.readUnsignedShort();
+    } catch (IOException ioe) {
+      throw new IllegalStateException("I/O error, can't check class version", ioe);
+    }
+    if (minorVersion != 0xFFFF) {
+      throw new IllegalStateException(
+          "ASM9_EXPERIMENTAL can only be used by classes compiled with --enable-preview");
+    }
+  }
 }
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Context.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Context.java
old mode 100755
new mode 100644
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/CurrentFrame.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/CurrentFrame.java
old mode 100755
new mode 100644
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Edge.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Edge.java
old mode 100755
new mode 100644
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/FieldVisitor.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/FieldVisitor.java
old mode 100755
new mode 100644
index 26b235d..c5e5195
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/FieldVisitor.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/FieldVisitor.java
@@ -38,18 +38,20 @@
 
   /**
    * The ASM API version implemented by this visitor. The value of this field must be one of {@link
-   * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
+   * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7} or {@link
+   * Opcodes#ASM8}.
    */
   protected final int api;
 
-  /** The field visitor to which this visitor must delegate method calls. May be null. */
+  /** The field visitor to which this visitor must delegate method calls. May be {@literal null}. */
   protected FieldVisitor fv;
 
   /**
    * Constructs a new {@link FieldVisitor}.
    *
    * @param api the ASM API version implemented by this visitor. Must be one of {@link
-   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
+   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7} or {@link
+   *     Opcodes#ASM8}.
    */
   public FieldVisitor(final int api) {
     this(api, null);
@@ -59,13 +61,22 @@
    * Constructs a new {@link FieldVisitor}.
    *
    * @param api the ASM API version implemented by this visitor. Must be one of {@link
-   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
+   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7} or {@link
+   *     Opcodes#ASM8}.
    * @param fieldVisitor the field visitor to which this visitor must delegate method calls. May be
    *     null.
    */
   public FieldVisitor(final int api, final FieldVisitor fieldVisitor) {
-    if (api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4 && api != Opcodes.ASM7) {
-      throw new IllegalArgumentException();
+    if (api != Opcodes.ASM8
+        && api != Opcodes.ASM7
+        && api != Opcodes.ASM6
+        && api != Opcodes.ASM5
+        && api != Opcodes.ASM4
+        && api != Opcodes.ASM9_EXPERIMENTAL) {
+      throw new IllegalArgumentException("Unsupported api " + api);
+    }
+    if (api == Opcodes.ASM9_EXPERIMENTAL) {
+      Constants.checkAsmExperimental(this);
     }
     this.api = api;
     this.fv = fieldVisitor;
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/FieldWriter.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/FieldWriter.java
old mode 100755
new mode 100644
index 008525c..099ac8f
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/FieldWriter.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/FieldWriter.java
@@ -124,7 +124,7 @@
       final String descriptor,
       final String signature,
       final Object constantValue) {
-    super(Opcodes.ASM7);
+    super(/* latest api = */ Opcodes.ASM8);
     this.symbolTable = symbolTable;
     this.accessFlags = access;
     this.nameIndex = symbolTable.addConstantUtf8(name);
@@ -143,37 +143,26 @@
 
   @Override
   public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
-    // Create a ByteVector to hold an 'annotation' JVMS structure.
-    // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.
-    ByteVector annotation = new ByteVector();
-    // Write type_index and reserve space for num_element_value_pairs.
-    annotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0);
     if (visible) {
       return lastRuntimeVisibleAnnotation =
-          new AnnotationWriter(symbolTable, annotation, lastRuntimeVisibleAnnotation);
+          AnnotationWriter.create(symbolTable, descriptor, lastRuntimeVisibleAnnotation);
     } else {
       return lastRuntimeInvisibleAnnotation =
-          new AnnotationWriter(symbolTable, annotation, lastRuntimeInvisibleAnnotation);
+          AnnotationWriter.create(symbolTable, descriptor, lastRuntimeInvisibleAnnotation);
     }
   }
 
   @Override
   public AnnotationVisitor visitTypeAnnotation(
       final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
-    // Create a ByteVector to hold a 'type_annotation' JVMS structure.
-    // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20.
-    ByteVector typeAnnotation = new ByteVector();
-    // Write target_type, target_info, and target_path.
-    TypeReference.putTarget(typeRef, typeAnnotation);
-    TypePath.put(typePath, typeAnnotation);
-    // Write type_index and reserve space for num_element_value_pairs.
-    typeAnnotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0);
     if (visible) {
       return lastRuntimeVisibleTypeAnnotation =
-          new AnnotationWriter(symbolTable, typeAnnotation, lastRuntimeVisibleTypeAnnotation);
+          AnnotationWriter.create(
+              symbolTable, typeRef, typePath, descriptor, lastRuntimeVisibleTypeAnnotation);
     } else {
       return lastRuntimeInvisibleTypeAnnotation =
-          new AnnotationWriter(symbolTable, typeAnnotation, lastRuntimeInvisibleTypeAnnotation);
+          AnnotationWriter.create(
+              symbolTable, typeRef, typePath, descriptor, lastRuntimeInvisibleTypeAnnotation);
     }
   }
 
@@ -208,44 +197,13 @@
       symbolTable.addConstantUtf8(Constants.CONSTANT_VALUE);
       size += 8;
     }
-    // Before Java 1.5, synthetic fields are represented with a Synthetic attribute.
-    if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0
-        && symbolTable.getMajorVersion() < Opcodes.V1_5) {
-      // Synthetic attributes always use 6 bytes.
-      symbolTable.addConstantUtf8(Constants.SYNTHETIC);
-      size += 6;
-    }
-    if (signatureIndex != 0) {
-      // Signature attributes always use 8 bytes.
-      symbolTable.addConstantUtf8(Constants.SIGNATURE);
-      size += 8;
-    }
-    // ACC_DEPRECATED is ASM specific, the ClassFile format uses a Deprecated attribute instead.
-    if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) {
-      // Deprecated attributes always use 6 bytes.
-      symbolTable.addConstantUtf8(Constants.DEPRECATED);
-      size += 6;
-    }
-    if (lastRuntimeVisibleAnnotation != null) {
-      size +=
-          lastRuntimeVisibleAnnotation.computeAnnotationsSize(
-              Constants.RUNTIME_VISIBLE_ANNOTATIONS);
-    }
-    if (lastRuntimeInvisibleAnnotation != null) {
-      size +=
-          lastRuntimeInvisibleAnnotation.computeAnnotationsSize(
-              Constants.RUNTIME_INVISIBLE_ANNOTATIONS);
-    }
-    if (lastRuntimeVisibleTypeAnnotation != null) {
-      size +=
-          lastRuntimeVisibleTypeAnnotation.computeAnnotationsSize(
-              Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS);
-    }
-    if (lastRuntimeInvisibleTypeAnnotation != null) {
-      size +=
-          lastRuntimeInvisibleTypeAnnotation.computeAnnotationsSize(
-              Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS);
-    }
+    size += Attribute.computeAttributesSize(symbolTable, accessFlags, signatureIndex);
+    size +=
+        AnnotationWriter.computeAnnotationsSize(
+            lastRuntimeVisibleAnnotation,
+            lastRuntimeInvisibleAnnotation,
+            lastRuntimeVisibleTypeAnnotation,
+            lastRuntimeInvisibleTypeAnnotation);
     if (firstAttribute != null) {
       size += firstAttribute.computeAttributesSize(symbolTable);
     }
@@ -302,34 +260,14 @@
           .putInt(2)
           .putShort(constantValueIndex);
     }
-    if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0 && useSyntheticAttribute) {
-      output.putShort(symbolTable.addConstantUtf8(Constants.SYNTHETIC)).putInt(0);
-    }
-    if (signatureIndex != 0) {
-      output
-          .putShort(symbolTable.addConstantUtf8(Constants.SIGNATURE))
-          .putInt(2)
-          .putShort(signatureIndex);
-    }
-    if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) {
-      output.putShort(symbolTable.addConstantUtf8(Constants.DEPRECATED)).putInt(0);
-    }
-    if (lastRuntimeVisibleAnnotation != null) {
-      lastRuntimeVisibleAnnotation.putAnnotations(
-          symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_ANNOTATIONS), output);
-    }
-    if (lastRuntimeInvisibleAnnotation != null) {
-      lastRuntimeInvisibleAnnotation.putAnnotations(
-          symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_ANNOTATIONS), output);
-    }
-    if (lastRuntimeVisibleTypeAnnotation != null) {
-      lastRuntimeVisibleTypeAnnotation.putAnnotations(
-          symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS), output);
-    }
-    if (lastRuntimeInvisibleTypeAnnotation != null) {
-      lastRuntimeInvisibleTypeAnnotation.putAnnotations(
-          symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS), output);
-    }
+    Attribute.putAttributes(symbolTable, accessFlags, signatureIndex, output);
+    AnnotationWriter.putAnnotations(
+        symbolTable,
+        lastRuntimeVisibleAnnotation,
+        lastRuntimeInvisibleAnnotation,
+        lastRuntimeVisibleTypeAnnotation,
+        lastRuntimeInvisibleTypeAnnotation,
+        output);
     if (firstAttribute != null) {
       firstAttribute.putAttributes(symbolTable, output);
     }
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Frame.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Frame.java
old mode 100755
new mode 100644
index 7d2cdf8..e6533ec
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Frame.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Frame.java
@@ -54,19 +54,19 @@
  *
  * <pre>
  *   =====================================
- *   |.DIM|KIND|FLAG|...............VALUE|
+ *   |...DIM|KIND|.F|...............VALUE|
  *   =====================================
  * </pre>
  *
  * <ul>
- *   <li>the DIM field, stored in the 4 most significant bits, is a signed number of array
- *       dimensions (from -8 to 7, included). It can be retrieved with {@link #DIM_MASK} and a right
- *       shift of {@link #DIM_SHIFT}.
+ *   <li>the DIM field, stored in the 6 most significant bits, is a signed number of array
+ *       dimensions (from -32 to 31, included). It can be retrieved with {@link #DIM_MASK} and a
+ *       right shift of {@link #DIM_SHIFT}.
  *   <li>the KIND field, stored in 4 bits, indicates the kind of VALUE used. These 4 bits can be
  *       retrieved with {@link #KIND_MASK} and, without any shift, must be equal to {@link
  *       #CONSTANT_KIND}, {@link #REFERENCE_KIND}, {@link #UNINITIALIZED_KIND}, {@link #LOCAL_KIND}
  *       or {@link #STACK_KIND}.
- *   <li>the FLAGS field, stored in 4 bits, contains up to 4 boolean flags. Currently only one flag
+ *   <li>the FLAGS field, stored in 2 bits, contains up to 2 boolean flags. Currently only one flag
  *       is defined, namely {@link #TOP_IF_LONG_OR_DOUBLE_FLAG}.
  *   <li>the VALUE field, stored in the remaining 20 bits, contains either
  *       <ul>
@@ -89,9 +89,9 @@
  * <p>Output frames can contain abstract types of any kind and with a positive or negative array
  * dimension (and even unassigned types, represented by 0 - which does not correspond to any valid
  * abstract type value). Input frames can only contain CONSTANT_KIND, REFERENCE_KIND or
- * UNINITIALIZED_KIND abstract types of positive or null array dimension. In all cases the type
- * table contains only internal type names (array type descriptors are forbidden - array dimensions
- * must be represented through the DIM field).
+ * UNINITIALIZED_KIND abstract types of positive or {@literal null} array dimension. In all cases
+ * the type table contains only internal type names (array type descriptors are forbidden - array
+ * dimensions must be represented through the DIM field).
  *
  * <p>The LONG and DOUBLE types are always represented by using two slots (LONG + TOP or DOUBLE +
  * TOP), for local variables as well as in the operand stack. This is necessary to be able to
@@ -129,18 +129,25 @@
   private static final int ITEM_ASM_CHAR = 11;
   private static final int ITEM_ASM_SHORT = 12;
 
+  // The size and offset in bits of each field of an abstract type.
+
+  private static final int DIM_SIZE = 6;
+  private static final int KIND_SIZE = 4;
+  private static final int FLAGS_SIZE = 2;
+  private static final int VALUE_SIZE = 32 - DIM_SIZE - KIND_SIZE - FLAGS_SIZE;
+
+  private static final int DIM_SHIFT = KIND_SIZE + FLAGS_SIZE + VALUE_SIZE;
+  private static final int KIND_SHIFT = FLAGS_SIZE + VALUE_SIZE;
+  private static final int FLAGS_SHIFT = VALUE_SIZE;
+
   // Bitmasks to get each field of an abstract type.
 
-  private static final int DIM_MASK = 0xF0000000;
-  private static final int KIND_MASK = 0x0F000000;
-  private static final int FLAGS_MASK = 0x00F00000;
-  private static final int VALUE_MASK = 0x000FFFFF;
+  private static final int DIM_MASK = ((1 << DIM_SIZE) - 1) << DIM_SHIFT;
+  private static final int KIND_MASK = ((1 << KIND_SIZE) - 1) << KIND_SHIFT;
+  private static final int VALUE_MASK = (1 << VALUE_SIZE) - 1;
 
   // Constants to manipulate the DIM field of an abstract type.
 
-  /** The number of right shift bits to use to get the array dimensions of an abstract type. */
-  private static final int DIM_SHIFT = 28;
-
   /** The constant to be added to an abstract type to get one with one more array dimension. */
   private static final int ARRAY_OF = +1 << DIM_SHIFT;
 
@@ -149,11 +156,11 @@
 
   // Possible values for the KIND field of an abstract type.
 
-  private static final int CONSTANT_KIND = 0x01000000;
-  private static final int REFERENCE_KIND = 0x02000000;
-  private static final int UNINITIALIZED_KIND = 0x03000000;
-  private static final int LOCAL_KIND = 0x04000000;
-  private static final int STACK_KIND = 0x05000000;
+  private static final int CONSTANT_KIND = 1 << KIND_SHIFT;
+  private static final int REFERENCE_KIND = 2 << KIND_SHIFT;
+  private static final int UNINITIALIZED_KIND = 3 << KIND_SHIFT;
+  private static final int LOCAL_KIND = 4 << KIND_SHIFT;
+  private static final int STACK_KIND = 5 << KIND_SHIFT;
 
   // Possible flags for the FLAGS field of an abstract type.
 
@@ -162,7 +169,7 @@
    * concrete type is LONG or DOUBLE, TOP should be used instead (because the value has been
    * partially overridden with an xSTORE instruction).
    */
-  private static final int TOP_IF_LONG_OR_DOUBLE_FLAG = 0x00100000 & FLAGS_MASK;
+  private static final int TOP_IF_LONG_OR_DOUBLE_FLAG = 1 << FLAGS_SHIFT;
 
   // Useful predefined abstract types (all the possible CONSTANT_KIND types).
 
@@ -540,7 +547,8 @@
    * @param descriptor a type or method descriptor (in which case its return type is pushed).
    */
   private void push(final SymbolTable symbolTable, final String descriptor) {
-    int typeDescriptorOffset = descriptor.charAt(0) == '(' ? descriptor.indexOf(')') + 1 : 0;
+    int typeDescriptorOffset =
+        descriptor.charAt(0) == '(' ? Type.getReturnTypeOffset(descriptor) : 0;
     int abstractType = getAbstractTypeFromDescriptor(symbolTable, descriptor, typeDescriptorOffset);
     if (abstractType != 0) {
       push(abstractType);
@@ -1104,6 +1112,42 @@
   // -----------------------------------------------------------------------------------------------
 
   /**
+   * Computes the concrete output type corresponding to a given abstract output type.
+   *
+   * @param abstractOutputType an abstract output type.
+   * @param numStack the size of the input stack, used to resolve abstract output types of
+   *     STACK_KIND kind.
+   * @return the concrete output type corresponding to 'abstractOutputType'.
+   */
+  private int getConcreteOutputType(final int abstractOutputType, final int numStack) {
+    int dim = abstractOutputType & DIM_MASK;
+    int kind = abstractOutputType & KIND_MASK;
+    if (kind == LOCAL_KIND) {
+      // By definition, a LOCAL_KIND type designates the concrete type of a local variable at
+      // the beginning of the basic block corresponding to this frame (which is known when
+      // this method is called, but was not when the abstract type was computed).
+      int concreteOutputType = dim + inputLocals[abstractOutputType & VALUE_MASK];
+      if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0
+          && (concreteOutputType == LONG || concreteOutputType == DOUBLE)) {
+        concreteOutputType = TOP;
+      }
+      return concreteOutputType;
+    } else if (kind == STACK_KIND) {
+      // By definition, a STACK_KIND type designates the concrete type of a local variable at
+      // the beginning of the basic block corresponding to this frame (which is known when
+      // this method is called, but was not when the abstract type was computed).
+      int concreteOutputType = dim + inputStack[numStack - (abstractOutputType & VALUE_MASK)];
+      if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0
+          && (concreteOutputType == LONG || concreteOutputType == DOUBLE)) {
+        concreteOutputType = TOP;
+      }
+      return concreteOutputType;
+    } else {
+      return abstractOutputType;
+    }
+  }
+
+  /**
    * Merges the input frame of the given {@link Frame} with the input and output frames of this
    * {@link Frame}. Returns {@literal true} if the given frame has been changed by this operation
    * (the input and output frames of this {@link Frame} are never changed).
@@ -1137,29 +1181,7 @@
           // value at the beginning of the block.
           concreteOutputType = inputLocals[i];
         } else {
-          int dim = abstractOutputType & DIM_MASK;
-          int kind = abstractOutputType & KIND_MASK;
-          if (kind == LOCAL_KIND) {
-            // By definition, a LOCAL_KIND type designates the concrete type of a local variable at
-            // the beginning of the basic block corresponding to this frame (which is known when
-            // this method is called, but was not when the abstract type was computed).
-            concreteOutputType = dim + inputLocals[abstractOutputType & VALUE_MASK];
-            if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0
-                && (concreteOutputType == LONG || concreteOutputType == DOUBLE)) {
-              concreteOutputType = TOP;
-            }
-          } else if (kind == STACK_KIND) {
-            // By definition, a STACK_KIND type designates the concrete type of a local variable at
-            // the beginning of the basic block corresponding to this frame (which is known when
-            // this method is called, but was not when the abstract type was computed).
-            concreteOutputType = dim + inputStack[numStack - (abstractOutputType & VALUE_MASK)];
-            if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0
-                && (concreteOutputType == LONG || concreteOutputType == DOUBLE)) {
-              concreteOutputType = TOP;
-            }
-          } else {
-            concreteOutputType = abstractOutputType;
-          }
+          concreteOutputType = getConcreteOutputType(abstractOutputType, numStack);
         }
       } else {
         // If the local variable has never been assigned in this basic block, it is equal to its
@@ -1213,25 +1235,8 @@
     // Then, do this for the stack operands that have pushed in the basic block (this code is the
     // same as the one above for local variables).
     for (int i = 0; i < outputStackTop; ++i) {
-      int concreteOutputType;
       int abstractOutputType = outputStack[i];
-      int dim = abstractOutputType & DIM_MASK;
-      int kind = abstractOutputType & KIND_MASK;
-      if (kind == LOCAL_KIND) {
-        concreteOutputType = dim + inputLocals[abstractOutputType & VALUE_MASK];
-        if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0
-            && (concreteOutputType == LONG || concreteOutputType == DOUBLE)) {
-          concreteOutputType = TOP;
-        }
-      } else if (kind == STACK_KIND) {
-        concreteOutputType = dim + inputStack[numStack - (abstractOutputType & VALUE_MASK)];
-        if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0
-            && (concreteOutputType == LONG || concreteOutputType == DOUBLE)) {
-          concreteOutputType = TOP;
-        }
-      } else {
-        concreteOutputType = abstractOutputType;
-      }
+      int concreteOutputType = getConcreteOutputType(abstractOutputType, numStack);
       if (initializations != null) {
         concreteOutputType = getInitializedType(symbolTable, concreteOutputType);
       }
@@ -1248,10 +1253,10 @@
    * @param symbolTable the type table to use to lookup and store type {@link Symbol}.
    * @param sourceType the abstract type with which the abstract type array element must be merged.
    *     This type should be of {@link #CONSTANT_KIND}, {@link #REFERENCE_KIND} or {@link
-   *     #UNINITIALIZED_KIND} kind, with positive or null array dimensions.
+   *     #UNINITIALIZED_KIND} kind, with positive or {@literal null} array dimensions.
    * @param dstTypes an array of abstract types. These types should be of {@link #CONSTANT_KIND},
-   *     {@link #REFERENCE_KIND} or {@link #UNINITIALIZED_KIND} kind, with positive or null array
-   *     dimensions.
+   *     {@link #REFERENCE_KIND} or {@link #UNINITIALIZED_KIND} kind, with positive or {@literal
+   *     null} array dimensions.
    * @param dstIndex the index of the type that must be merged in dstTypes.
    * @return {@literal true} if the type array has been modified by this operation.
    */
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Handle.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Handle.java
old mode 100755
new mode 100644
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Handler.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Handler.java
old mode 100755
new mode 100644
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Label.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Label.java
old mode 100755
new mode 100644
index 906ab98..7dc5c32
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Label.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Label.java
@@ -227,7 +227,8 @@
 
   /**
    * The maximum height reached by the output stack, relatively to the top of the input stack, in
-   * the basic block corresponding to this label. This maximum is always positive or null.
+   * the basic block corresponding to this label. This maximum is always positive or {@literal
+   * null}.
    */
   short outputStackMax;
 
@@ -264,12 +265,12 @@
   Edge outgoingEdges;
 
   /**
-   * The next element in the list of labels to which this label belongs, or null if it does not
-   * belong to any list. All lists of labels must end with the {@link #EMPTY_LIST} sentinel, in
-   * order to ensure that this field is null if and only if this label does not belong to a list of
-   * labels. Note that there can be several lists of labels at the same time, but that a label can
-   * belong to at most one list at a time (unless some lists share a common tail, but this is not
-   * used in practice).
+   * The next element in the list of labels to which this label belongs, or {@literal null} if it
+   * does not belong to any list. All lists of labels must end with the {@link #EMPTY_LIST}
+   * sentinel, in order to ensure that this field is null if and only if this label does not belong
+   * to a list of labels. Note that there can be several lists of labels at the same time, but that
+   * a label can belong to at most one list at a time (unless some lists share a common tail, but
+   * this is not used in practice).
    *
    * <p>List of labels are used in {@link MethodWriter#computeAllFrames} and {@link
    * MethodWriter#computeMaxStackAndLocal} to compute stack map frames and the maximum stack size,
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/MethodTooLargeException.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/MethodTooLargeException.java
old mode 100755
new mode 100644
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
old mode 100755
new mode 100644
index cc47afd..73614a2
--- 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
@@ -56,7 +56,9 @@
    */
   protected final int api;
 
-  /** The method visitor to which this visitor must delegate method calls. May be null. */
+  /**
+   * The method visitor to which this visitor must delegate method calls. May be {@literal null}.
+   */
   protected MethodVisitor mv;
 
   /**
@@ -78,8 +80,16 @@
    *     be null.
    */
   public MethodVisitor(final int api, final MethodVisitor methodVisitor) {
-    if (api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4 && api != Opcodes.ASM7) {
-      throw new IllegalArgumentException();
+    if (api != Opcodes.ASM8
+        && api != Opcodes.ASM7
+        && api != Opcodes.ASM6
+        && api != Opcodes.ASM5
+        && api != Opcodes.ASM4
+        && api != Opcodes.ASM9_EXPERIMENTAL) {
+      throw new IllegalArgumentException("Unsupported api " + api);
+    }
+    if (api == Opcodes.ASM9_EXPERIMENTAL) {
+      Constants.checkAsmExperimental(this);
     }
     this.api = api;
     this.mv = methodVisitor;
@@ -92,7 +102,7 @@
   /**
    * Visits a parameter of this method.
    *
-   * @param name parameter name or null if none is provided.
+   * @param name parameter name or {@literal null} if none is provided.
    * @param access the parameter's access flags, only {@code ACC_FINAL}, {@code ACC_SYNTHETIC}
    *     or/and {@code ACC_MANDATED} are allowed (see {@link Opcodes}).
    */
@@ -395,14 +405,8 @@
   @Deprecated
   public void visitMethodInsn(
       final int opcode, final String owner, final String name, final String descriptor) {
-    if (api >= Opcodes.ASM5) {
-      boolean isInterface = opcode == Opcodes.INVOKEINTERFACE;
-      visitMethodInsn(opcode, owner, name, descriptor, isInterface);
-      return;
-    }
-    if (mv != null) {
-      mv.visitMethodInsn(opcode, owner, name, descriptor);
-    }
+    int opcodeAndSource = opcode | (api < Opcodes.ASM5 ? Opcodes.SOURCE_DEPRECATED : 0);
+    visitMethodInsn(opcodeAndSource, owner, name, descriptor, opcode == Opcodes.INVOKEINTERFACE);
   }
 
   /**
@@ -422,15 +426,15 @@
       final String name,
       final String descriptor,
       final boolean isInterface) {
-    if (api < Opcodes.ASM5) {
+    if (api < Opcodes.ASM5 && (opcode & Opcodes.SOURCE_DEPRECATED) == 0) {
       if (isInterface != (opcode == Opcodes.INVOKEINTERFACE)) {
-        throw new IllegalArgumentException("INVOKESPECIAL/STATIC on interfaces requires ASM5");
+        throw new UnsupportedOperationException("INVOKESPECIAL/STATIC on interfaces requires ASM5");
       }
       visitMethodInsn(opcode, owner, name, descriptor);
       return;
     }
     if (mv != null) {
-      mv.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+      mv.visitMethodInsn(opcode & ~Opcodes.SOURCE_MASK, owner, name, descriptor, isInterface);
     }
   }
 
@@ -538,7 +542,7 @@
             || (value instanceof Type && ((Type) value).getSort() == Type.METHOD))) {
       throw new UnsupportedOperationException(REQUIRES_ASM5);
     }
-    if (api != Opcodes.ASM7 && value instanceof ConstantDynamic) {
+    if (api < Opcodes.ASM7 && value instanceof ConstantDynamic) {
       throw new UnsupportedOperationException("This feature requires ASM7");
     }
     if (mv != null) {
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
old mode 100755
new mode 100644
index 363da36..934d3e5
--- 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
@@ -592,7 +592,7 @@
       final String signature,
       final String[] exceptions,
       final int compute) {
-    super(Opcodes.ASM7);
+    super(/* latest api = */ Opcodes.ASM8);
     this.symbolTable = symbolTable;
     this.accessFlags = "<init>".equals(name) ? access | Constants.ACC_CONSTRUCTOR : access;
     this.nameIndex = symbolTable.addConstantUtf8(name);
@@ -654,37 +654,26 @@
 
   @Override
   public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
-    // Create a ByteVector to hold an 'annotation' JVMS structure.
-    // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.
-    ByteVector annotation = new ByteVector();
-    // Write type_index and reserve space for num_element_value_pairs.
-    annotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0);
     if (visible) {
       return lastRuntimeVisibleAnnotation =
-          new AnnotationWriter(symbolTable, annotation, lastRuntimeVisibleAnnotation);
+          AnnotationWriter.create(symbolTable, descriptor, lastRuntimeVisibleAnnotation);
     } else {
       return lastRuntimeInvisibleAnnotation =
-          new AnnotationWriter(symbolTable, annotation, lastRuntimeInvisibleAnnotation);
+          AnnotationWriter.create(symbolTable, descriptor, lastRuntimeInvisibleAnnotation);
     }
   }
 
   @Override
   public AnnotationVisitor visitTypeAnnotation(
       final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
-    // Create a ByteVector to hold a 'type_annotation' JVMS structure.
-    // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20.
-    ByteVector typeAnnotation = new ByteVector();
-    // Write target_type, target_info, and target_path.
-    TypeReference.putTarget(typeRef, typeAnnotation);
-    TypePath.put(typePath, typeAnnotation);
-    // Write type_index and reserve space for num_element_value_pairs.
-    typeAnnotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0);
     if (visible) {
       return lastRuntimeVisibleTypeAnnotation =
-          new AnnotationWriter(symbolTable, typeAnnotation, lastRuntimeVisibleTypeAnnotation);
+          AnnotationWriter.create(
+              symbolTable, typeRef, typePath, descriptor, lastRuntimeVisibleTypeAnnotation);
     } else {
       return lastRuntimeInvisibleTypeAnnotation =
-          new AnnotationWriter(symbolTable, typeAnnotation, lastRuntimeInvisibleTypeAnnotation);
+          AnnotationWriter.create(
+              symbolTable, typeRef, typePath, descriptor, lastRuntimeInvisibleTypeAnnotation);
     }
   }
 
@@ -700,27 +689,24 @@
   @Override
   public AnnotationVisitor visitParameterAnnotation(
       final int parameter, final String annotationDescriptor, final boolean visible) {
-    // Create a ByteVector to hold an 'annotation' JVMS structure.
-    // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.
-    ByteVector annotation = new ByteVector();
-    // Write type_index and reserve space for num_element_value_pairs.
-    annotation.putShort(symbolTable.addConstantUtf8(annotationDescriptor)).putShort(0);
     if (visible) {
       if (lastRuntimeVisibleParameterAnnotations == null) {
         lastRuntimeVisibleParameterAnnotations =
             new AnnotationWriter[Type.getArgumentTypes(descriptor).length];
       }
       return lastRuntimeVisibleParameterAnnotations[parameter] =
-          new AnnotationWriter(
-              symbolTable, annotation, lastRuntimeVisibleParameterAnnotations[parameter]);
+          AnnotationWriter.create(
+              symbolTable, annotationDescriptor, lastRuntimeVisibleParameterAnnotations[parameter]);
     } else {
       if (lastRuntimeInvisibleParameterAnnotations == null) {
         lastRuntimeInvisibleParameterAnnotations =
             new AnnotationWriter[Type.getArgumentTypes(descriptor).length];
       }
       return lastRuntimeInvisibleParameterAnnotations[parameter] =
-          new AnnotationWriter(
-              symbolTable, annotation, lastRuntimeInvisibleParameterAnnotations[parameter]);
+          AnnotationWriter.create(
+              symbolTable,
+              annotationDescriptor,
+              lastRuntimeInvisibleParameterAnnotations[parameter]);
     }
   }
 
@@ -789,6 +775,9 @@
       }
       visitFrameEnd();
     } else {
+      if (symbolTable.getMajorVersion() < Opcodes.V1_6) {
+        throw new IllegalArgumentException("Class versions V1_5 or less must use F_NEW frames.");
+      }
       int offsetDelta;
       if (stackMapTableEntries == null) {
         stackMapTableEntries = new ByteVector();
@@ -1415,20 +1404,22 @@
   @Override
   public AnnotationVisitor visitInsnAnnotation(
       final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
-    // Create a ByteVector to hold a 'type_annotation' JVMS structure.
-    // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20.
-    ByteVector typeAnnotation = new ByteVector();
-    // Write target_type, target_info, and target_path.
-    TypeReference.putTarget((typeRef & 0xFF0000FF) | (lastBytecodeOffset << 8), typeAnnotation);
-    TypePath.put(typePath, typeAnnotation);
-    // Write type_index and reserve space for num_element_value_pairs.
-    typeAnnotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0);
     if (visible) {
       return lastCodeRuntimeVisibleTypeAnnotation =
-          new AnnotationWriter(symbolTable, typeAnnotation, lastCodeRuntimeVisibleTypeAnnotation);
+          AnnotationWriter.create(
+              symbolTable,
+              (typeRef & 0xFF0000FF) | (lastBytecodeOffset << 8),
+              typePath,
+              descriptor,
+              lastCodeRuntimeVisibleTypeAnnotation);
     } else {
       return lastCodeRuntimeInvisibleTypeAnnotation =
-          new AnnotationWriter(symbolTable, typeAnnotation, lastCodeRuntimeInvisibleTypeAnnotation);
+          AnnotationWriter.create(
+              symbolTable,
+              (typeRef & 0xFF0000FF) | (lastBytecodeOffset << 8),
+              typePath,
+              descriptor,
+              lastCodeRuntimeInvisibleTypeAnnotation);
     }
   }
 
@@ -1449,20 +1440,14 @@
   @Override
   public AnnotationVisitor visitTryCatchAnnotation(
       final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
-    // Create a ByteVector to hold a 'type_annotation' JVMS structure.
-    // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20.
-    ByteVector typeAnnotation = new ByteVector();
-    // Write target_type, target_info, and target_path.
-    TypeReference.putTarget(typeRef, typeAnnotation);
-    TypePath.put(typePath, typeAnnotation);
-    // Write type_index and reserve space for num_element_value_pairs.
-    typeAnnotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0);
     if (visible) {
       return lastCodeRuntimeVisibleTypeAnnotation =
-          new AnnotationWriter(symbolTable, typeAnnotation, lastCodeRuntimeVisibleTypeAnnotation);
+          AnnotationWriter.create(
+              symbolTable, typeRef, typePath, descriptor, lastCodeRuntimeVisibleTypeAnnotation);
     } else {
       return lastCodeRuntimeInvisibleTypeAnnotation =
-          new AnnotationWriter(symbolTable, typeAnnotation, lastCodeRuntimeInvisibleTypeAnnotation);
+          AnnotationWriter.create(
+              symbolTable, typeRef, typePath, descriptor, lastCodeRuntimeInvisibleTypeAnnotation);
     }
   }
 
@@ -1530,10 +1515,18 @@
     typeAnnotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0);
     if (visible) {
       return lastCodeRuntimeVisibleTypeAnnotation =
-          new AnnotationWriter(symbolTable, typeAnnotation, lastCodeRuntimeVisibleTypeAnnotation);
+          new AnnotationWriter(
+              symbolTable,
+              /* useNamedValues = */ true,
+              typeAnnotation,
+              lastCodeRuntimeVisibleTypeAnnotation);
     } else {
       return lastCodeRuntimeInvisibleTypeAnnotation =
-          new AnnotationWriter(symbolTable, typeAnnotation, lastCodeRuntimeInvisibleTypeAnnotation);
+          new AnnotationWriter(
+              symbolTable,
+              /* useNamedValues = */ true,
+              typeAnnotation,
+              lastCodeRuntimeInvisibleTypeAnnotation);
     }
   }
 
@@ -2004,10 +1997,6 @@
    * attribute) are the same as the corresponding attributes in the given method.
    *
    * @param source the source ClassReader from which the attributes of this method might be copied.
-   * @param methodInfoOffset the offset in 'source.b' of the method_info JVMS structure from which
-   *     the attributes of this method might be copied.
-   * @param methodInfoLength the length in 'source.b' of the method_info JVMS structure from which
-   *     the attributes of this method might be copied.
    * @param hasSyntheticAttribute whether the method_info JVMS structure from which the attributes
    *     of this method might be copied contains a Synthetic attribute.
    * @param hasDeprecatedAttribute whether the method_info JVMS structure from which the attributes
@@ -2024,8 +2013,6 @@
    */
   boolean canCopyMethodAttributes(
       final ClassReader source,
-      final int methodInfoOffset,
-      final int methodInfoLength,
       final boolean hasSyntheticAttribute,
       final boolean hasDeprecatedAttribute,
       final int descriptorIndex,
@@ -2060,12 +2047,23 @@
         currentExceptionOffset += 2;
       }
     }
+    return true;
+  }
+
+  /**
+   * Sets the source from which the attributes of this method will be copied.
+   *
+   * @param methodInfoOffset the offset in 'symbolTable.getSource()' of the method_info JVMS
+   *     structure from which the attributes of this method will be copied.
+   * @param methodInfoLength the length in 'symbolTable.getSource()' of the method_info JVMS
+   *     structure from which the attributes of this method will be copied.
+   */
+  void setMethodAttributesSource(final int methodInfoOffset, final int methodInfoLength) {
     // Don't copy the attributes yet, instead store their location in the source class reader so
     // they can be copied later, in {@link #putMethodInfo}. Note that we skip the 6 header bytes
     // of the method_info JVMS structure.
     this.sourceOffset = methodInfoOffset + 6;
     this.sourceLength = methodInfoLength - 6;
-    return true;
   }
 
   /**
@@ -2133,29 +2131,13 @@
       symbolTable.addConstantUtf8(Constants.EXCEPTIONS);
       size += 8 + 2 * numberOfExceptions;
     }
-    boolean useSyntheticAttribute = symbolTable.getMajorVersion() < Opcodes.V1_5;
-    if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0 && useSyntheticAttribute) {
-      symbolTable.addConstantUtf8(Constants.SYNTHETIC);
-      size += 6;
-    }
-    if (signatureIndex != 0) {
-      symbolTable.addConstantUtf8(Constants.SIGNATURE);
-      size += 8;
-    }
-    if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) {
-      symbolTable.addConstantUtf8(Constants.DEPRECATED);
-      size += 6;
-    }
-    if (lastRuntimeVisibleAnnotation != null) {
-      size +=
-          lastRuntimeVisibleAnnotation.computeAnnotationsSize(
-              Constants.RUNTIME_VISIBLE_ANNOTATIONS);
-    }
-    if (lastRuntimeInvisibleAnnotation != null) {
-      size +=
-          lastRuntimeInvisibleAnnotation.computeAnnotationsSize(
-              Constants.RUNTIME_INVISIBLE_ANNOTATIONS);
-    }
+    size += Attribute.computeAttributesSize(symbolTable, accessFlags, signatureIndex);
+    size +=
+        AnnotationWriter.computeAnnotationsSize(
+            lastRuntimeVisibleAnnotation,
+            lastRuntimeInvisibleAnnotation,
+            lastRuntimeVisibleTypeAnnotation,
+            lastRuntimeInvisibleTypeAnnotation);
     if (lastRuntimeVisibleParameterAnnotations != null) {
       size +=
           AnnotationWriter.computeParameterAnnotationsSize(
@@ -2174,16 +2156,6 @@
                   ? lastRuntimeInvisibleParameterAnnotations.length
                   : invisibleAnnotableParameterCount);
     }
-    if (lastRuntimeVisibleTypeAnnotation != null) {
-      size +=
-          lastRuntimeVisibleTypeAnnotation.computeAnnotationsSize(
-              Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS);
-    }
-    if (lastRuntimeInvisibleTypeAnnotation != null) {
-      size +=
-          lastRuntimeInvisibleTypeAnnotation.computeAnnotationsSize(
-              Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS);
-    }
     if (defaultValue != null) {
       symbolTable.addConstantUtf8(Constants.ANNOTATION_DEFAULT);
       size += 6 + defaultValue.length;
@@ -2211,7 +2183,7 @@
     output.putShort(accessFlags & ~mask).putShort(nameIndex).putShort(descriptorIndex);
     // If this method_info must be copied from an existing one, copy it now and return early.
     if (sourceOffset != 0) {
-      output.putByteArray(symbolTable.getSource().b, sourceOffset, sourceLength);
+      output.putByteArray(symbolTable.getSource().classFileBuffer, sourceOffset, sourceLength);
       return;
     }
     // For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS.
@@ -2365,26 +2337,14 @@
         output.putShort(exceptionIndex);
       }
     }
-    if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0 && useSyntheticAttribute) {
-      output.putShort(symbolTable.addConstantUtf8(Constants.SYNTHETIC)).putInt(0);
-    }
-    if (signatureIndex != 0) {
-      output
-          .putShort(symbolTable.addConstantUtf8(Constants.SIGNATURE))
-          .putInt(2)
-          .putShort(signatureIndex);
-    }
-    if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) {
-      output.putShort(symbolTable.addConstantUtf8(Constants.DEPRECATED)).putInt(0);
-    }
-    if (lastRuntimeVisibleAnnotation != null) {
-      lastRuntimeVisibleAnnotation.putAnnotations(
-          symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_ANNOTATIONS), output);
-    }
-    if (lastRuntimeInvisibleAnnotation != null) {
-      lastRuntimeInvisibleAnnotation.putAnnotations(
-          symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_ANNOTATIONS), output);
-    }
+    Attribute.putAttributes(symbolTable, accessFlags, signatureIndex, output);
+    AnnotationWriter.putAnnotations(
+        symbolTable,
+        lastRuntimeVisibleAnnotation,
+        lastRuntimeInvisibleAnnotation,
+        lastRuntimeVisibleTypeAnnotation,
+        lastRuntimeInvisibleTypeAnnotation,
+        output);
     if (lastRuntimeVisibleParameterAnnotations != null) {
       AnnotationWriter.putParameterAnnotations(
           symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS),
@@ -2403,14 +2363,6 @@
               : invisibleAnnotableParameterCount,
           output);
     }
-    if (lastRuntimeVisibleTypeAnnotation != null) {
-      lastRuntimeVisibleTypeAnnotation.putAnnotations(
-          symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS), output);
-    }
-    if (lastRuntimeInvisibleTypeAnnotation != null) {
-      lastRuntimeInvisibleTypeAnnotation.putAnnotations(
-          symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS), output);
-    }
     if (defaultValue != null) {
       output
           .putShort(symbolTable.addConstantUtf8(Constants.ANNOTATION_DEFAULT))
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
old mode 100755
new mode 100644
index 1967491..b4f1e65
--- 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
@@ -42,7 +42,9 @@
    */
   protected final int api;
 
-  /** The module visitor to which this visitor must delegate method calls. May be null. */
+  /**
+   * The module visitor to which this visitor must delegate method calls. May be {@literal null}.
+   */
   protected ModuleVisitor mv;
 
   /**
@@ -64,8 +66,16 @@
    *     be null.
    */
   public ModuleVisitor(final int api, final ModuleVisitor moduleVisitor) {
-    if (api != Opcodes.ASM6 && api != Opcodes.ASM7) {
-      throw new IllegalArgumentException();
+    if (api != Opcodes.ASM8
+        && api != Opcodes.ASM7
+        && api != Opcodes.ASM6
+        && api != Opcodes.ASM5
+        && api != Opcodes.ASM4
+        && api != Opcodes.ASM9_EXPERIMENTAL) {
+      throw new IllegalArgumentException("Unsupported api " + api);
+    }
+    if (api == Opcodes.ASM9_EXPERIMENTAL) {
+      Constants.checkAsmExperimental(this);
     }
     this.api = api;
     this.mv = moduleVisitor;
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/ModuleWriter.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/ModuleWriter.java
old mode 100755
new mode 100644
index 1a8f355..cd4396d
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/ModuleWriter.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/ModuleWriter.java
@@ -94,7 +94,7 @@
   private int mainClassIndex;
 
   ModuleWriter(final SymbolTable symbolTable, final int name, final int access, final int version) {
-    super(Opcodes.ASM7);
+    super(/* latest api = */ Opcodes.ASM8);
     this.symbolTable = symbolTable;
     this.moduleNameIndex = name;
     this.moduleFlags = access;
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
old mode 100755
new mode 100644
index 94b98f9..646412a
--- 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
@@ -47,9 +47,222 @@
   int ASM5 = 5 << 16 | 0 << 8;
   int ASM6 = 6 << 16 | 0 << 8;
   int ASM7 = 7 << 16 | 0 << 8;
+  int ASM8 = 8 << 16 | 0 << 8;
 
-  // Java ClassFile versions (the minor version is stored in the 16 most
-  // significant bits, and the
+  /**
+   * <i>Experimental, use at your own risk. This field will be renamed when it becomes stable, this
+   * will break existing code using it. Only code compiled with --enable-preview can use this.</i>
+   *
+   * @deprecated This API is experimental.
+   */
+  @Deprecated int ASM9_EXPERIMENTAL = 1 << 24 | 9 << 16 | 0 << 8;
+
+  /*
+   * Internal flags used to redirect calls to deprecated methods. For instance, if a visitOldStuff
+   * method in API_OLD is deprecated and replaced with visitNewStuff in API_NEW, then the
+   * redirection should be done as follows:
+   *
+   * <pre>
+   * public class StuffVisitor {
+   *   ...
+   *
+   *   &#64;Deprecated public void visitOldStuff(int arg, ...) {
+   *     // SOURCE_DEPRECATED means "a call from a deprecated method using the old 'api' value".
+   *     visitNewStuf(arg | (api &#60; API_NEW ? SOURCE_DEPRECATED : 0), ...);
+   *   }
+   *
+   *   public void visitNewStuff(int argAndSource, ...) {
+   *     if (api &#60; API_NEW &#38;&#38; (argAndSource &#38; SOURCE_DEPRECATED) == 0) {
+   *       visitOldStuff(argAndSource, ...);
+   *     } else {
+   *       int arg = argAndSource &#38; ~SOURCE_MASK;
+   *       [ do stuff ]
+   *     }
+   *   }
+   * }
+   * </pre>
+   *
+   * <p>If 'api' is equal to API_NEW, there are two cases:
+   *
+   * <ul>
+   *   <li>call visitNewStuff: the redirection test is skipped and 'do stuff' is executed directly.
+   *   <li>call visitOldSuff: the source is not set to SOURCE_DEPRECATED before calling
+   *       visitNewStuff, but the redirection test is skipped anyway in visitNewStuff, which
+   *       directly executes 'do stuff'.
+   * </ul>
+   *
+   * <p>If 'api' is equal to API_OLD, there are two cases:
+   *
+   * <ul>
+   *   <li>call visitOldSuff: the source is set to SOURCE_DEPRECATED before calling visitNewStuff.
+   *       Because of this visitNewStuff does not redirect back to visitOldStuff, and instead
+   *       executes 'do stuff'.
+   *   <li>call visitNewStuff: the call is redirected to visitOldStuff because the source is 0.
+   *       visitOldStuff now sets the source to SOURCE_DEPRECATED and calls visitNewStuff back. This
+   *       time visitNewStuff does not redirect the call, and instead executes 'do stuff'.
+   * </ul>
+   *
+   * <h1>User subclasses</h1>
+   *
+   * <p>If a user subclass overrides one of these methods, there are only two cases: either 'api' is
+   * API_OLD and visitOldStuff is overridden (and visitNewStuff is not), or 'api' is API_NEW or
+   * more, and visitNewStuff is overridden (and visitOldStuff is not). Any other case is a user
+   * programming error.
+   *
+   * <p>If 'api' is equal to API_NEW, the class hierarchy is equivalent to
+   *
+   * <pre>
+   * public class StuffVisitor {
+   *   &#64;Deprecated public void visitOldStuff(int arg, ...) { visitNewStuf(arg, ...); }
+   *   public void visitNewStuff(int arg, ...) { [ do stuff ] }
+   * }
+   * class UserStuffVisitor extends StuffVisitor {
+   *   &#64;Override public void visitNewStuff(int arg, ...) {
+   *     super.visitNewStuff(int arg, ...); // optional
+   *     [ do user stuff ]
+   *   }
+   * }
+   * </pre>
+   *
+   * <p>It is then obvious that whether visitNewStuff or visitOldStuff is called, 'do stuff' and 'do
+   * user stuff' will be executed, in this order.
+   *
+   * <p>If 'api' is equal to API_OLD, the class hierarchy is equivalent to
+   *
+   * <pre>
+   * public class StuffVisitor {
+   *   &#64;Deprecated public void visitOldStuff(int arg, ...) {
+   *     visitNewStuf(arg | SOURCE_DEPRECATED, ...);
+   *   }
+   *   public void visitNewStuff(int argAndSource...) {
+   *     if ((argAndSource & SOURCE_DEPRECATED) == 0) {
+   *       visitOldStuff(argAndSource, ...);
+   *     } else {
+   *       int arg = argAndSource &#38; ~SOURCE_MASK;
+   *       [ do stuff ]
+   *     }
+   *   }
+   * }
+   * class UserStuffVisitor extends StuffVisitor {
+   *   &#64;Override public void visitOldStuff(int arg, ...) {
+   *     super.visitOldStuff(int arg, ...); // optional
+   *     [ do user stuff ]
+   *   }
+   * }
+   * </pre>
+   *
+   * <p>and there are two cases:
+   *
+   * <ul>
+   *   <li>call visitOldSuff: in the call to super.visitOldStuff, the source is set to
+   *       SOURCE_DEPRECATED and visitNewStuff is called. Here 'do stuff' is run because the source
+   *       was previously set to SOURCE_DEPRECATED, and execution eventually returns to
+   *       UserStuffVisitor.visitOldStuff, where 'do user stuff' is run.
+   *   <li>call visitNewStuff: the call is redirected to UserStuffVisitor.visitOldStuff because the
+   *       source is 0. Execution continues as in the previous case, resulting in 'do stuff' and 'do
+   *       user stuff' being executed, in this order.
+   * </ul>
+   *
+   * <h1>ASM subclasses</h1>
+   *
+   * <p>In ASM packages, subclasses of StuffVisitor can typically be sub classed again by the user,
+   * and can be used with API_OLD or API_NEW. Because of this, if such a subclass must override
+   * visitNewStuff, it must do so in the following way (and must not override visitOldStuff):
+   *
+   * <pre>
+   * public class AsmStuffVisitor extends StuffVisitor {
+   *   &#64;Override public void visitNewStuff(int argAndSource, ...) {
+   *     if (api &#60; API_NEW &#38;&#38; (argAndSource &#38; SOURCE_DEPRECATED) == 0) {
+   *       super.visitNewStuff(argAndSource, ...);
+   *       return;
+   *     }
+   *     super.visitNewStuff(argAndSource, ...); // optional
+   *     int arg = argAndSource &#38; ~SOURCE_MASK;
+   *     [ do other stuff ]
+   *   }
+   * }
+   * </pre>
+   *
+   * <p>If a user class extends this with 'api' equal to API_NEW, the class hierarchy is equivalent
+   * to
+   *
+   * <pre>
+   * public class StuffVisitor {
+   *   &#64;Deprecated public void visitOldStuff(int arg, ...) { visitNewStuf(arg, ...); }
+   *   public void visitNewStuff(int arg, ...) { [ do stuff ] }
+   * }
+   * public class AsmStuffVisitor extends StuffVisitor {
+   *   &#64;Override public void visitNewStuff(int arg, ...) {
+   *     super.visitNewStuff(arg, ...);
+   *     [ do other stuff ]
+   *   }
+   * }
+   * class UserStuffVisitor extends StuffVisitor {
+   *   &#64;Override public void visitNewStuff(int arg, ...) {
+   *     super.visitNewStuff(int arg, ...);
+   *     [ do user stuff ]
+   *   }
+   * }
+   * </pre>
+   *
+   * <p>It is then obvious that whether visitNewStuff or visitOldStuff is called, 'do stuff', 'do
+   * other stuff' and 'do user stuff' will be executed, in this order. If, on the other hand, a user
+   * class extends AsmStuffVisitor with 'api' equal to API_OLD, the class hierarchy is equivalent to
+   *
+   * <pre>
+   * public class StuffVisitor {
+   *   &#64;Deprecated public void visitOldStuff(int arg, ...) {
+   *     visitNewStuf(arg | SOURCE_DEPRECATED, ...);
+   *   }
+   *   public void visitNewStuff(int argAndSource, ...) {
+   *     if ((argAndSource & SOURCE_DEPRECATED) == 0) {
+   *       visitOldStuff(argAndSource, ...);
+   *     } else {
+   *       int arg = argAndSource &#38; ~SOURCE_MASK;
+   *       [ do stuff ]
+   *     }
+   *   }
+   * }
+   * public class AsmStuffVisitor extends StuffVisitor {
+   *   &#64;Override public void visitNewStuff(int argAndSource, ...) {
+   *     if ((argAndSource &#38; SOURCE_DEPRECATED) == 0) {
+   *       super.visitNewStuff(argAndSource, ...);
+   *       return;
+   *     }
+   *     super.visitNewStuff(argAndSource, ...); // optional
+   *     int arg = argAndSource &#38; ~SOURCE_MASK;
+   *     [ do other stuff ]
+   *   }
+   * }
+   * class UserStuffVisitor extends StuffVisitor {
+   *   &#64;Override public void visitOldStuff(int arg, ...) {
+   *     super.visitOldStuff(arg, ...);
+   *     [ do user stuff ]
+   *   }
+   * }
+   * </pre>
+   *
+   * <p>and, here again, whether visitNewStuff or visitOldStuff is called, 'do stuff', 'do other
+   * stuff' and 'do user stuff' will be executed, in this order (exercise left to the reader).
+   *
+   * <h1>Notes</h1>
+   *
+   * <ul>
+   *   <li>the SOURCE_DEPRECATED flag is set only if 'api' is API_OLD, just before calling
+   *       visitNewStuff. By hypothesis, this method is not overridden by the user. Therefore, user
+   *       classes can never see this flag. Only ASM subclasses must take care of extracting the
+   *       actual argument value by clearing the source flags.
+   *   <li>because the SOURCE_DEPRECATED flag is immediately cleared in the caller, the caller can
+   *       call visitOldStuff or visitNewStuff (in 'do stuff' and 'do user stuff') on a delegate
+   *       visitor without any risks (breaking the redirection logic, "leaking" the flag, etc).
+   *   <li>all the scenarios discussed above are unit tested in MethodVisitorTest.
+   * </ul>
+   */
+
+  int SOURCE_DEPRECATED = 0x100;
+  int SOURCE_MASK = SOURCE_DEPRECATED;
+
+  // Java ClassFile versions (the minor version is stored in the 16 most significant bits, and the
   // major version in the 16 least significant bits).
 
   int V1_1 = 3 << 16 | 45;
@@ -64,6 +277,9 @@
   int V10 = 0 << 16 | 54;
   int V11 = 0 << 16 | 55;
   int V12 = 0 << 16 | 56;
+  int V13 = 0 << 16 | 57;
+  int V14 = 0 << 16 | 58;
+  int V15 = 0 << 16 | 59;
 
   /**
    * Version flag indicating that the class is using 'preview' features.
@@ -100,7 +316,7 @@
   int ACC_SYNTHETIC = 0x1000; // class, field, method, parameter, module *
   int ACC_ANNOTATION = 0x2000; // class
   int ACC_ENUM = 0x4000; // class(?) field inner
-  int ACC_MANDATED = 0x8000; // parameter, module, module *
+  int ACC_MANDATED = 0x8000; // field, method, parameter, module, module *
   int ACC_MODULE = 0x8000; // class
 
   // ASM specific access flags.
@@ -108,6 +324,7 @@
   // access flags, and also to make sure that these flags are automatically filtered out when
   // written in class files (because access flags are stored using 16 bits only).
 
+  int ACC_RECORD = 0x10000; // class
   int ACC_DEPRECATED = 0x20000; // class, field, method
 
   // Possible values for the type operand of the NEWARRAY instruction.
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/RecordComponentVisitor.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/RecordComponentVisitor.java
new file mode 100644
index 0000000..41254a3
--- /dev/null
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/RecordComponentVisitor.java
@@ -0,0 +1,150 @@
+// ASM: a very small and fast Java bytecode manipulation framework
+// Copyright (c) 2000-2011 INRIA, France Telecom
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// 1. Redistributions of source code must retain the above copyright
+//    notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+//    notice, this list of conditions and the following disclaimer in the
+//    documentation and/or other materials provided with the distribution.
+// 3. Neither the name of the copyright holders nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+// THE POSSIBILITY OF SUCH DAMAGE.
+package org.apache.tapestry5.internal.plastic.asm;
+
+/**
+ * A visitor to visit a record component. The methods of this class must be called in the following
+ * order: ( {@code visitAnnotation} | {@code visitTypeAnnotation} | {@code visitAttribute} )* {@code
+ * visitEnd}.
+ *
+ * @author Remi Forax
+ * @author Eric Bruneton
+ */
+public abstract class RecordComponentVisitor {
+  /**
+   * The ASM API version implemented by this visitor. The value of this field must be {@link
+   * Opcodes#ASM8}.
+   */
+  protected final int api;
+
+  /**
+   * The record visitor to which this visitor must delegate method calls. May be {@literal null}.
+   */
+  /*package-private*/ RecordComponentVisitor delegate;
+
+  /**
+   * Constructs a new {@link RecordComponentVisitor}.
+   *
+   * @param api the ASM API version implemented by this visitor. Must be {@link Opcodes#ASM8}.
+   */
+  public RecordComponentVisitor(final int api) {
+    this(api, null);
+  }
+
+  /**
+   * Constructs a new {@link RecordComponentVisitor}.
+   *
+   * @param api the ASM API version implemented by this visitor. Must be {@link Opcodes#ASM8}.
+   * @param recordComponentVisitor the record component visitor to which this visitor must delegate
+   *     method calls. May be null.
+   */
+  public RecordComponentVisitor(
+      final int api, final RecordComponentVisitor recordComponentVisitor) {
+    if (api != Opcodes.ASM8
+        && api != Opcodes.ASM7
+        && api != Opcodes.ASM6
+        && api != Opcodes.ASM5
+        && api != Opcodes.ASM4
+        && api != Opcodes.ASM9_EXPERIMENTAL) {
+      throw new IllegalArgumentException("Unsupported api " + api);
+    }
+    if (api == Opcodes.ASM9_EXPERIMENTAL) {
+      Constants.checkAsmExperimental(this);
+    }
+    this.api = api;
+    this.delegate = recordComponentVisitor;
+  }
+
+  /**
+   * The record visitor to which this visitor must delegate method calls. May be {@literal null}.
+   *
+   * @return the record visitor to which this visitor must delegate method calls or {@literal null}.
+   */
+  public RecordComponentVisitor getDelegate() {
+    return delegate;
+  }
+
+  /**
+   * Visits an annotation of the record component.
+   *
+   * @param descriptor the class descriptor of the annotation class.
+   * @param visible {@literal true} if the annotation is visible at runtime.
+   * @return a visitor to visit the annotation values, or {@literal null} if this visitor is not
+   *     interested in visiting this annotation.
+   */
+  public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
+    if (delegate != null) {
+      return delegate.visitAnnotation(descriptor, visible);
+    }
+    return null;
+  }
+
+  /**
+   * Visits an annotation on a type in the record component signature.
+   *
+   * @param typeRef a reference to the annotated type. The sort of this type reference must be
+   *     {@link TypeReference#CLASS_TYPE_PARAMETER}, {@link
+   *     TypeReference#CLASS_TYPE_PARAMETER_BOUND} or {@link TypeReference#CLASS_EXTENDS}. See
+   *     {@link TypeReference}.
+   * @param typePath the path to the annotated type argument, wildcard bound, array element type, or
+   *     static inner type within 'typeRef'. May be {@literal null} if the annotation targets
+   *     'typeRef' as a whole.
+   * @param descriptor the class descriptor of the annotation class.
+   * @param visible {@literal true} if the annotation is visible at runtime.
+   * @return a visitor to visit the annotation values, or {@literal null} if this visitor is not
+   *     interested in visiting this annotation.
+   */
+  public AnnotationVisitor visitTypeAnnotation(
+      final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
+    if (delegate != null) {
+      return delegate.visitTypeAnnotation(typeRef, typePath, descriptor, visible);
+    }
+    return null;
+  }
+
+  /**
+   * Visits a non standard attribute of the record component.
+   *
+   * @param attribute an attribute.
+   */
+  public void visitAttribute(final Attribute attribute) {
+    if (delegate != null) {
+      delegate.visitAttribute(attribute);
+    }
+  }
+
+  /**
+   * Visits the end of the record component. This method, which is the last one to be called, is
+   * used to inform the visitor that everything have been visited.
+   */
+  public void visitEnd() {
+    if (delegate != null) {
+      delegate.visitEnd();
+    }
+  }
+}
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/RecordComponentWriter.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/RecordComponentWriter.java
new file mode 100644
index 0000000..f41ad92
--- /dev/null
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/RecordComponentWriter.java
@@ -0,0 +1,225 @@
+// ASM: a very small and fast Java bytecode manipulation framework
+// Copyright (c) 2000-2011 INRIA, France Telecom
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// 1. Redistributions of source code must retain the above copyright
+//    notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+//    notice, this list of conditions and the following disclaimer in the
+//    documentation and/or other materials provided with the distribution.
+// 3. Neither the name of the copyright holders nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+// THE POSSIBILITY OF SUCH DAMAGE.
+package org.apache.tapestry5.internal.plastic.asm;
+
+final class RecordComponentWriter extends RecordComponentVisitor {
+  /** Where the constants used in this RecordComponentWriter must be stored. */
+  private final SymbolTable symbolTable;
+
+  // Note: fields are ordered as in the record_component_info structure, and those related to
+  // attributes are ordered as in Section 4.7 of the JVMS.
+
+  /** The name_index field of the Record attribute. */
+  private final int nameIndex;
+
+  /** The descriptor_index field of the the Record attribute. */
+  private final int descriptorIndex;
+
+  /**
+   * The signature_index field of the Signature attribute of this record component, or 0 if there is
+   * no Signature attribute.
+   */
+  private int signatureIndex;
+
+  /**
+   * The last runtime visible annotation of this record component. The previous ones can be accessed
+   * with the {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}.
+   */
+  private AnnotationWriter lastRuntimeVisibleAnnotation;
+
+  /**
+   * The last runtime invisible annotation of this record component. The previous ones can be
+   * accessed with the {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}.
+   */
+  private AnnotationWriter lastRuntimeInvisibleAnnotation;
+
+  /**
+   * The last runtime visible type annotation of this record component. The previous ones can be
+   * accessed with the {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}.
+   */
+  private AnnotationWriter lastRuntimeVisibleTypeAnnotation;
+
+  /**
+   * The last runtime invisible type annotation of this record component. The previous ones can be
+   * accessed with the {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}.
+   */
+  private AnnotationWriter lastRuntimeInvisibleTypeAnnotation;
+
+  /**
+   * The first non standard attribute of this record component. The next ones can be accessed with
+   * the {@link Attribute#nextAttribute} field. May be {@literal null}.
+   *
+   * <p><b>WARNING</b>: this list stores the attributes in the <i>reverse</i> order of their visit.
+   * firstAttribute is actually the last attribute visited in {@link #visitAttribute(Attribute)}.
+   * The {@link #putRecordComponentInfo(ByteVector)} method writes the attributes in the order
+   * defined by this list, i.e. in the reverse order specified by the user.
+   */
+  private Attribute firstAttribute;
+
+  /**
+   * Constructs a new {@link RecordComponentWriter}.
+   *
+   * @param symbolTable where the constants used in this RecordComponentWriter must be stored.
+   * @param name the record component name.
+   * @param descriptor the record component descriptor (see {@link Type}).
+   * @param signature the record component signature. May be {@literal null}.
+   */
+  RecordComponentWriter(
+      final SymbolTable symbolTable,
+      final String name,
+      final String descriptor,
+      final String signature) {
+    super(/* latest api = */ Opcodes.ASM8);
+    this.symbolTable = symbolTable;
+    this.nameIndex = symbolTable.addConstantUtf8(name);
+    this.descriptorIndex = symbolTable.addConstantUtf8(descriptor);
+    if (signature != null) {
+      this.signatureIndex = symbolTable.addConstantUtf8(signature);
+    }
+  }
+
+  // -----------------------------------------------------------------------------------------------
+  // Implementation of the FieldVisitor abstract class
+  // -----------------------------------------------------------------------------------------------
+
+  @Override
+  public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
+    if (visible) {
+      return lastRuntimeVisibleAnnotation =
+          AnnotationWriter.create(symbolTable, descriptor, lastRuntimeVisibleAnnotation);
+    } else {
+      return lastRuntimeInvisibleAnnotation =
+          AnnotationWriter.create(symbolTable, descriptor, lastRuntimeInvisibleAnnotation);
+    }
+  }
+
+  @Override
+  public AnnotationVisitor visitTypeAnnotation(
+      final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
+    if (visible) {
+      return lastRuntimeVisibleTypeAnnotation =
+          AnnotationWriter.create(
+              symbolTable, typeRef, typePath, descriptor, lastRuntimeVisibleTypeAnnotation);
+    } else {
+      return lastRuntimeInvisibleTypeAnnotation =
+          AnnotationWriter.create(
+              symbolTable, typeRef, typePath, descriptor, lastRuntimeInvisibleTypeAnnotation);
+    }
+  }
+
+  @Override
+  public void visitAttribute(final Attribute attribute) {
+    // Store the attributes in the <i>reverse</i> order of their visit by this method.
+    attribute.nextAttribute = firstAttribute;
+    firstAttribute = attribute;
+  }
+
+  @Override
+  public void visitEnd() {
+    // Nothing to do.
+  }
+
+  // -----------------------------------------------------------------------------------------------
+  // Utility methods
+  // -----------------------------------------------------------------------------------------------
+
+  /**
+   * Returns the size of the record component JVMS structure generated by this
+   * RecordComponentWriter. Also adds the names of the attributes of this record component in the
+   * constant pool.
+   *
+   * @return the size in bytes of the record_component_info of the Record attribute.
+   */
+  int computeRecordComponentInfoSize() {
+    // name_index, descriptor_index and attributes_count fields use 6 bytes.
+    int size = 6;
+    size += Attribute.computeAttributesSize(symbolTable, 0, signatureIndex);
+    size +=
+        AnnotationWriter.computeAnnotationsSize(
+            lastRuntimeVisibleAnnotation,
+            lastRuntimeInvisibleAnnotation,
+            lastRuntimeVisibleTypeAnnotation,
+            lastRuntimeInvisibleTypeAnnotation);
+    if (firstAttribute != null) {
+      size += firstAttribute.computeAttributesSize(symbolTable);
+    }
+    return size;
+  }
+
+  /**
+   * Puts the content of the record component generated by this RecordComponentWriter into the given
+   * ByteVector.
+   *
+   * @param output where the record_component_info structure must be put.
+   */
+  void putRecordComponentInfo(final ByteVector output) {
+    output.putShort(nameIndex).putShort(descriptorIndex);
+    // Compute and put the attributes_count field.
+    // For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS.
+    int attributesCount = 0;
+    if (signatureIndex != 0) {
+      ++attributesCount;
+    }
+    if (lastRuntimeVisibleAnnotation != null) {
+      ++attributesCount;
+    }
+    if (lastRuntimeInvisibleAnnotation != null) {
+      ++attributesCount;
+    }
+    if (lastRuntimeVisibleTypeAnnotation != null) {
+      ++attributesCount;
+    }
+    if (lastRuntimeInvisibleTypeAnnotation != null) {
+      ++attributesCount;
+    }
+    if (firstAttribute != null) {
+      attributesCount += firstAttribute.getAttributeCount();
+    }
+    output.putShort(attributesCount);
+    Attribute.putAttributes(symbolTable, 0, signatureIndex, output);
+    AnnotationWriter.putAnnotations(
+        symbolTable,
+        lastRuntimeVisibleAnnotation,
+        lastRuntimeInvisibleAnnotation,
+        lastRuntimeVisibleTypeAnnotation,
+        lastRuntimeInvisibleTypeAnnotation,
+        output);
+    if (firstAttribute != null) {
+      firstAttribute.putAttributes(symbolTable, output);
+    }
+  }
+
+  /**
+   * Collects the attributes of this record component into the given set of attribute prototypes.
+   *
+   * @param attributePrototypes a set of attribute prototypes.
+   */
+  final void collectAttributePrototypes(final Attribute.Set attributePrototypes) {
+    attributePrototypes.addAttributes(firstAttribute);
+  }
+}
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
old mode 100755
new mode 100644
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
old mode 100755
new mode 100644
index e6574bb..ecb03f9
--- 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
@@ -31,11 +31,11 @@
  * The constant pool entries, the BootstrapMethods attribute entries and the (ASM specific) type
  * table entries of a class.
  *
+ * @author Eric Bruneton
  * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.4">JVMS
  *     4.4</a>
  * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.23">JVMS
  *     4.7.23</a>
- * @author Eric Bruneton
  */
 final class SymbolTable {
 
@@ -139,7 +139,7 @@
     this.sourceClassReader = classReader;
 
     // Copy the constant pool binary content.
-    byte[] inputBytes = classReader.b;
+    byte[] inputBytes = classReader.classFileBuffer;
     int constantPoolOffset = classReader.getItem(1) - 1;
     int constantPoolLength = classReader.header - constantPoolOffset;
     constantPoolCount = classReader.getItemCount();
@@ -242,7 +242,7 @@
    */
   private void copyBootstrapMethods(final ClassReader classReader, final char[] charBuffer) {
     // Find attributOffset of the 'bootstrap_methods' array.
-    byte[] inputBytes = classReader.b;
+    byte[] inputBytes = classReader.classFileBuffer;
     int currentAttributeOffset = classReader.getFirstAttributeOffset();
     for (int i = classReader.readUnsignedShort(currentAttributeOffset - 2); i > 0; --i) {
       String attributeName = classReader.readUTF8(currentAttributeOffset, charBuffer);
@@ -1046,8 +1046,10 @@
     // bootstrap methods. We must therefore add the bootstrap method arguments to the constant pool
     // and BootstrapMethods attribute first, so that the BootstrapMethods attribute is not modified
     // while adding the given bootstrap method to it, in the rest of this method.
-    for (Object bootstrapMethodArgument : bootstrapMethodArguments) {
-      addConstant(bootstrapMethodArgument);
+    int numBootstrapArguments = bootstrapMethodArguments.length;
+    int[] bootstrapMethodArgumentIndexes = new int[numBootstrapArguments];
+    for (int i = 0; i < numBootstrapArguments; i++) {
+      bootstrapMethodArgumentIndexes[i] = addConstant(bootstrapMethodArguments[i]).index;
     }
 
     // Write the bootstrap method in the BootstrapMethods table. This is necessary to be able to
@@ -1062,10 +1064,10 @@
                 bootstrapMethodHandle.getDesc(),
                 bootstrapMethodHandle.isInterface())
             .index);
-    int numBootstrapArguments = bootstrapMethodArguments.length;
+
     bootstrapMethodsAttribute.putShort(numBootstrapArguments);
-    for (Object bootstrapMethodArgument : bootstrapMethodArguments) {
-      bootstrapMethodsAttribute.putShort(addConstant(bootstrapMethodArgument).index);
+    for (int i = 0; i < numBootstrapArguments; i++) {
+      bootstrapMethodsAttribute.putShort(bootstrapMethodArgumentIndexes[i]);
     }
 
     // Compute the length and the hash code of the bootstrap method.
@@ -1183,8 +1185,10 @@
    *     corresponding to the common super class of the given types.
    */
   int addMergedType(final int typeTableIndex1, final int typeTableIndex2) {
-    // TODO sort the arguments? The merge result should be independent of their order.
-    long data = typeTableIndex1 | (((long) typeTableIndex2) << 32);
+    long data =
+        typeTableIndex1 < typeTableIndex2
+            ? typeTableIndex1 | (((long) typeTableIndex2) << 32)
+            : typeTableIndex2 | (((long) typeTableIndex1) << 32);
     int hashCode = hash(Symbol.MERGED_TYPE_TAG, typeTableIndex1 + typeTableIndex2);
     Entry entry = get(hashCode);
     while (entry != null) {
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Type.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Type.java
old mode 100755
new mode 100644
index 2174939..7413a36
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Type.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Type.java
@@ -305,7 +305,8 @@
       }
       if (methodDescriptor.charAt(currentOffset++) == 'L') {
         // Skip the argument descriptor content.
-        currentOffset = methodDescriptor.indexOf(';', currentOffset) + 1;
+        int semiColumnOffset = methodDescriptor.indexOf(';', currentOffset);
+        currentOffset = Math.max(currentOffset, semiColumnOffset + 1);
       }
       ++numArgumentTypes;
     }
@@ -323,7 +324,8 @@
       }
       if (methodDescriptor.charAt(currentOffset++) == 'L') {
         // Skip the argument descriptor content.
-        currentOffset = methodDescriptor.indexOf(';', currentOffset) + 1;
+        int semiColumnOffset = methodDescriptor.indexOf(';', currentOffset);
+        currentOffset = Math.max(currentOffset, semiColumnOffset + 1);
       }
       argumentTypes[currentArgumentTypeIndex++] =
           getTypeInternal(methodDescriptor, currentArgumentTypeOffset, currentOffset);
@@ -363,19 +365,8 @@
    * @return the {@link Type} corresponding to the return type of the given method descriptor.
    */
   public static Type getReturnType(final String methodDescriptor) {
-    // Skip the first character, which is always a '('.
-    int currentOffset = 1;
-    // Skip the argument types, one at a each loop iteration.
-    while (methodDescriptor.charAt(currentOffset) != ')') {
-      while (methodDescriptor.charAt(currentOffset) == '[') {
-        currentOffset++;
-      }
-      if (methodDescriptor.charAt(currentOffset++) == 'L') {
-        // Skip the argument descriptor content.
-        currentOffset = methodDescriptor.indexOf(';', currentOffset) + 1;
-      }
-    }
-    return getTypeInternal(methodDescriptor, currentOffset + 1, methodDescriptor.length());
+    return getTypeInternal(
+        methodDescriptor, getReturnTypeOffset(methodDescriptor), methodDescriptor.length());
   }
 
   /**
@@ -389,6 +380,29 @@
   }
 
   /**
+   * Returns the start index of the return type of the given method descriptor.
+   *
+   * @param methodDescriptor a method descriptor.
+   * @return the start index of the return type of the given method descriptor.
+   */
+  static int getReturnTypeOffset(final String methodDescriptor) {
+    // Skip the first character, which is always a '('.
+    int currentOffset = 1;
+    // Skip the argument types, one at a each loop iteration.
+    while (methodDescriptor.charAt(currentOffset) != ')') {
+      while (methodDescriptor.charAt(currentOffset) == '[') {
+        currentOffset++;
+      }
+      if (methodDescriptor.charAt(currentOffset++) == 'L') {
+        // Skip the argument descriptor content.
+        int semiColumnOffset = methodDescriptor.indexOf(';', currentOffset);
+        currentOffset = Math.max(currentOffset, semiColumnOffset + 1);
+      }
+    }
+    return currentOffset + 1;
+  }
+
+  /**
    * Returns the {@link Type} corresponding to the given field or method descriptor.
    *
    * @param descriptorBuffer a buffer containing the field or method descriptor.
@@ -505,11 +519,7 @@
     if (sort == OBJECT) {
       return valueBuffer.substring(valueBegin - 1, valueEnd + 1);
     } else if (sort == INTERNAL) {
-      return new StringBuilder()
-          .append('L')
-          .append(valueBuffer, valueBegin, valueEnd)
-          .append(';')
-          .toString();
+      return 'L' + valueBuffer.substring(valueBegin, valueEnd) + ';';
     } else {
       return valueBuffer.substring(valueBegin, valueEnd);
     }
@@ -631,14 +641,7 @@
       }
       stringBuilder.append(descriptor);
     } else {
-      stringBuilder.append('L');
-      String name = currentClass.getName();
-      int nameLength = name.length();
-      for (int i = 0; i < nameLength; ++i) {
-        char car = name.charAt(i);
-        stringBuilder.append(car == '.' ? '/' : car);
-      }
-      stringBuilder.append(';');
+      stringBuilder.append('L').append(getInternalName(currentClass)).append(';');
     }
   }
 
@@ -737,7 +740,8 @@
         }
         if (methodDescriptor.charAt(currentOffset++) == 'L') {
           // Skip the argument descriptor content.
-          currentOffset = methodDescriptor.indexOf(';', currentOffset) + 1;
+          int semiColumnOffset = methodDescriptor.indexOf(';', currentOffset);
+          currentOffset = Math.max(currentOffset, semiColumnOffset + 1);
         }
         argumentsSize += 1;
       }
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/TypePath.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/TypePath.java
old mode 100755
new mode 100644
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/TypeReference.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/TypeReference.java
old mode 100755
new mode 100644
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
old mode 100755
new mode 100644
index 96e0c48..33d40d5
--- 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
@@ -121,8 +121,8 @@
   public void visitCode() {
     super.visitCode();
     if (isConstructor) {
-      stackFrame = new ArrayList<Object>();
-      forwardJumpStackFrames = new HashMap<Label, List<Object>>();
+      stackFrame = new ArrayList<>();
+      forwardJumpStackFrames = new HashMap<>();
     } else {
       onMethodEnter();
     }
@@ -351,6 +351,8 @@
           popValue();
           popValue();
           break;
+        case RET:
+          break;
         default:
           throw new IllegalArgumentException(INVALID_OPCODE + opcode);
       }
@@ -436,35 +438,21 @@
     }
   }
 
-  /**
-   * Deprecated.
-   *
-   * @deprecated use {@link #visitMethodInsn(int, String, String, String, boolean)} instead.
-   */
-  @Deprecated
   @Override
   public void visitMethodInsn(
-      final int opcode, final String owner, final String name, final String descriptor) {
-    if (api >= Opcodes.ASM5) {
-      super.visitMethodInsn(opcode, owner, name, descriptor);
-      return;
-    }
-    mv.visitMethodInsn(opcode, owner, name, descriptor, opcode == Opcodes.INVOKEINTERFACE);
-    doVisitMethodInsn(opcode, descriptor);
-  }
-
-  @Override
-  public void visitMethodInsn(
-      final int opcode,
+      final int opcodeAndSource,
       final String owner,
       final String name,
       final String descriptor,
       final boolean isInterface) {
-    if (api < Opcodes.ASM5) {
-      super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+    if (api < Opcodes.ASM5 && (opcodeAndSource & Opcodes.SOURCE_DEPRECATED) == 0) {
+      // Redirect the call to the deprecated version of this method.
+      super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface);
       return;
     }
-    mv.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+    super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface);
+    int opcode = opcodeAndSource & ~Opcodes.SOURCE_MASK;
+
     doVisitMethodInsn(opcode, descriptor);
   }
 
@@ -580,7 +568,7 @@
     // initialized twice), so this is not issue (in the sense that there is no risk to emit a wrong
     // 'onMethodEnter').
     if (isConstructor && !forwardJumpStackFrames.containsKey(handler)) {
-      List<Object> handlerStackFrame = new ArrayList<Object>();
+      List<Object> handlerStackFrame = new ArrayList<>();
       handlerStackFrame.add(OTHER);
       forwardJumpStackFrames.put(handler, handlerStackFrame);
     }
@@ -597,7 +585,7 @@
     if (forwardJumpStackFrames.containsKey(label)) {
       return;
     }
-    forwardJumpStackFrames.put(label, new ArrayList<Object>(stackFrame));
+    forwardJumpStackFrames.put(label, new ArrayList<>(stackFrame));
   }
 
   private Object popValue() {
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
old mode 100755
new mode 100644
index b541ecd..2025721
--- 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
@@ -116,7 +116,7 @@
       final String name,
       final String descriptor,
       final MethodVisitor methodVisitor) {
-    this(Opcodes.ASM7, owner, access, name, descriptor, methodVisitor);
+    this(/* latest api = */ Opcodes.ASM8, owner, access, name, descriptor, methodVisitor);
     if (getClass() != AnalyzerAdapter.class) {
       throw new IllegalStateException();
     }
@@ -126,7 +126,8 @@
    * Constructs a new {@link AnalyzerAdapter}.
    *
    * @param api the ASM API version implemented by this visitor. Must be one of {@link
-   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
+   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7} or {@link
+   *     Opcodes#ASM8}.
    * @param owner the owner's class name.
    * @param access the method's access flags (see {@link Opcodes}).
    * @param name the method's name.
@@ -143,9 +144,9 @@
       final MethodVisitor methodVisitor) {
     super(api, methodVisitor);
     this.owner = owner;
-    locals = new ArrayList<Object>();
-    stack = new ArrayList<Object>();
-    uninitializedTypes = new HashMap<Object, Object>();
+    locals = new ArrayList<>();
+    stack = new ArrayList<>();
+    uninitializedTypes = new HashMap<>();
 
     if ((access & Opcodes.ACC_STATIC) == 0) {
       if ("<init>".equals(name)) {
@@ -205,8 +206,8 @@
       this.locals.clear();
       this.stack.clear();
     } else {
-      this.locals = new ArrayList<Object>();
-      this.stack = new ArrayList<Object>();
+      this.locals = new ArrayList<>();
+      this.stack = new ArrayList<>();
     }
     visitFrameTypes(numLocal, local, this.locals);
     visitFrameTypes(numStack, stack, this.stack);
@@ -258,7 +259,7 @@
     if (opcode == Opcodes.NEW) {
       if (labels == null) {
         Label label = new Label();
-        labels = new ArrayList<Label>(3);
+        labels = new ArrayList<>(3);
         labels.add(label);
         if (mv != null) {
           mv.visitLabel(label);
@@ -279,45 +280,21 @@
     execute(opcode, 0, descriptor);
   }
 
-  /**
-   * Deprecated.
-   *
-   * @deprecated use {@link #visitMethodInsn(int, String, String, String, boolean)} instead.
-   */
-  @Deprecated
   @Override
   public void visitMethodInsn(
-      final int opcode, final String owner, final String name, final String descriptor) {
-    if (api >= Opcodes.ASM5) {
-      super.visitMethodInsn(opcode, owner, name, descriptor);
-      return;
-    }
-    doVisitMethodInsn(opcode, owner, name, descriptor, opcode == Opcodes.INVOKEINTERFACE);
-  }
-
-  @Override
-  public void visitMethodInsn(
-      final int opcode,
+      final int opcodeAndSource,
       final String owner,
       final String name,
       final String descriptor,
       final boolean isInterface) {
-    if (api < Opcodes.ASM5) {
-      super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+    if (api < Opcodes.ASM5 && (opcodeAndSource & Opcodes.SOURCE_DEPRECATED) == 0) {
+      // Redirect the call to the deprecated version of this method.
+      super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface);
       return;
     }
-    doVisitMethodInsn(opcode, owner, name, descriptor, isInterface);
-  }
+    super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface);
+    int opcode = opcodeAndSource & ~Opcodes.SOURCE_MASK;
 
-  private void doVisitMethodInsn(
-      final int opcode,
-      final String owner,
-      final String name,
-      final String descriptor,
-      final boolean isInterface) {
-    if (mv != null) {
-      mv.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
-    }
     if (this.locals == null) {
       labels = null;
       return;
@@ -378,7 +355,7 @@
   public void visitLabel(final Label label) {
     super.visitLabel(label);
     if (labels == null) {
-      labels = new ArrayList<Label>(3);
+      labels = new ArrayList<>(3);
     }
     labels.add(label);
   }
@@ -495,9 +472,12 @@
     maxStack = Math.max(maxStack, stack.size());
   }
 
-  private void pushDescriptor(final String descriptor) {
-    int index = descriptor.charAt(0) == '(' ? descriptor.indexOf(')') + 1 : 0;
-    switch (descriptor.charAt(index)) {
+  private void pushDescriptor(final String fieldOrMethodDescriptor) {
+    String descriptor =
+        fieldOrMethodDescriptor.charAt(0) == '('
+            ? Type.getReturnType(fieldOrMethodDescriptor).getDescriptor()
+            : fieldOrMethodDescriptor;
+    switch (descriptor.charAt(0)) {
       case 'V':
         return;
       case 'Z':
@@ -519,18 +499,10 @@
         push(Opcodes.TOP);
         return;
       case '[':
-        if (index == 0) {
-          push(descriptor);
-        } else {
-          push(descriptor.substring(index, descriptor.length()));
-        }
+        push(descriptor);
         break;
       case 'L':
-        if (index == 0) {
-          push(descriptor.substring(1, descriptor.length() - 1));
-        } else {
-          push(descriptor.substring(index + 1, descriptor.length() - 1));
-        }
+        push(descriptor.substring(1, descriptor.length() - 1));
         break;
       default:
         throw new AssertionError();
@@ -566,6 +538,9 @@
   }
 
   private void execute(final int opcode, final int intArg, final String stringArg) {
+    if (opcode == Opcodes.JSR || opcode == Opcodes.RET) {
+      throw new IllegalArgumentException("JSR/RET are not supported");
+    }
     if (this.locals == null) {
       labels = null;
       return;
@@ -866,9 +841,6 @@
         pop(4);
         push(Opcodes.INTEGER);
         break;
-      case Opcodes.JSR:
-      case Opcodes.RET:
-        throw new IllegalArgumentException("JSR/RET are not supported");
       case Opcodes.GETSTATIC:
         pushDescriptor(stringArg);
         break;
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
old mode 100755
new mode 100644
index 24a2de5..19b03ff
--- 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
@@ -49,15 +49,16 @@
    * @param remapper the remapper to use to remap the types in the visited annotation.
    */
   public AnnotationRemapper(final AnnotationVisitor annotationVisitor, final Remapper remapper) {
-    this(Opcodes.ASM7, annotationVisitor, remapper);
+    this(/* latest api = */ Opcodes.ASM8, annotationVisitor, remapper);
   }
 
   /**
    * Constructs a new {@link AnnotationRemapper}.
    *
    * @param api the ASM API version supported by this remapper. Must be one of {@link
-   *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM4}, {@link org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM5} or {@link
-   *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM6}.
+   *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM4}, {@link org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM5}, {@link
+   *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM6}, {@link org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM7} or {@link
+   *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM8}
    * @param annotationVisitor the annotation visitor this remapper must deleted to.
    * @param remapper the remapper to use to remap the types in the visited annotation.
    */
@@ -83,9 +84,7 @@
     if (annotationVisitor == null) {
       return null;
     } else {
-      return annotationVisitor == av
-          ? this
-          : new AnnotationRemapper(api, annotationVisitor, remapper);
+      return annotationVisitor == av ? this : createAnnotationRemapper(annotationVisitor);
     }
   }
 
@@ -95,9 +94,18 @@
     if (annotationVisitor == null) {
       return null;
     } else {
-      return annotationVisitor == av
-          ? this
-          : new AnnotationRemapper(api, annotationVisitor, remapper);
+      return annotationVisitor == av ? this : createAnnotationRemapper(annotationVisitor);
     }
   }
+
+  /**
+   * Constructs a new remapper for annotations. The default implementation of this method returns a
+   * new {@link AnnotationRemapper}.
+   *
+   * @param annotationVisitor the AnnotationVisitor the remapper must delegate to.
+   * @return the newly created remapper.
+   */
+  protected AnnotationVisitor createAnnotationRemapper(final AnnotationVisitor annotationVisitor) {
+    return new AnnotationRemapper(api, 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
old mode 100755
new mode 100644
index f837457..99557bc
--- 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
@@ -36,11 +36,26 @@
 import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
 import org.apache.tapestry5.internal.plastic.asm.ModuleVisitor;
 import org.apache.tapestry5.internal.plastic.asm.Opcodes;
+import org.apache.tapestry5.internal.plastic.asm.RecordComponentVisitor;
 import org.apache.tapestry5.internal.plastic.asm.TypePath;
 
 /**
  * A {@link ClassVisitor} that remaps types with a {@link Remapper}.
  *
+ * <p><i>This visitor has several limitations</i>. A non-exhaustive list is the following:
+ *
+ * <ul>
+ *   <li>it cannot remap type names in dynamically computed strings (remapping of type names in
+ *       static values is supported).
+ *   <li>it cannot remap values derived from type names at compile time, such as
+ *       <ul>
+ *         <li>type name hashcodes used by some Java compilers to implement the string switch
+ *             statement.
+ *         <li>some compound strings used by some Java compilers to implement lambda
+ *             deserialization.
+ *       </ul>
+ * </ul>
+ *
  * @author Eugene Kuleshov
  */
 public class ClassRemapper extends ClassVisitor {
@@ -59,7 +74,7 @@
    * @param remapper the remapper to use to remap the types in the visited class.
    */
   public ClassRemapper(final ClassVisitor classVisitor, final Remapper remapper) {
-    this(Opcodes.ASM7, classVisitor, remapper);
+    this(/* latest api = */ Opcodes.ASM8, classVisitor, remapper);
   }
 
   /**
@@ -67,7 +82,8 @@
    *
    * @param api the ASM API version supported by this remapper. Must be one of {@link
    *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM4}, {@link org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM5}, {@link
-   *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM6} or {@link org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM7}.
+   *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM6}, {@link org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM7} or {@link
+   *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM8}.
    * @param classVisitor the class visitor this remapper must deleted to.
    * @param remapper the remapper to use to remap the types in the visited class.
    */
@@ -128,6 +144,19 @@
   }
 
   @Override
+  public RecordComponentVisitor visitRecordComponent(
+      final String name, final String descriptor, final String signature) {
+    RecordComponentVisitor recordComponentVisitor =
+        super.visitRecordComponent(
+            remapper.mapRecordComponentName(className, name, descriptor),
+            remapper.mapDesc(descriptor),
+            remapper.mapSignature(signature, true));
+    return recordComponentVisitor == null
+        ? null
+        : createRecordComponentRemapper(recordComponentVisitor);
+  }
+
+  @Override
   public FieldVisitor visitField(
       final int access,
       final String name,
@@ -191,6 +220,18 @@
   }
 
   /**
+   * <b>Experimental, use at your own risk.</b>.
+   *
+   * @param permittedSubtype the internal name of a permitted subtype.
+   * @deprecated this API is experimental.
+   */
+  @Override
+  @Deprecated
+  public void visitPermittedSubtypeExperimental(final String permittedSubtype) {
+    super.visitPermittedSubtypeExperimental(remapper.mapType(permittedSubtype));
+  }
+
+  /**
    * Constructs a new remapper for fields. The default implementation of this method returns a new
    * {@link FieldRemapper}.
    *
@@ -233,4 +274,16 @@
   protected ModuleVisitor createModuleRemapper(final ModuleVisitor moduleVisitor) {
     return new ModuleRemapper(api, moduleVisitor, remapper);
   }
+
+  /**
+   * Constructs a new remapper for record components. The default implementation of this method
+   * returns a new {@link RecordComponentRemapper}.
+   *
+   * @param recordComponentVisitor the RecordComponentVisitor the remapper must delegate to.
+   * @return the newly created remapper.
+   */
+  protected RecordComponentVisitor createRecordComponentRemapper(
+      final RecordComponentVisitor recordComponentVisitor) {
+    return new RecordComponentRemapper(api, recordComponentVisitor, remapper);
+  }
 }
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/CodeSizeEvaluator.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/CodeSizeEvaluator.java
old mode 100755
new mode 100644
index ecbea50..18a3543
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/CodeSizeEvaluator.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/CodeSizeEvaluator.java
@@ -47,7 +47,7 @@
   private int maxSize;
 
   public CodeSizeEvaluator(final MethodVisitor methodVisitor) {
-    this(Opcodes.ASM7, methodVisitor);
+    this(/* latest api = */ Opcodes.ASM8, methodVisitor);
   }
 
   protected CodeSizeEvaluator(final int api, final MethodVisitor methodVisitor) {
@@ -111,42 +111,20 @@
     super.visitFieldInsn(opcode, owner, name, descriptor);
   }
 
-  /**
-   * Deprecated.
-   *
-   * @deprecated use {@link #visitMethodInsn(int, String, String, String, boolean)} instead.
-   */
-  @Deprecated
   @Override
   public void visitMethodInsn(
-      final int opcode, final String owner, final String name, final String descriptor) {
-    if (api >= Opcodes.ASM5) {
-      super.visitMethodInsn(opcode, owner, name, descriptor);
-      return;
-    }
-    doVisitMethodInsn(opcode, owner, name, descriptor, opcode == Opcodes.INVOKEINTERFACE);
-  }
-
-  @Override
-  public void visitMethodInsn(
-      final int opcode,
+      final int opcodeAndSource,
       final String owner,
       final String name,
       final String descriptor,
       final boolean isInterface) {
-    if (api < Opcodes.ASM5) {
-      super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+    if (api < Opcodes.ASM5 && (opcodeAndSource & Opcodes.SOURCE_DEPRECATED) == 0) {
+      // Redirect the call to the deprecated version of this method.
+      super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface);
       return;
     }
-    doVisitMethodInsn(opcode, owner, name, descriptor, isInterface);
-  }
+    int opcode = opcodeAndSource & ~Opcodes.SOURCE_MASK;
 
-  private void doVisitMethodInsn(
-      final int opcode,
-      final String owner,
-      final String name,
-      final String descriptor,
-      final boolean isInterface) {
     if (opcode == INVOKEINTERFACE) {
       minSize += 5;
       maxSize += 5;
@@ -154,9 +132,7 @@
       minSize += 3;
       maxSize += 3;
     }
-    if (mv != null) {
-      mv.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
-    }
+    super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface);
   }
 
   @Override
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
old mode 100755
new mode 100644
index c8cbeed..d49aceb
--- 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
@@ -51,15 +51,15 @@
    * @param remapper the remapper to use to remap the types in the visited field.
    */
   public FieldRemapper(final FieldVisitor fieldVisitor, final Remapper remapper) {
-    this(Opcodes.ASM7, fieldVisitor, remapper);
+    this(/* latest api = */ Opcodes.ASM8, fieldVisitor, remapper);
   }
 
   /**
    * Constructs a new {@link FieldRemapper}.
    *
    * @param api the ASM API version supported by this remapper. Must be one of {@link
-   *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM4}, {@link org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM5} or {@link
-   *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM6}.
+   *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM4}, {@link org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM5}, {@link
+   *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM6}, {@link Opcodes#ASM7} or {@link Opcodes#ASM8}.
    * @param fieldVisitor the field visitor this remapper must deleted to.
    * @param remapper the remapper to use to remap the types in the visited field.
    */
@@ -72,9 +72,7 @@
   public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
     AnnotationVisitor annotationVisitor =
         super.visitAnnotation(remapper.mapDesc(descriptor), visible);
-    return annotationVisitor == null
-        ? null
-        : new AnnotationRemapper(api, annotationVisitor, remapper);
+    return annotationVisitor == null ? null : createAnnotationRemapper(annotationVisitor);
   }
 
   @Override
@@ -82,8 +80,17 @@
       final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
     AnnotationVisitor annotationVisitor =
         super.visitTypeAnnotation(typeRef, typePath, remapper.mapDesc(descriptor), visible);
-    return annotationVisitor == null
-        ? null
-        : new AnnotationRemapper(api, annotationVisitor, remapper);
+    return annotationVisitor == null ? null : createAnnotationRemapper(annotationVisitor);
+  }
+
+  /**
+   * Constructs a new remapper for annotations. The default implementation of this method returns a
+   * new {@link AnnotationRemapper}.
+   *
+   * @param annotationVisitor the AnnotationVisitor the remapper must delegate to.
+   * @return the newly created remapper.
+   */
+  protected AnnotationVisitor createAnnotationRemapper(final AnnotationVisitor annotationVisitor) {
+    return new AnnotationRemapper(api, 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
old mode 100755
new mode 100644
index 96d850a..6dc7c9e
--- 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
@@ -183,7 +183,7 @@
   private final Type[] argumentTypes;
 
   /** The types of the local variables of the visited method. */
-  private final List<Type> localTypes = new ArrayList<Type>();
+  private final List<Type> localTypes = new ArrayList<>();
 
   /**
    * Constructs a new {@link GeneratorAdapter}. <i>Subclasses must not use this constructor</i>.
@@ -201,7 +201,7 @@
       final int access,
       final String name,
       final String descriptor) {
-    this(Opcodes.ASM7, methodVisitor, access, name, descriptor);
+    this(/* latest api = */ Opcodes.ASM8, methodVisitor, access, name, descriptor);
     if (getClass() != GeneratorAdapter.class) {
       throw new IllegalStateException();
     }
@@ -211,7 +211,8 @@
    * Constructs a new {@link GeneratorAdapter}.
    *
    * @param api the ASM API version implemented by this visitor. Must be one of {@link
-   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
+   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7} or {@link
+   *     Opcodes#ASM8}.
    * @param methodVisitor the method visitor to which this adapter delegates calls.
    * @param access the method's access flags (see {@link Opcodes}).
    * @param name the method's name.
@@ -269,7 +270,7 @@
             method.getName(),
             method.getDescriptor(),
             signature,
-            getInternalNames(exceptions)));
+            exceptions == null ? null : getInternalNames(exceptions)));
   }
 
   /**
@@ -279,9 +280,6 @@
    * @return the internal names of the given types.
    */
   private static String[] getInternalNames(final Type[] types) {
-    if (types == null) {
-      return null;
-    }
     String[] names = new String[types.length];
     for (int i = 0; i < names.length; ++i) {
       names[i] = types[i].getInternalName();
@@ -761,48 +759,7 @@
           || to.getSort() > Type.DOUBLE) {
         throw new IllegalArgumentException("Cannot cast from " + from + " to " + to);
       }
-      if (from == Type.DOUBLE_TYPE) {
-        if (to == Type.FLOAT_TYPE) {
-          mv.visitInsn(Opcodes.D2F);
-        } else if (to == Type.LONG_TYPE) {
-          mv.visitInsn(Opcodes.D2L);
-        } else {
-          mv.visitInsn(Opcodes.D2I);
-          cast(Type.INT_TYPE, to);
-        }
-      } else if (from == Type.FLOAT_TYPE) {
-        if (to == Type.DOUBLE_TYPE) {
-          mv.visitInsn(Opcodes.F2D);
-        } else if (to == Type.LONG_TYPE) {
-          mv.visitInsn(Opcodes.F2L);
-        } else {
-          mv.visitInsn(Opcodes.F2I);
-          cast(Type.INT_TYPE, to);
-        }
-      } else if (from == Type.LONG_TYPE) {
-        if (to == Type.DOUBLE_TYPE) {
-          mv.visitInsn(Opcodes.L2D);
-        } else if (to == Type.FLOAT_TYPE) {
-          mv.visitInsn(Opcodes.L2F);
-        } else {
-          mv.visitInsn(Opcodes.L2I);
-          cast(Type.INT_TYPE, to);
-        }
-      } else {
-        if (to == Type.BYTE_TYPE) {
-          mv.visitInsn(Opcodes.I2B);
-        } else if (to == Type.CHAR_TYPE) {
-          mv.visitInsn(Opcodes.I2C);
-        } else if (to == Type.DOUBLE_TYPE) {
-          mv.visitInsn(Opcodes.I2D);
-        } else if (to == Type.FLOAT_TYPE) {
-          mv.visitInsn(Opcodes.I2F);
-        } else if (to == Type.LONG_TYPE) {
-          mv.visitInsn(Opcodes.I2L);
-        } else if (to == Type.SHORT_TYPE) {
-          mv.visitInsn(Opcodes.I2S);
-        }
-      }
+      InstructionAdapter.cast(mv, from, to);
     }
   }
 
@@ -1319,37 +1276,7 @@
    * @param type the type of the array elements.
    */
   public void newArray(final Type type) {
-    int arrayType;
-    switch (type.getSort()) {
-      case Type.BOOLEAN:
-        arrayType = Opcodes.T_BOOLEAN;
-        break;
-      case Type.CHAR:
-        arrayType = Opcodes.T_CHAR;
-        break;
-      case Type.BYTE:
-        arrayType = Opcodes.T_BYTE;
-        break;
-      case Type.SHORT:
-        arrayType = Opcodes.T_SHORT;
-        break;
-      case Type.INT:
-        arrayType = Opcodes.T_INT;
-        break;
-      case Type.FLOAT:
-        arrayType = Opcodes.T_FLOAT;
-        break;
-      case Type.LONG:
-        arrayType = Opcodes.T_LONG;
-        break;
-      case Type.DOUBLE:
-        arrayType = Opcodes.T_DOUBLE;
-        break;
-      default:
-        typeInsn(Opcodes.ANEWARRAY, type);
-        return;
-    }
-    mv.visitIntInsn(Opcodes.NEWARRAY, arrayType);
+    InstructionAdapter.newarray(mv, type);
   }
 
   // -----------------------------------------------------------------------------------------------
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
old mode 100755
new mode 100644
index 07d30ee..076a989
--- 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
@@ -53,7 +53,7 @@
    * @throws IllegalStateException If a subclass calls this constructor.
    */
   public InstructionAdapter(final MethodVisitor methodVisitor) {
-    this(Opcodes.ASM7, methodVisitor);
+    this(/* latest api = */ Opcodes.ASM8, methodVisitor);
     if (getClass() != InstructionAdapter.class) {
       throw new IllegalStateException();
     }
@@ -63,7 +63,8 @@
    * Constructs a new {@link InstructionAdapter}.
    *
    * @param api the ASM API version implemented by this visitor. Must be one of {@link
-   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
+   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7} or {@link
+   *     Opcodes#ASM8}.
    * @param methodVisitor the method visitor to which this adapter delegates calls.
    */
   protected InstructionAdapter(final int api, final MethodVisitor methodVisitor) {
@@ -506,42 +507,20 @@
     }
   }
 
-  /**
-   * Deprecated.
-   *
-   * @deprecated use {@link #visitMethodInsn(int, String, String, String, boolean)} instead.
-   */
-  @Deprecated
   @Override
   public void visitMethodInsn(
-      final int opcode, final String owner, final String name, final String descriptor) {
-    if (api >= Opcodes.ASM5) {
-      super.visitMethodInsn(opcode, owner, name, descriptor);
-      return;
-    }
-    doVisitMethodInsn(opcode, owner, name, descriptor, opcode == Opcodes.INVOKEINTERFACE);
-  }
-
-  @Override
-  public void visitMethodInsn(
-      final int opcode,
+      final int opcodeAndSource,
       final String owner,
       final String name,
       final String descriptor,
       final boolean isInterface) {
-    if (api < Opcodes.ASM5) {
-      super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+    if (api < Opcodes.ASM5 && (opcodeAndSource & Opcodes.SOURCE_DEPRECATED) == 0) {
+      // Redirect the call to the deprecated version of this method.
+      super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface);
       return;
     }
-    doVisitMethodInsn(opcode, owner, name, descriptor, isInterface);
-  }
+    int opcode = opcodeAndSource & ~Opcodes.SOURCE_MASK;
 
-  private void doVisitMethodInsn(
-      final int opcode,
-      final String owner,
-      final String name,
-      final String descriptor,
-      final boolean isInterface) {
     switch (opcode) {
       case Opcodes.INVOKESPECIAL:
         invokespecial(owner, name, descriptor, isInterface);
@@ -643,7 +622,7 @@
             || (value instanceof Type && ((Type) value).getSort() == Type.METHOD))) {
       throw new UnsupportedOperationException("This feature requires ASM5");
     }
-    if (api != Opcodes.ASM7 && value instanceof ConstantDynamic) {
+    if (api < Opcodes.ASM7 && value instanceof ConstantDynamic) {
       throw new UnsupportedOperationException("This feature requires ASM7");
     }
     if (value instanceof Integer) {
@@ -917,47 +896,58 @@
    * @param to a Type.
    */
   public void cast(final Type from, final Type to) {
+    cast(mv, from, to);
+  }
+
+  /**
+   * Generates the instruction to cast from the first given type to the other.
+   *
+   * @param methodVisitor the method visitor to use to generate the instruction.
+   * @param from a Type.
+   * @param to a Type.
+   */
+  static void cast(final MethodVisitor methodVisitor, final Type from, final Type to) {
     if (from != to) {
       if (from == Type.DOUBLE_TYPE) {
         if (to == Type.FLOAT_TYPE) {
-          mv.visitInsn(Opcodes.D2F);
+          methodVisitor.visitInsn(Opcodes.D2F);
         } else if (to == Type.LONG_TYPE) {
-          mv.visitInsn(Opcodes.D2L);
+          methodVisitor.visitInsn(Opcodes.D2L);
         } else {
-          mv.visitInsn(Opcodes.D2I);
-          cast(Type.INT_TYPE, to);
+          methodVisitor.visitInsn(Opcodes.D2I);
+          cast(methodVisitor, Type.INT_TYPE, to);
         }
       } else if (from == Type.FLOAT_TYPE) {
         if (to == Type.DOUBLE_TYPE) {
-          mv.visitInsn(Opcodes.F2D);
+          methodVisitor.visitInsn(Opcodes.F2D);
         } else if (to == Type.LONG_TYPE) {
-          mv.visitInsn(Opcodes.F2L);
+          methodVisitor.visitInsn(Opcodes.F2L);
         } else {
-          mv.visitInsn(Opcodes.F2I);
-          cast(Type.INT_TYPE, to);
+          methodVisitor.visitInsn(Opcodes.F2I);
+          cast(methodVisitor, Type.INT_TYPE, to);
         }
       } else if (from == Type.LONG_TYPE) {
         if (to == Type.DOUBLE_TYPE) {
-          mv.visitInsn(Opcodes.L2D);
+          methodVisitor.visitInsn(Opcodes.L2D);
         } else if (to == Type.FLOAT_TYPE) {
-          mv.visitInsn(Opcodes.L2F);
+          methodVisitor.visitInsn(Opcodes.L2F);
         } else {
-          mv.visitInsn(Opcodes.L2I);
-          cast(Type.INT_TYPE, to);
+          methodVisitor.visitInsn(Opcodes.L2I);
+          cast(methodVisitor, Type.INT_TYPE, to);
         }
       } else {
         if (to == Type.BYTE_TYPE) {
-          mv.visitInsn(Opcodes.I2B);
+          methodVisitor.visitInsn(Opcodes.I2B);
         } else if (to == Type.CHAR_TYPE) {
-          mv.visitInsn(Opcodes.I2C);
+          methodVisitor.visitInsn(Opcodes.I2C);
         } else if (to == Type.DOUBLE_TYPE) {
-          mv.visitInsn(Opcodes.I2D);
+          methodVisitor.visitInsn(Opcodes.I2D);
         } else if (to == Type.FLOAT_TYPE) {
-          mv.visitInsn(Opcodes.I2F);
+          methodVisitor.visitInsn(Opcodes.I2F);
         } else if (to == Type.LONG_TYPE) {
-          mv.visitInsn(Opcodes.I2L);
+          methodVisitor.visitInsn(Opcodes.I2L);
         } else if (to == Type.SHORT_TYPE) {
-          mv.visitInsn(Opcodes.I2S);
+          methodVisitor.visitInsn(Opcodes.I2S);
         }
       }
     }
@@ -1226,6 +1216,16 @@
    * @param type an array Type.
    */
   public void newarray(final Type type) {
+    newarray(mv, type);
+  }
+
+  /**
+   * Generates the instruction to create and push on the stack an array of the given type.
+   *
+   * @param methodVisitor the method visitor to use to generate the instruction.
+   * @param type an array Type.
+   */
+  static void newarray(final MethodVisitor methodVisitor, final Type type) {
     int arrayType;
     switch (type.getSort()) {
       case Type.BOOLEAN:
@@ -1253,10 +1253,10 @@
         arrayType = Opcodes.T_DOUBLE;
         break;
       default:
-        mv.visitTypeInsn(Opcodes.ANEWARRAY, type.getInternalName());
+        methodVisitor.visitTypeInsn(Opcodes.ANEWARRAY, type.getInternalName());
         return;
     }
-    mv.visitIntInsn(Opcodes.NEWARRAY, arrayType);
+    methodVisitor.visitIntInsn(Opcodes.NEWARRAY, arrayType);
   }
 
   public void arraylength() {
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
old mode 100755
new mode 100644
index dc43c0f..baa97c3
--- 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
@@ -69,7 +69,7 @@
    * instruction, bit i of the corresponding BitSet in this map is set iff instruction at index i
    * belongs to this subroutine.
    */
-  private final Map<LabelNode, BitSet> subroutinesInsns = new HashMap<LabelNode, BitSet>();
+  private final Map<LabelNode, BitSet> subroutinesInsns = new HashMap<>();
 
   /**
    * The instructions that belong to more that one subroutine. Bit i is set iff instruction at index
@@ -98,7 +98,14 @@
       final String descriptor,
       final String signature,
       final String[] exceptions) {
-    this(Opcodes.ASM7, methodVisitor, access, name, descriptor, signature, exceptions);
+    this(
+        /* latest api = */ Opcodes.ASM8,
+        methodVisitor,
+        access,
+        name,
+        descriptor,
+        signature,
+        exceptions);
     if (getClass() != JSRInlinerAdapter.class) {
       throw new IllegalStateException();
     }
@@ -108,7 +115,8 @@
    * Constructs a new {@link JSRInlinerAdapter}.
    *
    * @param api the ASM API version implemented by this visitor. Must be one of {@link
-   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
+   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7} or {@link
+   *     Opcodes#ASM8}.
    * @param methodVisitor the method visitor to send the resulting inlined method code to, or <code>
    *     null</code>.
    * @param access the method's access flags (see {@link Opcodes}). This parameter also indicates if
@@ -291,14 +299,14 @@
    * fully elaborated.
    */
   private void emitCode() {
-    LinkedList<Instantiation> worklist = new LinkedList<Instantiation>();
+    LinkedList<Instantiation> worklist = new LinkedList<>();
     // Create an instantiation of the main "subroutine", which is just the main routine.
     worklist.add(new Instantiation(null, mainSubroutineInsns));
 
     // Emit instantiations of each subroutine we encounter, including the main subroutine.
     InsnList newInstructions = new InsnList();
-    List<TryCatchBlockNode> newTryCatchBlocks = new ArrayList<TryCatchBlockNode>();
-    List<LocalVariableNode> newLocalVariables = new ArrayList<LocalVariableNode>();
+    List<TryCatchBlockNode> newTryCatchBlocks = new ArrayList<>();
+    List<LocalVariableNode> newLocalVariables = new ArrayList<>();
     while (!worklist.isEmpty()) {
       Instantiation instantiation = worklist.removeFirst();
       emitInstantiation(
@@ -455,7 +463,7 @@
       this.parent = parent;
       this.subroutineInsns = subroutineInsns;
       this.returnLabel = parent == null ? null : new LabelNode();
-      this.clonedLabels = new HashMap<LabelNode, LabelNode>();
+      this.clonedLabels = new HashMap<>();
 
       // Create a clone of each label in the original code of the subroutine. Note that we collapse
       // labels which point at the same instruction into one.
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/LocalVariablesSorter.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/LocalVariablesSorter.java
old mode 100755
new mode 100644
index 08657ee..79bd072
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/LocalVariablesSorter.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/LocalVariablesSorter.java
@@ -81,7 +81,7 @@
    */
   public LocalVariablesSorter(
       final int access, final String descriptor, final MethodVisitor methodVisitor) {
-    this(Opcodes.ASM7, access, descriptor, methodVisitor);
+    this(/* latest api = */ Opcodes.ASM8, access, descriptor, methodVisitor);
     if (getClass() != LocalVariablesSorter.class) {
       throw new IllegalStateException();
     }
@@ -91,7 +91,8 @@
    * Constructs a new {@link LocalVariablesSorter}.
    *
    * @param api the ASM API version implemented by this visitor. Must be one of {@link
-   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
+   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7} or {@link
+   *     Opcodes#ASM8}.
    * @param access access flags of the adapted method.
    * @param descriptor the method's descriptor (see {@link Type}).
    * @param methodVisitor the method visitor to which this adapter delegates calls.
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
old mode 100755
new mode 100644
index 70da1f5..44c02b6
--- 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
@@ -50,7 +50,7 @@
   private static final Map<String, String> PRIMITIVE_TYPE_DESCRIPTORS;
 
   static {
-    HashMap<String, String> descriptors = new HashMap<String, String>();
+    HashMap<String, String> descriptors = new HashMap<>();
     descriptors.put("void", "V");
     descriptors.put("byte", "B");
     descriptors.put("char", "C");
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
old mode 100755
new mode 100644
index 20cfa5a..2e5e56d
--- 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
@@ -53,7 +53,7 @@
    * @param remapper the remapper to use to remap the types in the visited method.
    */
   public MethodRemapper(final MethodVisitor methodVisitor, final Remapper remapper) {
-    this(Opcodes.ASM7, methodVisitor, remapper);
+    this(/* latest api = */ Opcodes.ASM8, methodVisitor, remapper);
   }
 
   /**
@@ -61,7 +61,8 @@
    *
    * @param api the ASM API version supported by this remapper. Must be one of {@link
    *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM4}, {@link org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM5} or {@link
-   *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM6}.
+   *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM6}, {@link org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM7} or {@link
+   *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM8}.
    * @param methodVisitor the method visitor this remapper must deleted to.
    * @param remapper the remapper to use to remap the types in the visited method.
    */
@@ -76,7 +77,7 @@
     AnnotationVisitor annotationVisitor = super.visitAnnotationDefault();
     return annotationVisitor == null
         ? annotationVisitor
-        : new AnnotationRemapper(api, annotationVisitor, remapper);
+        : createAnnotationRemapper(annotationVisitor);
   }
 
   @Override
@@ -85,7 +86,7 @@
         super.visitAnnotation(remapper.mapDesc(descriptor), visible);
     return annotationVisitor == null
         ? annotationVisitor
-        : new AnnotationRemapper(api, annotationVisitor, remapper);
+        : createAnnotationRemapper(annotationVisitor);
   }
 
   @Override
@@ -95,7 +96,7 @@
         super.visitTypeAnnotation(typeRef, typePath, remapper.mapDesc(descriptor), visible);
     return annotationVisitor == null
         ? annotationVisitor
-        : new AnnotationRemapper(api, annotationVisitor, remapper);
+        : createAnnotationRemapper(annotationVisitor);
   }
 
   @Override
@@ -105,7 +106,7 @@
         super.visitParameterAnnotation(parameter, remapper.mapDesc(descriptor), visible);
     return annotationVisitor == null
         ? annotationVisitor
-        : new AnnotationRemapper(api, annotationVisitor, remapper);
+        : createAnnotationRemapper(annotationVisitor);
   }
 
   @Override
@@ -150,53 +151,24 @@
         remapper.mapDesc(descriptor));
   }
 
-  /**
-   * Deprecated.
-   *
-   * @deprecated use {@link #visitMethodInsn(int, String, String, String, boolean)} instead.
-   */
-  @Deprecated
   @Override
   public void visitMethodInsn(
-      final int opcode, final String owner, final String name, final String descriptor) {
-    if (api >= Opcodes.ASM5) {
-      super.visitMethodInsn(opcode, owner, name, descriptor);
-      return;
-    }
-    doVisitMethodInsn(opcode, owner, name, descriptor, opcode == Opcodes.INVOKEINTERFACE);
-  }
-
-  @Override
-  public void visitMethodInsn(
-      final int opcode,
+      final int opcodeAndSource,
       final String owner,
       final String name,
       final String descriptor,
       final boolean isInterface) {
-    if (api < Opcodes.ASM5) {
-      super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+    if (api < Opcodes.ASM5 && (opcodeAndSource & Opcodes.SOURCE_DEPRECATED) == 0) {
+      // Redirect the call to the deprecated version of this method.
+      super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface);
       return;
     }
-    doVisitMethodInsn(opcode, owner, name, descriptor, isInterface);
-  }
-
-  private void doVisitMethodInsn(
-      final int opcode,
-      final String owner,
-      final String name,
-      final String descriptor,
-      final boolean isInterface) {
-    // Calling super.visitMethodInsn requires to call the correct version depending on this.api
-    // (otherwise infinite loops can occur). To simplify and to make it easier to automatically
-    // remove the backward compatibility code, we inline the code of the overridden method here.
-    if (mv != null) {
-      mv.visitMethodInsn(
-          opcode,
-          remapper.mapType(owner),
-          remapper.mapMethodName(owner, name, descriptor),
-          remapper.mapMethodDesc(descriptor),
-          isInterface);
-    }
+    super.visitMethodInsn(
+        opcodeAndSource,
+        remapper.mapType(owner),
+        remapper.mapMethodName(owner, name, descriptor),
+        remapper.mapMethodDesc(descriptor),
+        isInterface);
   }
 
   @Override
@@ -238,7 +210,7 @@
         super.visitInsnAnnotation(typeRef, typePath, remapper.mapDesc(descriptor), visible);
     return annotationVisitor == null
         ? annotationVisitor
-        : new AnnotationRemapper(api, annotationVisitor, remapper);
+        : createAnnotationRemapper(annotationVisitor);
   }
 
   @Override
@@ -254,7 +226,7 @@
         super.visitTryCatchAnnotation(typeRef, typePath, remapper.mapDesc(descriptor), visible);
     return annotationVisitor == null
         ? annotationVisitor
-        : new AnnotationRemapper(api, annotationVisitor, remapper);
+        : createAnnotationRemapper(annotationVisitor);
   }
 
   @Override
@@ -288,6 +260,17 @@
             typeRef, typePath, start, end, index, remapper.mapDesc(descriptor), visible);
     return annotationVisitor == null
         ? annotationVisitor
-        : new AnnotationRemapper(api, annotationVisitor, remapper);
+        : createAnnotationRemapper(annotationVisitor);
+  }
+
+  /**
+   * Constructs a new remapper for annotations. The default implementation of this method returns a
+   * new {@link AnnotationRemapper}.
+   *
+   * @param annotationVisitor the AnnotationVisitor the remapper must delegate to.
+   * @return the newly created remapper.
+   */
+  protected AnnotationVisitor createAnnotationRemapper(final AnnotationVisitor annotationVisitor) {
+    return new AnnotationRemapper(api, 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
old mode 100755
new mode 100644
index 62c54c6..8061c48
--- 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
@@ -91,8 +91,8 @@
     int numModules = classReader.readUnsignedShort(currentOffset);
     currentOffset += 2;
 
-    ArrayList<String> moduleList = new ArrayList<String>(numModules);
-    ArrayList<byte[]> hashList = new ArrayList<byte[]>(numModules);
+    ArrayList<String> moduleList = new ArrayList<>(numModules);
+    ArrayList<byte[]> hashList = new ArrayList<>(numModules);
 
     for (int i = 0; i < numModules; ++i) {
       String module = classReader.readModule(currentOffset, charBuffer);
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/ModuleRemapper.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/ModuleRemapper.java
old mode 100755
new mode 100644
index 8d9eda4..a11a57d
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/ModuleRemapper.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/ModuleRemapper.java
@@ -49,15 +49,16 @@
    * @param remapper the remapper to use to remap the types in the visited module.
    */
   public ModuleRemapper(final ModuleVisitor moduleVisitor, final Remapper remapper) {
-    this(Opcodes.ASM7, moduleVisitor, remapper);
+    this(/* latest api = */ Opcodes.ASM8, moduleVisitor, remapper);
   }
 
   /**
    * Constructs a new {@link ModuleRemapper}.
    *
    * @param api the ASM API version supported by this remapper. Must be one of {@link
-   *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM4}, {@link org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM5} or {@link
-   *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM6}.
+   *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM4}, {@link org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM5}, {@link
+   *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM6}, {@link org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM7} or {@link
+   *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM8}.
    * @param moduleVisitor the module visitor this remapper must deleted to.
    * @param remapper the remapper to use to remap the types in the visited module.
    */
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/ModuleResolutionAttribute.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/ModuleResolutionAttribute.java
old mode 100755
new mode 100644
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/ModuleTargetAttribute.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/ModuleTargetAttribute.java
old mode 100755
new mode 100644
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
new file mode 100644
index 0000000..ca4b69f
--- /dev/null
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/RecordComponentRemapper.java
@@ -0,0 +1,98 @@
+// ASM: a very small and fast Java bytecode manipulation framework
+// Copyright (c) 2000-2011 INRIA, France Telecom
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// 1. Redistributions of source code must retain the above copyright
+//    notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+//    notice, this list of conditions and the following disclaimer in the
+//    documentation and/or other materials provided with the distribution.
+// 3. Neither the name of the copyright holders nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+// THE POSSIBILITY OF SUCH DAMAGE.
+
+package org.apache.tapestry5.internal.plastic.asm.commons;
+
+import org.apache.tapestry5.internal.plastic.asm.AnnotationVisitor;
+import org.apache.tapestry5.internal.plastic.asm.Opcodes;
+import org.apache.tapestry5.internal.plastic.asm.RecordComponentVisitor;
+import org.apache.tapestry5.internal.plastic.asm.TypePath;
+
+/**
+ * A {@link RecordComponentVisitor} that remaps types with a {@link Remapper}.
+ *
+ * @author Remi Forax
+ */
+public class RecordComponentRemapper extends RecordComponentVisitor {
+
+  /** The remapper used to remap the types in the visited field. */
+  protected final Remapper remapper;
+
+  /**
+   * Constructs a new {@link RecordComponentRemapper}. <i>Subclasses must not use this
+   * constructor</i>. Instead, they must use the {@link
+   * #RecordComponentRemapper(int,RecordComponentVisitor,Remapper)} version.
+   *
+   * @param recordComponentVisitor the record component visitor this remapper must delegate to.
+   * @param remapper the remapper to use to remap the types in the visited record component.
+   */
+  public RecordComponentRemapper(
+      final RecordComponentVisitor recordComponentVisitor, final Remapper remapper) {
+    this(/* latest api = */ Opcodes.ASM8, recordComponentVisitor, remapper);
+  }
+
+  /**
+   * Constructs a new {@link RecordComponentRemapper}.
+   *
+   * @param api the ASM API version supported by this remapper. Must be {@link
+   *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM8}.
+   * @param recordComponentVisitor the record component visitor this remapper must delegate to.
+   * @param remapper the remapper to use to remap the types in the visited record component.
+   */
+  protected RecordComponentRemapper(
+      final int api, final RecordComponentVisitor recordComponentVisitor, final Remapper remapper) {
+    super(api, recordComponentVisitor);
+    this.remapper = remapper;
+  }
+
+  @Override
+  public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
+    AnnotationVisitor annotationVisitor =
+        super.visitAnnotation(remapper.mapDesc(descriptor), visible);
+    return annotationVisitor == null ? null : createAnnotationRemapper(annotationVisitor);
+  }
+
+  @Override
+  public AnnotationVisitor visitTypeAnnotation(
+      final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
+    AnnotationVisitor annotationVisitor =
+        super.visitTypeAnnotation(typeRef, typePath, remapper.mapDesc(descriptor), visible);
+    return annotationVisitor == null ? null : createAnnotationRemapper(annotationVisitor);
+  }
+
+  /**
+   * Constructs a new remapper for annotations. The default implementation of this method returns a
+   * new {@link AnnotationRemapper}.
+   *
+   * @param annotationVisitor the AnnotationVisitor the remapper must delegate to.
+   * @return the newly created remapper.
+   */
+  protected AnnotationVisitor createAnnotationRemapper(final AnnotationVisitor annotationVisitor) {
+    return new AnnotationRemapper(api, 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
old mode 100755
new mode 100644
index c6da2a5..5493f9e
--- 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
@@ -110,8 +110,7 @@
       String remappedInternalName = mapType(internalName);
       if (remappedInternalName != null) {
         if (remappedInternalNames == null) {
-          remappedInternalNames = new String[internalNames.length];
-          System.arraycopy(internalNames, 0, remappedInternalNames, 0, internalNames.length);
+          remappedInternalNames = internalNames.clone();
         }
         remappedInternalNames[i] = remappedInternalName;
       }
@@ -251,7 +250,12 @@
       final String name, final String ownerName, final String innerName) {
     final String remappedInnerName = this.mapType(name);
     if (remappedInnerName.contains("$")) {
-      return remappedInnerName.substring(remappedInnerName.lastIndexOf('$') + 1);
+      int index = remappedInnerName.lastIndexOf('$') + 1;
+      while (index < remappedInnerName.length()
+          && Character.isDigit(remappedInnerName.charAt(index))) {
+        index++;
+      }
+      return remappedInnerName.substring(index);
     } else {
       return innerName;
     }
@@ -283,6 +287,20 @@
   }
 
   /**
+   * Maps a record component name to its new name. The default implementation of this method returns
+   * the given name, unchanged. Subclasses can override.
+   *
+   * @param owner the internal name of the owner class of the field.
+   * @param name the name of the field.
+   * @param descriptor the descriptor of the field.
+   * @return the new name of the field.
+   */
+  public String mapRecordComponentName(
+      final String owner, final String name, final String descriptor) {
+    return name;
+  }
+
+  /**
    * Maps a field 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
old mode 100755
new mode 100644
index fed9697..a40f81b
--- 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.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.Comparator;
 import org.apache.tapestry5.internal.plastic.asm.ClassVisitor;
 import org.apache.tapestry5.internal.plastic.asm.FieldVisitor;
 import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
@@ -151,7 +150,7 @@
    * @throws IllegalStateException If a subclass calls this constructor.
    */
   public SerialVersionUIDAdder(final ClassVisitor classVisitor) {
-    this(Opcodes.ASM7, classVisitor);
+    this(/* latest api = */ Opcodes.ASM8, classVisitor);
     if (getClass() != SerialVersionUIDAdder.class) {
       throw new IllegalStateException();
     }
@@ -161,7 +160,8 @@
    * Constructs a new {@link SerialVersionUIDAdder}.
    *
    * @param api the ASM API version implemented by this visitor. Must be one of {@link
-   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
+   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7} or {@link
+   *     Opcodes#ASM8}.
    * @param classVisitor a {@link ClassVisitor} to which this visitor will delegate calls.
    */
   protected SerialVersionUIDAdder(final int api, final ClassVisitor classVisitor) {
@@ -187,11 +187,10 @@
     if (computeSvuid) {
       this.name = name;
       this.access = access;
-      this.interfaces = new String[interfaces.length];
-      this.svuidFields = new ArrayList<Item>();
-      this.svuidConstructors = new ArrayList<Item>();
-      this.svuidMethods = new ArrayList<Item>();
-      System.arraycopy(interfaces, 0, this.interfaces, 0, interfaces.length);
+      this.interfaces = interfaces.clone();
+      this.svuidFields = new ArrayList<>();
+      this.svuidConstructors = new ArrayList<>();
+      this.svuidMethods = new ArrayList<>();
     }
 
     super.visit(version, access, name, signature, superName, interfaces);
@@ -341,13 +340,10 @@
    */
   // DontCheck(AbbreviationAsWordInName): can't be renamed (for backward binary compatibility).
   protected long computeSVUID() throws IOException {
-    ByteArrayOutputStream byteArrayOutputStream = null;
-    DataOutputStream dataOutputStream = null;
     long svuid = 0;
 
-    try {
-      byteArrayOutputStream = new ByteArrayOutputStream();
-      dataOutputStream = new DataOutputStream(byteArrayOutputStream);
+    try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+        DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream)) {
 
       // 1. The class name written using UTF encoding.
       dataOutputStream.writeUTF(name.replace('/', '.'));
@@ -414,10 +410,6 @@
       for (int i = Math.min(hashBytes.length, 8) - 1; i >= 0; i--) {
         svuid = (svuid << 8) | (hashBytes[i] & 0xFF);
       }
-    } finally {
-      if (dataOutputStream != null) {
-        dataOutputStream.close();
-      }
     }
 
     return svuid;
@@ -452,18 +444,7 @@
       final boolean dotted)
       throws IOException {
     Item[] items = itemCollection.toArray(new Item[0]);
-    Arrays.sort(
-        items,
-        new Comparator<Item>() {
-          @Override
-          public int compare(final Item item1, final Item item2) {
-            int result = item1.name.compareTo(item2.name);
-            if (result == 0) {
-              result = item1.descriptor.compareTo(item2.descriptor);
-            }
-            return result;
-          }
-        });
+    Arrays.sort(items);
     for (Item item : items) {
       dataOutputStream.writeUTF(item.name);
       dataOutputStream.writeInt(item.access);
@@ -475,7 +456,7 @@
   // Inner classes
   // -----------------------------------------------------------------------------------------------
 
-  private static final class Item {
+  private static final class Item implements Comparable<Item> {
 
     final String name;
     final int access;
@@ -486,5 +467,27 @@
       this.access = access;
       this.descriptor = descriptor;
     }
+
+    @Override
+    public int compareTo(final Item item) {
+      int result = name.compareTo(item.name);
+      if (result == 0) {
+        result = descriptor.compareTo(item.descriptor);
+      }
+      return result;
+    }
+
+    @Override
+    public boolean equals(final Object other) {
+      if (other instanceof Item) {
+        return compareTo((Item) other) == 0;
+      }
+      return false;
+    }
+
+    @Override
+    public int hashCode() {
+      return name.hashCode() ^ descriptor.hashCode();
+    }
   }
 }
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
old mode 100755
new mode 100644
index d5d29ff..3e30b10
--- 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
@@ -43,7 +43,7 @@
 
   private final Remapper remapper;
 
-  private ArrayList<String> classNames = new ArrayList<String>();
+  private ArrayList<String> classNames = new ArrayList<>();
 
   /**
    * Constructs a new {@link SignatureRemapper}. <i>Subclasses must not use this constructor</i>.
@@ -53,15 +53,16 @@
    * @param remapper the remapper to use to remap the types in the visited signature.
    */
   public SignatureRemapper(final SignatureVisitor signatureVisitor, final Remapper remapper) {
-    this(Opcodes.ASM7, signatureVisitor, remapper);
+    this(/* latest api = */ Opcodes.ASM8, signatureVisitor, remapper);
   }
 
   /**
    * Constructs a new {@link SignatureRemapper}.
    *
    * @param api the ASM API version supported by this remapper. Must be one of {@link
-   *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM4}, {@link org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM5} or {@link
-   *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM6}.
+   *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM4}, {@link org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM5},{@link
+   *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM6}, {@link org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM7} or {@link
+   *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM8}.
    * @param signatureVisitor the signature visitor this remapper must deleted to.
    * @param remapper the remapper to use to remap the types in the visited signature.
    */
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
old mode 100755
new mode 100644
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/StaticInitMerger.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/StaticInitMerger.java
old mode 100755
new mode 100644
index ee0228d..59ae5de
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/StaticInitMerger.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/StaticInitMerger.java
@@ -61,14 +61,15 @@
    *     null.
    */
   public StaticInitMerger(final String prefix, final ClassVisitor classVisitor) {
-    this(Opcodes.ASM7, prefix, classVisitor);
+    this(/* latest api = */ Opcodes.ASM8, prefix, classVisitor);
   }
 
   /**
    * Constructs a new {@link StaticInitMerger}.
    *
    * @param api the ASM API version implemented by this visitor. Must be one of {@link
-   *     Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
+   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7} or {@link
+   *     Opcodes#ASM8}.
    * @param prefix the prefix to use to rename the existing &lt;clinit&gt; methods.
    * @param classVisitor the class visitor to which this visitor must delegate method calls. May be
    *     null.
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/TableSwitchGenerator.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/TableSwitchGenerator.java
old mode 100755
new mode 100644
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
old mode 100755
new mode 100644
index e579d76..918dd1f
--- 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
@@ -70,7 +70,14 @@
       final String descriptor,
       final String signature,
       final String[] exceptions) {
-    this(Opcodes.ASM7, methodVisitor, access, name, descriptor, signature, exceptions);
+    this(
+        /* latest api = */ Opcodes.ASM8,
+        methodVisitor,
+        access,
+        name,
+        descriptor,
+        signature,
+        exceptions);
     if (getClass() != TryCatchBlockSorter.class) {
       throw new IllegalStateException();
     }
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/package.html b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/package.html
old mode 100755
new mode 100644
index 4ce0db8..f24ae4f
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/package.html
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/package.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
 <!--
  * ASM: a very small and fast Java bytecode manipulation framework
  * Copyright (c) 2000-2011 INRIA, France Telecom
@@ -28,10 +29,13 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGE.
 -->
+<head>
+  <title>Package org.objectweb.asm.commons</title>
+</head>
 <body>
-Provides some useful class and method adapters. <i>The preferred way of using
+Provides some useful class and method adapters. <em>The preferred way of using
 these adapters is by chaining them together and to custom adapters (instead of
-inheriting from them)</i>. Indeed this approach provides more combination
+inheriting from them)</em>. Indeed this approach provides more combination
 possibilities than inheritance. For instance, suppose you want to implement an
 adapter MyAdapter than needs sorted local variables and intermediate stack map
 frame values taking into account the local variables sort. By using inheritance,
@@ -45,4 +49,4 @@
 can get the stack map frame state before each instruction by reading the locals
 and stack fields in AnalyzerAdapter (this requires references from MyAdapter
 back to LocalVariablesSorter and AnalyzerAdapter).
-</body>
\ No newline at end of file
+</body>
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/package.html b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/package.html
old mode 100755
new mode 100644
index 85de04d..db0d8a2
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/package.html
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/package.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
 <!--
  * ASM: a very small and fast Java bytecode manipulation framework
  * Copyright (c) 2000-2011 INRIA, France Telecom
@@ -28,6 +29,9 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGE.
 -->
+<head>
+  <title>Package org.objectweb.asm</title>
+</head>
 <body>
 Provides a small and fast bytecode manipulation framework.
 
@@ -50,7 +54,7 @@
 <p>
 In order to generate a class from scratch, only the {@link
 org.objectweb.asm.ClassWriter ClassWriter} class is necessary. Indeed,
-in order to generate a class, one must just call its visit<i>Xxx</i>
+in order to generate a class, one must just call its visit<em>Xxx</em>
 methods with the appropriate arguments to generate the desired fields
 and methods.
 
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/signature/SignatureReader.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/signature/SignatureReader.java
old mode 100755
new mode 100644
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/signature/SignatureVisitor.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/signature/SignatureVisitor.java
old mode 100755
new mode 100644
index 3f0cc9a..ec77381
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/signature/SignatureVisitor.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/signature/SignatureVisitor.java
@@ -72,8 +72,13 @@
    *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
    */
   public SignatureVisitor(final int api) {
-    if (api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4 && api != Opcodes.ASM7) {
-      throw new IllegalArgumentException();
+    if (api != Opcodes.ASM8
+        && api != Opcodes.ASM7
+        && api != Opcodes.ASM6
+        && api != Opcodes.ASM5
+        && api != Opcodes.ASM4
+        && api != Opcodes.ASM9_EXPERIMENTAL) {
+      throw new IllegalArgumentException("Unsupported api " + api);
     }
     this.api = api;
   }
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/signature/SignatureWriter.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/signature/SignatureWriter.java
old mode 100755
new mode 100644
index 4adc4e2..3a9c79f
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/signature/SignatureWriter.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/signature/SignatureWriter.java
@@ -70,7 +70,7 @@
 
   /** Constructs a new {@link SignatureWriter}. */
   public SignatureWriter() {
-    super(Opcodes.ASM7);
+    super(/* latest api =*/ Opcodes.ASM8);
   }
 
   // -----------------------------------------------------------------------------------------------
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/signature/package.html b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/signature/package.html
old mode 100755
new mode 100644
index 0c07d12..d1657d4
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/signature/package.html
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/signature/package.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
 <!--
  * ASM: a very small and fast Java bytecode manipulation framework
  * Copyright (c) 2000-2011 INRIA, France Telecom
@@ -28,6 +29,9 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGE.
 -->
+<head>
+  <title>Package org.objectweb.asm.signature</title>
+</head>
 <body>
 Provides support for type signatures.
 
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
old mode 100755
new mode 100644
index 9005565..a4c7614
--- 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
@@ -239,7 +239,7 @@
    */
   protected final AbstractInsnNode cloneAnnotations(final AbstractInsnNode insnNode) {
     if (insnNode.visibleTypeAnnotations != null) {
-      this.visibleTypeAnnotations = new ArrayList<TypeAnnotationNode>();
+      this.visibleTypeAnnotations = new ArrayList<>();
       for (int i = 0, n = insnNode.visibleTypeAnnotations.size(); i < n; ++i) {
         TypeAnnotationNode sourceAnnotation = insnNode.visibleTypeAnnotations.get(i);
         TypeAnnotationNode cloneAnnotation =
@@ -250,7 +250,7 @@
       }
     }
     if (insnNode.invisibleTypeAnnotations != null) {
-      this.invisibleTypeAnnotations = new ArrayList<TypeAnnotationNode>();
+      this.invisibleTypeAnnotations = new ArrayList<>();
       for (int i = 0, n = insnNode.invisibleTypeAnnotations.size(); i < n; ++i) {
         TypeAnnotationNode sourceAnnotation = insnNode.invisibleTypeAnnotations.get(i);
         TypeAnnotationNode cloneAnnotation =
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
old mode 100755
new mode 100644
index 3cc051f..dbf7673
--- 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
@@ -60,7 +60,7 @@
    * @throws IllegalStateException If a subclass calls this constructor.
    */
   public AnnotationNode(final String descriptor) {
-    this(Opcodes.ASM7, descriptor);
+    this(/* latest api = */ Opcodes.ASM8, descriptor);
     if (getClass() != AnnotationNode.class) {
       throw new IllegalStateException();
     }
@@ -70,7 +70,8 @@
    * Constructs a new {@link AnnotationNode}.
    *
    * @param api the ASM API version implemented by this visitor. Must be one of {@link
-   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
+   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7} or {@link
+   *     Opcodes#ASM8}
    * @param descriptor the class descriptor of the annotation class.
    */
   public AnnotationNode(final int api, final String descriptor) {
@@ -84,7 +85,7 @@
    * @param values where the visited values must be stored.
    */
   AnnotationNode(final List<Object> values) {
-    super(Opcodes.ASM7);
+    super(/* latest api = */ Opcodes.ASM8);
     this.values = values;
   }
 
@@ -95,7 +96,7 @@
   @Override
   public void visit(final String name, final Object value) {
     if (values == null) {
-      values = new ArrayList<Object>(this.desc != null ? 2 : 1);
+      values = new ArrayList<>(this.desc != null ? 2 : 1);
     }
     if (this.desc != null) {
       values.add(name);
@@ -124,7 +125,7 @@
   @Override
   public void visitEnum(final String name, final String descriptor, final String value) {
     if (values == null) {
-      values = new ArrayList<Object>(this.desc != null ? 2 : 1);
+      values = new ArrayList<>(this.desc != null ? 2 : 1);
     }
     if (this.desc != null) {
       values.add(name);
@@ -135,7 +136,7 @@
   @Override
   public AnnotationVisitor visitAnnotation(final String name, final String descriptor) {
     if (values == null) {
-      values = new ArrayList<Object>(this.desc != null ? 2 : 1);
+      values = new ArrayList<>(this.desc != null ? 2 : 1);
     }
     if (this.desc != null) {
       values.add(name);
@@ -148,12 +149,12 @@
   @Override
   public AnnotationVisitor visitArray(final String name) {
     if (values == null) {
-      values = new ArrayList<Object>(this.desc != null ? 2 : 1);
+      values = new ArrayList<>(this.desc != null ? 2 : 1);
     }
     if (this.desc != null) {
       values.add(name);
     }
-    List<Object> array = new ArrayList<Object>();
+    List<Object> array = new ArrayList<>();
     values.add(array);
     return new AnnotationNode(array);
   }
@@ -173,7 +174,7 @@
    * introduced in more recent versions of the ASM API than the given version.
    *
    * @param api an ASM API version. Must be one of {@link Opcodes#ASM4}, {@link Opcodes#ASM5},
-   *     {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
+   *     {@link Opcodes#ASM6}, {@link Opcodes#ASM7} or {@link Opcodes#ASM8}.
    */
   public void check(final int api) {
     // nothing to do
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
old mode 100755
new mode 100644
index 83cf5d4..bf0e437
--- 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
@@ -36,6 +36,7 @@
 import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
 import org.apache.tapestry5.internal.plastic.asm.ModuleVisitor;
 import org.apache.tapestry5.internal.plastic.asm.Opcodes;
+import org.apache.tapestry5.internal.plastic.asm.RecordComponentVisitor;
 import org.apache.tapestry5.internal.plastic.asm.TypePath;
 
 /**
@@ -53,7 +54,7 @@
 
   /**
    * The class's access flags (see {@link org.apache.tapestry5.internal.plastic.asm.Opcodes}). This field also indicates if
-   * the class is deprecated.
+   * the class is deprecated {@link Opcodes#ACC_DEPRECATED} or a record {@link Opcodes#ACC_RECORD}.
    */
   public int access;
 
@@ -126,6 +127,18 @@
   /** The internal names of the nest members of this class. May be {@literal null}. */
   public List<String> nestMembers;
 
+  /**
+   * <b>Experimental, use at your own risk. This method will be renamed when it becomes stable, this
+   * will break existing code using it</b>. The internal names of the permitted subtypes of this
+   * class. May be {@literal null}.
+   *
+   * @deprecated this API is experimental.
+   */
+  @Deprecated public List<String> permittedSubtypesExperimental;
+
+  /** The record components of this class. May be {@literal null}. */
+  public List<RecordComponentNode> recordComponents;
+
   /** The fields of this class. */
   public List<FieldNode> fields;
 
@@ -139,7 +152,7 @@
    * @throws IllegalStateException If a subclass calls this constructor.
    */
   public ClassNode() {
-    this(Opcodes.ASM7);
+    this(Opcodes.ASM8);
     if (getClass() != ClassNode.class) {
       throw new IllegalStateException();
     }
@@ -149,14 +162,15 @@
    * Constructs a new {@link ClassNode}.
    *
    * @param api the ASM API version implemented by this visitor. Must be one of {@link
-   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
+   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7} or {@link
+   *     Opcodes#ASM8}.
    */
   public ClassNode(final int api) {
     super(api);
-    this.interfaces = new ArrayList<String>();
-    this.innerClasses = new ArrayList<InnerClassNode>();
-    this.fields = new ArrayList<FieldNode>();
-    this.methods = new ArrayList<MethodNode>();
+    this.interfaces = new ArrayList<>();
+    this.innerClasses = new ArrayList<>();
+    this.fields = new ArrayList<>();
+    this.methods = new ArrayList<>();
   }
 
   // -----------------------------------------------------------------------------------------------
@@ -207,15 +221,9 @@
   public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
     AnnotationNode annotation = new AnnotationNode(descriptor);
     if (visible) {
-      if (visibleAnnotations == null) {
-        visibleAnnotations = new ArrayList<AnnotationNode>(1);
-      }
-      visibleAnnotations.add(annotation);
+      visibleAnnotations = Util.add(visibleAnnotations, annotation);
     } else {
-      if (invisibleAnnotations == null) {
-        invisibleAnnotations = new ArrayList<AnnotationNode>(1);
-      }
-      invisibleAnnotations.add(annotation);
+      invisibleAnnotations = Util.add(invisibleAnnotations, annotation);
     }
     return annotation;
   }
@@ -225,33 +233,33 @@
       final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
     TypeAnnotationNode typeAnnotation = new TypeAnnotationNode(typeRef, typePath, descriptor);
     if (visible) {
-      if (visibleTypeAnnotations == null) {
-        visibleTypeAnnotations = new ArrayList<TypeAnnotationNode>(1);
-      }
-      visibleTypeAnnotations.add(typeAnnotation);
+      visibleTypeAnnotations = Util.add(visibleTypeAnnotations, typeAnnotation);
     } else {
-      if (invisibleTypeAnnotations == null) {
-        invisibleTypeAnnotations = new ArrayList<TypeAnnotationNode>(1);
-      }
-      invisibleTypeAnnotations.add(typeAnnotation);
+      invisibleTypeAnnotations = Util.add(invisibleTypeAnnotations, typeAnnotation);
     }
     return typeAnnotation;
   }
 
   @Override
   public void visitAttribute(final Attribute attribute) {
-    if (attrs == null) {
-      attrs = new ArrayList<Attribute>(1);
-    }
-    attrs.add(attribute);
+    attrs = Util.add(attrs, attribute);
   }
 
   @Override
   public void visitNestMember(final String nestMember) {
-    if (nestMembers == null) {
-      nestMembers = new ArrayList<String>();
-    }
-    nestMembers.add(nestMember);
+    nestMembers = Util.add(nestMembers, nestMember);
+  }
+
+  /**
+   * <b>Experimental, use at your own risk.</b>.
+   *
+   * @param permittedSubtype the internal name of a permitted subtype.
+   * @deprecated this API is experimental.
+   */
+  @Override
+  @Deprecated
+  public void visitPermittedSubtypeExperimental(final String permittedSubtype) {
+    permittedSubtypesExperimental = Util.add(permittedSubtypesExperimental, permittedSubtype);
   }
 
   @Override
@@ -262,6 +270,14 @@
   }
 
   @Override
+  public RecordComponentVisitor visitRecordComponent(
+      final String name, final String descriptor, final String signature) {
+    RecordComponentNode recordComponent = new RecordComponentNode(name, descriptor, signature);
+    recordComponents = Util.add(recordComponents, recordComponent);
+    return recordComponent;
+  }
+
+  @Override
   public FieldVisitor visitField(
       final int access,
       final String name,
@@ -300,9 +316,15 @@
    * in more recent versions of the ASM API than the given version.
    *
    * @param api an ASM API version. Must be one of {@link Opcodes#ASM4}, {@link Opcodes#ASM5},
-   *     {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
+   *     {@link Opcodes#ASM6}, {@link Opcodes#ASM7}. or {@link Opcodes#ASM8}.
    */
   public void check(final int api) {
+    if (api != Opcodes.ASM9_EXPERIMENTAL && permittedSubtypesExperimental != null) {
+      throw new UnsupportedClassVersionException();
+    }
+    if (api < Opcodes.ASM8 && ((access & Opcodes.ACC_RECORD) != 0 || recordComponents != null)) {
+      throw new UnsupportedClassVersionException();
+    }
     if (api < Opcodes.ASM7 && (nestHostClass != null || nestMembers != null)) {
       throw new UnsupportedClassVersionException();
     }
@@ -338,6 +360,11 @@
         invisibleTypeAnnotations.get(i).check(api);
       }
     }
+    if (recordComponents != null) {
+      for (int i = recordComponents.size() - 1; i >= 0; --i) {
+        recordComponents.get(i).check(api);
+      }
+    }
     for (int i = fields.size() - 1; i >= 0; --i) {
       fields.get(i).check(api);
     }
@@ -413,10 +440,22 @@
         classVisitor.visitNestMember(nestMembers.get(i));
       }
     }
+    // Visit the permitted subtypes.
+    if (permittedSubtypesExperimental != null) {
+      for (int i = 0, n = permittedSubtypesExperimental.size(); i < n; ++i) {
+        classVisitor.visitPermittedSubtypeExperimental(permittedSubtypesExperimental.get(i));
+      }
+    }
     // Visit the inner classes.
     for (int i = 0, n = innerClasses.size(); i < n; ++i) {
       innerClasses.get(i).accept(classVisitor);
     }
+    // Visit the record components.
+    if (recordComponents != null) {
+      for (int i = 0, n = recordComponents.size(); i < n; ++i) {
+        recordComponents.get(i).accept(classVisitor);
+      }
+    }
     // Visit the fields.
     for (int i = 0, n = fields.size(); i < n; ++i) {
       fields.get(i).accept(classVisitor);
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
old mode 100755
new mode 100644
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
old mode 100755
new mode 100644
index c1b516c..5c2bf66
--- 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
@@ -27,7 +27,6 @@
 // THE POSSIBILITY OF SUCH DAMAGE.
 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;
@@ -100,17 +99,18 @@
       final String descriptor,
       final String signature,
       final Object value) {
-    this(Opcodes.ASM7, access, name, descriptor, signature, value);
+    this(/* latest api = */ Opcodes.ASM8, access, name, descriptor, signature, value);
     if (getClass() != FieldNode.class) {
       throw new IllegalStateException();
     }
   }
 
   /**
-   * Constructs a new {@link FieldNode}. <i>Subclasses must not use this constructor</i>.
+   * Constructs a new {@link FieldNode}.
    *
-   * @param api the ASM API version implemented by this visitor. Must be one of {@link Opcodes#ASM4}
-   *     or {@link Opcodes#ASM5}.
+   * @param api the ASM API version implemented by this visitor. Must be one of {@link
+   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7} or {@link
+   *     Opcodes#ASM8}.
    * @param access the field's access flags (see {@link org.apache.tapestry5.internal.plastic.asm.Opcodes}). This parameter
    *     also indicates if the field is synthetic and/or deprecated.
    * @param name the field's name.
@@ -143,15 +143,9 @@
   public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
     AnnotationNode annotation = new AnnotationNode(descriptor);
     if (visible) {
-      if (visibleAnnotations == null) {
-        visibleAnnotations = new ArrayList<AnnotationNode>(1);
-      }
-      visibleAnnotations.add(annotation);
+      visibleAnnotations = Util.add(visibleAnnotations, annotation);
     } else {
-      if (invisibleAnnotations == null) {
-        invisibleAnnotations = new ArrayList<AnnotationNode>(1);
-      }
-      invisibleAnnotations.add(annotation);
+      invisibleAnnotations = Util.add(invisibleAnnotations, annotation);
     }
     return annotation;
   }
@@ -161,25 +155,16 @@
       final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
     TypeAnnotationNode typeAnnotation = new TypeAnnotationNode(typeRef, typePath, descriptor);
     if (visible) {
-      if (visibleTypeAnnotations == null) {
-        visibleTypeAnnotations = new ArrayList<TypeAnnotationNode>(1);
-      }
-      visibleTypeAnnotations.add(typeAnnotation);
+      visibleTypeAnnotations = Util.add(visibleTypeAnnotations, typeAnnotation);
     } else {
-      if (invisibleTypeAnnotations == null) {
-        invisibleTypeAnnotations = new ArrayList<TypeAnnotationNode>(1);
-      }
-      invisibleTypeAnnotations.add(typeAnnotation);
+      invisibleTypeAnnotations = Util.add(invisibleTypeAnnotations, typeAnnotation);
     }
     return typeAnnotation;
   }
 
   @Override
   public void visitAttribute(final Attribute attribute) {
-    if (attrs == null) {
-      attrs = new ArrayList<Attribute>(1);
-    }
-    attrs.add(attribute);
+    attrs = Util.add(attrs, attribute);
   }
 
   @Override
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
old mode 100755
new mode 100644
index a71d05d..249c1f0
--- 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
@@ -152,7 +152,7 @@
     FrameNode clone = new FrameNode();
     clone.type = type;
     if (local != null) {
-      clone.local = new ArrayList<Object>();
+      clone.local = new ArrayList<>();
       for (int i = 0, n = local.size(); i < n; ++i) {
         Object localElement = local.get(i);
         if (localElement instanceof LabelNode) {
@@ -162,7 +162,7 @@
       }
     }
     if (stack != null) {
-      clone.stack = new ArrayList<Object>();
+      clone.stack = new ArrayList<>();
       for (int i = 0, n = stack.size(); i < n; ++i) {
         Object stackElement = stack.get(i);
         if (stackElement instanceof LabelNode) {
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
old mode 100755
new mode 100644
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/InnerClassNode.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/InnerClassNode.java
old mode 100755
new mode 100644
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
old mode 100755
new mode 100644
index 23d4c9f..4f0ba73
--- 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
@@ -27,16 +27,15 @@
 // THE POSSIBILITY OF SUCH DAMAGE.
 package org.apache.tapestry5.internal.plastic.asm.tree;
 
-import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
-
 import java.util.ListIterator;
 import java.util.NoSuchElementException;
+import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
 
 /**
  * A doubly linked list of {@link AbstractInsnNode} objects. <i>This implementation is not thread
  * safe</i>.
  */
-public class InsnList {
+public class InsnList implements Iterable<AbstractInsnNode> {
 
   /** The number of instructions in this list. */
   private int size;
@@ -152,6 +151,7 @@
    *
    * @return an iterator over the instructions in this list.
    */
+  @Override
   public ListIterator<AbstractInsnNode> iterator() {
     return iterator(0);
   }
@@ -210,7 +210,7 @@
       cache[index] = newInsnNode;
       newInsnNode.index = index;
     } else {
-      newInsnNode.index = 0; // newInnsnNode now belongs to an InsnList.
+      newInsnNode.index = 0; // newInsnNode now belongs to an InsnList.
     }
     oldInsnNode.index = -1; // oldInsnNode no longer belongs to an InsnList.
     oldInsnNode.previousInsn = null;
@@ -535,6 +535,9 @@
 
     @Override
     public Object previous() {
+      if (previousInsn == null) {
+        throw new NoSuchElementException();
+      }
       AbstractInsnNode result = previousInsn;
       nextInsn = result;
       previousInsn = result.previousInsn;
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
old mode 100755
new mode 100644
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
old mode 100755
new mode 100644
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
old mode 100755
new mode 100644
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
old mode 100755
new mode 100644
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
old mode 100755
new mode 100644
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
old mode 100755
new mode 100644
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
old mode 100755
new mode 100644
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
old mode 100755
new mode 100644
index ac1bc9c..5de4048
--- 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
@@ -84,14 +84,15 @@
       final LabelNode[] end,
       final int[] index,
       final String descriptor) {
-    this(Opcodes.ASM7, typeRef, typePath, start, end, index, descriptor);
+    this(/* latest api = */ Opcodes.ASM8, typeRef, typePath, start, end, index, descriptor);
   }
 
   /**
    * Constructs a new {@link LocalVariableAnnotationNode}.
    *
    * @param api the ASM API version implemented by this visitor. Must be one of {@link
-   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
+   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7} or {@link
+   *     Opcodes#ASM8}.
    * @param typeRef a reference to the annotated type. See {@link org.apache.tapestry5.internal.plastic.asm.TypeReference}.
    * @param start the fist instructions corresponding to the continuous ranges that make the scope
    *     of this local variable (inclusive).
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/LocalVariableNode.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/LocalVariableNode.java
old mode 100755
new mode 100644
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
old mode 100755
new mode 100644
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
old mode 100755
new mode 100644
index 530ea1d..9e1e613
--- 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
@@ -27,11 +27,10 @@
 // THE POSSIBILITY OF SUCH DAMAGE.
 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;
 
-import java.util.Map;
-
 /**
  * A node that represents a method instruction. A method instruction is an instruction that invokes
  * a method.
@@ -66,9 +65,7 @@
    *     org.apache.tapestry5.internal.plastic.asm.Type#getInternalName()}).
    * @param name the method's name.
    * @param descriptor the method's descriptor (see {@link org.apache.tapestry5.internal.plastic.asm.Type}).
-   * @deprecated use {@link #MethodInsnNode(int, String, String, String, boolean)} instead.
    */
-  @Deprecated
   public MethodInsnNode(
       final int opcode, final String owner, final String name, final String descriptor) {
     this(opcode, owner, name, descriptor, opcode == Opcodes.INVOKEINTERFACE);
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
old mode 100755
new mode 100644
index 39114ac..85bcb54
--- 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
@@ -155,7 +155,7 @@
    * @throws IllegalStateException If a subclass calls this constructor.
    */
   public MethodNode() {
-    this(Opcodes.ASM7);
+    this(/* latest api = */ Opcodes.ASM8);
     if (getClass() != MethodNode.class) {
       throw new IllegalStateException();
     }
@@ -165,7 +165,8 @@
    * Constructs an uninitialized {@link MethodNode}.
    *
    * @param api the ASM API version implemented by this visitor. Must be one of {@link
-   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
+   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7} or {@link
+   *     Opcodes#ASM8}.
    */
   public MethodNode(final int api) {
     super(api);
@@ -191,7 +192,7 @@
       final String descriptor,
       final String signature,
       final String[] exceptions) {
-    this(Opcodes.ASM7, access, name, descriptor, signature, exceptions);
+    this(/* latest api = */ Opcodes.ASM8, access, name, descriptor, signature, exceptions);
     if (getClass() != MethodNode.class) {
       throw new IllegalStateException();
     }
@@ -201,7 +202,8 @@
    * Constructs a new {@link MethodNode}.
    *
    * @param api the ASM API version implemented by this visitor. Must be one of {@link
-   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
+   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7} or {@link
+   *     Opcodes#ASM8}.
    * @param access the method's access flags (see {@link Opcodes}). This parameter also indicates if
    *     the method is synthetic and/or deprecated.
    * @param name the method's name.
@@ -224,9 +226,9 @@
     this.signature = signature;
     this.exceptions = Util.asArrayList(exceptions);
     if ((access & Opcodes.ACC_ABSTRACT) == 0) {
-      this.localVariables = new ArrayList<LocalVariableNode>(5);
+      this.localVariables = new ArrayList<>(5);
     }
-    this.tryCatchBlocks = new ArrayList<TryCatchBlockNode>();
+    this.tryCatchBlocks = new ArrayList<>();
     this.instructions = new InsnList();
   }
 
@@ -237,7 +239,7 @@
   @Override
   public void visitParameter(final String name, final int access) {
     if (parameters == null) {
-      parameters = new ArrayList<ParameterNode>(5);
+      parameters = new ArrayList<>(5);
     }
     parameters.add(new ParameterNode(name, access));
   }
@@ -259,15 +261,9 @@
   public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
     AnnotationNode annotation = new AnnotationNode(descriptor);
     if (visible) {
-      if (visibleAnnotations == null) {
-        visibleAnnotations = new ArrayList<AnnotationNode>(1);
-      }
-      visibleAnnotations.add(annotation);
+      visibleAnnotations = Util.add(visibleAnnotations, annotation);
     } else {
-      if (invisibleAnnotations == null) {
-        invisibleAnnotations = new ArrayList<AnnotationNode>(1);
-      }
-      invisibleAnnotations.add(annotation);
+      invisibleAnnotations = Util.add(invisibleAnnotations, annotation);
     }
     return annotation;
   }
@@ -277,15 +273,9 @@
       final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
     TypeAnnotationNode typeAnnotation = new TypeAnnotationNode(typeRef, typePath, descriptor);
     if (visible) {
-      if (visibleTypeAnnotations == null) {
-        visibleTypeAnnotations = new ArrayList<TypeAnnotationNode>(1);
-      }
-      visibleTypeAnnotations.add(typeAnnotation);
+      visibleTypeAnnotations = Util.add(visibleTypeAnnotations, typeAnnotation);
     } else {
-      if (invisibleTypeAnnotations == null) {
-        invisibleTypeAnnotations = new ArrayList<TypeAnnotationNode>(1);
-      }
-      invisibleTypeAnnotations.add(typeAnnotation);
+      invisibleTypeAnnotations = Util.add(invisibleTypeAnnotations, typeAnnotation);
     }
     return typeAnnotation;
   }
@@ -309,29 +299,22 @@
         int params = Type.getArgumentTypes(desc).length;
         visibleParameterAnnotations = (List<AnnotationNode>[]) new List<?>[params];
       }
-      if (visibleParameterAnnotations[parameter] == null) {
-        visibleParameterAnnotations[parameter] = new ArrayList<AnnotationNode>(1);
-      }
-      visibleParameterAnnotations[parameter].add(annotation);
+      visibleParameterAnnotations[parameter] =
+          Util.add(visibleParameterAnnotations[parameter], annotation);
     } else {
       if (invisibleParameterAnnotations == null) {
         int params = Type.getArgumentTypes(desc).length;
         invisibleParameterAnnotations = (List<AnnotationNode>[]) new List<?>[params];
       }
-      if (invisibleParameterAnnotations[parameter] == null) {
-        invisibleParameterAnnotations[parameter] = new ArrayList<AnnotationNode>(1);
-      }
-      invisibleParameterAnnotations[parameter].add(annotation);
+      invisibleParameterAnnotations[parameter] =
+          Util.add(invisibleParameterAnnotations[parameter], annotation);
     }
     return annotation;
   }
 
   @Override
   public void visitAttribute(final Attribute attribute) {
-    if (attrs == null) {
-      attrs = new ArrayList<Attribute>(1);
-    }
-    attrs.add(attribute);
+    attrs = Util.add(attrs, attribute);
   }
 
   @Override
@@ -381,33 +364,20 @@
     instructions.add(new FieldInsnNode(opcode, owner, name, descriptor));
   }
 
-  /**
-   * Deprecated.
-   *
-   * @deprecated use {@link #visitMethodInsn(int, String, String, String, boolean)} instead.
-   */
-  @Deprecated
   @Override
   public void visitMethodInsn(
-      final int opcode, final String owner, final String name, final String descriptor) {
-    if (api >= Opcodes.ASM5) {
-      super.visitMethodInsn(opcode, owner, name, descriptor);
-      return;
-    }
-    instructions.add(new MethodInsnNode(opcode, owner, name, descriptor));
-  }
-
-  @Override
-  public void visitMethodInsn(
-      final int opcode,
+      final int opcodeAndSource,
       final String owner,
       final String name,
       final String descriptor,
       final boolean isInterface) {
-    if (api < Opcodes.ASM5) {
-      super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+    if (api < Opcodes.ASM5 && (opcodeAndSource & Opcodes.SOURCE_DEPRECATED) == 0) {
+      // Redirect the call to the deprecated version of this method.
+      super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface);
       return;
     }
+    int opcode = opcodeAndSource & ~Opcodes.SOURCE_MASK;
+
     instructions.add(new MethodInsnNode(opcode, owner, name, descriptor, isInterface));
   }
 
@@ -469,15 +439,11 @@
     // Add the annotation to this instruction.
     TypeAnnotationNode typeAnnotation = new TypeAnnotationNode(typeRef, typePath, descriptor);
     if (visible) {
-      if (currentInsn.visibleTypeAnnotations == null) {
-        currentInsn.visibleTypeAnnotations = new ArrayList<TypeAnnotationNode>(1);
-      }
-      currentInsn.visibleTypeAnnotations.add(typeAnnotation);
+      currentInsn.visibleTypeAnnotations =
+          Util.add(currentInsn.visibleTypeAnnotations, typeAnnotation);
     } else {
-      if (currentInsn.invisibleTypeAnnotations == null) {
-        currentInsn.invisibleTypeAnnotations = new ArrayList<TypeAnnotationNode>(1);
-      }
-      currentInsn.invisibleTypeAnnotations.add(typeAnnotation);
+      currentInsn.invisibleTypeAnnotations =
+          Util.add(currentInsn.invisibleTypeAnnotations, typeAnnotation);
     }
     return typeAnnotation;
   }
@@ -485,8 +451,9 @@
   @Override
   public void visitTryCatchBlock(
       final Label start, final Label end, final Label handler, final String type) {
-    tryCatchBlocks.add(
-        new TryCatchBlockNode(getLabelNode(start), getLabelNode(end), getLabelNode(handler), type));
+    TryCatchBlockNode tryCatchBlock =
+        new TryCatchBlockNode(getLabelNode(start), getLabelNode(end), getLabelNode(handler), type);
+    tryCatchBlocks = Util.add(tryCatchBlocks, tryCatchBlock);
   }
 
   @Override
@@ -495,15 +462,11 @@
     TryCatchBlockNode tryCatchBlock = tryCatchBlocks.get((typeRef & 0x00FFFF00) >> 8);
     TypeAnnotationNode typeAnnotation = new TypeAnnotationNode(typeRef, typePath, descriptor);
     if (visible) {
-      if (tryCatchBlock.visibleTypeAnnotations == null) {
-        tryCatchBlock.visibleTypeAnnotations = new ArrayList<TypeAnnotationNode>(1);
-      }
-      tryCatchBlock.visibleTypeAnnotations.add(typeAnnotation);
+      tryCatchBlock.visibleTypeAnnotations =
+          Util.add(tryCatchBlock.visibleTypeAnnotations, typeAnnotation);
     } else {
-      if (tryCatchBlock.invisibleTypeAnnotations == null) {
-        tryCatchBlock.invisibleTypeAnnotations = new ArrayList<TypeAnnotationNode>(1);
-      }
-      tryCatchBlock.invisibleTypeAnnotations.add(typeAnnotation);
+      tryCatchBlock.invisibleTypeAnnotations =
+          Util.add(tryCatchBlock.invisibleTypeAnnotations, typeAnnotation);
     }
     return typeAnnotation;
   }
@@ -516,9 +479,10 @@
       final Label start,
       final Label end,
       final int index) {
-    localVariables.add(
+    LocalVariableNode localVariable =
         new LocalVariableNode(
-            name, descriptor, signature, getLabelNode(start), getLabelNode(end), index));
+            name, descriptor, signature, getLabelNode(start), getLabelNode(end), index);
+    localVariables = Util.add(localVariables, localVariable);
   }
 
   @Override
@@ -534,15 +498,11 @@
         new LocalVariableAnnotationNode(
             typeRef, typePath, getLabelNodes(start), getLabelNodes(end), index, descriptor);
     if (visible) {
-      if (visibleLocalVariableAnnotations == null) {
-        visibleLocalVariableAnnotations = new ArrayList<LocalVariableAnnotationNode>(1);
-      }
-      visibleLocalVariableAnnotations.add(localVariableAnnotation);
+      visibleLocalVariableAnnotations =
+          Util.add(visibleLocalVariableAnnotations, localVariableAnnotation);
     } else {
-      if (invisibleLocalVariableAnnotations == null) {
-        invisibleLocalVariableAnnotations = new ArrayList<LocalVariableAnnotationNode>(1);
-      }
-      invisibleLocalVariableAnnotations.add(localVariableAnnotation);
+      invisibleLocalVariableAnnotations =
+          Util.add(invisibleLocalVariableAnnotations, localVariableAnnotation);
     }
     return localVariableAnnotation;
   }
@@ -608,7 +568,7 @@
    * in more recent versions of the ASM API than the given version.
    *
    * @param api an ASM API version. Must be one of {@link Opcodes#ASM4}, {@link Opcodes#ASM5},
-   *     {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
+   *     {@link Opcodes#ASM6}, {@link Opcodes#ASM7} or {@link Opcodes#ASM8}.
    */
   public void check(final int api) {
     if (api == Opcodes.ASM4) {
@@ -663,7 +623,7 @@
         throw new UnsupportedClassVersionException();
       }
     }
-    if (api != Opcodes.ASM7) {
+    if (api < Opcodes.ASM7) {
       for (int i = instructions.size() - 1; i >= 0; --i) {
         AbstractInsnNode insn = instructions.get(i);
         if (insn instanceof LdcInsnNode) {
@@ -682,8 +642,7 @@
    * @param classVisitor a class visitor.
    */
   public void accept(final ClassVisitor classVisitor) {
-    String[] exceptionsArray = new String[this.exceptions.size()];
-    this.exceptions.toArray(exceptionsArray);
+    String[] exceptionsArray = exceptions == null ? null : exceptions.toArray(new String[0]);
     MethodVisitor methodVisitor =
         classVisitor.visitMethod(access, name, desc, signature, exceptionsArray);
     if (methodVisitor != null) {
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
old mode 100755
new mode 100644
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
old mode 100755
new mode 100644
index fe05259..4e42126
--- 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
@@ -84,7 +84,7 @@
    * @throws IllegalStateException If a subclass calls this constructor.
    */
   public ModuleNode(final String name, final int access, final String version) {
-    super(Opcodes.ASM7);
+    super(/* latest api = */ Opcodes.ASM8);
     if (getClass() != ModuleNode.class) {
       throw new IllegalStateException();
     }
@@ -97,8 +97,8 @@
   /**
    * Constructs a {@link ModuleNode}.
    *
-   * @param api the ASM API version implemented by this visitor. Must be one of {@link Opcodes#ASM6}
-   *     or {@link Opcodes#ASM7}.
+   * @param api the ASM API version implemented by this visitor. Must be one of {@link
+   *     Opcodes#ASM6}, {@link Opcodes#ASM7} or {@link Opcodes#ASM8}.
    * @param name the fully qualified name (using dots) of the module.
    * @param access the module access flags, among {@code ACC_OPEN}, {@code ACC_SYNTHETIC} and {@code
    *     ACC_MANDATED}.
@@ -138,7 +138,7 @@
   @Override
   public void visitPackage(final String packaze) {
     if (packages == null) {
-      packages = new ArrayList<String>(5);
+      packages = new ArrayList<>(5);
     }
     packages.add(packaze);
   }
@@ -146,7 +146,7 @@
   @Override
   public void visitRequire(final String module, final int access, final String version) {
     if (requires == null) {
-      requires = new ArrayList<ModuleRequireNode>(5);
+      requires = new ArrayList<>(5);
     }
     requires.add(new ModuleRequireNode(module, access, version));
   }
@@ -154,7 +154,7 @@
   @Override
   public void visitExport(final String packaze, final int access, final String... modules) {
     if (exports == null) {
-      exports = new ArrayList<ModuleExportNode>(5);
+      exports = new ArrayList<>(5);
     }
     exports.add(new ModuleExportNode(packaze, access, Util.asArrayList(modules)));
   }
@@ -162,7 +162,7 @@
   @Override
   public void visitOpen(final String packaze, final int access, final String... modules) {
     if (opens == null) {
-      opens = new ArrayList<ModuleOpenNode>(5);
+      opens = new ArrayList<>(5);
     }
     opens.add(new ModuleOpenNode(packaze, access, Util.asArrayList(modules)));
   }
@@ -170,7 +170,7 @@
   @Override
   public void visitUse(final String service) {
     if (uses == null) {
-      uses = new ArrayList<String>(5);
+      uses = new ArrayList<>(5);
     }
     uses.add(service);
   }
@@ -178,7 +178,7 @@
   @Override
   public void visitProvide(final String service, final String... providers) {
     if (provides == null) {
-      provides = new ArrayList<ModuleProvideNode>(5);
+      provides = new ArrayList<>(5);
     }
     provides.add(new ModuleProvideNode(service, Util.asArrayList(providers)));
   }
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
old mode 100755
new mode 100644
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
old mode 100755
new mode 100644
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/ModuleRequireNode.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/ModuleRequireNode.java
old mode 100755
new mode 100644
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
old mode 100755
new mode 100644
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/ParameterNode.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/ParameterNode.java
old mode 100755
new mode 100644
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
new file mode 100644
index 0000000..49e7286
--- /dev/null
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/RecordComponentNode.java
@@ -0,0 +1,203 @@
+// ASM: a very small and fast Java bytecode manipulation framework
+// Copyright (c) 2000-2011 INRIA, France Telecom
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// 1. Redistributions of source code must retain the above copyright
+//    notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+//    notice, this list of conditions and the following disclaimer in the
+//    documentation and/or other materials provided with the distribution.
+// 3. Neither the name of the copyright holders nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+// THE POSSIBILITY OF SUCH DAMAGE.
+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;
+import org.apache.tapestry5.internal.plastic.asm.Opcodes;
+import org.apache.tapestry5.internal.plastic.asm.RecordComponentVisitor;
+import org.apache.tapestry5.internal.plastic.asm.TypePath;
+
+/**
+ * A node that represents a record component.
+ *
+ * @author Remi Forax
+ */
+public class RecordComponentNode extends RecordComponentVisitor {
+
+  /** The record component name. */
+  public String name;
+
+  /** The record component descriptor (see {@link org.apache.tapestry5.internal.plastic.asm.Type}). */
+  public String descriptor;
+
+  /** The record component signature. May be {@literal null}. */
+  public String signature;
+
+  /** The runtime visible annotations of this record component. May be {@literal null}. */
+  public List<AnnotationNode> visibleAnnotations;
+
+  /** The runtime invisible annotations of this record component. May be {@literal null}. */
+  public List<AnnotationNode> invisibleAnnotations;
+
+  /** The runtime visible type annotations of this record component. May be {@literal null}. */
+  public List<TypeAnnotationNode> visibleTypeAnnotations;
+
+  /** The runtime invisible type annotations of this record component. May be {@literal null}. */
+  public List<TypeAnnotationNode> invisibleTypeAnnotations;
+
+  /** The non standard attributes of this record component. * May be {@literal null}. */
+  public List<Attribute> attrs;
+
+  /**
+   * Constructs a new {@link RecordComponentNode}. <i>Subclasses must not use this constructor</i>.
+   * Instead, they must use the {@link #RecordComponentNode(int, String, String, String)} version.
+   *
+   * @param name the record component name.
+   * @param descriptor the record component descriptor (see {@link org.apache.tapestry5.internal.plastic.asm.Type}).
+   * @param signature the record component signature.
+   * @throws IllegalStateException If a subclass calls this constructor.
+   */
+  public RecordComponentNode(final String name, final String descriptor, final String signature) {
+    this(/* latest api = */ Opcodes.ASM8, name, descriptor, signature);
+    if (getClass() != RecordComponentNode.class) {
+      throw new IllegalStateException();
+    }
+  }
+
+  /**
+   * Constructs a new {@link RecordComponentNode}.
+   *
+   * @param api the ASM API version implemented by this visitor. Must be {@link Opcodes#ASM8}.
+   * @param name the record component name.
+   * @param descriptor the record component descriptor (see {@link org.apache.tapestry5.internal.plastic.asm.Type}).
+   * @param signature the record component signature.
+   */
+  public RecordComponentNode(
+      final int api, final String name, final String descriptor, final String signature) {
+    super(api);
+    this.name = name;
+    this.descriptor = descriptor;
+    this.signature = signature;
+  }
+
+  // -----------------------------------------------------------------------------------------------
+  // Implementation of the FieldVisitor abstract class
+  // -----------------------------------------------------------------------------------------------
+
+  @Override
+  public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
+    AnnotationNode annotation = new AnnotationNode(descriptor);
+    if (visible) {
+      visibleAnnotations = Util.add(visibleAnnotations, annotation);
+    } else {
+      invisibleAnnotations = Util.add(invisibleAnnotations, annotation);
+    }
+    return annotation;
+  }
+
+  @Override
+  public AnnotationVisitor visitTypeAnnotation(
+      final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
+    TypeAnnotationNode typeAnnotation = new TypeAnnotationNode(typeRef, typePath, descriptor);
+    if (visible) {
+      visibleTypeAnnotations = Util.add(visibleTypeAnnotations, typeAnnotation);
+    } else {
+      invisibleTypeAnnotations = Util.add(invisibleTypeAnnotations, typeAnnotation);
+    }
+    return typeAnnotation;
+  }
+
+  @Override
+  public void visitAttribute(final Attribute attribute) {
+    attrs = Util.add(attrs, attribute);
+  }
+
+  @Override
+  public void visitEnd() {
+    // Nothing to do.
+  }
+
+  // -----------------------------------------------------------------------------------------------
+  // Accept methods
+  // -----------------------------------------------------------------------------------------------
+
+  /**
+   * Checks that this record component node is compatible with the given ASM API version. This
+   * method checks that this node, and all its children recursively, do not contain elements that
+   * were introduced in more recent versions of the ASM API than the given version.
+   *
+   * @param api an ASM API version. Must be {@link Opcodes#ASM8}.
+   */
+  public void check(final int api) {
+    if (api < Opcodes.ASM8) {
+      throw new UnsupportedClassVersionException();
+    }
+  }
+
+  /**
+   * Makes the given class visitor visit this record component.
+   *
+   * @param classVisitor a class visitor.
+   */
+  public void accept(final ClassVisitor classVisitor) {
+    RecordComponentVisitor recordComponentVisitor =
+        classVisitor.visitRecordComponent(name, descriptor, signature);
+    if (recordComponentVisitor == null) {
+      return;
+    }
+    // Visit the annotations.
+    if (visibleAnnotations != null) {
+      for (int i = 0, n = visibleAnnotations.size(); i < n; ++i) {
+        AnnotationNode annotation = visibleAnnotations.get(i);
+        annotation.accept(recordComponentVisitor.visitAnnotation(annotation.desc, true));
+      }
+    }
+    if (invisibleAnnotations != null) {
+      for (int i = 0, n = invisibleAnnotations.size(); i < n; ++i) {
+        AnnotationNode annotation = invisibleAnnotations.get(i);
+        annotation.accept(recordComponentVisitor.visitAnnotation(annotation.desc, false));
+      }
+    }
+    if (visibleTypeAnnotations != null) {
+      for (int i = 0, n = visibleTypeAnnotations.size(); i < n; ++i) {
+        TypeAnnotationNode typeAnnotation = visibleTypeAnnotations.get(i);
+        typeAnnotation.accept(
+            recordComponentVisitor.visitTypeAnnotation(
+                typeAnnotation.typeRef, typeAnnotation.typePath, typeAnnotation.desc, true));
+      }
+    }
+    if (invisibleTypeAnnotations != null) {
+      for (int i = 0, n = invisibleTypeAnnotations.size(); i < n; ++i) {
+        TypeAnnotationNode typeAnnotation = invisibleTypeAnnotations.get(i);
+        typeAnnotation.accept(
+            recordComponentVisitor.visitTypeAnnotation(
+                typeAnnotation.typeRef, typeAnnotation.typePath, typeAnnotation.desc, false));
+      }
+    }
+    // Visit the non standard attributes.
+    if (attrs != null) {
+      for (int i = 0, n = attrs.size(); i < n; ++i) {
+        recordComponentVisitor.visitAttribute(attrs.get(i));
+      }
+    }
+    recordComponentVisitor.visitEnd();
+  }
+}
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
old mode 100755
new mode 100644
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
old mode 100755
new mode 100644
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/TypeAnnotationNode.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/TypeAnnotationNode.java
old mode 100755
new mode 100644
index e3c58cd..d22c17c
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/TypeAnnotationNode.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/TypeAnnotationNode.java
@@ -59,7 +59,7 @@
    * @throws IllegalStateException If a subclass calls this constructor.
    */
   public TypeAnnotationNode(final int typeRef, final TypePath typePath, final String descriptor) {
-    this(Opcodes.ASM7, typeRef, typePath, descriptor);
+    this(/* latest api = */ Opcodes.ASM8, typeRef, typePath, descriptor);
     if (getClass() != TypeAnnotationNode.class) {
       throw new IllegalStateException();
     }
@@ -69,7 +69,8 @@
    * Constructs a new {@link AnnotationNode}.
    *
    * @param api the ASM API version implemented by this visitor. Must be one of {@link
-   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
+   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7} or {@link
+   *     Opcodes#ASM8}.
    * @param typeRef a reference to the annotated type. See {@link org.apache.tapestry5.internal.plastic.asm.TypeReference}.
    * @param typePath the path to the annotated type argument, wildcard bound, array element type, or
    *     static inner type within 'typeRef'. May be {@literal null} if the annotation targets
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
old mode 100755
new mode 100644
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/UnsupportedClassVersionException.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/UnsupportedClassVersionException.java
old mode 100755
new mode 100644
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/Util.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/Util.java
old mode 100755
new mode 100644
index bf5d75f..b3af7a3
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/Util.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/Util.java
@@ -40,8 +40,14 @@
 
   private Util() {}
 
+  static <T> List<T> add(final List<T> list, final T element) {
+    List<T> newList = list == null ? new ArrayList<>(1) : list;
+    newList.add(element);
+    return newList;
+  }
+
   static <T> List<T> asArrayList(final int length) {
-    List<T> list = new ArrayList<T>(length);
+    List<T> list = new ArrayList<>(length);
     for (int i = 0; i < length; ++i) {
       list.add(null);
     }
@@ -50,9 +56,9 @@
 
   static <T> List<T> asArrayList(final T[] array) {
     if (array == null) {
-      return new ArrayList<T>();
+      return new ArrayList<>();
     }
-    ArrayList<T> list = new ArrayList<T>(array.length);
+    ArrayList<T> list = new ArrayList<>(array.length);
     for (T t : array) {
       list.add(t);
     }
@@ -61,9 +67,9 @@
 
   static List<Byte> asArrayList(final byte[] byteArray) {
     if (byteArray == null) {
-      return new ArrayList<Byte>();
+      return new ArrayList<>();
     }
-    ArrayList<Byte> byteList = new ArrayList<Byte>(byteArray.length);
+    ArrayList<Byte> byteList = new ArrayList<>(byteArray.length);
     for (byte b : byteArray) {
       byteList.add(b);
     }
@@ -72,9 +78,9 @@
 
   static List<Boolean> asArrayList(final boolean[] booleanArray) {
     if (booleanArray == null) {
-      return new ArrayList<Boolean>();
+      return new ArrayList<>();
     }
-    ArrayList<Boolean> booleanList = new ArrayList<Boolean>(booleanArray.length);
+    ArrayList<Boolean> booleanList = new ArrayList<>(booleanArray.length);
     for (boolean b : booleanArray) {
       booleanList.add(b);
     }
@@ -83,9 +89,9 @@
 
   static List<Short> asArrayList(final short[] shortArray) {
     if (shortArray == null) {
-      return new ArrayList<Short>();
+      return new ArrayList<>();
     }
-    ArrayList<Short> shortList = new ArrayList<Short>(shortArray.length);
+    ArrayList<Short> shortList = new ArrayList<>(shortArray.length);
     for (short s : shortArray) {
       shortList.add(s);
     }
@@ -94,9 +100,9 @@
 
   static List<Character> asArrayList(final char[] charArray) {
     if (charArray == null) {
-      return new ArrayList<Character>();
+      return new ArrayList<>();
     }
-    ArrayList<Character> charList = new ArrayList<Character>(charArray.length);
+    ArrayList<Character> charList = new ArrayList<>(charArray.length);
     for (char c : charArray) {
       charList.add(c);
     }
@@ -105,9 +111,9 @@
 
   static List<Integer> asArrayList(final int[] intArray) {
     if (intArray == null) {
-      return new ArrayList<Integer>();
+      return new ArrayList<>();
     }
-    ArrayList<Integer> intList = new ArrayList<Integer>(intArray.length);
+    ArrayList<Integer> intList = new ArrayList<>(intArray.length);
     for (int i : intArray) {
       intList.add(i);
     }
@@ -116,9 +122,9 @@
 
   static List<Float> asArrayList(final float[] floatArray) {
     if (floatArray == null) {
-      return new ArrayList<Float>();
+      return new ArrayList<>();
     }
-    ArrayList<Float> floatList = new ArrayList<Float>(floatArray.length);
+    ArrayList<Float> floatList = new ArrayList<>(floatArray.length);
     for (float f : floatArray) {
       floatList.add(f);
     }
@@ -127,9 +133,9 @@
 
   static List<Long> asArrayList(final long[] longArray) {
     if (longArray == null) {
-      return new ArrayList<Long>();
+      return new ArrayList<>();
     }
-    ArrayList<Long> longList = new ArrayList<Long>(longArray.length);
+    ArrayList<Long> longList = new ArrayList<>(longArray.length);
     for (long l : longArray) {
       longList.add(l);
     }
@@ -138,9 +144,9 @@
 
   static List<Double> asArrayList(final double[] doubleArray) {
     if (doubleArray == null) {
-      return new ArrayList<Double>();
+      return new ArrayList<>();
     }
-    ArrayList<Double> doubleList = new ArrayList<Double>(doubleArray.length);
+    ArrayList<Double> doubleList = new ArrayList<>(doubleArray.length);
     for (double d : doubleArray) {
       doubleList.add(d);
     }
@@ -148,7 +154,7 @@
   }
 
   static <T> List<T> asArrayList(final int length, final T[] array) {
-    List<T> list = new ArrayList<T>(length);
+    List<T> list = new ArrayList<>(length);
     for (int i = 0; i < length; ++i) {
       list.add(array[i]); // NOPMD(UseArraysAsList): we convert a part of the array.
     }
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
old mode 100755
new mode 100644
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
old mode 100755
new mode 100644
index 62de39d..87e5f5a
--- 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
@@ -124,7 +124,7 @@
       for (int j = startIndex; j < endIndex; ++j) {
         List<TryCatchBlockNode> insnHandlers = handlers[j];
         if (insnHandlers == null) {
-          insnHandlers = new ArrayList<TryCatchBlockNode>();
+          insnHandlers = new ArrayList<>();
           handlers[j] = insnHandlers;
         }
         insnHandlers.add(tryCatchBlock);
@@ -134,11 +134,11 @@
     // For each instruction, compute the subroutine to which it belongs.
     // Follow the main 'subroutine', and collect the jsr instructions to nested subroutines.
     Subroutine main = new Subroutine(null, method.maxLocals, null);
-    List<AbstractInsnNode> jsrInsns = new ArrayList<AbstractInsnNode>();
+    List<AbstractInsnNode> jsrInsns = new ArrayList<>();
     findSubroutine(0, main, jsrInsns);
     // Follow the nested subroutines, and collect their own nested subroutines, until all
     // subroutines are found.
-    Map<LabelNode, Subroutine> jsrSubroutines = new HashMap<LabelNode, Subroutine>();
+    Map<LabelNode, Subroutine> jsrSubroutines = new HashMap<>();
     while (!jsrInsns.isEmpty()) {
       JumpInsnNode jsrInsn = (JumpInsnNode) jsrInsns.remove(0);
       Subroutine subroutine = jsrSubroutines.get(jsrInsn.label);
@@ -233,7 +233,7 @@
             }
           } else if (insnOpcode == RET) {
             if (subroutine == null) {
-              throw new AnalyzerException(insnNode, "RET instruction outside of a sub routine");
+              throw new AnalyzerException(insnNode, "RET instruction outside of a subroutine");
             }
             for (int i = 0; i < subroutine.callers.size(); ++i) {
               JumpInsnNode caller = subroutine.callers.get(i);
@@ -313,7 +313,7 @@
   private void findSubroutine(
       final int insnIndex, final Subroutine subroutine, final List<AbstractInsnNode> jsrInsns)
       throws AnalyzerException {
-    ArrayList<Integer> instructionIndicesToProcess = new ArrayList<Integer>();
+    ArrayList<Integer> instructionIndicesToProcess = new ArrayList<>();
     instructionIndicesToProcess.add(insnIndex);
     while (!instructionIndicesToProcess.isEmpty()) {
       int currentInsnIndex =
@@ -459,7 +459,7 @@
    * @return the created frame.
    */
   protected Frame<V> newFrame(final int numLocals, final int numStack) {
-    return new Frame<V>(numLocals, numStack);
+    return new Frame<>(numLocals, numStack);
   }
 
   /**
@@ -469,7 +469,7 @@
    * @return the created frame.
    */
   protected Frame<V> newFrame(final Frame<? extends V> frame) {
-    return new Frame<V>(frame);
+    return new Frame<>(frame);
   }
 
   /**
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/AnalyzerException.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/AnalyzerException.java
old mode 100755
new mode 100644
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
old mode 100755
new mode 100644
index 037df4c..07649a9
--- 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
@@ -61,7 +61,7 @@
    * version.
    */
   public BasicInterpreter() {
-    super(ASM7);
+    super(/* latest api = */ ASM8);
     if (getClass() != BasicInterpreter.class) {
       throw new IllegalStateException();
     }
@@ -72,7 +72,8 @@
    *
    * @param api the ASM API version supported by this interpreter. Must be one of {@link
    *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM4}, {@link org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM5}, {@link
-   *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM6} or {@link org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM7}.
+   *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM6}, {@link org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM7} or {@link
+   *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM8}.
    */
   protected BasicInterpreter(final int api) {
     super(api);
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/BasicValue.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/BasicValue.java
old mode 100755
new mode 100644
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
old mode 100755
new mode 100644
index 8a8f965..58930a5
--- 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
@@ -47,7 +47,7 @@
    * use this constructor</i>. Instead, they must use the {@link #BasicVerifier(int)} version.
    */
   public BasicVerifier() {
-    super(ASM7);
+    super(/* latest api = */ ASM8);
     if (getClass() != BasicVerifier.class) {
       throw new IllegalStateException();
     }
@@ -58,7 +58,8 @@
    *
    * @param api the ASM API version supported by this interpreter. Must be one of {@link
    *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM4}, {@link org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM5}, {@link
-   *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM6} or {@link org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM7}.
+   *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM6}, {@link org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM7} or {@link
+   *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM8}.
    */
   protected BasicVerifier(final int api) {
     super(api);
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
old mode 100755
new mode 100644
index 09549cd..6a04fd5
--- 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
@@ -120,7 +120,9 @@
    *     this frame corresponds to the successor of the jump instruction (i.e. the next instruction
    *     in the instructions sequence).
    */
-  public void initJumpTarget(final int opcode, final LabelNode target) {}
+  public void initJumpTarget(final int opcode, final LabelNode target) {
+    // Does nothing by default.
+  }
 
   /**
    * Sets the expected return type of the analyzed method.
@@ -159,7 +161,7 @@
    */
   public V getLocal(final int index) {
     if (index >= numLocals) {
-      throw new IndexOutOfBoundsException("Trying to access an inexistant local variable");
+      throw new IndexOutOfBoundsException("Trying to get an inexistant local variable " + index);
     }
     return values[index];
   }
@@ -173,7 +175,7 @@
    */
   public void setLocal(final int index, final V value) {
     if (index >= numLocals) {
-      throw new IndexOutOfBoundsException("Trying to access an inexistant local variable " + index);
+      throw new IndexOutOfBoundsException("Trying to set an inexistant local variable " + index);
     }
     values[index] = value;
   }
@@ -206,7 +208,7 @@
    * @param value the new value of the stack slot.
    * @throws IndexOutOfBoundsException if the stack slot does not exist.
    */
-  public void setStack(final int index, final V value) throws IndexOutOfBoundsException {
+  public void setStack(final int index, final V value) {
     values[numLocals + index] = value;
   }
 
@@ -348,23 +350,8 @@
         break;
       case Opcodes.DUP_X2:
         value1 = pop();
-        if (value1.getSize() == 1) {
-          value2 = pop();
-          if (value2.getSize() == 1) {
-            value3 = pop();
-            if (value3.getSize() == 1) {
-              push(interpreter.copyOperation(insn, value1));
-              push(value3);
-              push(value2);
-              push(value1);
-              break;
-            }
-          } else {
-            push(interpreter.copyOperation(insn, value1));
-            push(value2);
-            push(value1);
-            break;
-          }
+        if (value1.getSize() == 1 && executeDupX2(insn, value1, interpreter)) {
+          break;
         }
         throw new AnalyzerException(insn, "Illegal use of DUP_X2");
       case Opcodes.DUP2:
@@ -435,23 +422,8 @@
               break;
             }
           }
-        } else {
-          value2 = pop();
-          if (value2.getSize() == 1) {
-            value3 = pop();
-            if (value3.getSize() == 1) {
-              push(interpreter.copyOperation(insn, value1));
-              push(value3);
-              push(value2);
-              push(value1);
-              break;
-            }
-          } else {
-            push(interpreter.copyOperation(insn, value1));
-            push(value2);
-            push(value1);
-            break;
-          }
+        } else if (executeDupX2(insn, value1, interpreter)) {
+          break;
         }
         throw new AnalyzerException(insn, "Illegal use of DUP2_X2");
       case Opcodes.SWAP:
@@ -598,36 +570,11 @@
       case Opcodes.INVOKESPECIAL:
       case Opcodes.INVOKESTATIC:
       case Opcodes.INVOKEINTERFACE:
-        {
-          List<V> valueList = new ArrayList<V>();
-          String methodDescriptor = ((MethodInsnNode) insn).desc;
-          for (int i = Type.getArgumentTypes(methodDescriptor).length; i > 0; --i) {
-            valueList.add(0, pop());
-          }
-          if (insn.getOpcode() != Opcodes.INVOKESTATIC) {
-            valueList.add(0, pop());
-          }
-          if (Type.getReturnType(methodDescriptor) == Type.VOID_TYPE) {
-            interpreter.naryOperation(insn, valueList);
-          } else {
-            push(interpreter.naryOperation(insn, valueList));
-          }
-          break;
-        }
+        executeInvokeInsn(insn, ((MethodInsnNode) insn).desc, interpreter);
+        break;
       case Opcodes.INVOKEDYNAMIC:
-        {
-          List<V> valueList = new ArrayList<V>();
-          String methodDesccriptor = ((InvokeDynamicInsnNode) insn).desc;
-          for (int i = Type.getArgumentTypes(methodDesccriptor).length; i > 0; --i) {
-            valueList.add(0, pop());
-          }
-          if (Type.getReturnType(methodDesccriptor) == Type.VOID_TYPE) {
-            interpreter.naryOperation(insn, valueList);
-          } else {
-            push(interpreter.naryOperation(insn, valueList));
-          }
-          break;
-        }
+        executeInvokeInsn(insn, ((InvokeDynamicInsnNode) insn).desc, interpreter);
+        break;
       case Opcodes.NEW:
         push(interpreter.newOperation(insn));
         break;
@@ -648,7 +595,7 @@
         interpreter.unaryOperation(insn, pop());
         break;
       case Opcodes.MULTIANEWARRAY:
-        List<V> valueList = new ArrayList<V>();
+        List<V> valueList = new ArrayList<>();
         for (int i = ((MultiANewArrayInsnNode) insn).dims; i > 0; --i) {
           valueList.add(0, pop());
         }
@@ -663,6 +610,45 @@
     }
   }
 
+  private boolean executeDupX2(
+      final AbstractInsnNode insn, final V value1, final Interpreter<V> interpreter)
+      throws AnalyzerException {
+    V value2 = pop();
+    if (value2.getSize() == 1) {
+      V value3 = pop();
+      if (value3.getSize() == 1) {
+        push(interpreter.copyOperation(insn, value1));
+        push(value3);
+        push(value2);
+        push(value1);
+        return true;
+      }
+    } else {
+      push(interpreter.copyOperation(insn, value1));
+      push(value2);
+      push(value1);
+      return true;
+    }
+    return false;
+  }
+
+  private void executeInvokeInsn(
+      final AbstractInsnNode insn, final String methodDescriptor, final Interpreter<V> interpreter)
+      throws AnalyzerException {
+    ArrayList<V> valueList = new ArrayList<>();
+    for (int i = Type.getArgumentTypes(methodDescriptor).length; i > 0; --i) {
+      valueList.add(0, pop());
+    }
+    if (insn.getOpcode() != Opcodes.INVOKESTATIC && insn.getOpcode() != Opcodes.INVOKEDYNAMIC) {
+      valueList.add(0, pop());
+    }
+    if (Type.getReturnType(methodDescriptor) == Type.VOID_TYPE) {
+      interpreter.naryOperation(insn, valueList);
+    } else {
+      push(interpreter.naryOperation(insn, valueList));
+    }
+  }
+
   /**
    * Merges the given frame into this frame.
    *
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
old mode 100755
new mode 100644
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
old mode 100755
new mode 100644
index 90487a4..cf446e1
--- 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
@@ -93,7 +93,12 @@
       final Type currentSuperClass,
       final List<Type> currentClassInterfaces,
       final boolean isInterface) {
-    this(ASM7, currentClass, currentSuperClass, currentClassInterfaces, isInterface);
+    this(
+        /* latest api = */ ASM8,
+        currentClass,
+        currentSuperClass,
+        currentClassInterfaces,
+        isInterface);
     if (getClass() != SimpleVerifier.class) {
       throw new IllegalStateException();
     }
@@ -105,7 +110,8 @@
    *
    * @param api the ASM API version supported by this verifier. Must be one of {@link
    *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM4}, {@link org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM5}, {@link
-   *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM6} or {@link org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM7}.
+   *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM6}, {@link org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM7} or {@link
+   *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM8}.
    * @param currentClass the type of the class to be verified.
    * @param currentSuperClass the type of the super class of the class to be verified.
    * @param currentClassInterfaces the types of the interfaces directly implemented by the class to
@@ -254,7 +260,7 @@
           type1 = type1.getElementType();
           type2 = type2.getElementType();
         }
-        do {
+        while (true) {
           if (type1 == null || isInterface(type1)) {
             return newArrayValue(Type.getObjectType("java/lang/Object"), numDimensions);
           }
@@ -262,7 +268,7 @@
           if (isAssignableFrom(type1, type2)) {
             return newArrayValue(type1, numDimensions);
           }
-        } while (true);
+        }
       }
       return BasicValue.UNINITIALIZED_VALUE;
     }
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/SmallSet.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/SmallSet.java
old mode 100755
new mode 100644
index 45df958..4eea27f
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/SmallSet.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/SmallSet.java
@@ -87,12 +87,18 @@
 
   @Override
   public Iterator<T> iterator() {
-    return new IteratorImpl<T>(element1, element2);
+    return new IteratorImpl<>(element1, element2);
   }
 
   @Override
   public int size() {
-    return element1 == null ? 0 : (element2 == null ? 1 : 2);
+    if (element1 == null) {
+      return 0;
+    } else if (element2 == null) {
+      return 1;
+    } else {
+      return 2;
+    }
   }
 
   // -----------------------------------------------------------------------------------------------
@@ -124,7 +130,7 @@
     if (otherSet.element2 == null) {
       // If this set also contains exactly one element, we have two distinct elements.
       if (element2 == null) {
-        return new SmallSet<T>(element1, otherSet.element1);
+        return new SmallSet<>(element1, otherSet.element1);
       }
       // If otherSet is included in this set, return this set.
       if (otherSet.element1 == element1 || otherSet.element1 == element2) {
@@ -139,7 +145,7 @@
 
     // At this point we know that there are at least 3 distinct elements, so we need a generic set
     // to store the result.
-    HashSet<T> result = new HashSet<T>(4);
+    HashSet<T> result = new HashSet<>(4);
     result.add(element1);
     if (element2 != null) {
       result.add(element2);
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
old mode 100755
new mode 100644
index a84b44a..ee6e6a9
--- 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
@@ -51,7 +51,7 @@
    * version.
    */
   public SourceInterpreter() {
-    super(ASM7);
+    super(/* latest api = */ ASM8);
     if (getClass() != SourceInterpreter.class) {
       throw new IllegalStateException();
     }
@@ -62,7 +62,8 @@
    *
    * @param api the ASM API version supported by this interpreter. Must be one of {@link
    *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM4}, {@link org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM5}, {@link
-   *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM6} or {@link org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM7}.
+   *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM6}, {@link org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM7} or {@link
+   *     org.apache.tapestry5.internal.plastic.asm.Opcodes#ASM8}.
    */
   protected SourceInterpreter(final int api) {
     super(api);
@@ -204,7 +205,7 @@
       }
     }
     if (value1.size != value2.size || !containsAll(value1.insns, value2.insns)) {
-      HashSet<AbstractInsnNode> setUnion = new HashSet<AbstractInsnNode>();
+      HashSet<AbstractInsnNode> setUnion = new HashSet<>();
       setUnion.addAll(value1.insns);
       setUnion.addAll(value2.insns);
       return new SourceValue(Math.min(value1.size, value2.size), setUnion);
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
old mode 100755
new mode 100644
index 3b68854..c61bbc9
--- 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
@@ -77,7 +77,7 @@
    */
   public SourceValue(final int size, final AbstractInsnNode insnNode) {
     this.size = size;
-    this.insns = new SmallSet<AbstractInsnNode>(insnNode);
+    this.insns = new SmallSet<>(insnNode);
   }
 
   /**
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
old mode 100755
new mode 100644
index d921aea..2e9cecc
--- 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
@@ -61,7 +61,7 @@
   Subroutine(final LabelNode start, final int maxLocals, final JumpInsnNode caller) {
     this.start = start;
     this.localsUsed = new boolean[maxLocals];
-    this.callers = new ArrayList<JumpInsnNode>();
+    this.callers = new ArrayList<>();
     callers.add(caller);
   }
 
@@ -72,9 +72,8 @@
    */
   Subroutine(final Subroutine subroutine) {
     this.start = subroutine.start;
-    this.localsUsed = new boolean[subroutine.localsUsed.length];
-    this.callers = new ArrayList<JumpInsnNode>(subroutine.callers);
-    System.arraycopy(subroutine.localsUsed, 0, this.localsUsed, 0, subroutine.localsUsed.length);
+    this.localsUsed = subroutine.localsUsed.clone();
+    this.callers = new ArrayList<>(subroutine.callers);
   }
 
   /**
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/Value.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/Value.java
old mode 100755
new mode 100644
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/package.html b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/package.html
old mode 100755
new mode 100644
index b60c7dd..05624f6
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/package.html
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/package.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
 <!--
  * ASM: a very small and fast Java bytecode manipulation framework
  * Copyright (c) 2000-2011 INRIA, France Telecom
@@ -28,6 +29,9 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGE.
 -->
+<head>
+  <title>Package org.objectweb.asm.tree.analysis</title>
+</head>
 <body>
 
 <p>
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/package.html b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/package.html
old mode 100755
new mode 100644
index ab295ac..9e7cd7a
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/package.html
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/package.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
 <!--
  * ASM: a very small and fast Java bytecode manipulation framework
  * Copyright (c) 2000-2011 INRIA, France Telecom
@@ -28,6 +29,9 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGE.
 -->
+<head>
+  <title>Package org.objectweb.asm.tree</title>
+</head>
 <body>
 
 <p>
@@ -45,8 +49,8 @@
 and write a class with a ClassVisitor than with a ClassNode). This is why
 this package is bundled in an optional <code>asm-tree.jar</code> library that
 is separated from (but requires) the <code>asm.jar</code> library, which contains
-the core ASM framework. This is also why <i><font color="red">it is recommended
-not to use this class adapter when it is possible</font></i>.
+the core ASM framework. This is also why <em>it is recommended not to use this
+class adapter when it is possible</em>.
 </p>
 
 <p>
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
old mode 100755
new mode 100644
index 8ddee8d..059fb1a
--- 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
@@ -28,8 +28,11 @@
 package org.apache.tapestry5.internal.plastic.asm.util;
 
 import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Arrays;
 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;
@@ -47,6 +50,11 @@
 // DontCheck(AbbreviationAsWordInName): can't be renamed (for backward binary compatibility).
 public class ASMifier extends Printer {
 
+  /** The help message shown when command line arguments are incorrect. */
+  private static final String USAGE =
+      "Prints the ASM code to generate the given class.\n"
+          + "Usage: ASMifier [-debug] <fully qualified class name or class file name>";
+
   /** A pseudo access flag used to distinguish class access flags. */
   private static final int ACCESS_CLASS = 0x40000;
 
@@ -61,15 +69,27 @@
 
   private static final String ANNOTATION_VISITOR = "annotationVisitor";
   private static final String ANNOTATION_VISITOR0 = "annotationVisitor0 = ";
-  private static final String NEW_OBJECT_ARRAY = ", new Object[] {";
+  private static final String COMMA = "\", \"";
   private static final String END_ARRAY = " });\n";
   private static final String END_PARAMETERS = ");\n\n";
+  private static final String NEW_OBJECT_ARRAY = ", new Object[] {";
   private static final String VISIT_END = ".visitEnd();\n";
 
+  private static final List<String> FRAME_TYPES =
+      Collections.unmodifiableList(
+          Arrays.asList(
+              "Opcodes.TOP",
+              "Opcodes.INTEGER",
+              "Opcodes.FLOAT",
+              "Opcodes.DOUBLE",
+              "Opcodes.LONG",
+              "Opcodes.NULL",
+              "Opcodes.UNINITIALIZED_THIS"));
+
   private static final Map<Integer, String> CLASS_VERSIONS;
 
   static {
-    HashMap<Integer, String> classVersions = new HashMap<Integer, String>();
+    HashMap<Integer, String> classVersions = new HashMap<>();
     classVersions.put(Opcodes.V1_1, "V1_1");
     classVersions.put(Opcodes.V1_2, "V1_2");
     classVersions.put(Opcodes.V1_3, "V1_3");
@@ -82,6 +102,9 @@
     classVersions.put(Opcodes.V10, "V10");
     classVersions.put(Opcodes.V11, "V11");
     classVersions.put(Opcodes.V12, "V12");
+    classVersions.put(Opcodes.V13, "V13");
+    classVersions.put(Opcodes.V14, "V14");
+    classVersions.put(Opcodes.V15, "V15");
     CLASS_VERSIONS = Collections.unmodifiableMap(classVersions);
   }
 
@@ -101,7 +124,7 @@
    * @throws IllegalStateException If a subclass calls this constructor.
    */
   public ASMifier() {
-    this(Opcodes.ASM7, "classWriter", 0);
+    this(/* latest api = */ Opcodes.ASM8, "classWriter", 0);
     if (getClass() != ASMifier.class) {
       throw new IllegalStateException();
     }
@@ -111,7 +134,7 @@
    * Constructs a new {@link ASMifier}.
    *
    * @param api the ASM API version implemented by this class. Must be one of {@link Opcodes#ASM4},
-   *     {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
+   *     {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7} or {@link Opcodes#ASM8}.
    * @param visitorVariableName the name of the visitor variable in the produced code.
    * @param annotationVisitorId identifier of the annotation visitor variable in the produced code.
    */
@@ -131,10 +154,22 @@
    * @throws IOException if the class cannot be found, or if an IOException occurs.
    */
   public static void main(final String[] args) throws IOException {
-    String usage =
-        "Prints the ASM code to generate the given class.\n"
-            + "Usage: ASMifier [-debug] <fully qualified class name or class file name>";
-    main(usage, new ASMifier(), args);
+    main(args, new PrintWriter(System.out, true), new PrintWriter(System.err, true));
+  }
+
+  /**
+   * Prints the ASM source code to generate the given class to the given output.
+   *
+   * <p>Usage: ASMifier [-debug] &lt;binary class name or class file name&gt;
+   *
+   * @param args the command line arguments.
+   * @param output where to print the result.
+   * @param logger where to log errors.
+   * @throws IOException if the class cannot be found, or if an IOException occurs.
+   */
+  static void main(final String[] args, final PrintWriter output, final PrintWriter logger)
+      throws IOException {
+    main(args, USAGE, new ASMifier(), output, logger);
   }
 
   // -----------------------------------------------------------------------------------------------
@@ -158,7 +193,7 @@
         simpleName = name;
       } else {
         text.add("package asm." + name.substring(0, lastSlashIndex).replace('/', '.') + ";\n");
-        simpleName = name.substring(lastSlashIndex + 1).replace('-', '_');
+        simpleName = name.substring(lastSlashIndex + 1).replaceAll("[-\\(\\)]", "_");
       }
     }
     text.add("import org.apache.tapestry5.internal.plastic.asm.AnnotationVisitor;\n");
@@ -171,12 +206,14 @@
     text.add("import org.apache.tapestry5.internal.plastic.asm.Label;\n");
     text.add("import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;\n");
     text.add("import org.apache.tapestry5.internal.plastic.asm.Opcodes;\n");
+    text.add("import org.apache.tapestry5.internal.plastic.asm.RecordComponentVisitor;\n");
     text.add("import org.apache.tapestry5.internal.plastic.asm.Type;\n");
     text.add("import org.apache.tapestry5.internal.plastic.asm.TypePath;\n");
     text.add("public class " + simpleName + "Dump implements Opcodes {\n\n");
     text.add("public static byte[] dump () throws Exception {\n\n");
     text.add("ClassWriter classWriter = new ClassWriter(0);\n");
     text.add("FieldVisitor fieldVisitor;\n");
+    text.add("RecordComponentVisitor recordComponentVisitor;\n");
     text.add("MethodVisitor methodVisitor;\n");
     text.add("AnnotationVisitor annotationVisitor0;\n\n");
 
@@ -244,7 +281,7 @@
     stringBuilder.setLength(0);
     stringBuilder.append("classWriter.visitNestHost(");
     appendConstant(nestHost);
-    stringBuilder.append(");\n\n");
+    stringBuilder.append(END_PARAMETERS);
     text.add(stringBuilder.toString());
   }
 
@@ -282,7 +319,23 @@
     stringBuilder.setLength(0);
     stringBuilder.append("classWriter.visitNestMember(");
     appendConstant(nestMember);
-    stringBuilder.append(");\n\n");
+    stringBuilder.append(END_PARAMETERS);
+    text.add(stringBuilder.toString());
+  }
+
+  /**
+   * <b>Experimental, use at your own risk.</b>.
+   *
+   * @param permittedSubtype the internal name of a permitted subtype.
+   * @deprecated this API is experimental.
+   */
+  @Override
+  @Deprecated
+  public void visitPermittedSubtypeExperimental(final String permittedSubtype) {
+    stringBuilder.setLength(0);
+    stringBuilder.append("classWriter.visitPermittedSubtypeExperimental(");
+    appendConstant(permittedSubtype);
+    stringBuilder.append(END_PARAMETERS);
     text.add(stringBuilder.toString());
   }
 
@@ -303,6 +356,25 @@
   }
 
   @Override
+  public ASMifier visitRecordComponent(
+      final String name, final String descriptor, final String signature) {
+    stringBuilder.setLength(0);
+    stringBuilder.append("{\n");
+    stringBuilder.append("recordComponentVisitor = classWriter.visitRecordComponent(");
+    appendConstant(name);
+    stringBuilder.append(", ");
+    appendConstant(descriptor);
+    stringBuilder.append(", ");
+    appendConstant(signature);
+    stringBuilder.append(");\n");
+    text.add(stringBuilder.toString());
+    ASMifier asmifier = createASMifier("recordComponentVisitor", 0);
+    text.add(asmifier.getText());
+    text.add("}\n");
+    return asmifier;
+  }
+
+  @Override
   public ASMifier visitField(
       final int access,
       final String name,
@@ -410,27 +482,18 @@
 
   @Override
   public void visitExport(final String packaze, final int access, final String... modules) {
-    stringBuilder.setLength(0);
-    stringBuilder.append("moduleVisitor.visitExport(");
-    appendConstant(packaze);
-    stringBuilder.append(", ");
-    appendAccessFlags(access | ACCESS_MODULE);
-    if (modules != null && modules.length > 0) {
-      stringBuilder.append(", new String[] {");
-      for (int i = 0; i < modules.length; ++i) {
-        stringBuilder.append(i == 0 ? " " : ", ");
-        appendConstant(modules[i]);
-      }
-      stringBuilder.append(" }");
-    }
-    stringBuilder.append(");\n");
-    text.add(stringBuilder.toString());
+    visitExportOrOpen("moduleVisitor.visitExport(", packaze, access, modules);
   }
 
   @Override
   public void visitOpen(final String packaze, final int access, final String... modules) {
+    visitExportOrOpen("moduleVisitor.visitOpen(", packaze, access, modules);
+  }
+
+  private void visitExportOrOpen(
+      final String visitMethod, final String packaze, final int access, final String... modules) {
     stringBuilder.setLength(0);
-    stringBuilder.append("moduleVisitor.visitOpen(");
+    stringBuilder.append(visitMethod);
     appendConstant(packaze);
     stringBuilder.append(", ");
     appendAccessFlags(access | ACCESS_MODULE);
@@ -549,6 +612,33 @@
   }
 
   // -----------------------------------------------------------------------------------------------
+  // Record components
+  // -----------------------------------------------------------------------------------------------
+
+  @Override
+  public ASMifier visitRecordComponentAnnotation(final String descriptor, final boolean visible) {
+    return visitAnnotation(descriptor, visible);
+  }
+
+  @Override
+  public ASMifier visitRecordComponentTypeAnnotation(
+      final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
+    return visitTypeAnnotation(typeRef, typePath, descriptor, visible);
+  }
+
+  @Override
+  public void visitRecordComponentAttribute(final Attribute attribute) {
+    visitAttribute(attribute);
+  }
+
+  @Override
+  public void visitRecordComponentEnd() {
+    stringBuilder.setLength(0);
+    stringBuilder.append(name).append(VISIT_END);
+    text.add(stringBuilder.toString());
+  }
+
+  // -----------------------------------------------------------------------------------------------
   // Fields
   // -----------------------------------------------------------------------------------------------
 
@@ -774,37 +864,8 @@
     text.add(stringBuilder.toString());
   }
 
-  /**
-   * Deprecated.
-   *
-   * @deprecated use {@link #visitMethodInsn(int, String, String, String, boolean)} instead.
-   */
-  @Deprecated
   @Override
   public void visitMethodInsn(
-      final int opcode, final String owner, final String name, final String descriptor) {
-    if (api >= Opcodes.ASM5) {
-      super.visitMethodInsn(opcode, owner, name, descriptor);
-      return;
-    }
-    doVisitMethodInsn(opcode, owner, name, descriptor, opcode == Opcodes.INVOKEINTERFACE);
-  }
-
-  @Override
-  public void visitMethodInsn(
-      final int opcode,
-      final String owner,
-      final String name,
-      final String descriptor,
-      final boolean isInterface) {
-    if (api < Opcodes.ASM5) {
-      super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
-      return;
-    }
-    doVisitMethodInsn(opcode, owner, name, descriptor, isInterface);
-  }
-
-  private void doVisitMethodInsn(
       final int opcode,
       final String owner,
       final String name,
@@ -1174,14 +1235,12 @@
   public void visitAttribute(final Attribute attribute) {
     stringBuilder.setLength(0);
     stringBuilder.append("// ATTRIBUTE ").append(attribute.type).append('\n');
-    if (attribute instanceof ASMifiable) {
+    if (attribute instanceof ASMifierSupport) {
       if (labelNames == null) {
-        labelNames = new HashMap<Label, String>();
+        labelNames = new HashMap<>();
       }
       stringBuilder.append("{\n");
-      StringBuffer stringBuffer = new StringBuffer();
-      ((ASMifiable) attribute).asmify(stringBuffer, "attribute", labelNames);
-      stringBuilder.append(stringBuffer.toString());
+      ((ASMifierSupport) attribute).asmify(stringBuilder, "attribute", labelNames);
       stringBuilder.append(name).append(".visitAttribute(attribute);\n");
       stringBuilder.append("}\n");
     }
@@ -1202,7 +1261,7 @@
   // DontCheck(AbbreviationAsWordInName): can't be renamed (for backward binary compatibility).
   protected ASMifier createASMifier(
       final String visitorVariableName, final int annotationVisitorId) {
-    return new ASMifier(Opcodes.ASM7, visitorVariableName, annotationVisitorId);
+    return new ASMifier(api, visitorVariableName, annotationVisitorId);
   }
 
   /**
@@ -1348,6 +1407,13 @@
       stringBuilder.append("ACC_DEPRECATED");
       isEmpty = false;
     }
+    if ((accessFlags & Opcodes.ACC_RECORD) != 0) {
+      if (!isEmpty) {
+        stringBuilder.append(" | ");
+      }
+      stringBuilder.append("ACC_RECORD");
+      isEmpty = false;
+    }
     if ((accessFlags & (Opcodes.ACC_MANDATED | Opcodes.ACC_MODULE)) != 0) {
       if (!isEmpty) {
         stringBuilder.append(" | ");
@@ -1384,17 +1450,17 @@
       stringBuilder.append("new Handle(");
       Handle handle = (Handle) value;
       stringBuilder.append("Opcodes.").append(HANDLE_TAG[handle.getTag()]).append(", \"");
-      stringBuilder.append(handle.getOwner()).append("\", \"");
-      stringBuilder.append(handle.getName()).append("\", \"");
+      stringBuilder.append(handle.getOwner()).append(COMMA);
+      stringBuilder.append(handle.getName()).append(COMMA);
       stringBuilder.append(handle.getDesc()).append("\", ");
       stringBuilder.append(handle.isInterface()).append(")");
     } else if (value instanceof ConstantDynamic) {
       stringBuilder.append("new ConstantDynamic(\"");
       ConstantDynamic constantDynamic = (ConstantDynamic) value;
-      stringBuilder.append(constantDynamic.getName()).append("\", \"");
+      stringBuilder.append(constantDynamic.getName()).append(COMMA);
       stringBuilder.append(constantDynamic.getDescriptor()).append("\", ");
       appendConstant(constantDynamic.getBootstrapMethod());
-      stringBuilder.append(", new Object[] {");
+      stringBuilder.append(NEW_OBJECT_ARRAY);
       int bootstrapMethodArgumentCount = constantDynamic.getBootstrapMethodArgumentCount();
       for (int i = 0; i < bootstrapMethodArgumentCount; ++i) {
         appendConstant(constantDynamic.getBootstrapMethodArgument(i));
@@ -1511,31 +1577,7 @@
       if (frameTypes[i] instanceof String) {
         appendConstant(frameTypes[i]);
       } else if (frameTypes[i] instanceof Integer) {
-        switch (((Integer) frameTypes[i]).intValue()) {
-          case 0:
-            stringBuilder.append("Opcodes.TOP");
-            break;
-          case 1:
-            stringBuilder.append("Opcodes.INTEGER");
-            break;
-          case 2:
-            stringBuilder.append("Opcodes.FLOAT");
-            break;
-          case 3:
-            stringBuilder.append("Opcodes.DOUBLE");
-            break;
-          case 4:
-            stringBuilder.append("Opcodes.LONG");
-            break;
-          case 5:
-            stringBuilder.append("Opcodes.NULL");
-            break;
-          case 6:
-            stringBuilder.append("Opcodes.UNINITIALIZED_THIS");
-            break;
-          default:
-            throw new IllegalArgumentException();
-        }
+        stringBuilder.append(FRAME_TYPES.get(((Integer) frameTypes[i]).intValue()));
       } else {
         appendLabel((Label) frameTypes[i]);
       }
@@ -1551,7 +1593,7 @@
    */
   protected void declareLabel(final Label label) {
     if (labelNames == null) {
-      labelNames = new HashMap<Label, String>();
+      labelNames = new HashMap<>();
     }
     String labelName = labelNames.get(label);
     if (labelName == null) {
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
new file mode 100644
index 0000000..4c5859d
--- /dev/null
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/ASMifierSupport.java
@@ -0,0 +1,45 @@
+/**
+ * ASM: a very small and fast Java bytecode manipulation framework Copyright (c) 2000-2011 INRIA,
+ * France Telecom All rights reserved.
+ *
+ * <p>Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met: 1. Redistributions of source code must retain the
+ * above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions
+ * in binary form must reproduce the above copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its contributors may be used to
+ * endorse or promote products derived from this software without specific prior written permission.
+ *
+ * <p>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.apache.tapestry5.internal.plastic.asm.util;
+
+import java.util.Map;
+import org.apache.tapestry5.internal.plastic.asm.Label;
+
+/**
+ * An {@link org.apache.tapestry5.internal.plastic.asm.Attribute} that can generate the ASM code to create an equivalent
+ * attribute.
+ *
+ * @author Eugene Kuleshov
+ */
+// DontCheck(AbbreviationAsWordInName): can't be renamed (for backward binary compatibility).
+public interface ASMifierSupport {
+
+  /**
+   * Generates the ASM code to create an attribute equal to this attribute.
+   *
+   * @param outputBuilder where the generated code must be appended.
+   * @param visitorVariableName the name of the visitor variable in the produced code.
+   * @param labelNames the names of the labels in the generated code.
+   */
+  void asmify(
+      StringBuilder outputBuilder, String visitorVariableName, Map<Label, String> labelNames);
+}
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckAnnotationAdapter.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckAnnotationAdapter.java
old mode 100755
new mode 100644
index e3bcae4..f2625a5
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckAnnotationAdapter.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckAnnotationAdapter.java
@@ -52,7 +52,7 @@
   }
 
   CheckAnnotationAdapter(final AnnotationVisitor annotationVisitor, final boolean useNamedValues) {
-    super(Opcodes.ASM7, annotationVisitor);
+    super(/* latest api = */ Opcodes.ASM8, annotationVisitor);
     this.useNamedValue = useNamedValues;
   }
 
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
old mode 100755
new mode 100644
index c0b129c..22db10d
--- 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
@@ -27,13 +27,6 @@
 // THE POSSIBILITY OF SUCH DAMAGE.
 package org.apache.tapestry5.internal.plastic.asm.util;
 
-import org.apache.tapestry5.internal.plastic.asm.*;
-import org.apache.tapestry5.internal.plastic.asm.tree.ClassNode;
-import org.apache.tapestry5.internal.plastic.asm.tree.MethodNode;
-import org.apache.tapestry5.internal.plastic.asm.tree.TryCatchBlockNode;
-import org.apache.tapestry5.internal.plastic.asm.tree.analysis.*;
-import org.apache.tapestry5.internal.plastic.asm.tree.analysis.Frame;
-
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -42,6 +35,27 @@
 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;
+import org.apache.tapestry5.internal.plastic.asm.ClassVisitor;
+import org.apache.tapestry5.internal.plastic.asm.FieldVisitor;
+import org.apache.tapestry5.internal.plastic.asm.Label;
+import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
+import org.apache.tapestry5.internal.plastic.asm.ModuleVisitor;
+import org.apache.tapestry5.internal.plastic.asm.Opcodes;
+import org.apache.tapestry5.internal.plastic.asm.RecordComponentVisitor;
+import org.apache.tapestry5.internal.plastic.asm.Type;
+import org.apache.tapestry5.internal.plastic.asm.TypePath;
+import org.apache.tapestry5.internal.plastic.asm.TypeReference;
+import org.apache.tapestry5.internal.plastic.asm.tree.ClassNode;
+import org.apache.tapestry5.internal.plastic.asm.tree.MethodNode;
+import org.apache.tapestry5.internal.plastic.asm.tree.TryCatchBlockNode;
+import org.apache.tapestry5.internal.plastic.asm.tree.analysis.Analyzer;
+import org.apache.tapestry5.internal.plastic.asm.tree.analysis.AnalyzerException;
+import org.apache.tapestry5.internal.plastic.asm.tree.analysis.BasicValue;
+import org.apache.tapestry5.internal.plastic.asm.tree.analysis.Frame;
+import org.apache.tapestry5.internal.plastic.asm.tree.analysis.SimpleVerifier;
 
 /**
  * A {@link ClassVisitor} that checks that its methods are properly used. More precisely this class
@@ -96,6 +110,11 @@
  */
 public class CheckClassAdapter extends ClassVisitor {
 
+  /** The help message shown when command line arguments are incorrect. */
+  private static final String USAGE =
+      "Verifies the given class.\n"
+          + "Usage: CheckClassAdapter <fully qualified class name or class file name>";
+
   private static final String ERROR_AT = ": error at index ";
 
   /** Whether the bytecode must be checked with a BasicVerifier. */
@@ -155,7 +174,7 @@
    * @throws IllegalStateException If a subclass calls this constructor.
    */
   public CheckClassAdapter(final ClassVisitor classVisitor, final boolean checkDataFlow) {
-    this(Opcodes.ASM7, classVisitor, checkDataFlow);
+    this(/* latest api = */ Opcodes.ASM8, classVisitor, checkDataFlow);
     if (getClass() != CheckClassAdapter.class) {
       throw new IllegalStateException();
     }
@@ -165,7 +184,8 @@
    * Constructs a new {@link CheckClassAdapter}.
    *
    * @param api the ASM API version implemented by this visitor. Must be one of {@link
-   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
+   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7} or {@link
+   *     Opcodes#ASM8}.
    * @param classVisitor the class visitor to which this adapter must delegate calls.
    * @param checkDataFlow {@literal true} to perform basic data flow checks, or {@literal false} to
    *     not perform any data flow check (see {@link CheckMethodAdapter}). This option requires
@@ -174,7 +194,7 @@
   protected CheckClassAdapter(
       final int api, final ClassVisitor classVisitor, final boolean checkDataFlow) {
     super(api, classVisitor);
-    this.labelInsnIndices = new HashMap<Label, Integer>();
+    this.labelInsnIndices = new HashMap<>();
     this.checkDataFlow = checkDataFlow;
   }
 
@@ -206,6 +226,7 @@
             | Opcodes.ACC_ANNOTATION
             | Opcodes.ACC_ENUM
             | Opcodes.ACC_DEPRECATED
+            | Opcodes.ACC_RECORD
             | Opcodes.ACC_MODULE);
     if (name == null) {
       throw new IllegalArgumentException("Illegal class name (null)");
@@ -301,6 +322,20 @@
     super.visitNestMember(nestMember);
   }
 
+  /**
+   * <b>Experimental, use at your own risk.</b>.
+   *
+   * @param permittedSubtype the internal name of a permitted subtype.
+   * @deprecated this API is experimental.
+   */
+  @Override
+  @Deprecated
+  public void visitPermittedSubtypeExperimental(final String permittedSubtype) {
+    checkState();
+    CheckMethodAdapter.checkInternalName(version, permittedSubtype, "permittedSubtype");
+    super.visitPermittedSubtypeExperimental(permittedSubtype);
+  }
+
   @Override
   public void visitOuterClass(final String owner, final String name, final String descriptor) {
     checkState();
@@ -350,6 +385,19 @@
   }
 
   @Override
+  public RecordComponentVisitor visitRecordComponent(
+      final String name, final String descriptor, final String signature) {
+    checkState();
+    CheckMethodAdapter.checkUnqualifiedName(version, name, "record component name");
+    CheckMethodAdapter.checkDescriptor(version, descriptor, /* canBeVoid = */ false);
+    if (signature != null) {
+      checkFieldSignature(signature);
+    }
+    return new CheckRecordComponentAdapter(
+        api, super.visitRecordComponent(name, descriptor, signature));
+  }
+
+  @Override
   public FieldVisitor visitField(
       final int access,
       final String name,
@@ -368,6 +416,7 @@
             | Opcodes.ACC_TRANSIENT
             | Opcodes.ACC_SYNTHETIC
             | Opcodes.ACC_ENUM
+            | Opcodes.ACC_MANDATED
             | Opcodes.ACC_DEPRECATED);
     CheckMethodAdapter.checkUnqualifiedName(version, name, "field name");
     CheckMethodAdapter.checkDescriptor(version, descriptor, /* canBeVoid = */ false);
@@ -402,6 +451,7 @@
             | Opcodes.ACC_ABSTRACT
             | Opcodes.ACC_STRICT
             | Opcodes.ACC_SYNTHETIC
+            | Opcodes.ACC_MANDATED
             | Opcodes.ACC_DEPRECATED);
     if (!"<init>".equals(name) && !"<clinit>".equals(name)) {
       CheckMethodAdapter.checkMethodIdentifier(version, name, "method name");
@@ -935,10 +985,19 @@
    * @throws IOException if the class cannot be found, or if an IO exception occurs.
    */
   public static void main(final String[] args) throws IOException {
+    main(args, new PrintWriter(System.err, true));
+  }
+
+  /**
+   * Checks the given class.
+   *
+   * @param args the command line arguments.
+   * @param logger where to log errors.
+   * @throws IOException if the class cannot be found, or if an IO exception occurs.
+   */
+  static void main(final String[] args, final PrintWriter logger) throws IOException {
     if (args.length != 1) {
-      System.err.println(
-          "Verifies the given class.\n"
-              + "Usage: CheckClassAdapter <fully qualified class name or class file name>");
+      logger.println(USAGE);
       return;
     }
 
@@ -951,7 +1010,7 @@
       classReader = new ClassReader(args[0]);
     }
 
-    verify(classReader, false, new PrintWriter(System.err));
+    verify(classReader, false, logger);
   }
 
   /**
@@ -982,12 +1041,13 @@
       final PrintWriter printWriter) {
     ClassNode classNode = new ClassNode();
     classReader.accept(
-        new CheckClassAdapter(Opcodes.ASM7, classNode, false) {}, ClassReader.SKIP_DEBUG);
+        new CheckClassAdapter(Opcodes.ASM9_EXPERIMENTAL, classNode, false) {},
+        ClassReader.SKIP_DEBUG);
 
     Type syperType = classNode.superName == null ? null : Type.getObjectType(classNode.superName);
     List<MethodNode> methods = classNode.methods;
 
-    List<Type> interfaces = new ArrayList<Type>();
+    List<Type> interfaces = new ArrayList<>();
     for (String interfaceName : classNode.interfaces) {
       interfaces.add(Type.getObjectType(interfaceName));
     }
@@ -999,7 +1059,7 @@
               syperType,
               interfaces,
               (classNode.access & Opcodes.ACC_INTERFACE) != 0);
-      Analyzer<BasicValue> analyzer = new Analyzer<BasicValue>(verifier);
+      Analyzer<BasicValue> analyzer = new Analyzer<>(verifier);
       if (loader != null) {
         verifier.setClassLoader(loader);
       }
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckFieldAdapter.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckFieldAdapter.java
old mode 100755
new mode 100644
index 2624841..053afb7
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckFieldAdapter.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckFieldAdapter.java
@@ -52,7 +52,7 @@
    * @throws IllegalStateException If a subclass calls this constructor.
    */
   public CheckFieldAdapter(final FieldVisitor fieldVisitor) {
-    this(Opcodes.ASM7, fieldVisitor);
+    this(/* latest api = */ Opcodes.ASM8, fieldVisitor);
     if (getClass() != CheckFieldAdapter.class) {
       throw new IllegalStateException();
     }
@@ -62,7 +62,8 @@
    * Constructs a new {@link CheckFieldAdapter}.
    *
    * @param api the ASM API version implemented by this visitor. Must be one of {@link
-   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
+   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7} or {@link
+   *     Opcodes#ASM8}.
    * @param fieldVisitor the field visitor to which this adapter must delegate calls.
    */
   protected CheckFieldAdapter(final int api, final FieldVisitor fieldVisitor) {
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
old mode 100755
new mode 100644
index e355c78..23513e1
--- 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
@@ -30,6 +30,7 @@
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -366,7 +367,7 @@
    */
   public CheckMethodAdapter(
       final MethodVisitor methodVisitor, final Map<Label, Integer> labelInsnIndices) {
-    this(Opcodes.ASM7, methodVisitor, labelInsnIndices);
+    this(/* latest api = */ Opcodes.ASM8, methodVisitor, labelInsnIndices);
     if (getClass() != CheckMethodAdapter.class) {
       throw new IllegalStateException();
     }
@@ -377,7 +378,8 @@
    * data flow check (see {@link #CheckMethodAdapter(int,String,String,MethodVisitor,Map)}).
    *
    * @param api the ASM API version implemented by this CheckMethodAdapter. Must be one of {@link
-   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
+   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7} or {@link
+   *     Opcodes#ASM8}.
    * @param methodVisitor the method visitor to which this adapter must delegate calls.
    * @param labelInsnIndices the index of the instruction designated by each visited label so far
    *     (in other methods). This map is updated with the labels from the visited method.
@@ -388,8 +390,8 @@
       final Map<Label, Integer> labelInsnIndices) {
     super(api, methodVisitor);
     this.labelInsnIndices = labelInsnIndices;
-    this.referencedLabels = new HashSet<Label>();
-    this.handlers = new ArrayList<Label>();
+    this.referencedLabels = new HashSet<>();
+    this.handlers = new ArrayList<>();
   }
 
   /**
@@ -412,7 +414,8 @@
       final String descriptor,
       final MethodVisitor methodVisitor,
       final Map<Label, Integer> labelInsnIndices) {
-    this(Opcodes.ASM7, access, name, descriptor, methodVisitor, labelInsnIndices);
+    this(
+        /* latest api = */ Opcodes.ASM8, access, name, descriptor, methodVisitor, labelInsnIndices);
     if (getClass() != CheckMethodAdapter.class) {
       throw new IllegalStateException();
     }
@@ -424,7 +427,8 @@
    * instruction IRETURN, or the invalid sequence IADD L2I will be detected.
    *
    * @param api the ASM API version implemented by this CheckMethodAdapter. Must be one of {@link
-   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
+   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7} or {@link
+   *     Opcodes#ASM8}.
    * @param access the method's access flags.
    * @param name the method's name.
    * @param descriptor the method's descriptor (see {@link Type}).
@@ -444,7 +448,7 @@
         new MethodNode(api, access, name, descriptor, null, null) {
           @Override
           public void visitEnd() {
-            Analyzer<BasicValue> analyzer = new Analyzer<BasicValue>(new BasicVerifier());
+            Analyzer<BasicValue> analyzer = new Analyzer<>(new BasicVerifier());
             try {
               analyzer.analyze("dummy", this);
             } catch (IndexOutOfBoundsException e) {
@@ -457,7 +461,9 @@
             } catch (AnalyzerException e) {
               throwError(analyzer, e);
             }
-            accept(methodVisitor);
+            if (methodVisitor != null) {
+              accept(methodVisitor);
+            }
           }
 
           private void throwError(final Analyzer<BasicValue> analyzer, final Exception e) {
@@ -704,42 +710,20 @@
     ++insnCount;
   }
 
-  /**
-   * Deprecated.
-   *
-   * @deprecated use {@link #visitMethodInsn(int, String, String, String, boolean)} instead.
-   */
-  @Deprecated
   @Override
   public void visitMethodInsn(
-      final int opcode, final String owner, final String name, final String descriptor) {
-    if (api >= Opcodes.ASM5) {
-      super.visitMethodInsn(opcode, owner, name, descriptor);
-      return;
-    }
-    doVisitMethodInsn(opcode, owner, name, descriptor, opcode == Opcodes.INVOKEINTERFACE);
-  }
-
-  @Override
-  public void visitMethodInsn(
-      final int opcode,
+      final int opcodeAndSource,
       final String owner,
       final String name,
       final String descriptor,
       final boolean isInterface) {
-    if (api < Opcodes.ASM5) {
-      super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+    if (api < Opcodes.ASM5 && (opcodeAndSource & Opcodes.SOURCE_DEPRECATED) == 0) {
+      // Redirect the call to the deprecated version of this method.
+      super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface);
       return;
     }
-    doVisitMethodInsn(opcode, owner, name, descriptor, isInterface);
-  }
+    int opcode = opcodeAndSource & ~Opcodes.SOURCE_MASK;
 
-  private void doVisitMethodInsn(
-      final int opcode,
-      final String owner,
-      final String name,
-      final String descriptor,
-      final boolean isInterface) {
     checkVisitCodeCalled();
     checkVisitMaxsNotCalled();
     checkOpcodeMethod(opcode, Method.VISIT_METHOD_INSN);
@@ -758,13 +742,7 @@
       throw new IllegalArgumentException(
           "INVOKESPECIAL can't be used with interfaces prior to Java 8");
     }
-
-    // Calling super.visitMethodInsn requires to call the correct version depending on this.api
-    // (otherwise infinite loops can occur). To simplify and to make it easier to automatically
-    // remove the backward compatibility code, we inline the code of the overridden method here.
-    if (mv != null) {
-      mv.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
-    }
+    super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface);
     ++insnCount;
   }
 
@@ -848,9 +826,7 @@
       checkLabel(labels[i], false, "label at index " + i);
     }
     super.visitTableSwitchInsn(min, max, dflt, labels);
-    for (Label label : labels) {
-      referencedLabels.add(label);
-    }
+    Collections.addAll(referencedLabels, labels);
     ++insnCount;
   }
 
@@ -867,9 +843,7 @@
     }
     super.visitLookupSwitchInsn(dflt, keys, labels);
     referencedLabels.add(dflt);
-    for (Label label : labels) {
-      referencedLabels.add(label);
-    }
+    Collections.addAll(referencedLabels, labels);
     ++insnCount;
   }
 
@@ -966,6 +940,9 @@
     checkVisitMaxsNotCalled();
     checkUnqualifiedName(version, name, "name");
     checkDescriptor(version, descriptor, false);
+    if (signature != null) {
+      CheckClassAdapter.checkFieldSignature(signature);
+    }
     checkLabel(start, true, START_LABEL);
     checkLabel(end, true, END_LABEL);
     checkUnsignedShort(index, INVALID_LOCAL_VARIABLE_INDEX);
@@ -1099,7 +1076,8 @@
         || value == Opcodes.NULL
         || value == Opcodes.UNINITIALIZED_THIS) {
       return;
-    } else if (value instanceof String) {
+    }
+    if (value instanceof String) {
       checkInternalName(version, (String) value, "Invalid stack frame value");
     } else if (value instanceof Label) {
       referencedLabels.add((Label) value);
@@ -1339,10 +1317,10 @@
       int startIndex = 0;
       int slashIndex;
       while ((slashIndex = name.indexOf('/', startIndex + 1)) != -1) {
-        CheckMethodAdapter.checkIdentifier(version, name, startIndex, slashIndex, null);
+        checkIdentifier(version, name, startIndex, slashIndex, null);
         startIndex = slashIndex + 1;
       }
-      CheckMethodAdapter.checkIdentifier(version, name, startIndex, name.length(), null);
+      checkIdentifier(version, name, startIndex, name.length(), null);
     } catch (IllegalArgumentException e) {
       throw new IllegalArgumentException(
           INVALID + message + " (must be an internal class name): " + name, e);
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
old mode 100755
new mode 100644
index 0d6d28d..c8be41b
--- 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
@@ -71,7 +71,7 @@
    * @throws IllegalStateException If a subclass calls this constructor.
    */
   public CheckModuleAdapter(final ModuleVisitor moduleVisitor, final boolean isOpen) {
-    this(Opcodes.ASM7, moduleVisitor, isOpen);
+    this(/* latest api = */ Opcodes.ASM8, moduleVisitor, isOpen);
     if (getClass() != CheckModuleAdapter.class) {
       throw new IllegalStateException();
     }
@@ -81,7 +81,8 @@
    * Constructs a new {@link CheckModuleAdapter}.
    *
    * @param api the ASM API version implemented by this visitor. Must be one of {@link
-   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
+   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7} or {@link
+   *     Opcodes#ASM8}.
    * @param moduleVisitor the module visitor to which this adapter must delegate calls.
    * @param isOpen whether the visited module is open. Open modules have their {@link
    *     Opcodes#ACC_OPEN} access flag set in {@link org.apache.tapestry5.internal.plastic.asm.ClassVisitor#visitModule}.
@@ -200,12 +201,12 @@
 
     NameSet(final String type) {
       this.type = type;
-      this.names = new HashSet<String>();
+      this.names = new HashSet<>();
     }
 
     void checkNameNotAlreadyDeclared(final String name) {
       if (!names.add(name)) {
-        throw new IllegalArgumentException(type + " " + name + " already declared");
+        throw new IllegalArgumentException(type + " '" + name + "' already declared");
       }
     }
   }
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckRecordComponentAdapter.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckRecordComponentAdapter.java
new file mode 100644
index 0000000..7cb7507
--- /dev/null
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckRecordComponentAdapter.java
@@ -0,0 +1,120 @@
+// ASM: a very small and fast Java bytecode manipulation framework
+// Copyright (c) 2000-2011 INRIA, France Telecom
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// 1. Redistributions of source code must retain the above copyright
+//    notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+//    notice, this list of conditions and the following disclaimer in the
+//    documentation and/or other materials provided with the distribution.
+// 3. Neither the name of the copyright holders nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+// THE POSSIBILITY OF SUCH DAMAGE.
+package org.apache.tapestry5.internal.plastic.asm.util;
+
+import org.apache.tapestry5.internal.plastic.asm.AnnotationVisitor;
+import org.apache.tapestry5.internal.plastic.asm.Attribute;
+import org.apache.tapestry5.internal.plastic.asm.Opcodes;
+import org.apache.tapestry5.internal.plastic.asm.RecordComponentVisitor;
+import org.apache.tapestry5.internal.plastic.asm.TypePath;
+import org.apache.tapestry5.internal.plastic.asm.TypeReference;
+
+/**
+ * A {@link RecordComponentVisitor} that checks that its methods are properly used.
+ *
+ * @author Eric Bruneton
+ * @author Remi Forax
+ */
+public class CheckRecordComponentAdapter extends RecordComponentVisitor {
+
+  /** Whether the {@link #visitEnd()} method has been called. */
+  private boolean visitEndCalled;
+
+  /**
+   * Constructs a new {@link CheckRecordComponentAdapter}. <i>Subclasses must not use this
+   * constructor</i>. Instead, they must use the {@link #CheckRecordComponentAdapter(int,
+   * RecordComponentVisitor)} version.
+   *
+   * @param recordComponentVisitor the record component visitor to which this adapter must delegate
+   *     calls.
+   * @throws IllegalStateException If a subclass calls this constructor.
+   */
+  public CheckRecordComponentAdapter(final RecordComponentVisitor recordComponentVisitor) {
+    this(/* latest api =*/ Opcodes.ASM8, recordComponentVisitor);
+    if (getClass() != CheckRecordComponentAdapter.class) {
+      throw new IllegalStateException();
+    }
+  }
+
+  /**
+   * Constructs a new {@link CheckRecordComponentAdapter}.
+   *
+   * @param api the ASM API version implemented by this visitor. Must be {@link Opcodes#ASM8}.
+   * @param recordComponentVisitor the record component visitor to which this adapter must delegate
+   *     calls.
+   */
+  protected CheckRecordComponentAdapter(
+      final int api, final RecordComponentVisitor recordComponentVisitor) {
+    super(api, recordComponentVisitor);
+  }
+
+  @Override
+  public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
+    checkVisitEndNotCalled();
+    // Annotations can only appear in V1_5 or more classes.
+    CheckMethodAdapter.checkDescriptor(Opcodes.V1_5, descriptor, false);
+    return new CheckAnnotationAdapter(super.visitAnnotation(descriptor, visible));
+  }
+
+  @Override
+  public AnnotationVisitor visitTypeAnnotation(
+      final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
+    checkVisitEndNotCalled();
+    int sort = new TypeReference(typeRef).getSort();
+    if (sort != TypeReference.FIELD) {
+      throw new IllegalArgumentException(
+          "Invalid type reference sort 0x" + Integer.toHexString(sort));
+    }
+    CheckClassAdapter.checkTypeRef(typeRef);
+    CheckMethodAdapter.checkDescriptor(Opcodes.V1_5, descriptor, false);
+    return new CheckAnnotationAdapter(
+        super.visitTypeAnnotation(typeRef, typePath, descriptor, visible));
+  }
+
+  @Override
+  public void visitAttribute(final Attribute attribute) {
+    checkVisitEndNotCalled();
+    if (attribute == null) {
+      throw new IllegalArgumentException("Invalid attribute (must not be null)");
+    }
+    super.visitAttribute(attribute);
+  }
+
+  @Override
+  public void visitEnd() {
+    checkVisitEndNotCalled();
+    visitEndCalled = true;
+    super.visitEnd();
+  }
+
+  private void checkVisitEndNotCalled() {
+    if (visitEndCalled) {
+      throw new IllegalStateException("Cannot call a visit method after visitEnd has been called");
+    }
+  }
+}
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
old mode 100755
new mode 100644
index 8103a10..027e28c
--- 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
@@ -123,14 +123,15 @@
    *     null}.
    */
   public CheckSignatureAdapter(final int type, final SignatureVisitor signatureVisitor) {
-    this(Opcodes.ASM7, type, signatureVisitor);
+    this(/* latest api = */ Opcodes.ASM8, type, signatureVisitor);
   }
 
   /**
    * Constructs a new {@link CheckSignatureAdapter}.
    *
    * @param api the ASM API version implemented by this visitor. Must be one of {@link
-   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
+   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7} or {@link
+   *     Opcodes#ASM8}.
    * @param type the type of signature to be checked. See {@link #CLASS_SIGNATURE}, {@link
    *     #METHOD_SIGNATURE} and {@link #TYPE_SIGNATURE}.
    * @param signatureVisitor the visitor to which this adapter must delegate calls. May be {@literal
@@ -171,7 +172,7 @@
   @Override
   public SignatureVisitor visitInterfaceBound() {
     if (type == TYPE_SIGNATURE || !VISIT_INTERFACE_BOUND_STATES.contains(state)) {
-      throw new IllegalArgumentException();
+      throw new IllegalStateException();
     }
     return new CheckSignatureAdapter(
         TYPE_SIGNATURE, signatureVisitor == null ? null : signatureVisitor.visitInterfaceBound());
@@ -182,7 +183,7 @@
   @Override
   public SignatureVisitor visitSuperclass() {
     if (type != CLASS_SIGNATURE || !VISIT_SUPER_CLASS_STATES.contains(state)) {
-      throw new IllegalArgumentException();
+      throw new IllegalStateException();
     }
     state = State.SUPER;
     return new CheckSignatureAdapter(
@@ -203,7 +204,7 @@
   @Override
   public SignatureVisitor visitParameterType() {
     if (type != METHOD_SIGNATURE || !VISIT_PARAMETER_TYPE_STATES.contains(state)) {
-      throw new IllegalArgumentException();
+      throw new IllegalStateException();
     }
     state = State.PARAM;
     return new CheckSignatureAdapter(
@@ -213,7 +214,7 @@
   @Override
   public SignatureVisitor visitReturnType() {
     if (type != METHOD_SIGNATURE || !VISIT_RETURN_TYPE_STATES.contains(state)) {
-      throw new IllegalArgumentException();
+      throw new IllegalStateException();
     }
     state = State.RETURN;
     CheckSignatureAdapter checkSignatureAdapter =
@@ -241,11 +242,11 @@
     }
     if (descriptor == 'V') {
       if (!canBeVoid) {
-        throw new IllegalArgumentException();
+        throw new IllegalArgumentException("Base type descriptor can't be V");
       }
     } else {
       if ("ZCBSIFJD".indexOf(descriptor) == -1) {
-        throw new IllegalArgumentException();
+        throw new IllegalArgumentException("Base type descriptor must be one of ZCBSIFJD");
       }
     }
     state = State.SIMPLE_TYPE;
@@ -315,7 +316,7 @@
       throw new IllegalStateException();
     }
     if ("+-=".indexOf(wildcard) == -1) {
-      throw new IllegalArgumentException();
+      throw new IllegalArgumentException("Wildcard must be one of +-=");
     }
     return new CheckSignatureAdapter(
         TYPE_SIGNATURE,
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
old mode 100755
new mode 100644
index 1dbdbbd..599f86f
--- 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
@@ -41,6 +41,7 @@
 import org.apache.tapestry5.internal.plastic.asm.Opcodes;
 import org.apache.tapestry5.internal.plastic.asm.Type;
 import org.apache.tapestry5.internal.plastic.asm.TypePath;
+import org.apache.tapestry5.internal.plastic.asm.TypeReference;
 
 /**
  * An abstract converter from visit events to text.
@@ -295,13 +296,6 @@
    */
   protected final int api;
 
-  /**
-   * A buffer that can be used to create strings.
-   *
-   * @deprecated use {@link #stringBuilder} instead.
-   */
-  @Deprecated protected final StringBuffer buf;
-
   /** The builder used to build strings in the various visit methods. */
   protected final StringBuilder stringBuilder;
 
@@ -329,9 +323,8 @@
    */
   protected Printer(final int api) {
     this.api = api;
-    this.buf = null;
     this.stringBuilder = new StringBuilder();
-    this.text = new ArrayList<Object>();
+    this.text = new ArrayList<>();
   }
 
   // -----------------------------------------------------------------------------------------------
@@ -461,6 +454,22 @@
   }
 
   /**
+   * <b>Experimental, use at your own risk. This method will be renamed when it becomes stable, this
+   * will break existing code using it</b>.
+   *
+   * <p>Visits a permitted subtypes. A permitted subtypes is one of the allowed subtypes of the
+   * current class. See {@link
+   * org.apache.tapestry5.internal.plastic.asm.ClassVisitor#visitPermittedSubtypeExperimental(String)}.
+   *
+   * @param permittedSubtype the internal name of a permitted subtype.
+   * @deprecated this API is experimental.
+   */
+  @Deprecated
+  public void visitPermittedSubtypeExperimental(final String permittedSubtype) {
+    throw new UnsupportedOperationException(UNSUPPORTED_OPERATION);
+  }
+
+  /**
    * Class inner name. See {@link org.apache.tapestry5.internal.plastic.asm.ClassVisitor#visitInnerClass}.
    *
    * @param name the internal name of an inner class (see {@link
@@ -475,6 +484,22 @@
   public abstract void visitInnerClass(String name, String outerName, String innerName, int access);
 
   /**
+   * Visits a record component of the class. See {@link
+   * org.apache.tapestry5.internal.plastic.asm.ClassVisitor#visitRecordComponent(String, String, String)}.
+   *
+   * @param name the field's name.
+   * @param descriptor the record component descriptor (see {@link Type}).
+   * @param signature the record component signature. May be {@literal null} if the record component
+   *     type does not use generic types.
+   * @return a visitor to visit this record component annotations and attributes, or {@literal null}
+   *     if this class visitor is not interested in visiting these annotations and attributes.
+   */
+  public Printer visitRecordComponent(
+      final String name, final String descriptor, final String signature) {
+    throw new UnsupportedOperationException(UNSUPPORTED_OPERATION);
+  }
+
+  /**
    * Class field. See {@link org.apache.tapestry5.internal.plastic.asm.ClassVisitor#visitField}.
    *
    * @param access the field's access flags (see {@link Opcodes}). This parameter also indicates if
@@ -646,6 +671,63 @@
   public abstract void visitAnnotationEnd();
 
   // -----------------------------------------------------------------------------------------------
+  // Record components
+  // -----------------------------------------------------------------------------------------------
+
+  /**
+   * Visits an annotation of the record component. See {@link
+   * org.apache.tapestry5.internal.plastic.asm.RecordComponentVisitor#visitAnnotation}.
+   *
+   * @param descriptor the class descriptor of the annotation class.
+   * @param visible {@literal true} if the annotation is visible at runtime.
+   * @return a visitor to visit the annotation values, or {@literal null} if this visitor is not
+   *     interested in visiting this annotation.
+   */
+  public Printer visitRecordComponentAnnotation(final String descriptor, final boolean visible) {
+    throw new UnsupportedOperationException(UNSUPPORTED_OPERATION);
+  }
+
+  /**
+   * Visits an annotation on a type in the record component signature. See {@link
+   * org.apache.tapestry5.internal.plastic.asm.RecordComponentVisitor#visitTypeAnnotation}.
+   *
+   * @param typeRef a reference to the annotated type. The sort of this type reference must be
+   *     {@link TypeReference#CLASS_TYPE_PARAMETER}, {@link
+   *     TypeReference#CLASS_TYPE_PARAMETER_BOUND} or {@link TypeReference#CLASS_EXTENDS}. See
+   *     {@link TypeReference}.
+   * @param typePath the path to the annotated type argument, wildcard bound, array element type, or
+   *     static inner type within 'typeRef'. May be {@literal null} if the annotation targets
+   *     'typeRef' as a whole.
+   * @param descriptor the class descriptor of the annotation class.
+   * @param visible {@literal true} if the annotation is visible at runtime.
+   * @return a visitor to visit the annotation values, or {@literal null} if this visitor is not
+   *     interested in visiting this annotation.
+   */
+  public Printer visitRecordComponentTypeAnnotation(
+      final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
+    throw new UnsupportedOperationException(UNSUPPORTED_OPERATION);
+  }
+
+  /**
+   * Visits a non standard attribute of the record component. See {@link
+   * org.apache.tapestry5.internal.plastic.asm.RecordComponentVisitor#visitAttribute}.
+   *
+   * @param attribute an attribute.
+   */
+  public void visitRecordComponentAttribute(final Attribute attribute) {
+    throw new UnsupportedOperationException(UNSUPPORTED_OPERATION);
+  }
+
+  /**
+   * Visits the end of the record component. See {@link
+   * org.apache.tapestry5.internal.plastic.asm.RecordComponentVisitor#visitEnd}. This method, which is the last one to be
+   * called, is used to inform the visitor that everything have been visited.
+   */
+  public void visitRecordComponentEnd() {
+    throw new UnsupportedOperationException(UNSUPPORTED_OPERATION);
+  }
+
+  // -----------------------------------------------------------------------------------------------
   // Fields
   // -----------------------------------------------------------------------------------------------
 
@@ -692,7 +774,7 @@
   /**
    * Method parameter. See {@link org.apache.tapestry5.internal.plastic.asm.MethodVisitor#visitParameter(String, int)}.
    *
-   * @param name parameter name or null if none is provided.
+   * @param name parameter name or {@literal null} if none is provided.
    * @param access the parameter's access flags, only {@code ACC_FINAL}, {@code ACC_SYNTHETIC}
    *     or/and {@code ACC_MANDATED} are allowed (see {@link Opcodes}).
    */
@@ -882,12 +964,10 @@
   @Deprecated
   public void visitMethodInsn(
       final int opcode, final String owner, final String name, final String descriptor) {
-    if (api >= Opcodes.ASM5) {
-      boolean isInterface = opcode == Opcodes.INVOKEINTERFACE;
-      visitMethodInsn(opcode, owner, name, descriptor, isInterface);
-      return;
-    }
-    throw new UnsupportedOperationException(UNSUPPORTED_OPERATION);
+    // This method was abstract before ASM5, and was therefore always overridden (without any
+    // call to 'super'). Thus, at this point we necessarily have api >= ASM5, and we must then
+    // redirect the method call to the ASM5 visitMethodInsn() method.
+    visitMethodInsn(opcode, owner, name, descriptor, opcode == Opcodes.INVOKEINTERFACE);
   }
 
   /**
@@ -907,13 +987,6 @@
       final String name,
       final String descriptor,
       final boolean isInterface) {
-    if (api < Opcodes.ASM5) {
-      if (isInterface != (opcode == Opcodes.INVOKEINTERFACE)) {
-        throw new IllegalArgumentException("INVOKESPECIAL/STATIC on interfaces require ASM 5");
-      }
-      visitMethodInsn(opcode, owner, name, descriptor);
-      return;
-    }
     throw new UnsupportedOperationException(UNSUPPORTED_OPERATION);
   }
 
@@ -1164,20 +1237,6 @@
   }
 
   /**
-   * Appends a quoted string to the given string buffer.
-   *
-   * @param stringBuffer the buffer where the string must be added.
-   * @param string the string to be added.
-   * @deprecated use {@link #appendString(StringBuilder, String)} instead.
-   */
-  @Deprecated
-  public static void appendString(final StringBuffer stringBuffer, final String string) {
-    StringBuilder stringBuilder = new StringBuilder();
-    appendString(stringBuilder, string);
-    stringBuffer.append(stringBuilder.toString());
-  }
-
-  /**
    * Appends a quoted string to the given string builder.
    *
    * @param stringBuilder the buffer where the string must be added.
@@ -1213,24 +1272,30 @@
   }
 
   /**
-   * Prints a the given class to the standard output.
+   * Prints a the given class to the given output.
    *
    * <p>Command line arguments: [-debug] &lt;binary class name or class file name &gt;
    *
+   * @param args the command line arguments.
    * @param usage the help message to show when command line arguments are incorrect.
    * @param printer the printer to convert the class into text.
-   * @param args the command line arguments.
+   * @param output where to print the result.
+   * @param logger where to log errors.
    * @throws IOException if the class cannot be found, or if an IOException occurs.
    */
-  static void main(final String usage, final Printer printer, final String[] args)
+  static void main(
+      final String[] args,
+      final String usage,
+      final Printer printer,
+      final PrintWriter output,
+      final PrintWriter logger)
       throws IOException {
     if (args.length < 1 || args.length > 2 || (args[0].equals("-debug") && args.length != 2)) {
-      System.err.println(usage);
+      logger.println(usage);
       return;
     }
 
-    TraceClassVisitor traceClassVisitor =
-        new TraceClassVisitor(null, printer, new PrintWriter(System.out));
+    TraceClassVisitor traceClassVisitor = new TraceClassVisitor(null, printer, output);
 
     String className;
     int parsingOptions;
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
old mode 100755
new mode 100644
index 70a974d..7f0b534
--- 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
@@ -28,7 +28,11 @@
 package org.apache.tapestry5.internal.plastic.asm.util;
 
 import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Arrays;
+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.Handle;
@@ -46,6 +50,11 @@
  */
 public class Textifier extends Printer {
 
+  /** The help message shown when command line arguments are incorrect. */
+  private static final String USAGE =
+      "Prints a disassembled view of the given class.\n"
+          + "Usage: Textifier [-debug] <fully qualified class name or class file name>";
+
   /** The type of internal names. See {@link #appendDescriptor}. */
   public static final int INTERNAL_NAME = 0;
 
@@ -64,34 +73,17 @@
   /** The type of class signatures. See {@link #appendDescriptor}. */
   public static final int CLASS_SIGNATURE = 5;
 
-  /**
-   * Deprecated.
-   *
-   * @deprecated this constant has never been used.
-   */
-  @Deprecated public static final int TYPE_DECLARATION = 6;
-
-  /**
-   * Deprecated.
-   *
-   * @deprecated this constant has never been used.
-   */
-  @Deprecated public static final int CLASS_DECLARATION = 7;
-
-  /**
-   * Deprecated.
-   *
-   * @deprecated this constant has never been used.
-   */
-  @Deprecated public static final int PARAMETERS_DECLARATION = 8;
-
   /** The type of method handle descriptors. See {@link #appendDescriptor}. */
   public static final int HANDLE_DESCRIPTOR = 9;
 
   private static final String CLASS_SUFFIX = ".class";
   private static final String DEPRECATED = "// DEPRECATED\n";
+  private static final String RECORD = "// RECORD\n";
   private static final String INVISIBLE = " // invisible\n";
 
+  private static final List<String> FRAME_TYPES =
+      Collections.unmodifiableList(Arrays.asList("T", "I", "F", "D", "J", "N", "U"));
+
   /** The indentation of class members at depth level 1 (e.g. fields, methods). */
   protected String tab = "  ";
 
@@ -120,7 +112,7 @@
    * @throws IllegalStateException If a subclass calls this constructor.
    */
   public Textifier() {
-    this(Opcodes.ASM7);
+    this(/* latest api = */ Opcodes.ASM8);
     if (getClass() != Textifier.class) {
       throw new IllegalStateException();
     }
@@ -130,7 +122,8 @@
    * Constructs a new {@link Textifier}.
    *
    * @param api the ASM API version implemented by this visitor. Must be one of {@link
-   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
+   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7} or {@link
+   *     Opcodes#ASM8}.
    */
   protected Textifier(final int api) {
     super(api);
@@ -145,10 +138,22 @@
    * @throws IOException if the class cannot be found, or if an IOException occurs.
    */
   public static void main(final String[] args) throws IOException {
-    String usage =
-        "Prints a disassembled view of the given class.\n"
-            + "Usage: Textifier [-debug] <fully qualified class name or class file name>";
-    main(usage, new Textifier(), args);
+    main(args, new PrintWriter(System.out, true), new PrintWriter(System.err, true));
+  }
+
+  /**
+   * Prints a disassembled view of the given class to the given output.
+   *
+   * <p>Usage: Textifier [-debug] &lt;binary class name or class file name &gt;
+   *
+   * @param args the command line arguments.
+   * @param output where to print the result.
+   * @param logger where to log errors.
+   * @throws IOException if the class cannot be found, or if an IOException occurs.
+   */
+  static void main(final String[] args, final PrintWriter output, final PrintWriter logger)
+      throws IOException {
+    main(args, USAGE, new Textifier(), output, logger);
   }
 
   // -----------------------------------------------------------------------------------------------
@@ -182,6 +187,9 @@
     if ((access & Opcodes.ACC_DEPRECATED) != 0) {
       stringBuilder.append(DEPRECATED);
     }
+    if ((access & Opcodes.ACC_RECORD) != 0) {
+      stringBuilder.append(RECORD);
+    }
     appendRawAccess(access);
 
     appendDescriptor(CLASS_SIGNATURE, signature);
@@ -298,6 +306,22 @@
     text.add(stringBuilder.toString());
   }
 
+  /**
+   * <b>Experimental, use at your own risk.</b>.
+   *
+   * @param permittedSubtype the internal name of a permitted subtype.
+   * @deprecated this API is experimental.
+   */
+  @Override
+  @Deprecated
+  public void visitPermittedSubtypeExperimental(final String permittedSubtype) {
+    stringBuilder.setLength(0);
+    stringBuilder.append(tab).append("PERMITTEDSUBTYPE ");
+    appendDescriptor(INTERNAL_NAME, permittedSubtype);
+    stringBuilder.append('\n');
+    text.add(stringBuilder.toString());
+  }
+
   @Override
   public void visitInnerClass(
       final String name, final String outerName, final String innerName, final int access) {
@@ -317,6 +341,28 @@
   }
 
   @Override
+  public Printer visitRecordComponent(
+      final String name, final String descriptor, final String signature) {
+    stringBuilder.setLength(0);
+    stringBuilder.append(tab).append("RECORDCOMPONENT ");
+    if (signature != null) {
+      stringBuilder.append(tab);
+      appendDescriptor(FIELD_SIGNATURE, signature);
+      stringBuilder.append(tab);
+      appendJavaDeclaration(name, signature);
+    }
+
+    stringBuilder.append(tab);
+
+    appendDescriptor(FIELD_DESCRIPTOR, descriptor);
+    stringBuilder.append(' ').append(name);
+
+    stringBuilder.append('\n');
+    text.add(stringBuilder.toString());
+    return addNewTextifier(null);
+  }
+
+  @Override
   public Textifier visitField(
       final int access,
       final String name,
@@ -451,30 +497,20 @@
   }
 
   @Override
-  public void visitExport(final String export, final int access, final String... modules) {
-    stringBuilder.setLength(0);
-    stringBuilder.append(tab).append("exports ");
-    stringBuilder.append(export);
-    if (modules != null && modules.length > 0) {
-      stringBuilder.append(" to");
-    } else {
-      stringBuilder.append(';');
-    }
-    appendRawAccess(access);
-    if (modules != null && modules.length > 0) {
-      for (int i = 0; i < modules.length; ++i) {
-        stringBuilder.append(tab2).append(modules[i]);
-        stringBuilder.append(i != modules.length - 1 ? ",\n" : ";\n");
-      }
-    }
-    text.add(stringBuilder.toString());
+  public void visitExport(final String packaze, final int access, final String... modules) {
+    visitExportOrOpen("exports ", packaze, access, modules);
   }
 
   @Override
-  public void visitOpen(final String export, final int access, final String... modules) {
+  public void visitOpen(final String packaze, final int access, final String... modules) {
+    visitExportOrOpen("opens ", packaze, access, modules);
+  }
+
+  private void visitExportOrOpen(
+      final String method, final String packaze, final int access, final String... modules) {
     stringBuilder.setLength(0);
-    stringBuilder.append(tab).append("opens ");
-    stringBuilder.append(export);
+    stringBuilder.append(tab).append(method);
+    stringBuilder.append(packaze);
     if (modules != null && modules.length > 0) {
       stringBuilder.append(" to");
     } else {
@@ -682,6 +718,31 @@
   }
 
   // -----------------------------------------------------------------------------------------------
+  // Record components
+  // -----------------------------------------------------------------------------------------------
+
+  @Override
+  public Textifier visitRecordComponentAnnotation(final String descriptor, final boolean visible) {
+    return visitAnnotation(descriptor, visible);
+  }
+
+  @Override
+  public Printer visitRecordComponentTypeAnnotation(
+      final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
+    return visitTypeAnnotation(typeRef, typePath, descriptor, visible);
+  }
+
+  @Override
+  public void visitRecordComponentAttribute(final Attribute attribute) {
+    visitAttribute(attribute);
+  }
+
+  @Override
+  public void visitRecordComponentEnd() {
+    // Nothing to do.
+  }
+
+  // -----------------------------------------------------------------------------------------------
   // Fields
   // -----------------------------------------------------------------------------------------------
 
@@ -765,19 +826,7 @@
 
   @Override
   public void visitMethodAttribute(final Attribute attribute) {
-    stringBuilder.setLength(0);
-    stringBuilder.append(tab).append("ATTRIBUTE ");
-    appendDescriptor(-1, attribute.type);
-
-    if (attribute instanceof Textifiable) {
-      StringBuffer stringBuffer = new StringBuffer();
-      ((Textifiable) attribute).textify(stringBuffer, labelNames);
-      stringBuilder.append(stringBuffer.toString());
-    } else {
-      stringBuilder.append(" : unknown\n");
-    }
-
-    text.add(stringBuilder.toString());
+    visitAttribute(attribute);
   }
 
   @Override
@@ -873,37 +922,8 @@
     text.add(stringBuilder.toString());
   }
 
-  /**
-   * Deprecated.
-   *
-   * @deprecated use {@link #visitMethodInsn(int, String, String, String, boolean)} instead.
-   */
-  @Deprecated
   @Override
   public void visitMethodInsn(
-      final int opcode, final String owner, final String name, final String descriptor) {
-    if (api >= Opcodes.ASM5) {
-      super.visitMethodInsn(opcode, owner, name, descriptor);
-      return;
-    }
-    doVisitMethodInsn(opcode, owner, name, descriptor, opcode == Opcodes.INVOKEINTERFACE);
-  }
-
-  @Override
-  public void visitMethodInsn(
-      final int opcode,
-      final String owner,
-      final String name,
-      final String descriptor,
-      final boolean isInterface) {
-    if (api < Opcodes.ASM5) {
-      super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
-      return;
-    }
-    doVisitMethodInsn(opcode, owner, name, descriptor, isInterface);
-  }
-
-  private void doVisitMethodInsn(
       final int opcode,
       final String owner,
       final String name,
@@ -1230,10 +1250,11 @@
     stringBuilder.append(tab).append("ATTRIBUTE ");
     appendDescriptor(-1, attribute.type);
 
-    if (attribute instanceof Textifiable) {
-      StringBuffer stringBuffer = new StringBuffer();
-      ((Textifiable) attribute).textify(stringBuffer, null);
-      stringBuilder.append(stringBuffer.toString());
+    if (attribute instanceof TextifierSupport) {
+      if (labelNames == null) {
+        labelNames = new HashMap<>();
+      }
+      ((TextifierSupport) attribute).textify(stringBuilder, labelNames);
     } else {
       stringBuilder.append(" : unknown\n");
     }
@@ -1309,8 +1330,7 @@
    *
    * @param type the type of 'value'. Must be one of {@link #INTERNAL_NAME}, {@link
    *     #FIELD_DESCRIPTOR}, {@link #FIELD_SIGNATURE}, {@link #METHOD_DESCRIPTOR}, {@link
-   *     #METHOD_SIGNATURE}, {@link #CLASS_SIGNATURE}, {@link #TYPE_DECLARATION}, {@link
-   *     #CLASS_DECLARATION}, {@link #PARAMETERS_DECLARATION} of {@link #HANDLE_DESCRIPTOR}.
+   *     #METHOD_SIGNATURE}, {@link #CLASS_SIGNATURE} or {@link #HANDLE_DESCRIPTOR}.
    * @param value an internal name, type descriptor or a type signature. May be {@literal null}.
    */
   protected void appendDescriptor(final int type, final String value) {
@@ -1353,7 +1373,7 @@
    */
   protected void appendLabel(final Label label) {
     if (labelNames == null) {
-      labelNames = new HashMap<Label, String>();
+      labelNames = new HashMap<>();
     }
     String name = labelNames.get(label);
     if (name == null) {
@@ -1556,31 +1576,7 @@
           appendDescriptor(INTERNAL_NAME, descriptor);
         }
       } else if (frameTypes[i] instanceof Integer) {
-        switch (((Integer) frameTypes[i]).intValue()) {
-          case 0:
-            appendDescriptor(FIELD_DESCRIPTOR, "T");
-            break;
-          case 1:
-            appendDescriptor(FIELD_DESCRIPTOR, "I");
-            break;
-          case 2:
-            appendDescriptor(FIELD_DESCRIPTOR, "F");
-            break;
-          case 3:
-            appendDescriptor(FIELD_DESCRIPTOR, "D");
-            break;
-          case 4:
-            appendDescriptor(FIELD_DESCRIPTOR, "J");
-            break;
-          case 5:
-            appendDescriptor(FIELD_DESCRIPTOR, "N");
-            break;
-          case 6:
-            appendDescriptor(FIELD_DESCRIPTOR, "U");
-            break;
-          default:
-            throw new IllegalArgumentException();
-        }
+        stringBuilder.append(FRAME_TYPES.get(((Integer) frameTypes[i]).intValue()));
       } else {
         appendLabel((Label) frameTypes[i]);
       }
@@ -1608,6 +1604,6 @@
    * @return a new {@link Textifier}.
    */
   protected Textifier createTextifier() {
-    return new Textifier();
+    return new Textifier(api);
   }
 }
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
new file mode 100644
index 0000000..457c935
--- /dev/null
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TextifierSupport.java
@@ -0,0 +1,41 @@
+/**
+ * ASM: a very small and fast Java bytecode manipulation framework Copyright (c) 2000-2011 INRIA,
+ * France Telecom All rights reserved.
+ *
+ * <p>Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met: 1. Redistributions of source code must retain the
+ * above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions
+ * in binary form must reproduce the above copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its contributors may be used to
+ * endorse or promote products derived from this software without specific prior written permission.
+ *
+ * <p>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.apache.tapestry5.internal.plastic.asm.util;
+
+import java.util.Map;
+import org.apache.tapestry5.internal.plastic.asm.Label;
+
+/**
+ * An {@link org.apache.tapestry5.internal.plastic.asm.Attribute} that can print a readable representation of itself.
+ *
+ * @author Eugene Kuleshov
+ */
+public interface TextifierSupport {
+
+  /**
+   * Generates a human readable representation of this attribute.
+   *
+   * @param outputBuilder where the human representation of this attribute must be appended.
+   * @param labelNames the human readable names of the labels.
+   */
+  void textify(StringBuilder outputBuilder, Map<Label, String> labelNames);
+}
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceAnnotationVisitor.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceAnnotationVisitor.java
old mode 100755
new mode 100644
index e991756..e3cccb3
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceAnnotationVisitor.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceAnnotationVisitor.java
@@ -57,7 +57,7 @@
    * @param printer the printer to convert the visited annotation into text.
    */
   public TraceAnnotationVisitor(final AnnotationVisitor annotationVisitor, final Printer printer) {
-    super(Opcodes.ASM7, annotationVisitor);
+    super(/* latest api = */ Opcodes.ASM8, annotationVisitor);
     this.printer = printer;
   }
 
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
old mode 100755
new mode 100644
index 4dc095a..1b2e706
--- 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
@@ -35,6 +35,7 @@
 import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
 import org.apache.tapestry5.internal.plastic.asm.ModuleVisitor;
 import org.apache.tapestry5.internal.plastic.asm.Opcodes;
+import org.apache.tapestry5.internal.plastic.asm.RecordComponentVisitor;
 import org.apache.tapestry5.internal.plastic.asm.TypePath;
 
 /**
@@ -118,7 +119,7 @@
    */
   public TraceClassVisitor(
       final ClassVisitor classVisitor, final Printer printer, final PrintWriter printWriter) {
-    super(Opcodes.ASM7, classVisitor);
+    super(/* latest api = */ Opcodes.ASM9_EXPERIMENTAL, classVisitor);
     this.printWriter = printWriter;
     this.p = printer;
   }
@@ -186,6 +187,19 @@
     super.visitNestMember(nestMember);
   }
 
+  /**
+   * <b>Experimental, use at your own risk.</b>.
+   *
+   * @param permittedSubtype the internal name of a permitted subtype.
+   * @deprecated this API is experimental.
+   */
+  @Override
+  @Deprecated
+  public void visitPermittedSubtypeExperimental(final String permittedSubtype) {
+    p.visitPermittedSubtypeExperimental(permittedSubtype);
+    super.visitPermittedSubtypeExperimental(permittedSubtype);
+  }
+
   @Override
   public void visitInnerClass(
       final String name, final String outerName, final String innerName, final int access) {
@@ -194,6 +208,14 @@
   }
 
   @Override
+  public RecordComponentVisitor visitRecordComponent(
+      final String name, final String descriptor, final String signature) {
+    Printer recordComponentPrinter = p.visitRecordComponent(name, descriptor, signature);
+    return new TraceRecordComponentVisitor(
+        super.visitRecordComponent(name, descriptor, signature), recordComponentPrinter);
+  }
+
+  @Override
   public FieldVisitor visitField(
       final int access,
       final String name,
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceFieldVisitor.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceFieldVisitor.java
old mode 100755
new mode 100644
index c01176c..1ccd3b7
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceFieldVisitor.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceFieldVisitor.java
@@ -60,7 +60,7 @@
    * @param printer the printer to convert the visited field into text.
    */
   public TraceFieldVisitor(final FieldVisitor fieldVisitor, final Printer printer) {
-    super(Opcodes.ASM7, fieldVisitor);
+    super(/* latest api = */ Opcodes.ASM8, fieldVisitor);
     this.p = printer;
   }
 
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceMethodVisitor.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceMethodVisitor.java
old mode 100755
new mode 100644
index b152c48..59b8687
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceMethodVisitor.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceMethodVisitor.java
@@ -62,7 +62,7 @@
    * @param printer the printer to convert the visited method into text.
    */
   public TraceMethodVisitor(final MethodVisitor methodVisitor, final Printer printer) {
-    super(Opcodes.ASM7, methodVisitor);
+    super(/* latest api = */ Opcodes.ASM8, methodVisitor);
     this.p = printer;
   }
 
@@ -161,37 +161,27 @@
     super.visitFieldInsn(opcode, owner, name, descriptor);
   }
 
-  /**
-   * Deprecated.
-   *
-   * @deprecated use {@link #visitMethodInsn(int, String, String, String, boolean)} instead.
-   */
-  @Deprecated
   @Override
-  public void visitMethodInsn(
-      final int opcode, final String owner, final String name, final String descriptor) {
-    if (api >= Opcodes.ASM5) {
-      super.visitMethodInsn(opcode, owner, name, descriptor);
-      return;
-    }
-    p.visitMethodInsn(opcode, owner, name, descriptor);
-    if (mv != null) {
-      mv.visitMethodInsn(opcode, owner, name, descriptor);
-    }
-  }
-
-  @Override
+  @SuppressWarnings("deprecation")
   public void visitMethodInsn(
       final int opcode,
       final String owner,
       final String name,
       final String descriptor,
       final boolean isInterface) {
-    if (api < Opcodes.ASM5) {
-      super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
-      return;
+    // Call the method that p is supposed to implement, depending on its api version.
+    if (p.api < Opcodes.ASM5) {
+      if (isInterface != (opcode == Opcodes.INVOKEINTERFACE)) {
+        throw new IllegalArgumentException("INVOKESPECIAL/STATIC on interfaces require ASM5");
+      }
+      // If p is an ASMifier (resp. Textifier), or a subclass that does not override the old
+      // visitMethodInsn method, the default implementation in Printer will redirect this to the
+      // new method in ASMifier (resp. Textifier). In all other cases, p overrides the old method
+      // and this call executes it.
+      p.visitMethodInsn(opcode, owner, name, descriptor);
+    } else {
+      p.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
     }
-    p.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
     if (mv != null) {
       mv.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
     }
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceModuleVisitor.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceModuleVisitor.java
old mode 100755
new mode 100644
index 610ff75..a524a66
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceModuleVisitor.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceModuleVisitor.java
@@ -57,7 +57,7 @@
    * @param printer the printer to convert the visited module into text.
    */
   public TraceModuleVisitor(final ModuleVisitor moduleVisitor, final Printer printer) {
-    super(Opcodes.ASM7, moduleVisitor);
+    super(/* latest api = */ Opcodes.ASM8, moduleVisitor);
     this.p = printer;
   }
 
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceRecordComponentVisitor.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceRecordComponentVisitor.java
new file mode 100644
index 0000000..4ae7dba
--- /dev/null
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceRecordComponentVisitor.java
@@ -0,0 +1,96 @@
+// ASM: a very small and fast Java bytecode manipulation framework
+// Copyright (c) 2000-2011 INRIA, France Telecom
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// 1. Redistributions of source code must retain the above copyright
+//    notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+//    notice, this list of conditions and the following disclaimer in the
+//    documentation and/or other materials provided with the distribution.
+// 3. Neither the name of the copyright holders nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+// THE POSSIBILITY OF SUCH DAMAGE.
+package org.apache.tapestry5.internal.plastic.asm.util;
+
+import org.apache.tapestry5.internal.plastic.asm.AnnotationVisitor;
+import org.apache.tapestry5.internal.plastic.asm.Attribute;
+import org.apache.tapestry5.internal.plastic.asm.Opcodes;
+import org.apache.tapestry5.internal.plastic.asm.RecordComponentVisitor;
+import org.apache.tapestry5.internal.plastic.asm.TypePath;
+
+/**
+ * A {@link RecordComponentVisitor} that prints the record components it visits with a {@link
+ * Printer}.
+ *
+ * @author Remi Forax
+ */
+public final class TraceRecordComponentVisitor extends RecordComponentVisitor {
+
+  /** The printer to convert the visited record component into text. */
+  public final Printer printer;
+
+  /**
+   * Constructs a new {@link TraceRecordComponentVisitor}.
+   *
+   * @param printer the printer to convert the visited record component into text.
+   */
+  public TraceRecordComponentVisitor(final Printer printer) {
+    this(null, printer);
+  }
+
+  /**
+   * Constructs a new {@link TraceRecordComponentVisitor}.
+   *
+   * @param recordComponentVisitor the record component visitor to which to delegate calls. May be
+   *     {@literal null}.
+   * @param printer the printer to convert the visited record component into text.
+   */
+  public TraceRecordComponentVisitor(
+      final RecordComponentVisitor recordComponentVisitor, final Printer printer) {
+    super(/* latest api ='*/ Opcodes.ASM8, recordComponentVisitor);
+    this.printer = printer;
+  }
+
+  @Override
+  public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
+    Printer annotationPrinter = printer.visitRecordComponentAnnotation(descriptor, visible);
+    return new TraceAnnotationVisitor(
+        super.visitAnnotation(descriptor, visible), annotationPrinter);
+  }
+
+  @Override
+  public AnnotationVisitor visitTypeAnnotation(
+      final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
+    Printer annotationPrinter =
+        printer.visitRecordComponentTypeAnnotation(typeRef, typePath, descriptor, visible);
+    return new TraceAnnotationVisitor(
+        super.visitTypeAnnotation(typeRef, typePath, descriptor, visible), annotationPrinter);
+  }
+
+  @Override
+  public void visitAttribute(final Attribute attribute) {
+    printer.visitRecordComponentAttribute(attribute);
+    super.visitAttribute(attribute);
+  }
+
+  @Override
+  public void visitEnd() {
+    printer.visitRecordComponentEnd();
+    super.visitEnd();
+  }
+}
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
old mode 100755
new mode 100644
index fe1908a..6006673
--- 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
@@ -27,6 +27,9 @@
 // THE POSSIBILITY OF SUCH DAMAGE.
 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;
 
@@ -43,6 +46,22 @@
   private static final String EXTENDS_SEPARATOR = " extends ";
   private static final String IMPLEMENTS_SEPARATOR = " implements ";
 
+  private static final Map<Character, String> BASE_TYPES;
+
+  static {
+    HashMap<Character, String> baseTypes = new HashMap<>();
+    baseTypes.put('Z', "boolean");
+    baseTypes.put('B', "byte");
+    baseTypes.put('C', "char");
+    baseTypes.put('S', "short");
+    baseTypes.put('I', "int");
+    baseTypes.put('J', "long");
+    baseTypes.put('F', "float");
+    baseTypes.put('D', "double");
+    baseTypes.put('V', "void");
+    BASE_TYPES = Collections.unmodifiableMap(baseTypes);
+  }
+
   /** Whether the visited signature is a class signature of a Java interface. */
   private final boolean isInterface;
 
@@ -90,13 +109,13 @@
    * @param accessFlags for class type signatures, the access flags of the class.
    */
   public TraceSignatureVisitor(final int accessFlags) {
-    super(Opcodes.ASM7);
+    super(/* latest api = */ Opcodes.ASM8);
     this.isInterface = (accessFlags & Opcodes.ACC_INTERFACE) != 0;
     this.declaration = new StringBuilder();
   }
 
   private TraceSignatureVisitor(final StringBuilder stringBuilder) {
-    super(Opcodes.ASM7);
+    super(/* latest api = */ Opcodes.ASM8);
     this.isInterface = false;
     this.declaration = stringBuilder;
   }
@@ -181,37 +200,11 @@
 
   @Override
   public void visitBaseType(final char descriptor) {
-    switch (descriptor) {
-      case 'V':
-        declaration.append("void");
-        break;
-      case 'B':
-        declaration.append("byte");
-        break;
-      case 'J':
-        declaration.append("long");
-        break;
-      case 'Z':
-        declaration.append("boolean");
-        break;
-      case 'I':
-        declaration.append("int");
-        break;
-      case 'S':
-        declaration.append("short");
-        break;
-      case 'C':
-        declaration.append("char");
-        break;
-      case 'F':
-        declaration.append("float");
-        break;
-      case 'D':
-        declaration.append("double");
-        break;
-      default:
-        throw new IllegalArgumentException();
+    String baseType = BASE_TYPES.get(descriptor);
+    if (baseType == null) {
+      throw new IllegalArgumentException();
     }
+    declaration.append(baseType);
     endType();
   }
 
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/package.html b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/package.html
old mode 100755
new mode 100644
index 91fb0db..d2fed34
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/package.html
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/package.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
 <!--
  * ASM: a very small and fast Java bytecode manipulation framework
  * Copyright (c) 2000-2011 INRIA, France Telecom
@@ -28,6 +29,9 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGE.
 -->
+<head>
+  <title>Package org.objectweb.asm.util</title>
+</head>
 <body>
 Provides ASM visitors that can be useful for programming and
 debugging purposes. These class visitors are normally not used by applications