KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > speedo > generation > enhancer > ClassAccessorModifier


1 /**
2  * Copyright (C) 2001-2004 France Telecom R&D
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  */

18 package org.objectweb.speedo.generation.enhancer;
19
20 import org.objectweb.asm.CodeAdapter;
21 import org.objectweb.asm.CodeVisitor;
22 import org.objectweb.asm.Constants;
23 import org.objectweb.asm.ClassVisitor;
24 import org.objectweb.asm.Label;
25 import org.objectweb.asm.Type;
26 import org.objectweb.asm.Attribute;
27 import org.objectweb.speedo.metadata.SpeedoField;
28 import org.objectweb.speedo.metadata.SpeedoClass;
29 import org.objectweb.speedo.generation.lib.NamingRules;
30 import org.objectweb.util.monolog.api.Logger;
31 import org.objectweb.util.monolog.api.BasicLevel;
32
33 import java.util.List JavaDoc;
34 import java.util.ArrayList JavaDoc;
35 import java.util.Collection JavaDoc;
36
37 /**
38  * Replaces field accesses by calls to getter and setter methods.
39  *
40  * Adapted from modifyMethods and replaceInstruction in EnhancerTool.
41  */

42 public class ClassAccessorModifier extends LoggedClassAdapter {
43
44     /**
45      * Stack size variation corresponding to each JVM instruction. This stack
46      * variation is equal to the size of the values produced by an instruction,
47      * minus the size of the values consumed by this instruction.
48      */

49
50     final static int[] SIZE;
51
52     /**
53      * Computes the stack size variation corresponding to each JVM instruction.
54      * Copied from the org.objectweb.asm.CodeWriter class.
55      */

56
57     static {
58         int i;
59         int[] b = new int[202];
60         String JavaDoc s =
61                 "EFFFFFFFFGGFFFGGFFFEEFGFGFEEEEEEEEEEEEEEEEEEEEDEDEDDDDDCDCDEEEEEEEEE" +
62                 "EEEEEEEEEEEBABABBBBDCFFFGGGEDCDCDCDCDCDCDCDCDCDCEEEEDDDDDDDCDCDCEFEF" +
63                 "DDEEFFDEDEEEBDDBBDDDDDDCCCCCCCCEFEDDDCDCDEEEEEEEEEEFEEEEEEDDEEDDEE";
64         for (i = 0; i < b.length; ++i) {
65             b[i] = s.charAt(i) - 'E';
66         }
67         SIZE = b;
68     }
69
70     /**
71      * A collections of SpeedoXMLDescriptors describing known persistent classes.
72      */

73     private final Collection JavaDoc xmlDescriptors;
74
75     /**
76      * Internal name of the visited class.
77      */

78     String JavaDoc owner;
79
80     /**
81      * Internal name of the corresponding "XXXFields" class.
82      */

83     String JavaDoc fieldsOwner;
84
85     int nbfields = 128;
86
87     /**
88      * Constructs a new {@link ClassAccessorModifier}.
89      *
90      * @param cv the class visitor to be used to generate the modified class
91      * @param xmlDescriptors the JDO Model describing known JDO Instances.
92      */

93     public ClassAccessorModifier(final ClassVisitor cv,
94                                  final Collection JavaDoc xmlDescriptors,
95                                  Logger logger) {
96         super(cv, logger);
97         this.xmlDescriptors = xmlDescriptors;
98     }
99
100     // IMPLEMENTATION OF THE ClassVisitor INTERFACE //
101
// ---------------------------------------------//
102

103     public void visit(final int version, final int access,
104                       final String JavaDoc name,
105                       final String JavaDoc superName,
106                       final String JavaDoc[] interfaces,
107                       final String JavaDoc sourceFile) {
108         cv.visit(version, access, name, superName, interfaces, sourceFile);
109         owner = name;
110         nbfields = Util.isPersistentCapable(owner.replace('/', '.'), xmlDescriptors).computeFieldNumbers();
111         fieldsOwner = NamingRules.fieldsName(owner);
112     }
113
114     public CodeVisitor visitMethod(final int access,
115                                    final String JavaDoc name,
116                                    final String JavaDoc desc,
117                                    final String JavaDoc[] exceptions,
118                                    final Attribute attrs) {
119         CodeVisitor _cv = this.cv.visitMethod(access, name, desc, exceptions, attrs);
120         if ((access & Constants.ACC_ABSTRACT) != 0) {
121             // nothing to be modified for abstract methods
122
logger.log(BasicLevel.DEBUG,
123                     "ignore the abstract method " + name + " " + desc);
124             return _cv;
125         }
126         if (name.startsWith("jdo")
127                 && !name.equals("jdoPreDelete")
128                 && !name.equals("jdoPreStore")
129                 && !name.equals("jdoPreClear")
130                 && !name.equals("jdoPostLoad")
131                 ) {
132             // do not modify the accessor methods generated by the enhancer
133
logger.log(BasicLevel.DEBUG,
134                     "ignoe the method " + name + " " + desc);
135             return _cv;
136         }
137         if (name.equals("<clinit>") || (access & Constants.ACC_STATIC) != 0) {
138             // for static methods replace accesses to fields by calls to
139
// accessor methods.
140
return new DefaultCodeAccessorModifier(desc, false, _cv, name, this);
141         } else {
142             // Fields accessing to 'this' are replaced by field accesses to
143
// the state object (the added actual parameter), and a header
144
// is added to call read or jdoWriteIntention. In case of the
145
// constructor the call to jdoRead or jdoWriteIntention is added
146
// after the super call.
147
return new OptimisticCodeAccessorModifier(desc, _cv, name, this);
148         }
149     }
150
151     // OTHER METHODS //
152
// --------------//
153

154     /**
155      * Looks for a specific <code>SpeedoField</code> in the object model.
156      *
157      * @param name the name of the field to be fetched
158      * @param className the complete class name that the field belongs to
159      * @return the corresponding SpeedoField if it exists, null either
160      */

161     SpeedoField fetchJDOField(final String JavaDoc name, final String JavaDoc className) {
162         SpeedoClass c = Util.isPersistentCapable(className, xmlDescriptors);
163         if (c != null) {
164             SpeedoField speedoField = null;
165             //try to find the field in the current class
166
// and in its parents (if any)
167
while (speedoField == null && c != null) {
168                 speedoField = (SpeedoField) c.jdoField.get(name);
169                 c = c.getSuper();
170             }
171             return speedoField;
172         } else {
173             return null;
174         }
175     }
176
177 }
178     /**
179      * The default method code modifier, used for constructors, static methods
180      * and class initializers. This code modifier does not modify accesses to
181      * 'this' fields, but replaces accesses to other fields by calls to accessor
182      * methods.
183      */

