private void writeGroovyMethods(ClassVisitor visitor, Class<?> managedTypeClass) {
    // Object propertyMissing(String name)
    MethodVisitor methodVisitor =
        declareMethod(
            visitor, "propertyMissing", GET_PROPERTY_MISSING_METHOD_DESCRIPTOR, CONCRETE_SIGNATURE);

    // throw new MissingPropertyException(name, <managed-type>.class)
    methodVisitor.visitTypeInsn(NEW, MISSING_PROPERTY_EXCEPTION_TYPE);
    methodVisitor.visitInsn(DUP);
    putFirstMethodArgumentOnStack(methodVisitor);
    putClassOnStack(methodVisitor, managedTypeClass);
    methodVisitor.visitMethodInsn(
        INVOKESPECIAL,
        MISSING_PROPERTY_EXCEPTION_TYPE,
        "<init>",
        MISSING_PROPERTY_CONSTRUCTOR_DESCRIPTOR,
        false);
    finishVisitingMethod(methodVisitor, ATHROW);

    // Object propertyMissing(String name, Object value)

    methodVisitor =
        declareMethod(
            visitor, "propertyMissing", SET_PROPERTY_MISSING_METHOD_DESCRIPTOR, CONCRETE_SIGNATURE);

    // throw new MissingPropertyException(name, <managed-type>.class)
    methodVisitor.visitTypeInsn(NEW, MISSING_PROPERTY_EXCEPTION_TYPE);
    methodVisitor.visitInsn(DUP);
    putFirstMethodArgumentOnStack(methodVisitor);
    putClassOnStack(methodVisitor, managedTypeClass);
    methodVisitor.visitMethodInsn(
        INVOKESPECIAL,
        MISSING_PROPERTY_EXCEPTION_TYPE,
        "<init>",
        MISSING_PROPERTY_CONSTRUCTOR_DESCRIPTOR,
        false);
    finishVisitingMethod(methodVisitor, ATHROW);

    // Object methodMissing(String name, Object args)
    methodVisitor =
        declareMethod(
            visitor, "methodMissing", METHOD_MISSING_METHOD_DESCRIPTOR, CONCRETE_SIGNATURE);

    // throw new MissingMethodException(name, <managed-type>.class, args)
    methodVisitor.visitTypeInsn(NEW, MISSING_METHOD_EXCEPTION_TYPE);
    methodVisitor.visitInsn(DUP);
    putMethodArgumentOnStack(methodVisitor, 1);
    putClassOnStack(methodVisitor, managedTypeClass);
    putMethodArgumentOnStack(methodVisitor, 2);
    methodVisitor.visitTypeInsn(CHECKCAST, OBJECT_ARRAY_TYPE);
    methodVisitor.visitMethodInsn(
        INVOKESPECIAL,
        MISSING_METHOD_EXCEPTION_TYPE,
        "<init>",
        MISSING_METHOD_EXCEPTION_CONSTRUCTOR_DESCRIPTOR,
        false);
    finishVisitingMethod(methodVisitor, ATHROW);
  }
  /** Generate a call to the delegate object. */
  protected MethodVisitor makeDelegateCall(
      final String name,
      final String desc,
      final String signature,
      final String[] exceptions,
      final int accessFlags) {
    MethodVisitor mv = super.visitMethod(accessFlags, name, desc, signature, exceptions);
    mv.visitVarInsn(ALOAD, 0); // load this
    mv.visitFieldInsn(
        GETFIELD,
        proxyName,
        DELEGATE_OBJECT_FIELD,
        BytecodeHelper.getTypeDescription(delegateClass)); // load delegate
    // using InvokerHelper to allow potential intercepted calls
    int size;
    mv.visitLdcInsn(name); // method name
    Type[] args = Type.getArgumentTypes(desc);
    BytecodeHelper.pushConstant(mv, args.length);
    mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
    size = 3;
    int idx = 1;
    for (int i = 0; i < args.length; i++) {
      Type arg = args[i];
      mv.visitInsn(DUP);
      BytecodeHelper.pushConstant(mv, i);
      // primitive types must be boxed
      if (isPrimitive(arg)) {
        mv.visitIntInsn(getLoadInsn(arg), idx);
        String wrappedType = getWrappedClassDescriptor(arg);
        mv.visitMethodInsn(
            INVOKESTATIC,
            wrappedType,
            "valueOf",
            "(" + arg.getDescriptor() + ")L" + wrappedType + ";");
      } else {
        mv.visitVarInsn(ALOAD, idx); // load argument i
      }
      size = Math.max(6, 5 + registerLen(arg));
      idx += registerLen(arg);
      mv.visitInsn(AASTORE); // store value into array
    }
    mv.visitMethodInsn(
        INVOKESTATIC,
        "org/codehaus/groovy/runtime/InvokerHelper",
        "invokeMethod",
        "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;");
    unwrapResult(mv, desc);
    mv.visitMaxs(size, registerLen(args) + 1);

    return EMPTY_VISITOR;
  }
  // the overload of type Object for Groovy coercions:  public void setFoo(Object foo)
  private void createTypeConvertingSetter(
      ClassVisitor visitor, Type generatedType, ModelProperty<?> property) {
    if (!property.isWritable() || !(property.getSchema() instanceof ScalarValueSchema)) {
      return;
    }

    Class<?> propertyClass = property.getType().getConcreteClass();
    Type propertyType = Type.getType(propertyClass);
    Class<?> boxedClass =
        propertyClass.isPrimitive() ? BOXED_TYPES.get(propertyClass) : propertyClass;
    Type boxedType = Type.getType(boxedClass);

    Method setter = property.getSetter().getMethod();
    MethodVisitor methodVisitor =
        declareMethod(
            visitor,
            setter.getName(),
            SET_OBJECT_PROPERTY_DESCRIPTOR,
            SET_OBJECT_PROPERTY_DESCRIPTOR);

    putThisOnStack(methodVisitor);
    putTypeConverterFieldValueOnStack(methodVisitor, generatedType);

    // Object converted = $typeConverter.convert(foo, Float.class, false);
    methodVisitor.visitVarInsn(ALOAD, 1); // put var #1 ('foo') on the stack
    methodVisitor.visitLdcInsn(boxedType); // push the constant Class onto the stack
    methodVisitor.visitInsn(
        propertyClass.isPrimitive()
            ? ICONST_1
            : ICONST_0); // push int 1 or 0 (interpreted as true or false) onto the stack
    methodVisitor.visitMethodInsn(
        INVOKEINTERFACE,
        TYPE_CONVERTER_TYPE.getInternalName(),
        "convert",
        COERCE_TO_SCALAR_DESCRIPTOR,
        true);
    methodVisitor.visitTypeInsn(CHECKCAST, boxedType.getInternalName());

    if (propertyClass.isPrimitive()) {
      unboxType(methodVisitor, propertyClass);
    }

    // invoke the typed setter, popping 'this' and 'converted' from the stack
    methodVisitor.visitMethodInsn(
        INVOKEVIRTUAL,
        generatedType.getInternalName(),
        setter.getName(),
        Type.getMethodDescriptor(Type.VOID_TYPE, propertyType),
        false);
    finishVisitingMethod(methodVisitor);
  }
 private void writeHashCodeMethod(ClassVisitor visitor, Type generatedType) {
   MethodVisitor methodVisitor =
       declareMethod(visitor, "hashCode", HASH_CODE_METHOD_DESCRIPTOR, null);
   methodVisitor.visitVarInsn(ALOAD, 0);
   methodVisitor.visitMethodInsn(
       INVOKEVIRTUAL,
       generatedType.getInternalName(),
       "getBackingNode",
       GET_BACKING_NODE_METHOD_DESCRIPTOR,
       false);
   methodVisitor.visitMethodInsn(
       INVOKEINTERFACE, MUTABLE_MODEL_NODE_TYPE, "hashCode", HASH_CODE_METHOD_DESCRIPTOR, true);
   methodVisitor.visitInsn(IRETURN);
   finishVisitingMethod(methodVisitor, Opcodes.IRETURN);
 }
 private void invokeStateSetMethod(MethodVisitor methodVisitor) {
   methodVisitor.visitMethodInsn(
       INVOKEINTERFACE,
       MODEL_ELEMENT_STATE_TYPE_INTERNAL_NAME,
       "set",
       STATE_SET_METHOD_DESCRIPTOR,
       true);
 }
 private void boxType(MethodVisitor methodVisitor, Class<?> primitiveType) {
   Class<?> boxedType = BOXED_TYPES.get(primitiveType);
   methodVisitor.visitMethodInsn(
       INVOKESTATIC,
       Type.getInternalName(boxedType),
       "valueOf",
       "(" + Type.getDescriptor(primitiveType) + ")" + Type.getDescriptor(boxedType),
       false);
 }
 private void invokeSuperConstructor(MethodVisitor constructorVisitor, Type superclassType) {
   putThisOnStack(constructorVisitor);
   constructorVisitor.visitMethodInsn(
       INVOKESPECIAL,
       superclassType.getInternalName(),
       CONSTRUCTOR_NAME,
       Type.getMethodDescriptor(Type.VOID_TYPE),
       false);
 }
 private void invokeSuperMethod(MethodVisitor methodVisitor, Class<?> superClass, Method method) {
   putThisOnStack(methodVisitor);
   methodVisitor.visitMethodInsn(
       INVOKESPECIAL,
       Type.getInternalName(superClass),
       method.getName(),
       Type.getMethodDescriptor(method),
       false);
 }
  private void throwExceptionBecauseCalledOnItself(MethodVisitor methodVisitor) {
    String exceptionInternalName = Type.getInternalName(UnsupportedOperationException.class);
    methodVisitor.visitTypeInsn(NEW, exceptionInternalName);
    methodVisitor.visitInsn(DUP);
    putConstantOnStack(methodVisitor, "Calling setters of a managed type on itself is not allowed");

    String constructorDescriptor = Type.getMethodDescriptor(Type.VOID_TYPE, STRING_TYPE);
    methodVisitor.visitMethodInsn(
        INVOKESPECIAL, exceptionInternalName, CONSTRUCTOR_NAME, constructorDescriptor, false);
    methodVisitor.visitInsn(ATHROW);
  }
 private void writeManagedTypeStaticField(
     Type generatedType, Class<?> managedTypeClass, MethodVisitor constructorVisitor) {
   constructorVisitor.visitLdcInsn(Type.getType(managedTypeClass));
   constructorVisitor.visitMethodInsn(
       INVOKESTATIC, MODELTYPE_INTERNAL_NAME, "of", MODELTYPE_OF_METHOD_DESCRIPTOR, false);
   constructorVisitor.visitFieldInsn(
       PUTSTATIC,
       generatedType.getInternalName(),
       MANAGED_TYPE_FIELD_NAME,
       Type.getDescriptor(ModelType.class));
 }
 private void writeDefaultToString(ClassVisitor visitor, Type generatedType) {
   MethodVisitor methodVisitor =
       declareMethod(visitor, "toString", TO_STRING_METHOD_DESCRIPTOR, CONCRETE_SIGNATURE);
   putStateFieldValueOnStack(methodVisitor, generatedType);
   methodVisitor.visitMethodInsn(
       INVOKEINTERFACE,
       MODEL_ELEMENT_STATE_TYPE_INTERNAL_NAME,
       "getDisplayName",
       TO_STRING_METHOD_DESCRIPTOR,
       true);
   finishVisitingMethod(methodVisitor, ARETURN);
 }
    private void backportLambda(
        String invokedName, Type invokedType, Handle bsm, Object[] bsmArgs) {
      Class<?> invoker = loadClass(className);
      Handle implMethod = (Handle) bsmArgs[1];
      Handle accessMethod = getLambdaAccessMethod(implMethod);

      LambdaFactoryMethod factory =
          LambdaReifier.reifyLambdaClass(
              implMethod, accessMethod, invoker, invokedName, invokedType, bsm, bsmArgs);
      super.visitMethodInsn(
          INVOKESTATIC, factory.getOwner(), factory.getName(), factory.getDesc(), false);
    }
 private void writeDelegatingToString(
     ClassVisitor visitor, Type generatedType, Type delegateType) {
   MethodVisitor methodVisitor =
       declareMethod(visitor, "toString", TO_STRING_METHOD_DESCRIPTOR, CONCRETE_SIGNATURE);
   putDelegateFieldValueOnStack(methodVisitor, generatedType, delegateType);
   methodVisitor.visitMethodInsn(
       INVOKEVIRTUAL,
       delegateType.getInternalName(),
       "getDisplayName",
       TO_STRING_METHOD_DESCRIPTOR,
       false);
   finishVisitingMethod(methodVisitor, ARETURN);
 }
 private void invokeDelegateMethod(
     MethodVisitor methodVisitor, Type generatedType, Type delegateType, Method method) {
   putDelegateFieldValueOnStack(methodVisitor, generatedType, delegateType);
   Class<?>[] parameterTypes = method.getParameterTypes();
   for (int paramNo = 0; paramNo < parameterTypes.length; paramNo++) {
     putMethodArgumentOnStack(methodVisitor, Type.getType(parameterTypes[paramNo]), paramNo + 1);
   }
   methodVisitor.visitMethodInsn(
       INVOKEVIRTUAL,
       delegateType.getInternalName(),
       method.getName(),
       Type.getMethodDescriptor(method),
       false);
 }
 private void writeEqualsMethod(ClassVisitor cw, Type generatedType) {
   MethodVisitor methodVisitor =
       cw.visitMethod(Opcodes.ACC_PUBLIC, "equals", EQUALS_METHOD_DESCRIPTOR, null, null);
   methodVisitor.visitCode();
   methodVisitor.visitVarInsn(ALOAD, 0);
   methodVisitor.visitVarInsn(ALOAD, 1);
   Label notSameLabel = new Label();
   methodVisitor.visitJumpInsn(IF_ACMPNE, notSameLabel);
   methodVisitor.visitInsn(ICONST_1);
   methodVisitor.visitInsn(IRETURN);
   methodVisitor.visitLabel(notSameLabel);
   methodVisitor.visitVarInsn(ALOAD, 1);
   methodVisitor.visitTypeInsn(INSTANCEOF, MANAGED_INSTANCE_TYPE);
   Label notManagedInstanceLabel = new Label();
   methodVisitor.visitJumpInsn(IFNE, notManagedInstanceLabel);
   methodVisitor.visitInsn(ICONST_0);
   methodVisitor.visitInsn(IRETURN);
   methodVisitor.visitLabel(notManagedInstanceLabel);
   methodVisitor.visitVarInsn(ALOAD, 0);
   methodVisitor.visitMethodInsn(
       INVOKEVIRTUAL,
       generatedType.getInternalName(),
       "getBackingNode",
       GET_BACKING_NODE_METHOD_DESCRIPTOR,
       false);
   methodVisitor.visitVarInsn(ALOAD, 1);
   methodVisitor.visitTypeInsn(CHECKCAST, MANAGED_INSTANCE_TYPE);
   methodVisitor.visitMethodInsn(
       INVOKEINTERFACE,
       MANAGED_INSTANCE_TYPE,
       "getBackingNode",
       GET_BACKING_NODE_METHOD_DESCRIPTOR,
       true);
   methodVisitor.visitMethodInsn(
       INVOKEINTERFACE, MUTABLE_MODEL_NODE_TYPE, "equals", EQUALS_METHOD_DESCRIPTOR, true);
   finishVisitingMethod(methodVisitor, Opcodes.IRETURN);
 }
 private void writeManagedInstanceGetBackingNodeMethod(ClassVisitor visitor, Type generatedType) {
   MethodVisitor methodVisitor =
       declareMethod(
           visitor,
           "getBackingNode",
           GET_BACKING_NODE_METHOD_DESCRIPTOR,
           CONCRETE_SIGNATURE,
           ACC_PUBLIC | ACC_SYNTHETIC);
   putStateFieldValueOnStack(methodVisitor, generatedType);
   methodVisitor.visitMethodInsn(
       INVOKEINTERFACE,
       MODEL_ELEMENT_STATE_TYPE_INTERNAL_NAME,
       "getBackingNode",
       GET_BACKING_NODE_METHOD_DESCRIPTOR,
       true);
   finishVisitingMethod(methodVisitor, ARETURN);
 }
  public void visitMethodInsn(int opcode, String owner, String name, String desc) {
    if (name.equals("<init>")
        && constructorReplacements != null
        && constructorReplacements.containsKey(owner)) {
      Stack<String> replacementOwnerList = constructorReplacements.get(owner);
      if (!replacementOwnerList.isEmpty()) owner = replacementOwnerList.pop();
      int typeSeparatorIndex = owner.indexOf(":");
      if (typeSeparatorIndex > 0) {
        mv.visitLdcInsn(owner.substring(typeSeparatorIndex + 1));
        owner = owner.substring(0, typeSeparatorIndex);
        int resultSeparatorIndex = desc.indexOf(")");
        desc =
            desc.substring(0, resultSeparatorIndex)
                + "Ljava/lang/String;"
                + desc.substring(resultSeparatorIndex);
      }
    }
    super.visitMethodInsn(opcode, owner, name, desc);
    if (name.equals("<init>")
        && owner.equals(
            isObfuscated ? "boh" : "net/minecraft/client/renderer/entity/RendererLivingEntity")) {
      mv.visitVarInsn(Opcodes.ALOAD, 0);
      mv.visitVarInsn(Opcodes.ALOAD, 0);
      mv.visitMethodInsn(
          Opcodes.INVOKESTATIC,
          "api/player/render/RenderPlayerAPI",
          "create",
          "(Lapi/player/render/IRenderPlayerAPI;)Lapi/player/render/RenderPlayerAPI;");
      mv.visitFieldInsn(
          Opcodes.PUTFIELD,
          isObfuscated ? "bop" : "net/minecraft/client/renderer/entity/RenderPlayer",
          "renderPlayerAPI",
          "Lapi/player/render/RenderPlayerAPI;");

      mv.visitVarInsn(Opcodes.ALOAD, 0);
      mv.visitMethodInsn(
          Opcodes.INVOKESTATIC,
          "api/player/render/RenderPlayerAPI",
          "beforeLocalConstructing",
          "(Lapi/player/render/IRenderPlayerAPI;)V");
    }
  }
 private MethodVisitor createConstructor(
     final int access,
     final String name,
     final String desc,
     final String signature,
     final String[] exceptions) {
   Type[] args = Type.getArgumentTypes(desc);
   StringBuilder newDesc = new StringBuilder("(");
   for (Type arg : args) {
     newDesc.append(arg.getDescriptor());
   }
   newDesc.append("Ljava/util/Map;"); // the closure map
   if (generateDelegateField) {
     newDesc.append(BytecodeHelper.getTypeDescription(delegateClass));
   }
   newDesc.append(")V");
   MethodVisitor mv = super.visitMethod(access, name, newDesc.toString(), signature, exceptions);
   mv.visitCode();
   initializeDelegateClosure(mv, args.length);
   if (generateDelegateField) {
     initializeDelegateObject(mv, args.length + 1);
   }
   mv.visitVarInsn(ALOAD, 0);
   int idx = 1;
   for (Type arg : args) {
     if (isPrimitive(arg)) {
       mv.visitIntInsn(getLoadInsn(arg), idx);
     } else {
       mv.visitVarInsn(ALOAD, idx); // load argument i
     }
     idx += registerLen(arg);
   }
   mv.visitMethodInsn(
       INVOKESPECIAL, BytecodeHelper.getClassInternalName(superClass), "<init>", desc);
   mv.visitInsn(RETURN);
   int max = idx + 1 + (generateDelegateField ? 1 : 0);
   mv.visitMaxs(max, max);
   mv.visitEnd();
   return EMPTY_VISITOR;
 }
 private void unboxType(MethodVisitor methodVisitor, Class<?> primitiveClass) {
   // Float f = (Float) tmp
   // f==null?0:f.floatValue()
   Class<?> boxedType = BOXED_TYPES.get(primitiveClass);
   Type primitiveType = Type.getType(primitiveClass);
   methodVisitor.visitTypeInsn(CHECKCAST, Type.getInternalName(boxedType));
   methodVisitor.visitInsn(DUP);
   Label exit = new Label();
   Label elseValue = new Label();
   methodVisitor.visitJumpInsn(IFNONNULL, elseValue);
   methodVisitor.visitInsn(POP);
   pushDefaultValue(methodVisitor, primitiveClass);
   methodVisitor.visitJumpInsn(GOTO, exit);
   methodVisitor.visitLabel(elseValue);
   methodVisitor.visitMethodInsn(
       INVOKEVIRTUAL,
       Type.getInternalName(boxedType),
       primitiveClass.getSimpleName() + "Value",
       Type.getMethodDescriptor(primitiveType),
       false);
   methodVisitor.visitLabel(exit);
 }
  private void writeSetMethod(ClassVisitor visitor, Type generatedType, ModelProperty<?> property) {
    if (property.isWritable() && property.getSchema() instanceof ScalarValueSchema) {

      // TODO - should we support this?
      // Adds a void $propName(Object value) method that sets the value
      MethodVisitor methodVisitor =
          declareMethod(
              visitor,
              property.getName(),
              Type.getMethodDescriptor(Type.VOID_TYPE, OBJECT_TYPE),
              null);
      putThisOnStack(methodVisitor);
      putFirstMethodArgumentOnStack(methodVisitor);
      methodVisitor.visitMethodInsn(
          INVOKEVIRTUAL,
          generatedType.getInternalName(),
          property.getSetter().getName(),
          Type.getMethodDescriptor(Type.VOID_TYPE, OBJECT_TYPE),
          false);
      finishVisitingMethod(methodVisitor);
    }
  }
 protected MethodVisitor makeDelegateToClosureCall(
     final String name,
     final String desc,
     final String signature,
     final String[] exceptions,
     final int accessFlags) {
   MethodVisitor mv = super.visitMethod(accessFlags, name, desc, signature, exceptions);
   //        TraceMethodVisitor tmv = new TraceMethodVisitor(mv);
   //        mv = tmv;
   mv.visitCode();
   int stackSize = 0;
   // method body should be:
   //  this.$delegate$closure$methodName.call(new Object[] { method arguments })
   Type[] args = Type.getArgumentTypes(desc);
   int arrayStore = args.length + 1;
   BytecodeHelper.pushConstant(mv, args.length);
   mv.visitTypeInsn(ANEWARRAY, "java/lang/Object"); // stack size = 1
   stackSize = 1;
   int idx = 1;
   for (int i = 0; i < args.length; i++) {
     Type arg = args[i];
     mv.visitInsn(DUP); // stack size = 2
     BytecodeHelper.pushConstant(mv, i); // array index, stack size = 3
     stackSize = 3;
     // primitive types must be boxed
     if (isPrimitive(arg)) {
       mv.visitIntInsn(getLoadInsn(arg), idx);
       String wrappedType = getWrappedClassDescriptor(arg);
       mv.visitMethodInsn(
           INVOKESTATIC,
           wrappedType,
           "valueOf",
           "(" + arg.getDescriptor() + ")L" + wrappedType + ";");
     } else {
       mv.visitVarInsn(ALOAD, idx); // load argument i
     }
     idx += registerLen(arg);
     stackSize = Math.max(4, 3 + registerLen(arg));
     mv.visitInsn(AASTORE); // store value into array
   }
   mv.visitVarInsn(ASTORE, arrayStore); // store array
   int arrayIndex = arrayStore;
   mv.visitVarInsn(ALOAD, 0); // load this
   mv.visitFieldInsn(
       GETFIELD, proxyName, CLOSURES_MAP_FIELD, "Ljava/util/Map;"); // load closure map
   mv.visitLdcInsn(name); // load method name
   mv.visitMethodInsn(
       INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
   arrayStore++;
   mv.visitVarInsn(ASTORE, arrayStore);
   // if null, test if wildcard exists
   Label notNull = new Label();
   mv.visitIntInsn(ALOAD, arrayStore);
   mv.visitJumpInsn(IFNONNULL, notNull);
   mv.visitVarInsn(ALOAD, 0); // load this
   mv.visitFieldInsn(
       GETFIELD, proxyName, CLOSURES_MAP_FIELD, "Ljava/util/Map;"); // load closure map
   mv.visitLdcInsn("*"); // load wildcard
   mv.visitMethodInsn(
       INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
   mv.visitVarInsn(ASTORE, arrayStore);
   mv.visitLabel(notNull);
   mv.visitVarInsn(ALOAD, arrayStore);
   mv.visitMethodInsn(
       INVOKESTATIC,
       BytecodeHelper.getClassInternalName(this.getClass()),
       "ensureClosure",
       "(Ljava/lang/Object;)Lgroovy/lang/Closure;");
   mv.visitVarInsn(ALOAD, arrayIndex); // load argument array
   stackSize++;
   mv.visitMethodInsn(
       INVOKEVIRTUAL,
       "groovy/lang/Closure",
       "call",
       "([Ljava/lang/Object;)Ljava/lang/Object;"); // call closure
   unwrapResult(mv, desc);
   mv.visitMaxs(stackSize, arrayStore + 1);
   mv.visitEnd();
   //        System.out.println("tmv.getText() = " + tmv.getText());
   return EMPTY_VISITOR;
 }
 @Override
 public MethodVisitor visitMethod(
     final int access,
     final String name,
     final String desc,
     final String signature,
     final String[] exceptions) {
   Object key = Arrays.asList(name, desc);
   if (visitedMethods.contains(key)) return EMPTY_VISITOR;
   if (Modifier.isPrivate(access)
       || Modifier.isNative(access)
       || ((access & ACC_SYNTHETIC) != 0)) {
     // do not generate bytecode for private methods
     return EMPTY_VISITOR;
   }
   int accessFlags = access;
   visitedMethods.add(key);
   if ((objectDelegateMethods.contains(name)
           || delegatedClosures.containsKey(name)
           || (!"<init>".equals(name) && hasWildcard))
       && !Modifier.isStatic(access)
       && !Modifier.isFinal(access)) {
     if (!GROOVYOBJECT_METHOD_NAMESS.contains(name)) {
       if (Modifier.isAbstract(access)) {
         // prevents the proxy from being abstract
         accessFlags -= ACC_ABSTRACT;
       }
       if (delegatedClosures.containsKey(name) || (!"<init>".equals(name) && hasWildcard)) {
         delegatedClosures.put(name, Boolean.TRUE);
         return makeDelegateToClosureCall(name, desc, signature, exceptions, accessFlags);
       }
       if (generateDelegateField && objectDelegateMethods.contains(name)) {
         return makeDelegateCall(name, desc, signature, exceptions, accessFlags);
       }
       delegatedClosures.put(name, Boolean.TRUE);
       return makeDelegateToClosureCall(name, desc, signature, exceptions, accessFlags);
     }
   } else if ("<init>".equals(name)
       && (Modifier.isPublic(access) || Modifier.isProtected(access))) {
     return createConstructor(access, name, desc, signature, exceptions);
   } else if (Modifier.isAbstract(access) && !GROOVYOBJECT_METHOD_NAMESS.contains(name)) {
     accessFlags -= ACC_ABSTRACT;
     MethodVisitor mv = super.visitMethod(accessFlags, name, desc, signature, exceptions);
     mv.visitCode();
     Type[] args = Type.getArgumentTypes(desc);
     if (emptyBody) {
       Type returnType = Type.getReturnType(desc);
       if (returnType == Type.VOID_TYPE) {
         mv.visitInsn(RETURN);
       } else {
         int loadIns = getLoadInsn(returnType);
         switch (loadIns) {
           case ILOAD:
             mv.visitInsn(ICONST_0);
             break;
           case LLOAD:
             mv.visitInsn(LCONST_0);
             break;
           case FLOAD:
             mv.visitInsn(FCONST_0);
             break;
           case DLOAD:
             mv.visitInsn(DCONST_0);
             break;
           default:
             mv.visitInsn(ACONST_NULL);
         }
         mv.visitInsn(getReturnInsn(returnType));
         mv.visitMaxs(2, registerLen(args) + 1);
       }
     } else {
       // for compatibility with the legacy proxy generator, we should throw an
       // UnsupportedOperationException
       // instead of an AbtractMethodException
       mv.visitTypeInsn(NEW, "java/lang/UnsupportedOperationException");
       mv.visitInsn(DUP);
       mv.visitMethodInsn(
           INVOKESPECIAL, "java/lang/UnsupportedOperationException", "<init>", "()V");
       mv.visitInsn(ATHROW);
       mv.visitMaxs(2, registerLen(args) + 1);
     }
     mv.visitEnd();
   }
   return EMPTY_VISITOR;
 }
  /**
   * When an object doesn't implement the GroovyObject interface, we generate bytecode for the
   * {@link GroovyObject} interface methods. Otherwise, the superclass is expected to implement
   * them.
   */
  private void createGroovyObjectSupport() {
    visitField(ACC_PRIVATE + ACC_TRANSIENT, "metaClass", "Lgroovy/lang/MetaClass;", null, null);

    // getMetaClass
    MethodVisitor mv;
    {
      mv = super.visitMethod(ACC_PUBLIC, "getMetaClass", "()Lgroovy/lang/MetaClass;", null, null);
      mv.visitCode();
      Label l0 = new Label();
      mv.visitLabel(l0);
      mv.visitVarInsn(ALOAD, 0);
      mv.visitFieldInsn(GETFIELD, proxyName, "metaClass", "Lgroovy/lang/MetaClass;");
      Label l1 = new Label();
      mv.visitJumpInsn(IFNONNULL, l1);
      Label l2 = new Label();
      mv.visitLabel(l2);
      mv.visitVarInsn(ALOAD, 0);
      mv.visitVarInsn(ALOAD, 0);
      mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;");
      mv.visitMethodInsn(
          INVOKESTATIC,
          "org/codehaus/groovy/runtime/InvokerHelper",
          "getMetaClass",
          "(Ljava/lang/Class;)Lgroovy/lang/MetaClass;");
      mv.visitFieldInsn(PUTFIELD, proxyName, "metaClass", "Lgroovy/lang/MetaClass;");
      mv.visitLabel(l1);
      mv.visitVarInsn(ALOAD, 0);
      mv.visitFieldInsn(GETFIELD, proxyName, "metaClass", "Lgroovy/lang/MetaClass;");
      mv.visitInsn(ARETURN);
      mv.visitMaxs(2, 1);
      mv.visitEnd();
    }

    // getProperty
    {
      mv =
          super.visitMethod(
              ACC_PUBLIC, "getProperty", "(Ljava/lang/String;)Ljava/lang/Object;", null, null);
      mv.visitCode();
      mv.visitIntInsn(ALOAD, 0);
      mv.visitMethodInsn(
          INVOKEINTERFACE, "groovy/lang/GroovyObject", "getMetaClass", "()Lgroovy/lang/MetaClass;");
      mv.visitIntInsn(ALOAD, 0);
      mv.visitVarInsn(ALOAD, 1);
      mv.visitMethodInsn(
          INVOKEINTERFACE,
          "groovy/lang/MetaClass",
          "getProperty",
          "(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;");
      mv.visitInsn(ARETURN);
      mv.visitMaxs(3, 2);
      mv.visitEnd();
    }

    // setProperty
    {
      mv =
          super.visitMethod(
              ACC_PUBLIC, "setProperty", "(Ljava/lang/String;Ljava/lang/Object;)V", null, null);
      mv.visitCode();
      Label l0 = new Label();
      mv.visitLabel(l0);
      mv.visitVarInsn(ALOAD, 0);
      mv.visitMethodInsn(INVOKEVIRTUAL, proxyName, "getMetaClass", "()Lgroovy/lang/MetaClass;");
      mv.visitVarInsn(ALOAD, 0);
      mv.visitVarInsn(ALOAD, 1);
      mv.visitVarInsn(ALOAD, 2);
      mv.visitMethodInsn(
          INVOKEINTERFACE,
          "groovy/lang/MetaClass",
          "setProperty",
          "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;)V");
      Label l1 = new Label();
      mv.visitLabel(l1);
      mv.visitInsn(RETURN);
      Label l2 = new Label();
      mv.visitLabel(l2);
      mv.visitMaxs(4, 3);
      mv.visitEnd();
    }

    // invokeMethod
    {
      mv =
          super.visitMethod(
              ACC_PUBLIC,
              "invokeMethod",
              "(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;",
              null,
              null);
      Label l0 = new Label();
      mv.visitLabel(l0);
      mv.visitVarInsn(ALOAD, 0);
      mv.visitMethodInsn(INVOKEVIRTUAL, proxyName, "getMetaClass", "()Lgroovy/lang/MetaClass;");
      mv.visitVarInsn(ALOAD, 0);
      mv.visitVarInsn(ALOAD, 1);
      mv.visitVarInsn(ALOAD, 2);
      mv.visitMethodInsn(
          INVOKEINTERFACE,
          "groovy/lang/MetaClass",
          "invokeMethod",
          "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;");
      mv.visitInsn(ARETURN);
      Label l1 = new Label();
      mv.visitLabel(l1);
      mv.visitMaxs(4, 3);
      mv.visitEnd();
    }

    // setMetaClass
    {
      mv = super.visitMethod(ACC_PUBLIC, "setMetaClass", "(Lgroovy/lang/MetaClass;)V", null, null);
      mv.visitCode();
      Label l0 = new Label();
      mv.visitLabel(l0);
      mv.visitVarInsn(ALOAD, 0);
      mv.visitVarInsn(ALOAD, 1);
      mv.visitFieldInsn(PUTFIELD, proxyName, "metaClass", "Lgroovy/lang/MetaClass;");
      Label l1 = new Label();
      mv.visitLabel(l1);
      mv.visitInsn(RETURN);
      Label l2 = new Label();
      mv.visitLabel(l2);
      mv.visitMaxs(2, 2);
      mv.visitEnd();
    }
  }
 private void putClassOnStack(MethodVisitor methodVisitor, Class<?> managedTypeClass) {
   putConstantOnStack(methodVisitor, managedTypeClass.getName());
   methodVisitor.visitMethodInsn(
       INVOKESTATIC, CLASS_INTERNAL_NAME, "forName", FOR_NAME_METHOD_DESCRIPTOR, false);
 }
  private void writeConfigureMethod(
      ClassVisitor visitor, Type generatedType, ModelProperty<?> property) {
    if (!property.isWritable() && property.getSchema() instanceof CompositeSchema) {
      // Adds a void $propName(Closure<?> cl) method that delegates to model state

      MethodVisitor methodVisitor =
          declareMethod(
              visitor,
              property.getName(),
              Type.getMethodDescriptor(Type.VOID_TYPE, CLOSURE_TYPE),
              null);
      putStateFieldValueOnStack(methodVisitor, generatedType);
      putConstantOnStack(methodVisitor, property.getName());
      putFirstMethodArgumentOnStack(methodVisitor);
      methodVisitor.visitMethodInsn(
          INVOKEINTERFACE,
          MODEL_ELEMENT_STATE_TYPE_INTERNAL_NAME,
          "apply",
          STATE_APPLY_METHOD_DESCRIPTOR,
          true);
      finishVisitingMethod(methodVisitor);
      return;
    }
    if (!property.isWritable() && property.getSchema() instanceof UnmanagedImplStructSchema) {
      UnmanagedImplStructSchema<?> structSchema =
          (UnmanagedImplStructSchema<?>) property.getSchema();
      if (!structSchema.isAnnotated()) {
        return;
      }

      // Adds a void $propName(Closure<?> cl) method that executes the closure
      MethodVisitor methodVisitor =
          declareMethod(
              visitor,
              property.getName(),
              Type.getMethodDescriptor(Type.VOID_TYPE, CLOSURE_TYPE),
              null);
      putThisOnStack(methodVisitor);
      methodVisitor.visitMethodInsn(
          INVOKEVIRTUAL,
          generatedType.getInternalName(),
          property.getGetters().get(0).getName(),
          Type.getMethodDescriptor(Type.getType(property.getType().getConcreteClass())),
          false);
      putFirstMethodArgumentOnStack(methodVisitor);
      methodVisitor.visitMethodInsn(
          INVOKESTATIC,
          Type.getInternalName(ClosureBackedAction.class),
          "execute",
          Type.getMethodDescriptor(Type.VOID_TYPE, OBJECT_TYPE, CLOSURE_TYPE),
          false);
      finishVisitingMethod(methodVisitor);
      return;
    }

    // Adds a void $propName(Closure<?> cl) method that throws MME, to avoid attempts to convert
    // closure to something else
    MethodVisitor methodVisitor =
        declareMethod(
            visitor,
            property.getName(),
            Type.getMethodDescriptor(Type.VOID_TYPE, CLOSURE_TYPE),
            null);
    putThisOnStack(methodVisitor);
    putConstantOnStack(methodVisitor, property.getName());
    methodVisitor.visitInsn(Opcodes.ICONST_1);
    methodVisitor.visitTypeInsn(Opcodes.ANEWARRAY, OBJECT_TYPE.getInternalName());
    methodVisitor.visitInsn(Opcodes.DUP);
    methodVisitor.visitInsn(Opcodes.ICONST_0);
    putFirstMethodArgumentOnStack(methodVisitor);
    methodVisitor.visitInsn(Opcodes.AASTORE);
    methodVisitor.visitMethodInsn(
        INVOKEVIRTUAL,
        generatedType.getInternalName(),
        "methodMissing",
        METHOD_MISSING_METHOD_DESCRIPTOR,
        false);
    finishVisitingMethod(methodVisitor);
  }