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