001package io.ebean.enhance.entity; 002 003import io.ebean.enhance.asm.ClassVisitor; 004import io.ebean.enhance.asm.FieldVisitor; 005import io.ebean.enhance.asm.Label; 006import io.ebean.enhance.asm.MethodVisitor; 007import io.ebean.enhance.asm.Opcodes; 008import io.ebean.enhance.common.ClassMeta; 009import io.ebean.enhance.common.VisitUtil; 010 011import java.util.List; 012 013import static io.ebean.enhance.common.EnhanceConstants.CLINIT; 014import static io.ebean.enhance.common.EnhanceConstants.INIT; 015import static io.ebean.enhance.common.EnhanceConstants.NOARG_VOID; 016 017/** 018 * Generate the methods based on the list of fields. 019 * <p> 020 * This includes the createCopy, getField and setField methods etc. 021 * </p> 022 */ 023public class IndexFieldWeaver implements Opcodes { 024 025 private static final String _EBEAN_PROPS = "_ebean_props"; 026 027 public static void addPropertiesField(ClassVisitor cv) { 028 FieldVisitor fv = cv.visitField(ACC_PUBLIC + ACC_STATIC, _EBEAN_PROPS, "[Ljava/lang/String;", null, null); 029 fv.visitEnd(); 030 } 031 032 public static void addPropertiesInit(ClassVisitor cv, ClassMeta classMeta) { 033 MethodVisitor mv = cv.visitMethod(ACC_STATIC, CLINIT, NOARG_VOID, null, null); 034 mv.visitCode(); 035 addPropertiesInit(mv, classMeta); 036 037 Label l1 = new Label(); 038 mv.visitLabel(l1); 039 mv.visitLineNumber(1, l1); 040 mv.visitInsn(RETURN); 041 mv.visitMaxs(4, 0); 042 mv.visitEnd(); 043 } 044 045 public static void addPropertiesInit(MethodVisitor mv, ClassMeta classMeta) { 046 047 List<FieldMeta> fields = classMeta.getAllFields(); 048 049 Label l0 = new Label(); 050 mv.visitLabel(l0); 051 mv.visitLineNumber(1, l0); 052 VisitUtil.visitIntInsn(mv, fields.size()); 053 mv.visitTypeInsn(ANEWARRAY, "java/lang/String"); 054 055 if (fields.isEmpty()) { 056 if (classMeta.isLog(3)) { 057 classMeta.log("Has no fields?"); 058 } 059 060 } else { 061 for (int i=0; i<fields.size(); i++) { 062 FieldMeta field = fields.get(i); 063 mv.visitInsn(DUP); 064 VisitUtil.visitIntInsn(mv, i); 065 mv.visitLdcInsn(field.getName()); 066 mv.visitInsn(AASTORE); 067 } 068 } 069 070 mv.visitFieldInsn(PUTSTATIC, classMeta.getClassName(), _EBEAN_PROPS, "[Ljava/lang/String;"); 071 } 072 073 074 public static void addGetPropertyNames(ClassVisitor cv, ClassMeta classMeta) { 075 076 MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, "_ebean_getPropertyNames", "()[Ljava/lang/String;", null, null); 077 mv.visitCode(); 078 Label l0 = new Label(); 079 mv.visitLabel(l0); 080 mv.visitLineNumber(13, l0); 081 mv.visitFieldInsn(GETSTATIC, classMeta.getClassName(), _EBEAN_PROPS, "[Ljava/lang/String;"); 082 mv.visitInsn(ARETURN); 083 Label l1 = new Label(); 084 mv.visitLabel(l1); 085 mv.visitLocalVariable("this", "L" + classMeta.getClassName() + ";", null, l0, l1, 0); 086 mv.visitMaxs(1, 1); 087 mv.visitEnd(); 088 } 089 090 public static void addGetPropertyName(ClassVisitor cv, ClassMeta classMeta) { 091 MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, "_ebean_getPropertyName", "(I)Ljava/lang/String;", null, null); 092 mv.visitCode(); 093 Label l0 = new Label(); 094 mv.visitLabel(l0); 095 mv.visitLineNumber(16, l0); 096 mv.visitFieldInsn(GETSTATIC, classMeta.getClassName(), _EBEAN_PROPS, "[Ljava/lang/String;"); 097 mv.visitVarInsn(ILOAD, 1); 098 mv.visitInsn(AALOAD); 099 mv.visitInsn(ARETURN); 100 Label l1 = new Label(); 101 mv.visitLabel(l1); 102 mv.visitLocalVariable("this", "L" + classMeta.getClassName() + ";", null, l0, l1, 0); 103 mv.visitLocalVariable("pos", "I", null, l0, l1, 1); 104 mv.visitMaxs(2, 2); 105 mv.visitEnd(); 106 } 107 108 public static void addMethods(ClassVisitor cv, ClassMeta classMeta) { 109 110 List<FieldMeta> fields = classMeta.getAllFields(); 111 if (fields.isEmpty()) { 112 return; 113 } 114 115 if (classMeta.isLog(3)) { 116 classMeta.log("fields size:" + fields.size()+" "+fields.toString()); 117 } 118 119 generateGetField(cv, classMeta, fields, false); 120 generateGetField(cv, classMeta, fields, true); 121 122 generateSetField(cv, classMeta, fields, false); 123 generateSetField(cv, classMeta, fields, true); 124 125 if (classMeta.hasEqualsOrHashCode()) { 126 // equals or hashCode is already implemented 127 if (classMeta.isLog(3)) { 128 classMeta.log("... skipping add equals() ... already has equals() hashcode() methods"); 129 } 130 return; 131 } 132 133 // search for the id field... 134 int idIndex = -1; 135 FieldMeta idFieldMeta = null; 136 137 // find id field only local to this class 138 for (int i = 0; i < fields.size(); i++) { 139 FieldMeta fieldMeta = fields.get(i); 140 if (fieldMeta.isId() && fieldMeta.isLocalField(classMeta)) { 141 if (idIndex == -1) { 142 // we have found an id field 143 idIndex = i; 144 idFieldMeta = fieldMeta; 145 } else { 146 // there are 2 or more id fields 147 idIndex = -2; 148 } 149 } 150 } 151 152 if (idIndex == -2) { 153 // there are 2 or more id fields? 154 if (classMeta.isLog(1)) { 155 classMeta.log("has 2 or more id fields. Not adding equals() method."); 156 } 157 158 } else if (idIndex == -1) { 159 // there are no id fields local to this type 160 if (classMeta.isLog(3)) { 161 classMeta.log("has no id fields on this type. Not adding equals() method. Expected when Id property on superclass."); 162 } 163 164 } else { 165 // add the _ebean_getIdentity(), equals() and hashCode() methods 166 MethodEquals.addMethods(cv, classMeta, idIndex, idFieldMeta); 167 } 168 } 169 170 /** 171 * Generate the invokeGet method. 172 */ 173 private static void generateGetField(ClassVisitor cv, ClassMeta classMeta, List<FieldMeta> fields, boolean intercept) { 174 175 String className = classMeta.getClassName(); 176 177 MethodVisitor mv; 178 if (intercept) { 179 mv = cv.visitMethod(ACC_PUBLIC, "_ebean_getFieldIntercept", "(I)Ljava/lang/Object;",null, null); 180 } else { 181 mv = cv.visitMethod(ACC_PUBLIC, "_ebean_getField", "(I)Ljava/lang/Object;", null, null); 182 } 183 184 mv.visitCode(); 185 Label l0 = new Label(); 186 mv.visitLabel(l0); 187 mv.visitLineNumber(1, l0); 188 mv.visitVarInsn(ILOAD, 1); 189 190 Label[] switchLabels = new Label[fields.size()]; 191 for (int i = 0; i < switchLabels.length; i++) { 192 switchLabels[i] = new Label(); 193 } 194 195 int maxIndex = switchLabels.length - 1; 196 197 Label labelException = new Label(); 198 mv.visitTableSwitchInsn(0, maxIndex, labelException, switchLabels); 199 200 for (int i = 0; i < fields.size(); i++) { 201 202 FieldMeta fieldMeta = fields.get(i); 203 204 mv.visitLabel(switchLabels[i]); 205 mv.visitLineNumber(1, switchLabels[i]); 206 mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); 207 mv.visitVarInsn(ALOAD, 0); 208 209 fieldMeta.appendSwitchGet(mv, classMeta, intercept); 210 211 mv.visitInsn(ARETURN); 212 } 213 214 mv.visitLabel(labelException); 215 mv.visitLineNumber(1, labelException); 216 mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); 217 mv.visitTypeInsn(NEW, "java/lang/RuntimeException"); 218 mv.visitInsn(DUP); 219 mv.visitTypeInsn(NEW, "java/lang/StringBuilder"); 220 mv.visitInsn(DUP); 221 mv.visitLdcInsn("Invalid index "); 222 mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", INIT, "(Ljava/lang/String;)V", false); 223 mv.visitVarInsn(ILOAD, 1); 224 mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;", false); 225 mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false); 226 mv.visitMethodInsn(INVOKESPECIAL, "java/lang/RuntimeException", INIT, "(Ljava/lang/String;)V", false); 227 mv.visitInsn(ATHROW); 228 229 Label l5 = new Label(); 230 mv.visitLabel(l5); 231 mv.visitLocalVariable("this", "L" + className + ";", null, l0, l5, 0); 232 mv.visitLocalVariable("index", "I", null, l0, l5, 1); 233 mv.visitMaxs(5, 2); 234 mv.visitEnd(); 235 } 236 237 /** 238 * Generate the _ebean_setField or _ebean_setFieldBypass method. 239 * <p> 240 * Bypass will bypass the interception. The interception checks that the 241 * property has been loaded and creates oldValues if the bean is being made 242 * dirty for the first time. 243 * </p> 244 */ 245 private static void generateSetField(ClassVisitor cv, ClassMeta classMeta, List<FieldMeta> fields,boolean intercept) { 246 247 248 String className = classMeta.getClassName(); 249 250 MethodVisitor mv; 251 if (intercept) { 252 mv = cv.visitMethod(ACC_PUBLIC, "_ebean_setFieldIntercept", "(ILjava/lang/Object;)V", 253 null, null); 254 } else { 255 mv = cv.visitMethod(ACC_PUBLIC, "_ebean_setField", "(ILjava/lang/Object;)V", null, null); 256 } 257 258 mv.visitCode(); 259 Label l0 = new Label(); 260 mv.visitLabel(l0); 261 mv.visitLineNumber(1, l0); 262 263 Label l1 = new Label(); 264 mv.visitLabel(l1); 265 mv.visitLineNumber(1, l1); 266 mv.visitVarInsn(ILOAD, 1); 267 268 Label[] switchLabels = new Label[fields.size()]; 269 for (int i = 0; i < switchLabels.length; i++) { 270 switchLabels[i] = new Label(); 271 } 272 273 Label labelException = new Label(); 274 275 int maxIndex = switchLabels.length - 1; 276 277 mv.visitTableSwitchInsn(0, maxIndex, labelException, switchLabels); 278 279 for (int i = 0; i < fields.size(); i++) { 280 281 FieldMeta fieldMeta = fields.get(i); 282 283 mv.visitLabel(switchLabels[i]); 284 mv.visitLineNumber(1, switchLabels[i]); 285 286 mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); 287 mv.visitVarInsn(ALOAD, 0); 288 mv.visitVarInsn(ALOAD, 2); 289 290 fieldMeta.appendSwitchSet(mv, classMeta, intercept); 291 292 Label l6 = new Label(); 293 mv.visitLabel(l6); 294 mv.visitLineNumber(1, l6); 295 mv.visitInsn(RETURN); 296 } 297 298 mv.visitLabel(labelException); 299 mv.visitLineNumber(1, labelException); 300 mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); 301 mv.visitTypeInsn(NEW, "java/lang/RuntimeException"); 302 mv.visitInsn(DUP); 303 mv.visitTypeInsn(NEW, "java/lang/StringBuilder"); 304 mv.visitInsn(DUP); 305 mv.visitLdcInsn("Invalid index "); 306 mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", INIT, "(Ljava/lang/String;)V", false); 307 mv.visitVarInsn(ILOAD, 1); 308 mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;", false); 309 mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false); 310 mv.visitMethodInsn(INVOKESPECIAL, "java/lang/RuntimeException", INIT, "(Ljava/lang/String;)V", false); 311 mv.visitInsn(ATHROW); 312 Label l9 = new Label(); 313 mv.visitLabel(l9); 314 mv.visitLocalVariable("this", "L" + className + ";", null, l0, l9, 0); 315 mv.visitLocalVariable("index", "I", null, l0, l9, 1); 316 mv.visitLocalVariable("o", "Ljava/lang/Object;", null, l0, l9, 2); 317 mv.visitLocalVariable("arg", "Ljava/lang/Object;", null, l0, l9, 3); 318 mv.visitLocalVariable("p", "L" + className + ";", null, l1, l9, 4); 319 mv.visitMaxs(5, 5); 320 mv.visitEnd(); 321 } 322 323}