Repository: quarkusio/quarkus
Issue: org.objectweb.asm.ClassTooLargeException when building a project with 2K+ Hibernate entities (hbm.xmls).

### Describe the bug

When we are trying to build our project with 2K+ Hibernate Entities (hbm.xmls) we are getting below exception.

```
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  57.961 s
[INFO] Finished at: 2022-01-12T16:57:23+05:30
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal io.quarkus:quarkus-maven-plugin:999-SNAPSHOT:build (quarkus-final-build-before-package) on project dna-wireless-service: Failed to build quarkus application: io.quarkus.builder.BuildException: Build failure: Build failed due to errors
[ERROR] 	[error]: Build step io.quarkus.deployment.steps.MainClassBuildStep#build threw an exception: org.objectweb.asm.ClassTooLargeException: Class too large: io/quarkus/deployment/steps/HibernateOrmProcessor$build1373687952
[ERROR] 	at org.objectweb.asm.ClassWriter.toByteArray(ClassWriter.java:599)
[ERROR] 	at io.quarkus.gizmo.ClassCreator.writeTo(ClassCreator.java:214)
[ERROR] 	at io.quarkus.gizmo.ClassCreator.close(ClassCreator.java:225)
[ERROR] 	at io.quarkus.deployment.recording.BytecodeRecorderImpl.writeBytecode(BytecodeRecorderImpl.java:595)
[ERROR] 	at io.quarkus.deployment.steps.MainClassBuildStep.writeRecordedBytecode(MainClassBuildStep.java:436)
[ERROR] 	at io.quarkus.deployment.steps.MainClassBuildStep.build(MainClassBuildStep.java:168)
[ERROR] 	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[ERROR] 	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
[ERROR] 	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
[ERROR] 	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
[ERROR] 	at io.quarkus.deployment.ExtensionLoader$2.execute(ExtensionLoader.java:887)
[ERROR] 	at io.quarkus.builder.BuildContext.run(BuildContext.java:277)
[ERROR] 	at org.jboss.threads.ContextHandler$1.runWith(ContextHandler.java:18)
[ERROR] 	at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2449)
[ERROR] 	at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1478)
[ERROR] 	at java.base/java.lang.Thread.run(Thread.java:829)
[ERROR] 	at org.jboss.threads.JBossThread.run(JBossThread.java:501)
```

### Expected behavior

Build to go through successfully.

### Actual behavior

Build failure.

### How to Reproduce?

1. Compile a project with large set of entities (2K+)

### Output of `uname -a` or `ver`

Linux ubuntu 4.15.0-159-generic #167-Ubuntu SMP Tue Sep 21 08:55:05 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

### Output of `java -version`

openjdk version "11.0.11" 2021-04-20 OpenJDK Runtime Environment (build 11.0.11+9-Ubuntu-0ubuntu2.18.04) OpenJDK 64-Bit Server VM (build 11.0.11+9-Ubuntu-0ubuntu2.18.04, mixed mode, sharing)

### GraalVM version (if different from Java)

_No response_

### Quarkus version or git rev

2.6.2.Final

### Build tool (ie. output of `mvnw --version` or `gradlew --version`)

Apache Maven 3.8.1 (05c21c65bdfed0f71a2f2ada8b84da59348c4c5d)

### Additional information

_No response_

Changed files:
core/deployment/src/test/java/io/quarkus/deployment/recording/BytecodeRecorderTestCase.java
core/deployment/src/main/java/io/quarkus/deployment/recording/BytecodeRecorderImpl.java

