1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.jdo.impl.enhancer.core;
19
20 import java.util.Iterator;
21
22 import org.apache.jdo.impl.enhancer.classfile.ClassFile;
23 import org.apache.jdo.impl.enhancer.classfile.ClassMethod;
24 import org.apache.jdo.impl.enhancer.classfile.CodeAttribute;
25 import org.apache.jdo.impl.enhancer.classfile.ConstClass;
26 import org.apache.jdo.impl.enhancer.classfile.ConstFieldRef;
27 import org.apache.jdo.impl.enhancer.classfile.ConstMethodRef;
28 import org.apache.jdo.impl.enhancer.classfile.ConstNameAndType;
29 import org.apache.jdo.impl.enhancer.classfile.ConstantPool;
30 import org.apache.jdo.impl.enhancer.classfile.Descriptor;
31 import org.apache.jdo.impl.enhancer.classfile.Insn;
32 import org.apache.jdo.impl.enhancer.classfile.InsnConstOp;
33 import org.apache.jdo.impl.enhancer.classfile.VMConstants;
34 import org.apache.jdo.impl.enhancer.meta.EnhancerMetaData;
35 import org.apache.jdo.impl.enhancer.util.Support;
36
37
38
39
40
41 /***
42 * Handles the augmentation actions for a method.
43 */
44 class Annotater
45 extends Support
46 implements VMConstants
47 {
48 /***
49 * The classfile's enhancement controller.
50 */
51 private final Controller control;
52
53 /***
54 * The class analyzer for this class.
55 */
56 private final Analyzer analyzer;
57
58 /***
59 * The classfile to be enhanced.
60 */
61 private final ClassFile classFile;
62
63 /***
64 * The class name in user ('.' delimited) form.
65 */
66 private final String userClassName;
67
68 /***
69 * The classfile's constant pool.
70 */
71 private final ConstantPool pool;
72
73 /***
74 * Repository for the enhancer options.
75 */
76 private final Environment env;
77
78 /***
79 * Repository for JDO meta-data on classes.
80 */
81 private final EnhancerMetaData meta;
82
83 /***
84 * Constructor
85 */
86 public Annotater(Controller control,
87 Analyzer analyzer,
88 Environment env)
89 {
90 affirm(control != null);
91 affirm(analyzer != null);
92 affirm(env != null);
93
94 this.control = control;
95 this.analyzer = analyzer;
96 this.env = env;
97 this.meta = env.getEnhancerMetaData();
98 this.classFile = control.getClassFile();
99 this.userClassName = classFile.userClassName();
100 this.pool = classFile.pool();
101
102 affirm(classFile != null);
103 affirm(userClassName != null);
104 affirm(meta != null);
105 affirm(pool != null);
106 }
107
108 /***
109 * Performs necessary annotation actions on the class.
110 */
111 public void annotate()
112 {
113 affirm(analyzer.isAnnotateable() && !env.noAnnotate());
114 env.message("annotating class " + userClassName);
115
116 boolean annotated = false;
117 for (final Iterator i = analyzer.getAnnotatableMethods().iterator();
118 i.hasNext();) {
119 final ClassMethod method = (ClassMethod)i.next();
120 annotated |= annotated(method);
121 }
122
123
124 if (annotated) {
125 control.noteUpdate();
126 }
127
128
129
130
131
132
133
134
135
136 }
137
138 /***
139 * Annotate the class method. For now, brute force rules.
140 */
141 private boolean annotated(ClassMethod method)
142 {
143 boolean annotated = false;
144 final CodeAttribute codeAttr = method.codeAttribute();
145 if (codeAttr == null) {
146 return annotated;
147 }
148
149 env.message(
150 "annotating: " + userClassName
151 + "." + method.name().asString()
152 + Descriptor.userMethodArgs(method.signature().asString()));
153
154
155 final Insn firstInsn = codeAttr.theCode();
156 affirm(firstInsn.opcode() == Insn.opc_target);
157 Insn insn = firstInsn.next();
158 while (insn != null) {
159 switch(insn.opcode()) {
160 case opc_getfield:
161 case opc_putfield:
162 final Insn newInsn = insnAnnotation(insn);
163 if (insn != newInsn) {
164 annotated = true;
165 }
166 insn = newInsn;
167 break;
168 default:
169 }
170
171 insn = insn.next();
172 }
173
174 return annotated;
175 }
176
177 /***
178 * Generate annotations for put/getfield instructions.
179 */
180 private Insn insnAnnotation(final Insn insn)
181 {
182 if (false) {
183 System.out.println("MethodAnnotator.insnAnnotation(): ");
184 insn.printInsn(System.out);
185 System.out.println();
186 }
187
188 affirm(insn.opcode() == opc_getfield || insn.opcode() == opc_putfield);
189 final boolean isGet = (insn.opcode() == opc_getfield);
190
191
192 final InsnConstOp fieldInsn = (InsnConstOp)insn;
193 final ConstFieldRef fieldRef = (ConstFieldRef)fieldInsn.value();
194
195 final ConstNameAndType fieldNameAndType = fieldRef.nameAndType();
196 final String fieldName = fieldNameAndType.name().asString();
197 final String fieldType = fieldNameAndType.signature().asString();
198
199 final String qualifyingClassName = fieldRef.className().asString();
200
201 final String declClassName =
202 meta.getDeclaringClass(qualifyingClassName, fieldName);
203 affirm(declClassName != null, "Cannot get declaring class of "
204 + qualifyingClassName + "." + fieldName);
205 final ConstClass declClass = pool.addClass(declClassName);
206
207
208 if (meta.isKnownNonManagedField(declClassName, fieldName, fieldType)) {
209 return insn;
210 }
211
212
213 if (meta.isPersistenceCapableClass(declClassName)
214 && (fieldName.equals(JDOConstants.JDO_PC_jdoStateManager_Name)
215 || fieldName.equals(JDOConstants.JDO_PC_jdoFlags_Name))) {
216 return insn;
217 }
218
219 if (false) {
220 System.out.println(" " + (isGet ? "get" : "put") + "field "
221 + declClassName + "." + fieldName
222 + " : " + fieldType);
223 }
224
225 final String methodName;
226 final String methodSig;
227 if (isGet) {
228 methodName = "jdoGet" + fieldName;
229 methodSig = "(L" + declClassName + ";)" + fieldType;
230 } else {
231 methodName = "jdoSet" + fieldName;
232 methodSig = "(L" + declClassName + ";" + fieldType + ")V";
233 }
234
235 if (false) {
236 System.out.println(" "
237 + declClassName + "." + methodName
238 + " : " + methodSig);
239 }
240
241
242 final Insn frag = Insn.create(opc_invokestatic,
243 pool.addMethodRef(declClassName,
244 methodName,
245 methodSig));
246
247
248
249 final Insn prev = insn.prev();
250 insn.remove();
251
252
253 final Insn last = prev.insert(frag);
254 return last;
255 }
256
257 private void annotateClone(ClassMethod method,
258 String superName)
259 {
260
261
262 if (false) {
263 final String methodName = method.name().asString();
264 final String methodSig = method.signature().asString();
265 System.out.println("annotateClone()");
266 System.out.println(" methodName = " + methodName);
267 System.out.println(" methodSig = " + methodSig);
268 System.out.println(" superName = " + superName);
269 }
270
271 final CodeAttribute codeAttr = method.codeAttribute();
272 for (Insn insn = codeAttr.theCode();
273 insn != null;
274 insn = insn.next()) {
275
276
277
278
279
280 if (insn.opcode() != opc_invokespecial)
281 continue;
282
283 final InsnConstOp invoke = (InsnConstOp)insn;
284 final ConstMethodRef methodRef = (ConstMethodRef)invoke.value();
285 final ConstNameAndType methodNT = methodRef.nameAndType();
286 final String methodName = methodNT.name().asString();
287 final String methodSig = methodNT.signature().asString();
288
289 if (!(methodName.equals("clone")
290 && methodSig.equals("()Ljava/lang/Object;")))
291 continue;
292
293 if (false) {
294 final ConstClass methodClass = methodRef.className();
295 final String methodClassName = methodClass.asString();
296 System.out.println(" found invocation of: "
297 + methodClassName
298 + "." + methodName + methodSig);
299 }
300
301
302
303 final String thisClass = classFile.classNameString();
304 final Insn checkCastInsn = insn.next();
305 final boolean needCheckcast;
306 if (checkCastInsn.opcode() != opc_checkcast) {
307 needCheckcast = true;
308 } else {
309 ConstClass target =
310 (ConstClass) ((InsnConstOp)checkCastInsn).value();
311 if (target.asString().equals(thisClass)) {
312 insn = checkCastInsn;
313 needCheckcast = false;
314 } else {
315 needCheckcast = true;
316 }
317 }
318
319
320 {
321
322 final Insn newInsn = Insn.create(opc_dup);
323 if (needCheckcast) {
324 newInsn.append(Insn.create(opc_checkcast,
325 pool.addClass(thisClass)));
326 }
327 newInsn.append(Insn.create(opc_dup));
328
329
330 newInsn.append(Insn.create(opc_aconst_null));
331 newInsn.append(Insn.create(
332 opc_putfield,
333 pool.addFieldRef(
334 thisClass,
335 JDOConstants.JDO_PC_jdoStateManager_Name,
336 JDOConstants.JDO_PC_jdoStateManager_Sig)));
337 newInsn.append(Insn.create(opc_iconst_0));
338 newInsn.append(Insn.create(
339 opc_putfield,
340 pool.addFieldRef(
341 thisClass,
342 JDOConstants.JDO_PC_jdoFlags_Name,
343 JDOConstants.JDO_PC_jdoFlags_Sig)));
344
345
346 insn.insert(newInsn);
347
348
349 final int annotationStack = 3;
350 codeAttr.setStackUsed(codeAttr.stackUsed() + annotationStack);
351 }
352 }
353 }
354 }