184     class DefaultCodeAccessorModifier extends CodeAdapter {
185
186         /**
187          * Descriptor of the visited method
188          */

189         protected String JavaDoc desc;
190
191         /**
192          * Types of the parameters of the visited method
193          */

194         protected Type[] types;
195
196         /**
197          * Total size of the formal parameters of the visited method
198          */

199         protected int params;
200
201         /**
202          * A label that designates the start of the original method code
203          */

204         private Label start;
205
206         /**
207          * A label that designates the start of the header added to the method
208          */

209         private Label header;
210
211         /**
212          * A list of instructions that have been visited but not yet regenerated
213          * through {@link #cv cv}. This list is a list of Insn objects. It is
214          * used to be able to modify an ALOAD 0 instruction long after it has
215          * been visited by this visitor, when a GETFIELD or PUTFIELD instruction
216          * is encountered.
217          */

218         protected List JavaDoc insns;
219
220         /**
221          * Symbolic state of the execution stack at the current bytecode
222          * instruction. The last element corresponds to the top of the stack.
223          * Each element is either <tt>null</tt> or an Integer object. The last
224          * case signifies that, at execution time, and at the current bytecode
225          * instruction, this stack element will contain 'this'. Moreover the
226          * integer value indicates the index in 'insns' of the instruction that
227          * pushed this value on the stack.
228          */

229         protected List JavaDoc stack;
230
231         /**
232          * Indicates the fields that the visited method may read on 'this'.
233          * If the i-th bit is set to 1, then the i-th field may be read.
234          */

235         protected long[] readFields;
236
237         /**
238          * Indicates the fields that the visited method may write on 'this'.
239          * If the i-th bit is set to 1, then the i-th field may be witten.
240          */

241         protected long[] writtenFields;
242
243
244         boolean isConstructor;
245
246         boolean firstIns = false;
247         
248         ClassAccessorModifier cam;
249
250         /**
251          * Constructs a new {@link DefaultCodeAccessorModifier}.
252          *
253          * @param desc the descriptor of the visited method
254          * @param hasHeader indicates if a header must be added to the visited
255          * method
256          * @param cv the code visitor to be used to generate the modified method
257          */

258         public DefaultCodeAccessorModifier(final String JavaDoc desc,
259                                            final boolean hasHeader,
260                                            final CodeVisitor cv,
261                                            final String JavaDoc name,
262                                            final ClassAccessorModifier _cam) {
263             super(cv);
264             this.cam = _cam;
265             isConstructor = name.equals("<init>");
266             cam.logger.log(BasicLevel.INFO, "visit the method " + name + " " + desc
267                     + " (const=" + isConstructor
268                     + ", header=" + hasHeader + ")");
269             firstIns = true;
270             int size = (cam.nbfields/64) + ((cam.nbfields % 64) > 0 ? 1 : 0);
271             writtenFields = new long[size];
272             readFields = new long[size];
273             this.desc = desc;
274             this.types = Type.getArgumentTypes(desc);
275             params = 1;
276             for (int i = 0; i < types.length; ++i) {
277                 params += types[i].getSize();
278             }
279             this.insns = new ArrayList JavaDoc();
280             this.stack = new ArrayList JavaDoc();
281             if (hasHeader && !isConstructor) {
282                 cam.logger.log(BasicLevel.DEBUG, "Add Jump at the begin of method: " + name);
283                 addJumpHeader();
284             }
285         }
286
287         protected void addJumpHeader() {
288             start = new Label();
289             header = new Label();
290             flushInsns();
291             cv.visitJumpInsn(Constants.GOTO, header);
292             visitLabel(start);
293         }
294
295         // IMPLEMENTATION OF THE CodeVisitor INTERFACE //
296
// --------------------------------------------//
297

298         public void visitInsn(final int opcode) {
299             if ((opcode >= Constants.IRETURN && opcode <= Constants.RETURN) ||
300                     opcode == Constants.ATHROW) {
301                 flushInsns();
302                 cv.visitInsn(opcode);
303             } else {
304                 if (opcode == Constants.DUP && stack.size() > 0) {
305                     // optimization for DUP
306
stack.add(stack.get(stack.size() - 1));
307                     cam.logger.log(BasicLevel.DEBUG, "stack=" + stack);
308                 } else {
309                     updateStack(ClassAccessorModifier.SIZE[opcode]);
310                 }
311                 insns.add(new Insn(opcode));
312             }
313         }
314
315         public void visitIntInsn(final int opcode, final int operand) {
316             updateStack(ClassAccessorModifier.SIZE[opcode]);
317             insns.add(new Insn(true, opcode, operand));
318         }
319
320         public void visitVarInsn(final int opcode, final int var) {
321             if (opcode == Constants.RET) {
322                 flushInsns();
323                 cv.visitVarInsn(opcode, var);
324             } else {
325                 if (opcode == Constants.ALOAD && var == 0) {
326                     stack.add(new Integer JavaDoc(insns.size()));
327                     cam.logger.log(BasicLevel.DEBUG, "stack=" + stack);
328                 } else {
329                     updateStack(ClassAccessorModifier.SIZE[opcode]);
330                 }
331                 insns.add(new Insn(false, opcode, var));
332             }
333         }
334
335         public void visitTypeInsn(final int opcode, final String JavaDoc desc) {
336             updateStack(ClassAccessorModifier.SIZE[opcode]);
337             insns.add(new Insn(opcode, desc));
338         }
339
340         public void visitFieldInsn(final int opcode,
341                                    final String JavaDoc owner,
342                                    final String JavaDoc name,
343                                    final String JavaDoc desc) {
344             SpeedoField jdofield = cam.fetchJDOField(name, owner.replace('/', '.'));
345             char c = desc.charAt(0);
346             int fieldSize = (c == 'D' || c == 'J' ? 2 : 1);
347             //Calculate if the field type is reference to a persistence object
348
// (use an accessor) or primitive field (direct access)
349
// primitive fields or java.lang.* or java.math.*
350
boolean directFieldAccess = c != 'L' || desc.startsWith("Ljava/lang/") || desc.startsWith("Ljava/math/");
351             int stackSizeVariation;
352             int thisInsnIndex = -1;
353             switch (opcode) {
354             case Constants.GETSTATIC:
355                 stackSizeVariation = fieldSize;
356                 //No modification
357
break;
358             case Constants.PUTSTATIC:
359                 stackSizeVariation = -fieldSize;
360                 //No modification
361
break;
362             case Constants.GETFIELD:
363                 stackSizeVariation = fieldSize - 1;
364                 if (jdofield == null) {
365                     break;
366                 }
367                 if (directFieldAccess) {
368                     thisInsnIndex = getThisInsnIndex(0);
369                     //If the field holder is not 'this' then use a getter
370
directFieldAccess = thisInsnIndex != -1;
371                 }
372                 if (directFieldAccess) { //direct field access
373
readFields[jdofield.number / 64] |= (1L << (jdofield.number % 64));
374                     visitThisFieldInsn(
375                             opcode, owner, name, desc, thisInsnIndex);
376                     cam.logger.log(BasicLevel.DEBUG, "direct field use: " + name);
377                 } else { //use an getter
378
String JavaDoc getterName = NamingRules.getterName(jdofield);
379                     String JavaDoc getterDesc = "()" + desc;
380                     insns.add(new Insn(Constants.INVOKEVIRTUAL,
381                             owner, getterName, getterDesc));
382                     cam.logger.log(BasicLevel.DEBUG, "getter assignment: " + getterName);
383                 }
384                 updateStack(stackSizeVariation);
385                 return;
386             case Constants.PUTFIELD:
387                 stackSizeVariation = 0 - (fieldSize + 1);
388                 if (jdofield == null) {
389                     break;
390                 }
391                 if (directFieldAccess) {
392                     thisInsnIndex = getThisInsnIndex(fieldSize);
393                     //If the field holder is not 'this' then use a setter
394
directFieldAccess = thisInsnIndex != -1;
395                 }
396                 if (directFieldAccess) { // direct field access
397
writtenFields[jdofield.number / 64] |= (1L << (jdofield.number % 64));
398                     visitThisFieldInsn(opcode, owner, name, desc, thisInsnIndex);
399                     cam.logger.log(BasicLevel.DEBUG, "direct field assignment: " + name);
400                 } else { //use an setter
401
String JavaDoc setterName = NamingRules.setterName(jdofield);
402                     String JavaDoc setterDesc = "(" + desc + ")V";
403                     insns.add(new Insn(Constants.INVOKEVIRTUAL,
404                             owner, setterName, setterDesc));
405                     cam.logger.log(BasicLevel.DEBUG, "setter assignment: " + setterName);
406                 }
407                 updateStack(stackSizeVariation);
408                 return;
409             default:
410                 stackSizeVariation = - fieldSize -1;
411             }
412             updateStack(stackSizeVariation);
413             insns.add(new Insn(opcode, owner, name, desc));
414         }
415
416         /**
417          * Calculates the index of the ALOAD0 instruction with the field index
418          * @param fieldidx
419          * @return -1 if not ALOAD0 found, otherwise an integer value
420          */

421         private int getThisInsnIndex(int fieldidx) {
422             int stackSize = stack.size();
423             if (stackSize > 0 && stackSize > fieldidx) {
424                 Integer JavaDoc i = (Integer JavaDoc) stack.get(stackSize - 1 - fieldidx);
425                 if (i != null) {
426                     return i.intValue();
427                 } else {
428                     cam.logger.log(BasicLevel.DEBUG, "i is null (stack: " + stack + ")");
429                 }
430             } else {
431                 cam.logger.log(BasicLevel.DEBUG, "stack size: " + stackSize);
432             }
433             return -1;
434         }
435         public void visitMethodInsn(final int opcode,
436                                     final String JavaDoc owner,
437                                     final String JavaDoc name,
438                                     final String JavaDoc desc) {
439             // computes the stack size variation
440
int size = opcode == Constants.INVOKESTATIC ? 0 : 1;
441             int c = 1;
442             while (true) {
443                 char car = desc.charAt(c++);
444                 if (car == ')') {
445                     car = desc.charAt(c);
446                     if (car == 'V') {
447                         size = -size;
448                     } else {
449                         size = (car == 'D' || car == 'J' ? 2 : 1) - size;
450                     }
451                     break;
452                 } else if (car == 'L') {
453                     while (desc.charAt(c++) != ';') {
454                     }
455                     size += 1;
456                 } else if (car == '[') {
457                     while ((car = desc.charAt(c)) == '[') {
458                         ++c;
459                     }
460                     if (car == 'D' || car == 'J') {
461                         size -= 1;
462                     }
463                 } else if (car == 'D' || car == 'J') {
464                     size += 2;
465                 } else {
466                     size += 1;
467                 }
468             }
469             updateStack(size);
470             insns.add(new Insn(opcode, owner, name, desc));
471             if (firstIns && isConstructor) {
472                 if (opcode == Constants.INVOKESPECIAL) {
473                     firstIns = false;
474                     generateConstructorHeader();
475                 }
476             }
477         }
478
479         public void visitJumpInsn(final int opcode, final Label label) {
480             flushInsns();
481             cv.visitJumpInsn(opcode, label);
482         }
483
484         public void visitLabel(final Label label) {
485             insns.add(new Insn(label));
486         }
487
488         public void visitLdcInsn(final Object JavaDoc cst) {
489             int size;
490             if (cst instanceof Double JavaDoc || cst instanceof Long JavaDoc) {
491                 size = 2;
492             } else {
493                 size = 1;
494             }
495             updateStack(size);
496             insns.add(new Insn(cst));
497         }
498
499         public void visitIincInsn(final int var, final int increment) {
500             // no stack change
501
insns.add(new Insn(var, increment));
502         }
503
504         public void visitTableSwitchInsn(final int min,
505                                          final int max,
506                                          final Label dflt,
507                                          final Label labels[]) {
508             flushInsns();
509             cv.visitTableSwitchInsn(min, max, dflt, labels);
510         }
511
512         public void visitLookupSwitchInsn(final Label dflt,
513                                           final int keys[],
514                                           final Label labels[]) {
515             flushInsns();
516             cv.visitLookupSwitchInsn(dflt, keys, labels);
517         }
518
519         public void visitMultiANewArrayInsn(final String JavaDoc desc,
520                                             final int dims) {
521             updateStack(1 - dims);
522             insns.add(new Insn(desc, dims));
523         }
524
525         public void visitTryCatchBlock(final Label start,
526                                        final Label end,
527                                        final Label handler,
528                                        final String JavaDoc type) {
529             flushInsns();
530             super.visitTryCatchBlock(start, end, handler, type);
531         }
532
533         public void visitMaxs(final int maxStack, final int maxLocals) {
534             flushInsns();
535             if (header != null) {
536                 cv.visitLabel(header);
537                 generateMethodHeader();
538                 cv.visitJumpInsn(Constants.GOTO, start);
539             }
540             cv.visitMaxs(maxStack, maxLocals);
541         }
542
543         public void visitLocalVariable(final String JavaDoc name,
544                                        final String JavaDoc desc,
545                                        final Label start,
546                                        final Label end,
547                                        final int index) {
548             flushInsns();
549             super.visitLocalVariable(name, desc, start, end, index);
550         }
551
552         public void visitLineNumber(final int line, final Label start) {
553             flushInsns();
554             super.visitLineNumber(line, start);
555         }
556
557         // OTHER METHODS //
558
// --------------//
559

560         /**
561          * Generates a header which is added at the begining of the the visited
562          * method. The default implementation of this method does nothing.
563          */

564         protected void generateMethodHeader() {
565             // does nothing
566
}
567
568         protected void generateConstructorHeader() {
569             // does nothing
570
}
571
572         /**
573          * Visits field access instruction on 'this'.
574          *
575          * @param opcode GETFIELD or PUTFIELD
576          * @param owner internal name of the owner class of the field
577          * @param name name of the field
578          * @param desc type descriptor of te field
579          * @param pushThisInsn index in {@link #insns insns} of the ALOAD 0
580          * instruction that pushed the 'this' value which is, at this bytecode
581          * instruction, at the top (for GETFIELD) or just under the top (for
582          * PUTFIELD) of the stack.
583          */

584         protected void visitThisFieldInsn(final int opcode,
585                                           final String JavaDoc owner,
586                                           final String JavaDoc name,
587                                           final String JavaDoc desc,
588                                           final int pushThisInsn) {
589             // keeps the field access instructions on 'this' unchanged
590
insns.add(new Insn(opcode, owner, name, desc));
591         }
592
593         /**
594          * Makes {@link #cv cv} visit the instructions stored in {@link #insns
595          * insns}, and then clear this list, as well as the stack. This method
596          * is used when the end of method is encountered, but also each time
597          * a jump or return statement is visited, because we can not easily know
598          * the stack state at the next instruction (this is possible but
599          * complicated: we prefer to keep the algorithm simple. The cost of this
600          * simplification is that some field accesses on 'this' will not be
601          * statically detected, and will therefore be replaced by calls to
602          * accessor methods, which is less efficient but safe).
603          */

604         protected void flushInsns() {
605             int n = insns.size();
606             for (int i = 0; i < n; ++i) {
607                 ((Insn) insns.get(i)).accept(cv);
608             }
609             insns.clear();
610             stack.clear();
611             cam.logger.log(BasicLevel.DEBUG, "stack=" + stack);
612         }
613
614         /**
615          * Pushes some <tt>null</tt> elements on the stack, or pops some
616          * elements.
617          * @param n the number of elements to be pushed (if n is positive), or
618          * to be popped (if n is negative).
619          */

620         private void updateStack(final int n) {
621             int size = stack.size();
622             if (n > 0) {
623                 for (int i = 0; i < n; ++i) {
624                     stack.add(null);
625                     cam.logger.log(BasicLevel.DEBUG, "stack=" + stack);
626                 }
627             } else {
628                 int m = -n;
629                 if (m >= size) {
630                     stack.clear();
631                     cam.logger.log(BasicLevel.DEBUG, "stack=" + stack);
632                 } else {
633                     for (int i = 0; i < m; ++i) {
634                         stack.remove(--size);
635                         cam.logger.log(BasicLevel.DEBUG, "stack=" + stack);
636                     }
637                 }
638             }
639         }
640     }
641
642      /**
643      * The method code modifier used for all non static methods. This code modifier
644      * replaces accesses to 'this' fields by accesses to the 'state' object, and
645      * replaces accesses to other fields by calls to accessor methods. It also
646      * adds a header to the method to call read or jdoWriteIntention.
647      */

