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