diff --git a/core/deployment/src/main/java/io/quarkus/deployment/recording/BytecodeRecorderImpl.java b/core/deployment/src/main/java/io/quarkus/deployment/recording/BytecodeRecorderImpl.java
index 0cfef746ae1..9ce8bf24ff3 100644
--- a/core/deployment/src/main/java/io/quarkus/deployment/recording/BytecodeRecorderImpl.java
+++ b/core/deployment/src/main/java/io/quarkus/deployment/recording/BytecodeRecorderImpl.java
@@ -108,6 +108,7 @@ public class BytecodeRecorderImpl implements RecorderContext {
 
     private static final MethodDescriptor COLLECTION_ADD = ofMethod(Collection.class, "add", boolean.class, Object.class);
     private static final MethodDescriptor MAP_PUT = ofMethod(Map.class, "put", Object.class, Object.class, Object.class);
+    public static final String CREATE_ARRAY = "$quarkus$createArray";
 
     private final boolean staticInit;
     private final ClassLoader classLoader;
@@ -517,7 +518,10 @@ ResultHandle createValue(MethodContext context, MethodCreator method, ResultHand
         //when this is true it is no longer possible to allocate items in the array. this is a guard against programmer error
         loadComplete = true;
         //now we now know many items we have, create the array
-        ResultHandle array = mainMethod.newArray(Object.class, deferredParameterCount);
+
+        MethodDescriptor createArrayDescriptor = ofMethod(mainMethod.getMethodDescriptor().getDeclaringClass(), CREATE_ARRAY,
+                "[Ljava/lang/Object;");
+        ResultHandle array = mainMethod.invokeVirtualMethod(createArrayDescriptor, mainMethod.getThis());
 
         //this context manages the creation of new methods
         //it tracks the number of instruction groups and when they hit a threshold it
@@ -592,6 +596,8 @@ public void write(MethodContext context, MethodCreator method, ResultHandle arra
         }
         context.close();
         mainMethod.returnValue(null);
+        var createArray = file.getMethodCreator(createArrayDescriptor);
+        createArray.returnValue(createArray.newArray(Object.class, deferredParameterCount));
         file.close();
 
     }
@@ -1516,8 +1522,6 @@ void doPrepare(MethodContext context) {
                 for (SerializationStep i : setupSteps) {
                     //then prepare the steps (i.e. creating the values to be placed into this object)
                     i.prepare(context);
-                }
-                for (SerializationStep i : setupSteps) {
                     //now actually run the steps (i.e. actually stick the values into the object)
                     context.writeInstruction(new InstructionGroup() {
                         @Override
@@ -1866,7 +1870,7 @@ abstract class DeferredParameter {
 
         /**
          * The function that is called to read the value for use. This may be by reading the value from the Object[]
-         * array, or is may be a direct ldc instruction in the case of primitives.
+         * array, or is can be a direct ldc instruction in the case of primitives.
          * <p>
          * Code in this method is run in a single instruction group, so large objects should be serialized in the
          * {@link #doPrepare(MethodContext)} method instead
@@ -1894,15 +1898,17 @@ void doPrepare(MethodContext context) {
 
     abstract class DeferredArrayStoreParameter extends DeferredParameter {
 
-        final int arrayIndex;
+        int arrayIndex = -1;
         final String returnType;
+        ResultHandle originalResultHandle;
+        ResultHandle originalArrayResultHandle;
+        MethodCreator originalRhMethod;
 
         DeferredArrayStoreParameter(String expectedType) {
             returnType = expectedType;
             if (loadComplete) {
                 throw new RuntimeException("Cannot create new DeferredArrayStoreParameter after array has been allocated");
             }
-            arrayIndex = deferredParameterCount++;
         }
 
         DeferredArrayStoreParameter(Object target, Class<?> expectedType) {
@@ -1918,7 +1924,6 @@ abstract class DeferredArrayStoreParameter extends DeferredParameter {
             if (loadComplete) {
                 throw new RuntimeException("Cannot create new DeferredArrayStoreParameter after array has been allocated");
             }
-            arrayIndex = deferredParameterCount++;
         }
 
         /**
@@ -1933,8 +1938,9 @@ void doPrepare(MethodContext context) {
             context.writeInstruction(new InstructionGroup() {
                 @Override
                 public void write(MethodContext context, MethodCreator method, ResultHandle array) {
-                    ResultHandle val = createValue(context, method, array);
-                    method.writeArrayValue(array, arrayIndex, val);
+                    originalResultHandle = createValue(context, method, array);
+                    originalRhMethod = method;
+                    originalArrayResultHandle = array;
                 }
             });
             prepared = true;
@@ -1942,6 +1948,16 @@ public void write(MethodContext context, MethodCreator method, ResultHandle arra
 
         @Override
         final ResultHandle doLoad(MethodContext context, MethodCreator method, ResultHandle array) {
+            if (!prepared) {
+                prepare(context);
+            }
+            if (method == originalRhMethod) {
+                return originalResultHandle;
+            }
+            if (arrayIndex == -1) {
+                arrayIndex = deferredParameterCount++;
+                originalRhMethod.writeArrayValue(originalArrayResultHandle, arrayIndex, originalResultHandle);
+            }
             ResultHandle resultHandle = method.readArrayValue(array, arrayIndex);
             if (returnType == null) {
                 return resultHandle;
@@ -2050,10 +2066,14 @@ public ResultHandle loadDeferred(DeferredParameter parameter) {
                 //we don't want to have to go back to the array every time
                 //so we cache the result handles within the scope of the current method
                 int arrayIndex = ((DeferredArrayStoreParameter) parameter).arrayIndex;
-                if (currentMethodCache.containsKey(arrayIndex)) {
+                if (arrayIndex > 0 && currentMethodCache.containsKey(arrayIndex)) {
                     return currentMethodCache.get(arrayIndex);
                 }
                 ResultHandle loaded = parameter.doLoad(this, currentMethod, currentMethod.getMethodParam(1));
+                arrayIndex = ((DeferredArrayStoreParameter) parameter).arrayIndex;
+                if (arrayIndex < 0) {
+                    return loaded;
+                }
                 if (parent.currentMethod == currentMethod) {
                     currentMethodCache.put(arrayIndex, loaded);
                     return loaded;
diff --git a/core/deployment/src/test/java/io/quarkus/deployment/recording/BytecodeRecorderTestCase.java b/core/deployment/src/test/java/io/quarkus/deployment/recording/BytecodeRecorderTestCase.java
index 6fa3cbc65e3..499e7e8e310 100644
--- a/core/deployment/src/test/java/io/quarkus/deployment/recording/BytecodeRecorderTestCase.java
+++ b/core/deployment/src/test/java/io/quarkus/deployment/recording/BytecodeRecorderTestCase.java
@@ -189,7 +189,7 @@ public String get() {
     public void testLargeCollection() throws Exception {
 
         List<TestJavaBean> beans = new ArrayList<>();
-        for (int i = 0; i < 10000; ++i) {
+        for (int i = 0; i < 100000; ++i) {
             beans.add(new TestJavaBean("A string", 99));
         }