docs: static byte[] generateClassBytes(String wrapperClassName,
                                       Class<?>[] wrapperInterfaces,
                                       EJBWrapperType wrapperType,
                                       Method[] allMethods,
                                       EJBMethodInfoImpl[] methodInfos,
                                       String ejbClassName,
                                       String beanName)
                      throws EJBConfigurationException
      {
          Method[] methods;
          Class<?> mdbEnterpriseClass = null;
          final boolean isTraceOn = TraceComponent.isAnyTracingEnabled();
          final boolean isNoMethodInterfaceMDB = wrapperType == MDB_NO_METHOD_INTERFACE_PROXY;
  
          if (wrapperType == MDB_PROXY || wrapperType == MDB_NO_METHOD_INTERFACE_PROXY) {
              // For MDB's the second entry in wrapperInterfaces holds the MDB enterprise class.
              mdbEnterpriseClass = wrapperInterfaces[1];
              wrapperInterfaces = new Class<?>[] { wrapperInterfaces[0] };
          }
  
          // Only business local and no-interface supported for aggregate wrappers. d677413
          boolean isAggregateWrapper = wrapperInterfaces.length > 1;
          if (isAggregateWrapper && wrapperType != LOCAL_BEAN && wrapperType != BUSINESS_LOCAL) {
              throw new IllegalArgumentException("wrapper type = " + wrapperType);
          }
  
          // Home wrappers do have some slight differences in generated code
          boolean isHomeWrapper = (wrapperType == REMOTE_HOME ||
                          wrapperType == LOCAL_HOME)
                                          ? true : false;
  
          // This is the only wrapper interface for non-aggregate, and is the bean
          // class for LocalBean (No-Interface view).                        d677413
          Class<?> wrapperInterface = wrapperInterfaces[0];
  
          // ASM uses 'internal' java class names (like JNI) where '/' is
          // used instead of '.', so convert the parameters to 'internal' format.
          String internalClassName = convertClassName(wrapperClassName);
          String internalEJBClassName = convertClassName(ejbClassName);
          String[] internalInterfaceNames = new String[wrapperInterfaces.length];
          for (int i = 0; i < wrapperInterfaces.length; i++)
          {
              String wrapperInterfaceName = wrapperInterfaces[i].getName();
              internalInterfaceNames[i] = convertClassName(wrapperInterfaceName);
          }
  
          // no-method interface MDB's will directly extend the EJB Bean class.
          // Therefore, we will already be picking up the single interface, but
          // we need to add MessageEndpoint since the MDB proxy won't be extending
          // MessageEndpointBase.
          if (isNoMethodInterfaceMDB) {
              internalInterfaceNames[0] = "javax/resource/spi/endpoint/MessageEndpoint";
          }
  
          // The parent of the wrapper varies based on type of wrapper interface
          String internalParentName = getParentClassName(wrapperType, internalEJBClassName);
  
          // Remote Business interfaces may or may not extend java.rmi.Remote
          boolean isRmiRemote = (Remote.class).isAssignableFrom(wrapperInterface);
  
          // For No-Interface View (LocalBean), switch the interface and parent
          // names, as the EJB itself must be the parent and it will implement
          // a marker interface so passivation knows it is a wrapper.      F743-1756
          // Same is required for ManagedBean wrappers.                 F743-34301.1
          boolean isLocalBean = (wrapperType == LOCAL_BEAN ||
                          wrapperType == MANAGED_BEAN);
          if (isLocalBean)
          {
              // No-Interface is always first in the list                     d677413
              String tempName = internalInterfaceNames[0];
              internalInterfaceNames[0] = internalParentName;
              internalParentName = tempName;
          }
  
          if (isTraceOn)
          {
              if (tc.isEntryEnabled())
                  Tr.entry(tc, "generateClassBytes");
              if (tc.isDebugEnabled())
              {
                  Tr.debug(tc, INDENT + "className = " + internalClassName);
                  Tr.debug(tc, INDENT + "interface = " + Arrays.toString(internalInterfaceNames));
                  Tr.debug(tc, INDENT + "parent    = " + internalParentName);
                  Tr.debug(tc, INDENT + "ejb       = " + internalEJBClassName);
                  if (isRmiRemote)
                      Tr.debug(tc, INDENT + "implements java.rmi.Remote");
              }
          }
  
          // Before generating the wrapper... first perform some basic validation
          // on the configured wrapper interface to insure it conforms to the
          // spec.. for such things as implementing EJBObject (2.1), etc.    d431543
          // For aggregate wrappers, validation performed previously.        d677413
          if (!isAggregateWrapper) {
              validateInterfaceBasics(wrapperInterface, wrapperType, ejbClassName);
          }
  
          // Create the ASM Class Writer to write out a Wrapper
          ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); //F743-11995
  
          // Define the Wrapper Class object
          cw.visit(V1_2, ACC_PUBLIC + ACC_SUPER,
                   internalClassName,
                   null,
                   internalParentName,
                   internalInterfaceNames);
  
          // Define the source code file and debug settings
          String sourceFileName = wrapperClassName.substring(wrapperClassName.lastIndexOf(".") + 1) + ".java";
          cw.visitSource(sourceFileName, null);
  
          // Add static and instance variables per wrapper type.           F743-1756
          addFields(cw, wrapperType);
  
          // Add the public no parameter wrapper constructor
          addCtor(cw, internalParentName);
  
          // For no-method interface MDBs, add the MessageEndpoint interface methods.
          if (isNoMethodInterfaceMDB) {
              addBeforeDeliveryMethod(cw, internalClassName);
              addAfterDeliveryMethod(cw, internalClassName);
              addReleaseMethod(cw, internalClassName);
          }
  
          // d583041 - No-Interface (LocalBean) needs to override Object.equals and
          // Object.hashCode.  Other local views get this from EJSWrapperBase.
          if (wrapperType == LOCAL_BEAN)
          {
              addNoInterfaceEqualsMethod(cw, internalClassName);
              addNoInterfaceHashCodeMethod(cw, internalClassName);
          }
  
          // MDB Proxy instances need to override equals and hashcode to provide
          // the java.lang.Object default behavior. If not, then all Proxy instances
          // for an MDB where the Proxy class extends MessageEndpointBase will report
          // equal and have the same hashcode; which does not follow JCA specification.
          // Proxy classes that extend the customer class would have undefined behavior.
          if (wrapperType == MDB_PROXY || wrapperType == MDB_NO_METHOD_INTERFACE_PROXY) {
              addDefaultEqualsMethod(cw, internalClassName);
              addDefaultHashCodeMethod(cw, internalClassName);
          }
  
          // Use Reflections to determine which methods are implemented by
          // the interface, and therefore must be on the wrapper.  Note that
          // if this interface is the only interface of its type (i.e. Local,
          // Remote, etc), then the list of all methods will be the same
          // as the list for this interface.
          if (isHomeWrapper ||
              wrapperType == MDB_PROXY ||
              wrapperType == MDB_NO_METHOD_INTERFACE_PROXY)
          {
              methods = allMethods;
          }
          else if (wrapperType == BUSINESS_LOCAL ||
                   wrapperType == BUSINESS_REMOTE ||
                   wrapperType == LOCAL_BEAN ||
                   wrapperType == MANAGED_BEAN) // F743-34301.1
          {
              // For 3.0 and later business interfaces (including no-interface view),
              // DeploymentUtil is expecting them in the second parameter.  F743-1756
              methods = DeploymentUtil.getMethods(null, wrapperInterfaces);
          }
          else
          {
              // Component interfaces are passed as the first parameter.
              methods = DeploymentUtil.getMethods(wrapperInterface, null);
          }
  
          // Add all of the methods to the wrapper, based on the reflected
          // Method objects from the interface.
          int methodId = -1;
          for (int i = 0; i < methods.length; ++i)
          {
              Method method = methods[i];
              String implMethodName = method.getName();
  
              // For aggregate wrappers, validation performed previously.     d677413
              if (!isAggregateWrapper)
              {
                  // For Local Home wrappers, the create and find methods must have
                  // _Local appended, since there is only 1 home implementation.
                  if (isHomeWrapper && !isRmiRemote)
                  {
                      if (implMethodName.startsWith("create") ||
                          implMethodName.startsWith("find"))
                          implMethodName += "_Local";
                  }
  
                  // Business methods must not start with "ejb", per spec.     d457128
                  if (!isHomeWrapper && wrapperType != MANAGED_BEAN && implMethodName.startsWith("ejb"))
                  {
                      // Log the error and throw meaningful exception.        d457128.2
                      Tr.error(tc, "JIT_INVALID_MTHD_PREFIX_CNTR5010E",
                               new Object[] { beanName,
                                             wrapperInterface.getName(),
                                             implMethodName });
                      throw new EJBConfigurationException("EJB business method " + implMethodName +
                                                          " on interface " + wrapperInterface.getName() +
                                                          " must not start with 'ejb'.");
                  }
  
                  // No-Interface View (LocalBean) methods must not be final
                  // or throw a RemoteException.                             F743-1756
                  if (isLocalBean)
                  {
                      validateLocalBeanMethod(method, beanName);
                  }
              }
  
              // Determine the Method Id that will be hard coded into the
              // wrapper to allow preInvoke to quickly find the correct method.
              methodId = getMethodId(method, allMethods, ++methodId);
  
              // Determine if interceptors are called from methodinfo.      d367572.4
              EJBMethodInfoImpl methodInfo = methodInfos[methodId];
              boolean aroundInvoke =
                              (methodInfo.getAroundInterceptorProxies() != null); // F743-17763.1
  
              // Determine if this is a Stateless Home create, which will
              // optimize the wrapper to call a special preInvoke.
              boolean isStatelessCreate = methodInfo.isHomeCreate() &&
                                          methodInfo.isStatelessSessionBean();
  
              // For aggregate wrappers, if the same method (name and parameters) is
              // present on multiple interfaces, verify that the thrown exceptions
              // match across all occurrences. If there is a mismatch, then log a
              // warning, and use the method (and thus exceptions) from the
              // implementation. Only and all exceptions from the implementation are
              // possible.                                                    d677413
              if (isAggregateWrapper)
              {
                  Method previousMethod = null, currentMethod = null;
                  for (Class<?> curInterface : wrapperInterfaces)
                  {
                      try
                      {
                          currentMethod = curInterface.getMethod(implMethodName, method.getParameterTypes());
                      } catch (NoSuchMethodException ex)
                      {
                          continue; // not all interfaces have all methods... normal
                      }
  
                      if (previousMethod == null)
                      {
                          previousMethod = currentMethod;
                      }
                      else
                      {
                          Class<?>[] previousEx = previousMethod.getExceptionTypes();
                          Class<?>[] currentEx = currentMethod.getExceptionTypes();
                          if (!Arrays.equals(previousEx, currentEx))
                          {
                              method = methodInfo.getMethod();
                              Tr.warning(tc, "JIT_THROWS_CLAUSE_MISMATCH_CNTR5035W",
                                         new Object[] { beanName, method }); // d677413
                          }
                      }
                  }
              }
  
              // F743-609, F743-609CodRev F743-34301.1
              // Add the method using one of 3 styles:
              // 1 - ManagedBean method (no pre/post invoke processing)
              // 2 - Synchronous EJB method
              // 3 - Asynchronous EJB method
              // Each 'add' method accounts for interceptors (or lack of)
              if (wrapperType == MANAGED_BEAN)
              {
                  addManagedBeanMethod(cw,
                                       internalClassName,
                                       internalEJBClassName,
                                       method,
                                       implMethodName,
                                       methodId,
                                       aroundInvoke);
              }
              else if (!methodInfo.isAsynchMethod())
              {
                  addEJBMethod(cw,
                               internalClassName,
                               internalEJBClassName,
                               wrapperType, // RTC116527
                               method,
                               implMethodName,
                               methodId,
                               isRmiRemote,
                               aroundInvoke,
                               isStatelessCreate);
  
              }
              else
              {
                  addEJBAsynchMethod(cw,
                                     internalClassName,
                                     method,
                                     methodId,
                                     isRmiRemote,
                                     wrapperType);
              }
          }
  
          // For No-Interface View (LocalBean) or no-method interface MDB,
          // also add method overrides for all non-public methods, which
          // just throw EJBException.        F743-1756
          if (isLocalBean || isNoMethodInterfaceMDB)
          {
              Class<?> ejbClass;
              if (isNoMethodInterfaceMDB) {
                  // For no method interface MDBs we need to use the enterprise
                  // class instead of the interface for finding non-public methods.
                  ejbClass = mdbEnterpriseClass;
              } else {
                  ejbClass = wrapperInterface;
              }
              ArrayList<Method> nonPublicMethods =
                              DeploymentUtil.getNonPublicMethods(ejbClass,
                                                                 methods);
              if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled())
                  Tr.debug(tc, INDENT + "Non-public methods : " +
                               nonPublicMethods.size());
  
              for (Method method : nonPublicMethods)
              {
                  if (!isAggregateWrapper) { // d677413
                      validateLocalBeanMethod(method, beanName); // must not be final
                  }
                  addNonPublicMethod(cw, method);
              }
          }
  
          // Mark the end of the generated wrapper class
          cw.visitEnd();
  
          // Dump the class bytes out to a byte array.
          byte[] classBytes = cw.toByteArray();
  
          if (isTraceOn)
          {
              if (tc.isDebugEnabled())
                  writeToClassFile(internalClassName, classBytes);
  
              if (tc.isEntryEnabled())
                  Tr.exit(tc, "generateClassBytes: " + classBytes.length + " bytes");
          }
  
          return classBytes;
      }