View Javadoc

1   /*
2    * Copyright 2005 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at 
7    * 
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software 
11   * distributed under the License is distributed on an "AS IS" BASIS, 
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
13   * See the License for the specific language governing permissions and 
14   * limitations under the License.
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         // notify controller if class changed
124         if (annotated) {
125             control.noteUpdate();
126         }
127 
128 //^olsen: reenable
129 /*
130         //@olsen: do special annotation if detected super.clone()
131         if ((annotate & SuperClone) != 0) {
132             //final String superName = control.classFile().superName().asString();
133             //annotateClone(method, superName);
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         // first instruction is a target
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         // get the instruction arguments
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         // get the field's declaring class from the model
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         // check if field is known to be non-managed
208         if (meta.isKnownNonManagedField(declClassName, fieldName, fieldType)) {
209             return insn;
210         }
211 
212         // never annotate a jdo field; such may occur in pre-enhanced clone()
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         // call the PC's static accessor/mutator
242         final Insn frag = Insn.create(opc_invokestatic,
243                                       pool.addMethodRef(declClassName,
244                                                         methodName,
245                                                         methodSig));
246 
247         //insn.prev().insert(Insn.create(opc_nop));
248         // replace instruction
249         final Insn prev = insn.prev();
250         insn.remove();
251 
252         // replace current instruction with new fragment
253         final Insn last = prev.insert(frag);
254         return last;
255     }
256 
257     private void annotateClone(ClassMethod method,
258                                String superName)
259     {
260         //^olsen: extend for full support of inheritance on PC classes
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             // Found the clone method.  See if it is the flavor of clone()
277             // which does a super.clone() call, and if it is, add
278             // field initializations for the jdoStateManager and jdoFlags
279             // fields.
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             // check whether next instruction already is a downcast to a
302             // class implementing PersistenceCapable
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             // clear jdo fields of clone
320             {
321                 // duplicate downcasted reference
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                 // clear jdo fields
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                 // insert code
346                 insn.insert(newInsn);
347 
348                 // increase stack
349                 final int annotationStack = 3;
350                 codeAttr.setStackUsed(codeAttr.stackUsed() + annotationStack);
351             }
352         }
353     }
354 }