648     class OptimisticCodeAccessorModifier extends DefaultCodeAccessorModifier {
649
650         /**
651          * Constructs a new {@link OptimisticCodeAccessorModifier}.
652          *
653          * @param desc the descriptor of the PESSIMISTIC method, and not of
654          * the optimistic one
655          * @param cv the code visitor to be used to generate the modified method
656          */

657         public OptimisticCodeAccessorModifier(final String JavaDoc desc,
658                                               final CodeVisitor cv,
659                                               String JavaDoc name,
660                                               ClassAccessorModifier cam) {
661             super(desc, true, cv, name, cam);
662         }
663
664         // OVERRIDEN METHODS //
665
// ------------------//
666

667         public void visitVarInsn(final int opcode, final int var) {
668             // shifts local variables that are just after the original actual
669
// parameters (ie without counting the added formal paramter), to
670
// make room for the added actual parameter
671
super.visitVarInsn(opcode, var >= params ? var + 1 : var);
672         }
673
674         public void visitIincInsn(final int var, final int increment) {
675             // shifts local variables that are just after the original actual
676
// parameters (ie without counting the added formal paramter), to
677
// make room for the added actual parameter
678
super.visitIincInsn(var >= params ? var + 1 : var, increment);
679         }
680
681         public void visitMaxs(final int maxStack, final int maxLocals) {
682             boolean allUnmodified = true;
683             for (int i=0; allUnmodified && i<cam.nbfields; i++) {
684                 allUnmodified &= readFields[i/64] == 0 & writtenFields[i/64] == 0;
685             }
686             if (allUnmodified) {
687                 super.visitMaxs(maxStack, maxLocals + (cam.nbfields/64) + 1);
688             } else {
689                 super.visitMaxs(Math.max(maxStack, 4), maxLocals + (cam.nbfields/64) + 1);
690             }
691         }
692
693          protected void generateConstructorHeader() {
694              cam.logger.log(BasicLevel.DEBUG, "Create reference state after the super in the constructor");
695              flushInsns();
696
697              //if (getReferenceAccessor() == null) {
698
cv.visitVarInsn(Constants.ALOAD, 0);
699              cv.visitMethodInsn(Constants.INVOKEVIRTUAL, cam.owner,
700                      "getReferenceAccessor",
701                      "()Lorg/objectweb/speedo/mim/api/SpeedoAccessor;");
702              Label afterSetState = new Label();
703              cv.visitJumpInsn(Constants.IFNONNULL, afterSetState);
704
705              //creating the reference state by calling
706
// 'setReferenceAccessor(createAccessor())'
707
cv.visitVarInsn(Constants.ALOAD, 0);
708              cv.visitVarInsn(Constants.ALOAD, 0);
709              cv.visitMethodInsn(Constants.INVOKEVIRTUAL, cam.owner,
710                      "createAccessor",
711                      "()Lorg/objectweb/speedo/mim/api/SpeedoAccessor;");
712              cv.visitMethodInsn(Constants.INVOKEVIRTUAL, cam.owner,
713                      "setReferenceAccessor",
714                      "(Lorg/objectweb/speedo/mim/api/SpeedoAccessor;)V");
715
716              cv.visitLabel(afterSetState);
717
718              //Load the reference state in a local variable
719
cv.visitVarInsn(Constants.ALOAD, 0);
720              cv.visitMethodInsn(Constants.INVOKEVIRTUAL, cam.owner,
721                      "getReferenceAccessor",
722                      "()Lorg/objectweb/speedo/mim/api/SpeedoAccessor;");
723              cv.visitTypeInsn(Constants.CHECKCAST, NamingRules.fieldsName(cam.owner));
724              cv.visitVarInsn(Constants.ASTORE, params);
725          }
726
727         protected void generateMethodHeader() {
728             boolean allUnmodified = true;
729             boolean hasWritten = false;
730             for (int i=0; allUnmodified && i<cam.nbfields; i++) {
731                 hasWritten |= writtenFields[i/64] != 0;
732                 allUnmodified &= readFields[i/64] == 0 & writtenFields[i/64] == 0;
733             }
734             cam.logger.log(BasicLevel.DEBUG, "allUnmodified=" + allUnmodified);
735             if (allUnmodified) {
736                 return;
737             }
738             // generate code corresponding to
739
// 'XXXFields state = jdoRead/WriteIntention(fieldIds);'
740
cv.visitVarInsn(Constants.ALOAD, 0);
741             // we always use readFields, even for a writeIntention
742
// (fields that are written but not read do not need to be loaded
743
// from the database before being used)
744

745             // define the long[] value such as new long[]{56L, 45L, 45646163}
746
Util.visitIntConstant(cv, readFields.length);
747             cv.visitIntInsn(Constants.NEWARRAY, Constants.T_LONG);
748             StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
749             sb.append(hasWritten ? "jdoWriteIntention" : "jdoReadIntention");
750             sb.append("(new long[]{");
751             String JavaDoc sep = "";
752             for(int i=0; i<readFields.length; i++) {
753                 cv.visitInsn(Constants.DUP);
754                 Util.visitIntConstant(cv, i);
755                 Util.visitLongConstant(cv, readFields[i] | writtenFields[i]);
756                 sb.append(sep);
757                 sep = ", ";
758                 sb.append(readFields[i] | writtenFields[i]);
759                 sb.append("L");
760                 cv.visitInsn(Constants.LASTORE);
761             }
762             sb.append(")");
763             cam.logger.log(BasicLevel.INFO, sb.toString());
764             cv.visitMethodInsn(
765                     Constants.INVOKEVIRTUAL,
766                     cam.owner,
767                     hasWritten ? "jdoWriteIntention" : "jdoReadIntention",
768                     "([J)Lorg/objectweb/speedo/mim/api/SpeedoAccessor;");
769             //Store return value
770
cv.visitTypeInsn(Constants.CHECKCAST, NamingRules.fieldsName(cam.owner));
771             cv.visitVarInsn(Constants.ASTORE, params);
772         }
773         protected void visitThisFieldInsn(final int opcode,
774                                           final String JavaDoc owner,
775                                           final String JavaDoc name,
776                                           final String JavaDoc desc,
777                                           final int pushThisInsn) {
778             // replaces the field access instructions on 'this' by field
779
// access instructions on the state object
780
cam.logger.log(BasicLevel.DEBUG, "replace ALOAD0 at " + pushThisInsn);
781             insns.set(pushThisInsn, new Insn(false, Constants.ALOAD, params));
782             insns.add(new Insn(opcode, cam.fieldsOwner, name, desc));
783         }
784     }
785
786     /**
787      * Object representation of a sequential bytecode instruction. A sequential
788      * bytecode instruction is any instruction that is not a jump, a return,
789      * a ret or a switch instruction. A single class is used to represent
790      * several types of bytecode instruction, in order to minimize the size of
791      * the outer class.
792      */

