/* * Copyright 2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gradle.model.internal.manage.schema.extract; import com.google.common.base.Equivalence.Wrapper; import com.google.common.base.Predicate; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; import groovy.lang.Closure; import groovy.lang.MissingMethodException; import groovy.lang.MissingPropertyException; import groovy.lang.ReadOnlyPropertyException; import org.apache.commons.lang.StringUtils; import org.gradle.api.internal.ClosureBackedAction; import org.gradle.internal.Cast; import org.gradle.internal.reflect.Types.TypeVisitor; import org.gradle.internal.reflect.UnsupportedPropertyValueException; import org.gradle.internal.typeconversion.TypeConversionException; import org.gradle.internal.typeconversion.TypeConverter; import org.gradle.model.internal.asm.AsmClassGenerator; import org.gradle.model.internal.asm.AsmClassGeneratorUtils; import org.gradle.model.internal.core.MutableModelNode; import org.gradle.model.internal.manage.binding.BridgeMethodBinding; import org.gradle.model.internal.manage.binding.DelegateMethodBinding; import org.gradle.model.internal.manage.binding.DirectMethodBinding; import org.gradle.model.internal.manage.binding.ManagedProperty; import org.gradle.model.internal.manage.binding.ManagedPropertyMethodBinding; import org.gradle.model.internal.manage.binding.StructBindings; import org.gradle.model.internal.manage.binding.StructMethodBinding; import org.gradle.model.internal.manage.instance.GeneratedViewState; import org.gradle.model.internal.manage.instance.ManagedInstance; import org.gradle.model.internal.manage.instance.ModelElementState; import org.gradle.model.internal.manage.schema.CompositeSchema; import org.gradle.model.internal.manage.schema.ModelProperty; import org.gradle.model.internal.manage.schema.ScalarValueSchema; import org.gradle.model.internal.manage.schema.StructSchema; import org.gradle.model.internal.manage.schema.UnmanagedImplStructSchema; import org.gradle.model.internal.method.WeaklyTypeReferencingMethod; import org.gradle.model.internal.type.ModelType; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Collection; import java.util.Map; import java.util.Set; import static org.gradle.internal.reflect.Methods.SIGNATURE_EQUIVALENCE; import static org.gradle.internal.reflect.PropertyAccessorType.*; import static org.gradle.internal.reflect.Types.walkTypeHierarchy; import static org.gradle.model.internal.manage.schema.extract.ModelSchemaUtils.IGNORED_OBJECT_TYPES; import static org.objectweb.asm.Opcodes.*; public class ManagedProxyClassGenerator extends AbstractProxyClassGenerator { /* Note: there is deliberately no internal synchronizing or caching at this level. Class generation should be performed behind a ManagedProxyFactory. */ private static final String STATE_FIELD_NAME = "$state"; private static final String TYPE_CONVERTER_FIELD_NAME = "$typeConverter"; private static final String MANAGED_TYPE_FIELD_NAME = "$managedType"; private static final String DELEGATE_FIELD_NAME = "$delegate"; private static final String CAN_CALL_SETTERS_FIELD_NAME = "$canCallSetters"; private static final Type OBJECT_TYPE = Type.getType(Object.class); private static final Type STRING_TYPE = Type.getType(String.class); private static final Type CLASS_TYPE = Type.getType(Class.class); private static final Type CLOSURE_TYPE = Type.getType(Closure.class); private static final Type TYPE_CONVERTER_TYPE = Type.getType(TypeConverter.class); private static final Type MODEL_TYPE_TYPE = Type.getType(ModelType.class); private static final Type GENERATED_VIEW_STATE_TYPE = Type.getType(GeneratedViewState.class); private static final String GENERATED_VIEW_STATE_TYPE_NAME = GENERATED_VIEW_STATE_TYPE.getInternalName(); private static final Type MODEL_ELEMENT_STATE_TYPE = Type.getType(ModelElementState.class); private static final Type GENERATED_VIEW_TYPE = Type.getType(GeneratedView.class); private static final String GET_VIEW_STATE_METHOD_DESCRIPTOR = Type.getMethodDescriptor(GENERATED_VIEW_STATE_TYPE); private static final String STATE_SET_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.VOID_TYPE, STRING_TYPE, OBJECT_TYPE); private static final String STATE_GET_METHOD_DESCRIPTOR = Type.getMethodDescriptor(OBJECT_TYPE, STRING_TYPE); private static final String STATE_APPLY_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.VOID_TYPE, STRING_TYPE, CLOSURE_TYPE); private static final String MANAGED_INSTANCE_TYPE = Type.getInternalName(ManagedInstance.class); private static final String TO_STRING_METHOD_DESCRIPTOR = Type.getMethodDescriptor(STRING_TYPE); private static final String GET_BACKING_NODE_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.getType(MutableModelNode.class)); private static final String MODEL_TYPE_INTERNAL_NAME = MODEL_TYPE_TYPE.getInternalName(); private static final String MODEL_TYPE_OF_METHOD_DESCRIPTOR = Type.getMethodDescriptor(MODEL_TYPE_TYPE, CLASS_TYPE); private static final String GET_MANAGED_TYPE_METHOD_DESCRIPTOR = Type.getMethodDescriptor(MODEL_TYPE_TYPE); private static final String GET_PROPERTY_MISSING_METHOD_DESCRIPTOR = Type.getMethodDescriptor(OBJECT_TYPE, STRING_TYPE); private static final String MISSING_PROPERTY_EXCEPTION_TYPE = Type.getInternalName(MissingPropertyException.class); private static final String READ_ONLY_PROPERTY_EXCEPTION_TYPE = Type.getInternalName(ReadOnlyPropertyException.class); private static final String CLASS_INTERNAL_NAME = Type.getInternalName(Class.class); private static final String FOR_NAME_METHOD_DESCRIPTOR = Type.getMethodDescriptor(CLASS_TYPE, STRING_TYPE); private static final String HASH_CODE_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.getType(int.class)); private static final String EQUALS_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.getType(boolean.class), OBJECT_TYPE); private static final String OBJECT_ARRAY_TYPE = Type.getInternalName(Object[].class); private static final String MISSING_METHOD_EXCEPTION_TYPE = Type.getInternalName(MissingMethodException.class); private static final Type TYPE_CONVERSION_EXCEPTION_TYPE = Type.getType(TypeConversionException.class); private static final String MISSING_PROPERTY_CONSTRUCTOR_DESCRIPTOR = Type.getMethodDescriptor(Type.VOID_TYPE, STRING_TYPE, CLASS_TYPE); private static final String METHOD_MISSING_METHOD_DESCRIPTOR = Type.getMethodDescriptor(OBJECT_TYPE, STRING_TYPE, OBJECT_TYPE); private static final String SET_PROPERTY_MISSING_METHOD_DESCRIPTOR = Type.getMethodDescriptor(OBJECT_TYPE, STRING_TYPE, OBJECT_TYPE); private static final String MISSING_METHOD_EXCEPTION_CONSTRUCTOR_DESCRIPTOR = Type.getMethodDescriptor(Type.VOID_TYPE, STRING_TYPE, CLASS_TYPE, Type.getType(Object[].class)); private static final String SET_OBJECT_PROPERTY_DESCRIPTOR = Type.getMethodDescriptor(Type.VOID_TYPE, OBJECT_TYPE); private static final String COERCE_TO_SCALAR_DESCRIPTOR = Type.getMethodDescriptor(OBJECT_TYPE, OBJECT_TYPE, CLASS_TYPE, Type.getType(boolean.class)); private static final String MODEL_ELEMENT_STATE_TYPE_INTERNAL_NAME = MODEL_ELEMENT_STATE_TYPE.getInternalName(); private static final Map<Class<?>, Class<?>> BOXED_TYPES = ImmutableMap.<Class<?>, Class<?>>builder() .put(byte.class, Byte.class) .put(short.class, Short.class) .put(int.class, Integer.class) .put(boolean.class, Boolean.class) .put(float.class, Float.class) .put(char.class, Character.class) .put(double.class, Double.class) .put(long.class, Long.class) .build(); /** * Generates an implementation of the given managed type. * <p> * The generated class will implement/extend the managed type and will: * <ul> * <li>provide implementations for abstract getters and setters that delegate to the backing state</li> * <li>provide a `toString()` implementation</li> * <li>mix-in implementation of {@link ManagedInstance}</li> * <li>provide a constructor that accepts a {@link ModelElementState}, which will be used to implement the above.</li> * </ul> * * In case a delegate schema is supplied, the generated class will also have: * <ul> * <li>a constructor that also takes a delegate instance</li> * <li>methods that call through to the delegate instance</li> * </ul> */ public <T, M extends T, D extends T> Class<? extends M> generate(Class<? extends GeneratedViewState> backingStateType, StructSchema<M> viewSchema, StructBindings<?> structBindings) { if (!structBindings.getImplementedViewSchemas().contains(viewSchema)) { throw new IllegalArgumentException(String.format("View '%s' is not supported by struct '%s'", viewSchema.getType(), structBindings.getPublicSchema().getType())); } ModelType<M> viewType = viewSchema.getType(); StringBuilder generatedTypeNameBuilder = new StringBuilder(); if (backingStateType == GeneratedViewState.class) { generatedTypeNameBuilder.append("$View"); } else { generatedTypeNameBuilder.append("$NodeView"); } StructSchema<D> delegateSchema = Cast.uncheckedCast(structBindings.getDelegateSchema()); if (delegateSchema != null) { generatedTypeNameBuilder.append("$").append(delegateSchema.getType().getName().replaceAll("\\.", "_")); } String classNameSuffix = generatedTypeNameBuilder.toString(); Class<?> superclass; final ImmutableSet.Builder<String> interfacesToImplement = ImmutableSet.builder(); final ImmutableSet.Builder<ModelType<?>> typesToDelegate = ImmutableSet.builder(); typesToDelegate.add(viewType); interfacesToImplement.add(GENERATED_VIEW_TYPE.getInternalName()); if (backingStateType == ModelElementState.class) { interfacesToImplement.add(MANAGED_INSTANCE_TYPE); } Class<M> viewClass = viewType.getConcreteClass(); if (viewClass.isInterface()) { superclass = Object.class; interfacesToImplement.add(Type.getInternalName(viewClass)); } else { superclass = viewClass; } // TODO:LPTR This should be removed once BinaryContainer is a ModelMap // We need to also implement all the interfaces of the delegate type because otherwise // BinaryContainer won't recognize managed binaries as BinarySpecInternal if (delegateSchema != null) { walkTypeHierarchy(delegateSchema.getType().getConcreteClass(), IGNORED_OBJECT_TYPES, new TypeVisitor<D>() { @Override public void visitType(Class<? super D> type) { if (type.isInterface()) { typesToDelegate.add(ModelType.of(type)); interfacesToImplement.add(Type.getInternalName(type)); } } }); } ClassLoader targetClassLoader = viewClass.getClassLoader(); if (delegateSchema != null) { // TODO - remove this once the above is removed try { viewClass.getClassLoader().loadClass(delegateSchema.getType().getConcreteClass().getName()); } catch (ClassNotFoundException e) { // Delegate class is not visible to managed view type -> view type is more general than delegate type, so use the delegate classloader instead targetClassLoader = delegateSchema.getType().getConcreteClass().getClassLoader(); } } AsmClassGenerator generator = new AsmClassGenerator(viewClass, classNameSuffix); ClassWriter visitor = generator.getVisitor(); generateProxyClass(visitor, viewSchema, structBindings, interfacesToImplement.build(), typesToDelegate.build(), generator.getGeneratedType(), Type.getType(superclass), backingStateType); return generator.define(targetClassLoader); } private void generateProxyClass(ClassWriter visitor, StructSchema<?> viewSchema, StructBindings<?> bindings, Collection<String> interfacesToImplement, Collection<ModelType<?>> viewTypes, Type generatedType, Type superclassType, Class<? extends GeneratedViewState> backingStateType) { Class<?> viewClass = viewSchema.getType().getConcreteClass(); StructSchema<?> delegateSchema = bindings.getDelegateSchema(); declareClass(visitor, interfacesToImplement, generatedType, superclassType); declareStateField(visitor); declareTypeConverterField(visitor); declareManagedTypeField(visitor); declareCanCallSettersField(visitor); writeStaticConstructor(visitor, generatedType, viewClass); writeConstructor(visitor, generatedType, superclassType, delegateSchema, Type.getType(backingStateType)); writeToString(visitor, generatedType, viewClass, delegateSchema); writeGeneratedViewMethods(visitor, generatedType); if (backingStateType == ModelElementState.class) { writeManagedInstanceMethods(visitor, generatedType); } writeGroovyMethods(visitor, viewClass); writeViewMethods(visitor, generatedType, viewTypes, bindings); writeHashCodeMethod(visitor, generatedType); writeEqualsMethod(visitor, generatedType); visitor.visitEnd(); } private void declareClass(ClassVisitor visitor, Collection<String> interfaceInternalNames, Type generatedType, Type superclassType) { visitor.visit(V1_6, ACC_PUBLIC, generatedType.getInternalName(), null, superclassType.getInternalName(), Iterables.toArray(interfaceInternalNames, String.class)); } private void declareStateField(ClassVisitor visitor) { declareField(visitor, STATE_FIELD_NAME, GeneratedViewState.class); } private void declareTypeConverterField(ClassVisitor visitor) { declareField(visitor, TYPE_CONVERTER_FIELD_NAME, TypeConverter.class); } private void declareManagedTypeField(ClassVisitor visitor) { declareStaticField(visitor, MANAGED_TYPE_FIELD_NAME, ModelType.class); } private void declareDelegateField(ClassVisitor visitor, Class<?> delegateClass) { declareField(visitor, DELEGATE_FIELD_NAME, delegateClass); } private void declareCanCallSettersField(ClassVisitor visitor) { declareField(visitor, CAN_CALL_SETTERS_FIELD_NAME, Boolean.TYPE); } private void declareField(ClassVisitor visitor, String name, Class<?> fieldClass) { visitor.visitField(ACC_PRIVATE | ACC_FINAL | ACC_SYNTHETIC, name, Type.getDescriptor(fieldClass), null, null); } private FieldVisitor declareStaticField(ClassVisitor visitor, String name, Class<?> fieldClass) { return visitor.visitField(ACC_PRIVATE | ACC_FINAL | ACC_STATIC | ACC_SYNTHETIC, name, Type.getDescriptor(fieldClass), null, null); } private void writeConstructor(ClassVisitor visitor, Type generatedType, Type superclassType, StructSchema<?> delegateSchema, Type backingStateType) { String constructorDescriptor; Type delegateType; if (delegateSchema == null) { delegateType = null; constructorDescriptor = Type.getMethodDescriptor(Type.VOID_TYPE, backingStateType, TYPE_CONVERTER_TYPE); } else { delegateType = Type.getType(delegateSchema.getType().getConcreteClass()); constructorDescriptor = Type.getMethodDescriptor(Type.VOID_TYPE, backingStateType, TYPE_CONVERTER_TYPE, delegateType); } MethodVisitor constructorVisitor = declareMethod(visitor, CONSTRUCTOR_NAME, constructorDescriptor, CONCRETE_SIGNATURE); invokeSuperConstructor(constructorVisitor, superclassType); assignStateField(constructorVisitor, generatedType); assignTypeConverterField(constructorVisitor, generatedType); if (delegateType != null) { assignDelegateField(constructorVisitor, generatedType, delegateType); } setCanCallSettersField(constructorVisitor, generatedType, true); finishVisitingMethod(constructorVisitor); } private void writeStaticConstructor(ClassVisitor visitor, Type generatedType, Class<?> managedTypeClass) { MethodVisitor constructorVisitor = declareMethod(visitor, STATIC_CONSTRUCTOR_NAME, "()V", CONCRETE_SIGNATURE, ACC_STATIC); writeManagedTypeStaticField(generatedType, managedTypeClass, constructorVisitor); finishVisitingMethod(constructorVisitor); } private void writeManagedTypeStaticField(Type generatedType, Class<?> managedTypeClass, MethodVisitor constructorVisitor) { constructorVisitor.visitLdcInsn(Type.getType(managedTypeClass)); constructorVisitor.visitMethodInsn(INVOKESTATIC, MODEL_TYPE_INTERNAL_NAME, "of", MODEL_TYPE_OF_METHOD_DESCRIPTOR, false); constructorVisitor.visitFieldInsn(PUTSTATIC, generatedType.getInternalName(), MANAGED_TYPE_FIELD_NAME, Type.getDescriptor(ModelType.class)); } private void invokeSuperConstructor(MethodVisitor constructorVisitor, Type superclassType) { putThisOnStack(constructorVisitor); constructorVisitor.visitMethodInsn(INVOKESPECIAL, superclassType.getInternalName(), CONSTRUCTOR_NAME, Type.getMethodDescriptor(Type.VOID_TYPE), false); } private void writeToString(ClassVisitor visitor, Type generatedType, Class<?> viewClass, StructSchema<?> delegateSchema) { Method toStringMethod = getToStringMethod(viewClass); if (toStringMethod != null && !toStringMethod.getDeclaringClass().equals(Object.class)) { writeNonAbstractMethodWrapper(visitor, generatedType, viewClass, toStringMethod); } else if (delegateSchema != null && delegateSchema.hasProperty("displayName")) { writeDelegatingToString(visitor, generatedType, Type.getType(delegateSchema.getType().getConcreteClass())); } else { writeDefaultToString(visitor, generatedType); } } 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 writeDefaultToString(ClassVisitor visitor, Type generatedType) { MethodVisitor methodVisitor = declareMethod(visitor, "toString", TO_STRING_METHOD_DESCRIPTOR, CONCRETE_SIGNATURE); putStateFieldValueOnStack(methodVisitor, generatedType); methodVisitor.visitMethodInsn(INVOKEINTERFACE, GENERATED_VIEW_STATE_TYPE_NAME, "getDisplayName", TO_STRING_METHOD_DESCRIPTOR, true); finishVisitingMethod(methodVisitor, ARETURN); } private Method getToStringMethod(Class<?> managedTypeClass) { try { return managedTypeClass.getMethod("toString"); } catch (NoSuchMethodException e) { return null; } } private void writeGroovyMethods(ClassVisitor visitor, Class<?> viewClass) { // Object propertyMissing(String name) MethodVisitor methodVisitor = declareMethod(visitor, "propertyMissing", GET_PROPERTY_MISSING_METHOD_DESCRIPTOR, CONCRETE_SIGNATURE); // throw new MissingPropertyException(name, <view-type>.class) methodVisitor.visitTypeInsn(NEW, MISSING_PROPERTY_EXCEPTION_TYPE); methodVisitor.visitInsn(DUP); putFirstMethodArgumentOnStack(methodVisitor); putClassOnStack(methodVisitor, viewClass); 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, <view-type>.class) methodVisitor.visitTypeInsn(NEW, MISSING_PROPERTY_EXCEPTION_TYPE); methodVisitor.visitInsn(DUP); putFirstMethodArgumentOnStack(methodVisitor); putClassOnStack(methodVisitor, viewClass); 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, <view-type>.class, args) methodVisitor.visitTypeInsn(NEW, MISSING_METHOD_EXCEPTION_TYPE); methodVisitor.visitInsn(DUP); putMethodArgumentOnStack(methodVisitor, 1); putClassOnStack(methodVisitor, viewClass); 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); } 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 writeGeneratedViewMethods(ClassWriter visitor, Type generatedType) { MethodVisitor methodVisitor = declareMethod(visitor, "__view_state__", GET_VIEW_STATE_METHOD_DESCRIPTOR, CONCRETE_SIGNATURE, ACC_PUBLIC | ACC_SYNTHETIC); putStateFieldValueOnStack(methodVisitor, generatedType); finishVisitingMethod(methodVisitor, ARETURN); } private void writeManagedInstanceMethods(ClassVisitor visitor, Type generatedType) { writeManagedInstanceGetBackingNodeMethod(visitor, generatedType); writeManagedInstanceGetManagedTypeMethod(visitor, generatedType); } private void writeManagedInstanceGetBackingNodeMethod(ClassVisitor visitor, Type generatedType) { MethodVisitor methodVisitor = declareMethod(visitor, "getBackingNode", GET_BACKING_NODE_METHOD_DESCRIPTOR, CONCRETE_SIGNATURE, ACC_PUBLIC | ACC_SYNTHETIC); putNodeStateFieldValueOnStack(methodVisitor, generatedType); methodVisitor.visitMethodInsn(INVOKEINTERFACE, MODEL_ELEMENT_STATE_TYPE_INTERNAL_NAME, "getBackingNode", GET_BACKING_NODE_METHOD_DESCRIPTOR, true); finishVisitingMethod(methodVisitor, ARETURN); } private void writeManagedInstanceGetManagedTypeMethod(ClassVisitor visitor, Type generatedType) { MethodVisitor managedTypeVisitor = declareMethod(visitor, "getManagedType", GET_MANAGED_TYPE_METHOD_DESCRIPTOR, CONCRETE_SIGNATURE, ACC_PUBLIC | ACC_SYNTHETIC); putManagedTypeFieldValueOnStack(managedTypeVisitor, generatedType); finishVisitingMethod(managedTypeVisitor, ARETURN); } private void assignStateField(MethodVisitor constructorVisitor, Type generatedType) { putThisOnStack(constructorVisitor); putFirstMethodArgumentOnStack(constructorVisitor); constructorVisitor.visitFieldInsn(PUTFIELD, generatedType.getInternalName(), STATE_FIELD_NAME, GENERATED_VIEW_STATE_TYPE.getDescriptor()); } private void assignTypeConverterField(MethodVisitor constructorVisitor, Type generatedType) { putThisOnStack(constructorVisitor); putSecondMethodArgumentOnStack(constructorVisitor); constructorVisitor.visitFieldInsn(PUTFIELD, generatedType.getInternalName(), TYPE_CONVERTER_FIELD_NAME, TYPE_CONVERTER_TYPE.getDescriptor()); } private void assignDelegateField(MethodVisitor constructorVisitor, Type generatedType, Type delegateType) { putThisOnStack(constructorVisitor); putThirdMethodArgumentOnStack(constructorVisitor); constructorVisitor.visitFieldInsn(PUTFIELD, generatedType.getInternalName(), DELEGATE_FIELD_NAME, delegateType.getDescriptor()); } private void setCanCallSettersField(MethodVisitor methodVisitor, Type generatedType, boolean canCallSetters) { putThisOnStack(methodVisitor); methodVisitor.visitLdcInsn(canCallSetters); methodVisitor.visitFieldInsn(PUTFIELD, generatedType.getInternalName(), CAN_CALL_SETTERS_FIELD_NAME, Type.BOOLEAN_TYPE.getDescriptor()); } private void writeViewMethods(ClassVisitor visitor, Type generatedType, Collection<ModelType<?>> viewTypes, StructBindings<?> bindings) { Type delegateType; StructSchema<?> delegateSchema = bindings.getDelegateSchema(); if (delegateSchema != null) { Class<?> delegateClass = delegateSchema.getType().getRawClass(); declareDelegateField(visitor, delegateClass); delegateType = Type.getType(delegateClass); } else { delegateType = null; } Multimap<String, ModelProperty<?>> viewPropertiesByNameBuilder = ArrayListMultimap.create(); Set<Wrapper<Method>> viewMethods = Sets.newLinkedHashSet(); for (StructSchema<?> viewSchema : bindings.getImplementedViewSchemas()) { for (ModelType<?> viewType : viewTypes) { if (viewType.equals(viewSchema.getType())) { for (ModelProperty<?> property : viewSchema.getProperties()) { String propertyName = property.getName(); viewPropertiesByNameBuilder.put(propertyName, property); } for (WeaklyTypeReferencingMethod<?, ?> viewMethod : viewSchema.getAllMethods()) { viewMethods.add(SIGNATURE_EQUIVALENCE.wrap(viewMethod.getMethod())); } break; } } } Class<?> viewClass = bindings.getPublicSchema().getType().getConcreteClass(); for (Collection<ModelProperty<?>> viewProperties : viewPropertiesByNameBuilder.asMap().values()) { writeViewPropertyDslMethods(visitor, generatedType, viewProperties, viewClass); } for (StructMethodBinding methodBinding : bindings.getMethodBindings()) { WeaklyTypeReferencingMethod<?, ?> weakViewMethod = methodBinding.getViewMethod(); Method viewMethod = weakViewMethod.getMethod(); // Don't generate method if it's not part of the view schema Wrapper<Method> methodKey = SIGNATURE_EQUIVALENCE.wrap(viewMethod); if (!viewMethods.contains(methodKey)) { continue; } if (methodBinding instanceof DirectMethodBinding) { // TODO:LPTR What is with the "metaClass" property here? boolean isGetterMethod = methodBinding.getAccessorType() == GET_GETTER || methodBinding.getAccessorType() == IS_GETTER; if (isGetterMethod && !Modifier.isFinal(viewMethod.getModifiers()) && !viewMethod.getName().equals("getMetaClass")) { writeNonAbstractMethodWrapper(visitor, generatedType, viewClass, viewMethod); } } else if (methodBinding instanceof BridgeMethodBinding) { writeBridgeMethod(visitor, generatedType, viewMethod); } else if (methodBinding instanceof DelegateMethodBinding) { writeDelegatingMethod(visitor, generatedType, delegateType, viewMethod); } else if (methodBinding instanceof ManagedPropertyMethodBinding) { ManagedPropertyMethodBinding propertyBinding = (ManagedPropertyMethodBinding) methodBinding; ManagedProperty<?> managedProperty = bindings.getManagedProperty(propertyBinding.getPropertyName()); String propertyName = managedProperty.getName(); Class<?> propertyClass = managedProperty.getType().getRawClass(); WeaklyTypeReferencingMethod<?, ?> propertyAccessor = propertyBinding.getViewMethod(); switch (propertyBinding.getAccessorType()) { case GET_GETTER: case IS_GETTER: writeGetter(visitor, generatedType, propertyName, propertyClass, propertyAccessor); break; case SETTER: writeSetter(visitor, generatedType, propertyName, propertyClass, propertyAccessor); break; default: throw new AssertionError(); } } else { throw new AssertionError(); } } } private void writeViewPropertyDslMethods(ClassVisitor visitor, Type generatedType, Collection<ModelProperty<?>> viewProperties, Class<?> viewClass) { boolean writable = Iterables.any(viewProperties, new Predicate<ModelProperty<?>>() { @Override public boolean apply(ModelProperty<?> viewProperty) { return viewProperty.isWritable(); } }); // TODO:LPTR Instead of the first view property, we should figure out these parameters from the actual property ModelProperty<?> firstProperty = viewProperties.iterator().next(); writeConfigureMethod(visitor, generatedType, firstProperty, writable); writeSetMethod(visitor, generatedType, firstProperty); writeTypeConvertingSetter(visitor, generatedType, viewClass, firstProperty); // TODO - this should be applied to all methods, including delegating methods writeReadOnlySetter(visitor, viewClass, writable, firstProperty); } private void writeReadOnlySetter(ClassVisitor visitor, Class<?> viewClass, boolean writable, ModelProperty<?> property) { if (!writable) { // Adds a void set$PropName(Object value) method that fails String setterName = "set" + StringUtils.capitalize(property.getName()); MethodVisitor methodVisitor = declareMethod(visitor, setterName, SET_OBJECT_PROPERTY_DESCRIPTOR, null, ACC_PUBLIC | ACC_SYNTHETIC); // throw new ReadOnlyPropertyException(name, <view-type>.class) methodVisitor.visitTypeInsn(NEW, READ_ONLY_PROPERTY_EXCEPTION_TYPE); methodVisitor.visitInsn(DUP); putConstantOnStack(methodVisitor, property.getName()); putClassOnStack(methodVisitor, viewClass); methodVisitor.visitMethodInsn(INVOKESPECIAL, READ_ONLY_PROPERTY_EXCEPTION_TYPE, "<init>", MISSING_PROPERTY_CONSTRUCTOR_DESCRIPTOR, false); finishVisitingMethod(methodVisitor, ATHROW); } } private void writeSetMethod(ClassVisitor visitor, Type generatedType, ModelProperty<?> property) { WeaklyTypeReferencingMethod<?, ?> setter = property.getAccessor(SETTER); if (setter != null && property.getSchema() instanceof ScalarValueSchema) { // TODO - should we support this? // Adds a void $propName(Object value) method that simply delegates to the converting setter method MethodVisitor methodVisitor = declareMethod(visitor, property.getName(), Type.getMethodDescriptor(Type.VOID_TYPE, OBJECT_TYPE), null); putThisOnStack(methodVisitor); putFirstMethodArgumentOnStack(methodVisitor); methodVisitor.visitMethodInsn(INVOKEVIRTUAL, generatedType.getInternalName(), setter.getName(), Type.getMethodDescriptor(Type.VOID_TYPE, OBJECT_TYPE), false); finishVisitingMethod(methodVisitor); } } private void writeConfigureMethod(ClassVisitor visitor, Type generatedType, ModelProperty<?> property, boolean writable) { if (!writable && 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); putNodeStateFieldValueOnStack(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 (!writable && 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.getGetter().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); } private void writeSetter(ClassVisitor visitor, Type generatedType, String propertyName, Class<?> propertyClass, WeaklyTypeReferencingMethod<?, ?> weakSetter) { Type propertyType = Type.getType(propertyClass); Label calledOutsideOfConstructor = new Label(); Method setter = weakSetter.getMethod(); // the regular typed setter String methodDescriptor = Type.getMethodDescriptor(Type.VOID_TYPE, propertyType); MethodVisitor methodVisitor = declareMethod(visitor, setter.getName(), methodDescriptor, AsmClassGeneratorUtils.signature(setter)); putCanCallSettersFieldValueOnStack(methodVisitor, generatedType); jumpToLabelIfStackEvaluatesToTrue(methodVisitor, calledOutsideOfConstructor); throwExceptionBecauseCalledOnItself(methodVisitor); methodVisitor.visitLabel(calledOutsideOfConstructor); putStateFieldValueOnStack(methodVisitor, generatedType); putConstantOnStack(methodVisitor, propertyName); putFirstMethodArgumentOnStack(methodVisitor, propertyType); if (propertyClass.isPrimitive()) { boxType(methodVisitor, propertyClass); } invokeStateSetMethod(methodVisitor); finishVisitingMethod(methodVisitor); } // the overload of type Object for Groovy coercions: public void setFoo(Object foo) private void writeTypeConvertingSetter(ClassVisitor visitor, Type generatedType, Class<?> viewClass, ModelProperty<?> property) { WeaklyTypeReferencingMethod<?, ?> weakSetter = property.getAccessor(SETTER); // There is no setter for this property if (weakSetter == null) { return; } if (!(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 = weakSetter.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 Label startTry = new Label(); methodVisitor.visitLabel(startTry); methodVisitor.visitMethodInsn(INVOKEINTERFACE, TYPE_CONVERTER_TYPE.getInternalName(), "convert", COERCE_TO_SCALAR_DESCRIPTOR, true); Label endTry = new Label(); methodVisitor.visitLabel(endTry); methodVisitor.visitTypeInsn(CHECKCAST, boxedType.getInternalName()); if (propertyClass.isPrimitive()) { unboxType(methodVisitor, propertyClass); } // invoke the typed setter methodVisitor.visitMethodInsn(INVOKEVIRTUAL, generatedType.getInternalName(), setter.getName(), Type.getMethodDescriptor(Type.VOID_TYPE, propertyType), false); methodVisitor.visitInsn(RETURN); // catch(TypeConversionException e) { throw ... } Label startCatch = new Label(); methodVisitor.visitLabel(startCatch); methodVisitor.visitTryCatchBlock(startTry, endTry, startCatch, TYPE_CONVERSION_EXCEPTION_TYPE.getInternalName()); methodVisitor.visitVarInsn(ASTORE, 2); // store thrown exception putClassOnStack(methodVisitor, viewClass); methodVisitor.visitLdcInsn(property.getName()); putFirstMethodArgumentOnStack(methodVisitor); methodVisitor.visitVarInsn(ALOAD, 2); methodVisitor.visitMethodInsn(INVOKESTATIC, Type.getInternalName(ManagedProxyClassGenerator.class), "propertyValueConvertFailure", Type.getMethodDescriptor(Type.VOID_TYPE, CLASS_TYPE, STRING_TYPE, OBJECT_TYPE, TYPE_CONVERSION_EXCEPTION_TYPE), false); finishVisitingMethod(methodVisitor); } private void writeHashCodeMethod(ClassVisitor visitor, Type generatedType) { MethodVisitor methodVisitor = declareMethod(visitor, "hashCode", HASH_CODE_METHOD_DESCRIPTOR, null); putStateFieldValueOnStack(methodVisitor, generatedType); methodVisitor.visitMethodInsn(INVOKEINTERFACE, GENERATED_VIEW_STATE_TYPE_NAME, "hashCode", HASH_CODE_METHOD_DESCRIPTOR, true); finishVisitingMethod(methodVisitor, Opcodes.IRETURN); } private void writeEqualsMethod(ClassVisitor cw, Type generatedType) { MethodVisitor methodVisitor = cw.visitMethod(Opcodes.ACC_PUBLIC, "equals", EQUALS_METHOD_DESCRIPTOR, null, null); methodVisitor.visitCode(); // if (arg == this) { return true; } methodVisitor.visitVarInsn(ALOAD, 0); methodVisitor.visitVarInsn(ALOAD, 1); Label notSameLabel = new Label(); methodVisitor.visitJumpInsn(IF_ACMPNE, notSameLabel); methodVisitor.visitInsn(ICONST_1); methodVisitor.visitInsn(IRETURN); // if (!(age instanceof GeneratedView)) { return false; } methodVisitor.visitLabel(notSameLabel); methodVisitor.visitVarInsn(ALOAD, 1); methodVisitor.visitTypeInsn(INSTANCEOF, GENERATED_VIEW_TYPE.getInternalName()); Label generatedViewLabel = new Label(); methodVisitor.visitJumpInsn(IFNE, generatedViewLabel); methodVisitor.visitInsn(ICONST_0); methodVisitor.visitInsn(IRETURN); // return state.equals(((GeneratedView)arg).__view_state()); methodVisitor.visitLabel(generatedViewLabel); putStateFieldValueOnStack(methodVisitor, generatedType); methodVisitor.visitVarInsn(ALOAD, 1); methodVisitor.visitTypeInsn(CHECKCAST, GENERATED_VIEW_TYPE.getInternalName()); methodVisitor.visitMethodInsn(INVOKEINTERFACE, GENERATED_VIEW_TYPE.getInternalName(), "__view_state__", GET_VIEW_STATE_METHOD_DESCRIPTOR, true); methodVisitor.visitMethodInsn(INVOKEINTERFACE, GENERATED_VIEW_STATE_TYPE_NAME, "equals", EQUALS_METHOD_DESCRIPTOR, true); finishVisitingMethod(methodVisitor, Opcodes.IRETURN); } 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 jumpToLabelIfStackEvaluatesToTrue(MethodVisitor methodVisitor, Label label) { methodVisitor.visitJumpInsn(IFNE, label); } private void invokeStateSetMethod(MethodVisitor methodVisitor) { methodVisitor.visitMethodInsn(INVOKEINTERFACE, GENERATED_VIEW_STATE_TYPE_NAME, "set", STATE_SET_METHOD_DESCRIPTOR, true); } private void putConstantOnStack(MethodVisitor methodVisitor, Object value) { methodVisitor.visitLdcInsn(value); } private MethodVisitor declareMethod(ClassVisitor visitor, Method method) { return declareMethod(visitor, method.getName(), Type.getMethodDescriptor(method)); } private MethodVisitor declareMethod(ClassVisitor visitor, String methodName, String methodDescriptor) { return declareMethod(visitor, methodName, methodDescriptor, CONCRETE_SIGNATURE); } private MethodVisitor declareMethod(ClassVisitor visitor, String methodName, String methodDescriptor, String methodSignature) { return declareMethod(visitor, methodName, methodDescriptor, methodSignature, ACC_PUBLIC); } private MethodVisitor declareMethod(ClassVisitor visitor, String methodName, String methodDescriptor, String methodSignature, int access) { MethodVisitor methodVisitor = visitor.visitMethod(access, methodName, methodDescriptor, methodSignature, NO_EXCEPTIONS); methodVisitor.visitCode(); return methodVisitor; } private void putFirstMethodArgumentOnStack(MethodVisitor methodVisitor, Type argType) { int loadCode = argType.getOpcode(ILOAD); methodVisitor.visitVarInsn(loadCode, 1); } private void putFirstMethodArgumentOnStack(MethodVisitor methodVisitor) { putFirstMethodArgumentOnStack(methodVisitor, OBJECT_TYPE); } private void putSecondMethodArgumentOnStack(MethodVisitor methodVisitor) { methodVisitor.visitVarInsn(ALOAD, 2); } private void putThirdMethodArgumentOnStack(MethodVisitor methodVisitor) { methodVisitor.visitVarInsn(ALOAD, 3); } private void putMethodArgumentOnStack(MethodVisitor methodVisitor, int index) { methodVisitor.visitVarInsn(ALOAD, index); } private void putMethodArgumentOnStack(MethodVisitor methodVisitor, Type type, int index) { methodVisitor.visitVarInsn(type.getOpcode(ILOAD), index); } private void putStateFieldValueOnStack(MethodVisitor methodVisitor, Type generatedType) { putFieldValueOnStack(methodVisitor, generatedType, STATE_FIELD_NAME, GENERATED_VIEW_STATE_TYPE); } private void putNodeStateFieldValueOnStack(MethodVisitor methodVisitor, Type generatedType) { putFieldValueOnStack(methodVisitor, generatedType, STATE_FIELD_NAME, GENERATED_VIEW_STATE_TYPE); methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, MODEL_ELEMENT_STATE_TYPE_INTERNAL_NAME); } private void putTypeConverterFieldValueOnStack(MethodVisitor methodVisitor, Type generatedType) { putFieldValueOnStack(methodVisitor, generatedType, TYPE_CONVERTER_FIELD_NAME, TYPE_CONVERTER_TYPE); } private void putManagedTypeFieldValueOnStack(MethodVisitor methodVisitor, Type generatedType) { putStaticFieldValueOnStack(methodVisitor, generatedType, MANAGED_TYPE_FIELD_NAME, MODEL_TYPE_TYPE); } private void putDelegateFieldValueOnStack(MethodVisitor methodVisitor, Type generatedType, Type delegateType) { putFieldValueOnStack(methodVisitor, generatedType, DELEGATE_FIELD_NAME, delegateType); } private void putCanCallSettersFieldValueOnStack(MethodVisitor methodVisitor, Type generatedType) { putFieldValueOnStack(methodVisitor, generatedType, CAN_CALL_SETTERS_FIELD_NAME, Type.BOOLEAN_TYPE); } private void putFieldValueOnStack(MethodVisitor methodVisitor, Type generatedType, String name, Type fieldType) { putThisOnStack(methodVisitor); methodVisitor.visitFieldInsn(GETFIELD, generatedType.getInternalName(), name, fieldType.getDescriptor()); } private void putStaticFieldValueOnStack(MethodVisitor methodVisitor, Type generatedType, String name, Type fieldType) { methodVisitor.visitFieldInsn(GETSTATIC, generatedType.getInternalName(), name, fieldType.getDescriptor()); } private void writeGetter(ClassVisitor visitor, Type generatedType, String propertyName, Class<?> propertyClass, WeaklyTypeReferencingMethod<?, ?> weakGetter) { Method getter = weakGetter.getMethod(); Type propertyType = Type.getType(propertyClass); MethodVisitor methodVisitor = declareMethod( visitor, getter.getName(), Type.getMethodDescriptor(propertyType), AsmClassGeneratorUtils.signature(getter)); putStateFieldValueOnStack(methodVisitor, generatedType); putConstantOnStack(methodVisitor, propertyName); invokeStateGetMethod(methodVisitor); castFirstStackElement(methodVisitor, propertyClass); finishVisitingMethod(methodVisitor, returnCode(propertyType)); } private int returnCode(Type returnType) { return returnType.getOpcode(IRETURN); } private void castFirstStackElement(MethodVisitor methodVisitor, Class<?> returnType) { if (returnType.isPrimitive()) { unboxType(methodVisitor, returnType); } else { methodVisitor.visitTypeInsn(CHECKCAST, Type.getInternalName(returnType)); } } 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 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 pushDefaultValue(MethodVisitor methodVisitor, Class<?> primitiveType) { int ins = ICONST_0; if (long.class == primitiveType) { ins = LCONST_0; } else if (double.class == primitiveType) { ins = DCONST_0; } else if (float.class == primitiveType) { ins = FCONST_0; } methodVisitor.visitInsn(ins); } private void invokeStateGetMethod(MethodVisitor methodVisitor) { methodVisitor.visitMethodInsn(INVOKEINTERFACE, GENERATED_VIEW_STATE_TYPE_NAME, "get", STATE_GET_METHOD_DESCRIPTOR, true); } private void writeNonAbstractMethodWrapper(ClassVisitor visitor, Type generatedType, Class<?> managedTypeClass, Method method) { Label start = new Label(); Label end = new Label(); Label handler = new Label(); MethodVisitor methodVisitor = declareMethod(visitor, method); methodVisitor.visitTryCatchBlock(start, end, handler, null); setCanCallSettersField(methodVisitor, generatedType, false); methodVisitor.visitLabel(start); invokeSuperMethod(methodVisitor, managedTypeClass, method); methodVisitor.visitLabel(end); setCanCallSettersField(methodVisitor, generatedType, true); methodVisitor.visitInsn(ARETURN); methodVisitor.visitLabel(handler); setCanCallSettersField(methodVisitor, generatedType, true); methodVisitor.visitInsn(ATHROW); methodVisitor.visitMaxs(0, 0); methodVisitor.visitEnd(); } private void writeBridgeMethod(ClassVisitor visitor, Type generatedType, Method method) { MethodVisitor methodVisitor = declareMethod(visitor, method.getName(), Type.getMethodDescriptor(method), AsmClassGeneratorUtils.signature(method)); invokeBridgedMethod(methodVisitor, generatedType, method); Class<?> returnType = method.getReturnType(); finishVisitingMethod(methodVisitor, returnCode(Type.getType(returnType))); } private void invokeBridgedMethod(MethodVisitor methodVisitor, Type generatedType, Method method) { putThisOnStack(methodVisitor); Class<?>[] parameterTypes = method.getParameterTypes(); for (int paramNo = 0; paramNo < parameterTypes.length; paramNo++) { putMethodArgumentOnStack(methodVisitor, Type.getType(parameterTypes[paramNo]), paramNo + 1); } methodVisitor.visitMethodInsn(INVOKEVIRTUAL, generatedType.getInternalName(), method.getName(), Type.getMethodDescriptor(method), false); } private void writeDelegatingMethod(ClassVisitor visitor, Type generatedType, Type delegateType, Method method) { MethodVisitor methodVisitor = declareMethod(visitor, method.getName(), Type.getMethodDescriptor(method), AsmClassGeneratorUtils.signature(method)); invokeDelegateMethod(methodVisitor, generatedType, delegateType, method); Class<?> returnType = method.getReturnType(); finishVisitingMethod(methodVisitor, returnCode(Type.getType(returnType))); } 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 invokeSuperMethod(MethodVisitor methodVisitor, Class<?> superClass, Method method) { putThisOnStack(methodVisitor); methodVisitor.visitMethodInsn(INVOKESPECIAL, Type.getInternalName(superClass), method.getName(), Type.getMethodDescriptor(method), false); } // Called from generated code on failure to convert the supplied value for a property to the property type @SuppressWarnings("unused") public static void propertyValueConvertFailure(Class<?> viewType, String propertyName, Object value, TypeConversionException failure) throws UnsupportedPropertyValueException { throw new UnsupportedPropertyValueException(String.format("Cannot set property: %s for class: %s to value: %s.", propertyName, viewType.getName(), value), failure); } public interface GeneratedView { @SuppressWarnings("unused") GeneratedViewState __view_state__(); } }