// 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); }
/** * check whether a class should not be considered for transformation * * @param clazz the class to check * @return true if clazz should not be considered for transformation otherwise false */ protected boolean isSkipClass(Class<?> clazz) { if (!inst.isModifiableClass(clazz)) { return true; } // we can safely skip array classes, interfaces and primitive classes if (clazz.isArray()) { return true; } if (clazz.isInterface()) { return true; } if (clazz.isPrimitive()) { return true; } String name = clazz.getName(); if (isBytemanClass(name) || !isTransformable(name)) { return true; } return false; }
private void castFirstStackElement(MethodVisitor methodVisitor, Class<?> returnType) { if (returnType.isPrimitive()) { unboxType(methodVisitor, returnType); } else { methodVisitor.visitTypeInsn(CHECKCAST, Type.getInternalName(returnType)); } }
private void writeSetter(ClassVisitor visitor, Type generatedType, ModelProperty<?> property) { WeaklyTypeReferencingMethod<?, Void> weakSetter = property.getSetter(); // There is no setter for this property if (weakSetter == null) { return; } String propertyName = property.getName(); Class<?> propertyClass = property.getType().getConcreteClass(); 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); }
private void visitType(java.lang.reflect.Type type, StringBuilder builder) { if (type instanceof Class) { Class<?> cl = (Class<?>) type; if (cl.isPrimitive()) { builder.append(Type.getType(cl).getDescriptor()); } else { if (cl.isArray()) { builder.append(cl.getName().replace('.', '/')); } else { builder.append('L'); builder.append(cl.getName().replace('.', '/')); builder.append(';'); } } } else if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) type; visitNested(parameterizedType.getRawType(), builder); builder.append('<'); for (java.lang.reflect.Type param : parameterizedType.getActualTypeArguments()) { visitType(param, builder); } builder.append(">;"); } else if (type instanceof WildcardType) { WildcardType wildcardType = (WildcardType) type; if (wildcardType.getUpperBounds().length == 1 && wildcardType.getUpperBounds()[0].equals(Object.class)) { if (wildcardType.getLowerBounds().length == 0) { builder.append('*'); return; } } else { for (java.lang.reflect.Type upperType : wildcardType.getUpperBounds()) { builder.append('+'); visitType(upperType, builder); } } for (java.lang.reflect.Type lowerType : wildcardType.getLowerBounds()) { builder.append('-'); visitType(lowerType, builder); } } else if (type instanceof TypeVariable) { TypeVariable<?> typeVar = (TypeVariable) type; builder.append('T'); builder.append(typeVar.getName()); builder.append(';'); } else if (type instanceof GenericArrayType) { GenericArrayType arrayType = (GenericArrayType) type; builder.append('['); visitType(arrayType.getGenericComponentType(), builder); } else { throw new IllegalArgumentException( String.format("Cannot generate signature for %s.", type)); } }
private void visitNested(java.lang.reflect.Type type, StringBuilder builder) { if (type instanceof Class) { Class<?> cl = (Class<?>) type; if (cl.isPrimitive()) { builder.append(Type.getType(cl).getDescriptor()); } else { builder.append('L'); builder.append(cl.getName().replace('.', '/')); } } else { visitType(type, builder); } }
protected void injectSetByIndex( ClassWriter classWriter, String targetClassName, List<Field> fields) { MethodVisitor methodVisitor = classWriter.visitMethod( ACC_PUBLIC, "set", "(Ljava/lang/Object;ILjava/lang/Object;)V", null, new String[] {getInternalName(ILLEGAL_ACCESS_EXCEPTION.getCanonicalName())}); Boxer boxer = new Boxer(methodVisitor); methodVisitor.visitCode(); methodVisitor.visitVarInsn(ILOAD, 2); int maxStack = 6; Label[] labels = new Label[fields.size()]; Label errorLabel = new Label(); for (int i = 0; i < fields.size(); i++) { labels[i] = new Label(); } methodVisitor.visitTableSwitchInsn(0, labels.length - 1, errorLabel, labels); if (!fields.isEmpty()) { maxStack--; for (int i = 0; i < fields.size(); i++) { Field field = fields.get(i); Class<?> type = field.getType(); Class<?> inputType = Boxer.wrap(type); String fieldDescriptor = Type.getDescriptor(type); String inputPath = getInternalName(inputType.getName()); methodVisitor.visitLabel(labels[i]); if (i == 0) methodVisitor.visitFrame(F_APPEND, 1, new Object[] {targetClassName}, 0, null); else methodVisitor.visitFrame(F_SAME, 0, null, 0, null); if (isPublic(field) && !isFinal(field)) { methodVisitor.visitVarInsn(ALOAD, 1); methodVisitor.visitTypeInsn(CHECKCAST, targetClassName); methodVisitor.visitVarInsn(ALOAD, 3); if (!type.isPrimitive()) { methodVisitor.visitTypeInsn(CHECKCAST, inputPath); } else { boxer.unbox(Type.getType(type)); } methodVisitor.visitFieldInsn(PUTFIELD, targetClassName, field.getName(), fieldDescriptor); } else { injectReflectiveSetter(methodVisitor); } methodVisitor.visitInsn(RETURN); } methodVisitor.visitLabel(errorLabel); methodVisitor.visitFrame(F_SAME, 0, null, 0, null); } injectException(methodVisitor, IllegalAccessException.class); methodVisitor.visitMaxs(maxStack, 4); methodVisitor.visitEnd(); }