package act.util; /*- * #%L * ACT Framework * %% * Copyright (C) 2014 - 2017 ActFramework * %% * 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. * #L% */ import act.asm.Label; import act.asm.MethodVisitor; import act.asm.Opcodes; import act.asm.Type; import act.data.annotation.Data; import org.osgl.$; import org.osgl.util.C; import org.osgl.util.S; /** * Datastructure captures a class's meta information in related to {@link DataObjectEnhancer}. * The following info will be captured: * <ul> * <li>Is the class annotated with {@link Data} annotation</li> * <li>A list of {@link FieldMetaInfo} of all declared fields</li> * </ul> */ class ObjectMetaInfo implements Opcodes { /** * Datastructure captures a class's declared field meta info */ static class FieldMetaInfo implements Opcodes { private String name; private boolean isTransient = false; private boolean equalForce = false; private boolean equalIgnore = false; private Type type; FieldMetaInfo(String name, Type type, boolean isTransient) { this.name = $.NPE(name); this.type = $.NPE(type); this.isTransient = isTransient; } void setEqualForce() { equalForce = true; } void setEqualIgnore() { equalIgnore = true; } void addEqualInstructions(Type host, MethodVisitor mv, Label jumpTo) { if (!eligible()) { return; } String typeDesc = type.getDescriptor(); // ALOAD 0: this // ALOAD 2: that = (Type) obj (which is 1) mv.visitVarInsn(ALOAD, 2); mv.visitFieldInsn(GETFIELD, host.getInternalName(), name, typeDesc); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, host.getInternalName(), name, typeDesc); String s = typeDesc; if (s.length() > 1) { s = OBJECT_TYPE.getDescriptor(); } String op = "eq"; if (typeDesc.startsWith("[")) { // array needs deep eq op = "eq2"; } mv.visitMethodInsn(INVOKESTATIC, "org/osgl/Osgl", op, S.fmt("(%s%s)Z", s, s), false); mv.visitJumpInsn(IFEQ, jumpTo); } boolean addHashCodeInstruction(Type host, MethodVisitor mv) { if (!eligible()) { return false; } mv.visitVarInsn(ALOAD, 0); // load this pointer mv.visitFieldInsn(GETFIELD, host.getInternalName(), name, type.getDescriptor()); convertFromPrimaryType(type, mv); return true; } private void convertFromPrimaryType(Type fieldType, MethodVisitor mv) { switch (fieldType.getSort()) { case Type.BOOLEAN: mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;",false); break; case Type.BYTE: mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;",false); break; case Type.CHAR: mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;",false); break; case Type.SHORT: mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;",false); break; case Type.INT: mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;",false); break; case Type.LONG: mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;",false); break; case Type.FLOAT: mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;",false); break; case Type.DOUBLE: mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;",false); break; default: // do nothing } } boolean eligible() { return !equalIgnore && (!isTransient || equalForce); } } private Type superType; private Type type; private boolean callSuper; private C.List<FieldMetaInfo> fields = C.newList(); private boolean hasEqualMethod = false; private boolean hasHashCodeMethod = false; private boolean hasToStringMethod = false; private boolean hasAutoObjectAnnotation = false; private static Type OBJECT_TYPE = Type.getType(Object.class); ObjectMetaInfo(Type type, Type superType) { this.type = $.NPE(type); if (null != superType && !OBJECT_TYPE.equals(superType)) { this.superType = superType; } } Type type() { return type; } Type superType() { return superType; } C.List<FieldMetaInfo> fields() { return fields; } FieldMetaInfo addField(String fieldName, Type fieldType, boolean isTransient) { FieldMetaInfo fi = new FieldMetaInfo(fieldName, fieldType, isTransient); fields.add(fi); return fi; } void requireCallSuper() { callSuper = true; } boolean shouldCallSuper() { return callSuper && null != superType; } void equalMethodFound() { hasEqualMethod = true; } void hashCodeMethodFound() { hasHashCodeMethod = true; } void toStringMethodFound() { hasToStringMethod = true; } void autoObjectAnnotationFound() { hasAutoObjectAnnotation = true; } boolean hasDataAnnotation() { return hasAutoObjectAnnotation; } boolean shouldGenerateEqualsMethod() { return hasAutoObjectAnnotation && !hasEqualMethod; } boolean shouldGenerateHashCodeMethod() { return hasAutoObjectAnnotation && !hasHashCodeMethod; } }