793     class Insn {
794
795         // the sequential bytecode instruction types
796
final static int INSN = 1;
797         final static int INT_INSN = 2;
798         final static int VAR_INSN = 3;
799         final static int TYPE_INSN = 4;
800         final static int FIELD_INSN = 5;
801         final static int METH_INSN = 6;
802         final static int LABEL_INSN = 7;
803         final static int LDC_INSN = 8;
804         final static int IINC_INSN = 9;
805         final static int MANA_INSN = 10;
806
807         /**
808          * Type of this instruction
809          */

810         private int type;
811
812         /**
813          * Opcode of this instruction
814          */

815         private int opcode;
816
817         // operand(s) of this instruction
818

819         private int int1, int2;
820         private String JavaDoc str1, str2, str3;
821         private Object JavaDoc obj;
822
823         public Insn(final int opcode) {
824             this.type = INSN;
825             this.opcode = opcode;
826         }
827
828         public Insn(final boolean intInsn, final int opcode, final int val) {
829             this.type = intInsn ? INT_INSN : VAR_INSN;
830             this.opcode = opcode;
831             this.int1 = val;
832         }
833
834         public Insn(final int opcode, final String JavaDoc desc) {
835             this.type = TYPE_INSN;
836             this.opcode = opcode;
837             this.str1 = desc;
838         }
839
840         public Insn(final int opcode,
841                     final String JavaDoc owner,
842                     final String JavaDoc name,
843                     final String JavaDoc desc) {
844             this.type = opcode <= Constants.PUTFIELD ? FIELD_INSN : METH_INSN;
845             this.opcode = opcode;
846             this.str1 = owner;
847             this.str2 = name;
848             this.str3 = desc;
849         }
850
851         public Insn(final Label label) {
852             this.type = LABEL_INSN;
853             this.obj = label;
854         }
855
856         public Insn(final Object JavaDoc cst) {
857             this.type = LDC_INSN;
858             this.obj = cst;
859         }
860
861         public Insn(final int var, final int inc) {
862             this.type = IINC_INSN;
863             this.int1 = var;
864             this.int2 = inc;
865         }
866
867         public Insn(final String JavaDoc desc, final int dims) {
868             this.type = MANA_INSN;
869             this.str1 = desc;
870             this.int1 = dims;
871         }
872
873         /**
874          * Makes the given code visitor visit this instruction.
875          *
876          * @param cv a code visitor
877          */

878         public void accept(final CodeVisitor cv) {
879             switch (type) {
880             case INSN:
881                 cv.visitInsn(opcode);
882                 break;
883             case INT_INSN:
884                 cv.visitIntInsn(opcode, int1);
885                 break;
886             case VAR_INSN:
887                 cv.visitVarInsn(opcode, int1);
888                 break;
889             case TYPE_INSN:
890                 cv.visitTypeInsn(opcode, str1);
891                 break;
892             case FIELD_INSN:
893                 cv.visitFieldInsn(opcode, str1, str2, str3);
894                 break;
895             case METH_INSN:
896                 cv.visitMethodInsn(opcode, str1, str2, str3);
897                 break;
898             case LABEL_INSN:
899                 cv.visitLabel((Label) obj);
900                 break;
901             case LDC_INSN:
902                 cv.visitLdcInsn(obj);
903                 break;
904             case IINC_INSN:
905                 cv.visitIincInsn(int1, int2);
906                 break;
907             default:
908                 cv.visitMultiANewArrayInsn(str1, int1);
909             }
910         }
911     }
912
Popular Tags