001: /**
002: * Copyright (C) 2001-2005 France Telecom R&D
003: *
004: * This library is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License as published by the Free Software Foundation; either
007: * version 2 of the License, or (at your option) any later version.
008: *
009: * This library is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public
015: * License along with this library; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: */package org.objectweb.speedo.generation.enhancer.pc;
018:
019: import org.objectweb.asm.CodeAdapter;
020: import org.objectweb.asm.CodeVisitor;
021: import org.objectweb.asm.Constants;
022: import org.objectweb.asm.ClassVisitor;
023: import org.objectweb.asm.Label;
024: import org.objectweb.asm.Type;
025: import org.objectweb.asm.Attribute;
026: import org.objectweb.speedo.lib.Personality;
027: import org.objectweb.speedo.metadata.SpeedoField;
028: import org.objectweb.speedo.metadata.SpeedoClass;
029: import org.objectweb.speedo.metadata.SpeedoMetaInfo;
030: import org.objectweb.speedo.generation.enhancer.common.LoggedClassAdapter;
031: import org.objectweb.speedo.generation.enhancer.common.Util;
032: import org.objectweb.speedo.generation.lib.NamingRules;
033: import org.objectweb.util.monolog.api.Logger;
034: import org.objectweb.util.monolog.api.BasicLevel;
035:
036: import java.util.List;
037: import java.util.ArrayList;
038: import java.util.Collection;
039:
040: /**
041: * Replaces field accesses by calls to getter and setter methods.
042: *
043: * Adapted from modifyMethods and replaceInstruction in EnhancerTool.
044: */
045: public class FieldAccessModifier extends LoggedClassAdapter {
046:
047: /**
048: * Stack size variation corresponding to each JVM instruction. This stack
049: * variation is equal to the size of the values produced by an instruction,
050: * minus the size of the values consumed by this instruction.
051: */
052:
053: final static int[] SIZE;
054:
055: /**
056: * Computes the stack size variation corresponding to each JVM instruction.
057: * Copied from the org.objectweb.asm.CodeWriter class.
058: */
059:
060: static {
061: int i;
062: int[] b = new int[202];
063: String s = "EFFFFFFFFGGFFFGGFFFEEFGFGFEEEEEEEEEEEEEEEEEEEEDEDEDDDDDCDCDEEEEEEEEE"
064: + "EEEEEEEEEEEBABABBBBDCFFFGGGEDCDCDCDCDCDCDCDCDCDCEEEEDDDDDDDCDCDCEFEF"
065: + "DDEEFFDEDEEEBDDBBDDDDDDCCCCCCCCEFEDDDCDCDEEEEEEEEEEFEEEEEEDDEEDDEE";
066: for (i = 0; i < b.length; ++i) {
067: b[i] = s.charAt(i) - 'E';
068: }
069: SIZE = b;
070: }
071:
072: /**
073: * A collections of SpeedoXMLDescriptors describing known persistent classes.
074: */
075: private final SpeedoClass speedoClass;
076: private final SpeedoMetaInfo smi;;
077:
078: /**
079: * Internal name of the visited class.
080: */
081: final String owner;
082:
083: /**
084: * Internal name of the corresponding "XXXFields" class.
085: */
086: final String fieldsOwner;
087:
088: final int nbfields;
089:
090: /**
091: * Constructs a new {@link FieldAccessModifier}.
092: *
093: * @param cv the class visitor to be used to generate the modified class
094: * @param sc is the SpeedoMeta object representing the persistent class
095: * visited
096: * @param p is the personality of Speedo
097: */
098: public FieldAccessModifier(final ClassVisitor cv,
099: final SpeedoClass sc, Logger logger, Personality p) {
100: super (cv, p, logger);
101: this .speedoClass = sc;
102: this .smi = speedoClass.moPackage.xmlDescriptor.smi;
103: owner = getJVMClassName(sc.getFQName());
104: nbfields = speedoClass.computeFieldNumbers();
105: fieldsOwner = NamingRules.fieldsName(owner);
106: }
107:
108: // IMPLEMENTATION OF THE ClassVisitor INTERFACE //
109: // ---------------------------------------------//
110:
111: public void visit(final int version, final int access,
112: final String name, final String super Name,
113: final String[] interfaces, final String sourceFile) {
114: cv.visit(version, access, name, super Name, interfaces,
115: sourceFile);
116: }
117:
118: public CodeVisitor visitMethod(final int access, final String name,
119: final String desc, final String[] exceptions,
120: final Attribute attrs) {
121: CodeVisitor covis = this .cv.visitMethod(access, name, desc,
122: exceptions, attrs);
123: if ((access & Constants.ACC_ABSTRACT) != 0) {
124: // nothing to be modified for abstract methods
125: logger.log(BasicLevel.DEBUG, "ignore the abstract method "
126: + name + " " + desc);
127: return covis;
128: }
129: if (name.startsWith("jdo") && !name.equals("jdoPreDelete")
130: && !name.equals("jdoPreStore")
131: && !name.equals("jdoPreClear")
132: && !name.equals("jdoPostLoad")) {
133: // do not modify the accessor methods generated by the enhancer
134: logger.log(BasicLevel.DEBUG, "ignoe the method " + name
135: + " " + desc);
136: return covis;
137: }
138: if (name.equals("<clinit>")
139: || (access & Constants.ACC_STATIC) != 0) {
140: // for static methods replace accesses to fields by calls to
141: // accessor methods.
142: return new DefaultCodeAccessorModifier(desc, false, covis,
143: name, this );
144: } else {
145: // Fields accessing to 'this' are replaced by field accesses to
146: // the state object (the added actual parameter), and a header
147: // is added to call speedoReadIntention or speedoWriteIntention. In case of the
148: // constructor the call to speedoReadIntention or speedoWriteIntention is added
149: // after the super call.
150: return new OptimisticCodeAccessorModifier(desc, covis,
151: name, this );
152: }
153: }
154:
155: // OTHER METHODS //
156: // --------------//
157:
158: /**
159: * Looks for a specific <code>SpeedoField</code> in the object model.
160: *
161: * @param name the name of the field to be fetched
162: * @param className the complete class name that the field belongs to
163: * @return the corresponding SpeedoField if it exists, null either
164: */
165: SpeedoField fetchJDOField(final String name, final String className) {
166: SpeedoClass c = smi.getSpeedoClass(className);
167: if (c != null) {
168: SpeedoField speedoField = null;
169: //try to find the field in the current class
170: // and in its parents (if any)
171: while (speedoField == null && c != null) {
172: speedoField = (SpeedoField) c.fields.get(name);
173: c = c.getSuper();
174: }
175: return speedoField;
176: } else {
177: return null;
178: }
179: }
180:
181: }
182:
183: /**
184: * The default method code modifier, used for constructors, static methods
185: * and class initializers. This code modifier does not modify accesses to
186: * 'this' fields, but replaces accesses to other fields by calls to accessor
187: * methods.
188: */
189: class DefaultCodeAccessorModifier extends CodeAdapter {
190:
191: /**
192: * Descriptor of the visited method
193: */
194: protected String desc;
195:
196: /**
197: * Types of the parameters of the visited method
198: */
199: protected Type[] types;
200:
201: /**
202: * Total size of the formal parameters of the visited method
203: */
204: protected int params;
205:
206: /**
207: * A label that designates the start of the original method code
208: */
209: private Label start;
210:
211: /**
212: * A label that designates the start of the header added to the method
213: */
214: private Label header;
215:
216: /**
217: * A list of instructions that have been visited but not yet regenerated
218: * through {@link #cv cv}. This list is a list of Insn objects. It is
219: * used to be able to modify an ALOAD 0 instruction long after it has
220: * been visited by this visitor, when a GETFIELD or PUTFIELD instruction
221: * is encountered.
222: */
223: protected List insns;
224:
225: /**
226: * Symbolic state of the execution stack at the current bytecode
227: * instruction. The last element corresponds to the top of the stack.
228: * Each element is either <tt>null</tt> or an Integer object. The last
229: * case signifies that, at execution time, and at the current bytecode
230: * instruction, this stack element will contain 'this'. Moreover the
231: * integer value indicates the index in 'insns' of the instruction that
232: * pushed this value on the stack.
233: */
234: protected List stack;
235:
236: /**
237: * Indicates the fields that the visited method may read on 'this'.
238: * If the i-th bit is set to 1, then the i-th field may be read.
239: */
240: protected long[] readFields;
241:
242: /**
243: * Indicates the fields that the visited method may write on 'this'.
244: * If the i-th bit is set to 1, then the i-th field may be witten.
245: */
246: protected long[] writtenFields;
247:
248: boolean isConstructor;
249:
250: boolean firstIns = false;
251:
252: FieldAccessModifier cam;
253:
254: /**
255: * Constructs a new {@link DefaultCodeAccessorModifier}.
256: *
257: * @param desc the descriptor of the visited method
258: * @param hasHeader indicates if a header must be added to the visited
259: * method
260: * @param cv the code visitor to be used to generate the modified method
261: */
262: public DefaultCodeAccessorModifier(final String desc,
263: final boolean hasHeader, final CodeVisitor cv,
264: final String name, final FieldAccessModifier _cam) {
265: super (cv);
266: this .cam = _cam;
267: isConstructor = name.equals("<init>");
268: cam.getLogger()
269: .log(
270: BasicLevel.INFO,
271: "visit the method " + name + " " + desc
272: + " (const=" + isConstructor
273: + ", header=" + hasHeader + ")");
274: firstIns = true;
275: int size = (cam.nbfields / 64)
276: + ((cam.nbfields % 64) > 0 ? 1 : 0);
277: writtenFields = new long[size];
278: readFields = new long[size];
279: this .desc = desc;
280: this .types = Type.getArgumentTypes(desc);
281: params = 1;
282: for (int i = 0; i < types.length; ++i) {
283: params += types[i].getSize();
284: }
285: this .insns = new ArrayList();
286: this .stack = new ArrayList();
287: if (hasHeader && !isConstructor) {
288: cam.getLogger().log(BasicLevel.DEBUG,
289: "Add Jump at the begin of method: " + name);
290: addJumpHeader();
291: }
292: }
293:
294: protected void addJumpHeader() {
295: start = new Label();
296: header = new Label();
297: flushInsns();
298: cv.visitJumpInsn(Constants.GOTO, header);
299: visitLabel(start);
300: }
301:
302: // IMPLEMENTATION OF THE CodeVisitor INTERFACE //
303: // --------------------------------------------//
304:
305: public void visitInsn(final int opcode) {
306: if ((opcode >= Constants.IRETURN && opcode <= Constants.RETURN)
307: || opcode == Constants.ATHROW) {
308: flushInsns();
309: cv.visitInsn(opcode);
310: } else {
311: if (opcode == Constants.DUP && stack.size() > 0) {
312: // optimization for DUP
313: stack.add(stack.get(stack.size() - 1));
314: cam.getLogger().log(BasicLevel.DEBUG, "stack=" + stack);
315: } else {
316: updateStack(FieldAccessModifier.SIZE[opcode]);
317: }
318: insns.add(new Insn(opcode));
319: }
320: }
321:
322: public void visitIntInsn(final int opcode, final int operand) {
323: updateStack(FieldAccessModifier.SIZE[opcode]);
324: insns.add(new Insn(true, opcode, operand));
325: }
326:
327: public void visitVarInsn(final int opcode, final int var) {
328: if (opcode == Constants.RET) {
329: flushInsns();
330: cv.visitVarInsn(opcode, var);
331: } else {
332: if (opcode == Constants.ALOAD && var == 0) {
333: stack.add(new Integer(insns.size()));
334: cam.getLogger().log(BasicLevel.DEBUG, "stack=" + stack);
335: } else {
336: updateStack(FieldAccessModifier.SIZE[opcode]);
337: }
338: insns.add(new Insn(false, opcode, var));
339: }
340: }
341:
342: public void visitTypeInsn(final int opcode, final String desc) {
343: updateStack(FieldAccessModifier.SIZE[opcode]);
344: insns.add(new Insn(opcode, desc));
345: }
346:
347: public void visitFieldInsn(final int opcode, final String owner,
348: final String name, final String desc) {
349: SpeedoField field = cam.fetchJDOField(name, owner.replace('/',
350: '.'));
351: char c = desc.charAt(0);
352: int fieldSize = (c == 'D' || c == 'J' ? 2 : 1);
353: //Calculate if the field type is reference to a persistence object
354: // (use an accessor) or primitive field (direct access)
355: // primitive fields or java.lang.* or java.math.*
356: boolean directFieldAccess = c != 'L'
357: || desc.startsWith("Ljava/lang/")
358: || desc.startsWith("Ljava/math/");
359: int stackSizeVariation;
360: int this InsnIndex = -1;
361: switch (opcode) {
362: case Constants.GETSTATIC:
363: stackSizeVariation = fieldSize;
364: //No modification
365: break;
366: case Constants.PUTSTATIC:
367: stackSizeVariation = -fieldSize;
368: //No modification
369: break;
370: case Constants.GETFIELD:
371: stackSizeVariation = fieldSize - 1;
372: if (field == null) {
373: break;
374: }
375: if (directFieldAccess) {
376: this InsnIndex = getThisInsnIndex(0);
377: //If the field holder is not 'this' then use a getter
378: directFieldAccess = this InsnIndex != -1;
379: }
380: if (directFieldAccess) { //direct field access
381: readFields[field.number / 64] |= (1L << (field.number % 64));
382: visitThisFieldInsn(opcode, owner, name, desc,
383: this InsnIndex);
384: cam.getLogger().log(BasicLevel.DEBUG,
385: "direct field use: " + name);
386: } else { //use an getter
387: String getterName = NamingRules.getterName(field);
388: String getterDesc = "()" + desc;
389: insns.add(new Insn(Constants.INVOKEVIRTUAL, owner,
390: getterName, getterDesc));
391: cam.getLogger().log(BasicLevel.DEBUG,
392: "getter assignment: " + getterName);
393: }
394: updateStack(stackSizeVariation);
395: return;
396: case Constants.PUTFIELD:
397: stackSizeVariation = 0 - (fieldSize + 1);
398: if (field == null) {
399: break;
400: }
401: if (directFieldAccess) {
402: this InsnIndex = getThisInsnIndex(fieldSize);
403: //If the field holder is not 'this' then use a setter
404: directFieldAccess = this InsnIndex != -1;
405: }
406: if (directFieldAccess) { // direct field access
407: writtenFields[field.number / 64] |= (1L << (field.number % 64));
408: visitThisFieldInsn(opcode, owner, name, desc,
409: this InsnIndex);
410: cam.getLogger().log(BasicLevel.DEBUG,
411: "direct field assignment: " + name);
412: } else { //use an setter
413: String setterName = NamingRules.setterName(field);
414: String setterDesc = "(" + desc + ")V";
415: insns.add(new Insn(Constants.INVOKEVIRTUAL, owner,
416: setterName, setterDesc));
417: cam.getLogger().log(BasicLevel.DEBUG,
418: "setter assignment: " + setterName);
419: }
420: updateStack(stackSizeVariation);
421: return;
422: default:
423: stackSizeVariation = -fieldSize - 1;
424: }
425: updateStack(stackSizeVariation);
426: insns.add(new Insn(opcode, owner, name, desc));
427: }
428:
429: /**
430: * Calculates the index of the ALOAD0 instruction with the field index
431: * @param fieldidx
432: * @return -1 if not ALOAD0 found, otherwise an integer value
433: */
434: private int getThisInsnIndex(int fieldidx) {
435: int stackSize = stack.size();
436: if (stackSize > 0 && stackSize > fieldidx) {
437: Integer i = (Integer) stack.get(stackSize - 1 - fieldidx);
438: if (i != null) {
439: return i.intValue();
440: } else {
441: cam.getLogger().log(BasicLevel.DEBUG,
442: "i is null (stack: " + stack + ")");
443: }
444: } else {
445: cam.getLogger().log(BasicLevel.DEBUG,
446: "stack size: " + stackSize);
447: }
448: return -1;
449: }
450:
451: public void visitMethodInsn(final int opcode, final String owner,
452: final String name, final String desc) {
453: // computes the stack size variation
454: int size = opcode == Constants.INVOKESTATIC ? 0 : 1;
455: int c = 1;
456: while (true) {
457: char car = desc.charAt(c++);
458: if (car == ')') {
459: car = desc.charAt(c);
460: if (car == 'V') {
461: size = -size;
462: } else {
463: size = (car == 'D' || car == 'J' ? 2 : 1) - size;
464: }
465: break;
466: } else if (car == 'L') {
467: while (desc.charAt(c++) != ';') {
468: }
469: size += 1;
470: } else if (car == '[') {
471: while ((car = desc.charAt(c)) == '[') {
472: ++c;
473: }
474: if (car == 'D' || car == 'J') {
475: size -= 1;
476: }
477: } else if (car == 'D' || car == 'J') {
478: size += 2;
479: } else {
480: size += 1;
481: }
482: }
483: updateStack(size);
484: insns.add(new Insn(opcode, owner, name, desc));
485: if (firstIns && isConstructor) {
486: if (opcode == Constants.INVOKESPECIAL) {
487: firstIns = false;
488: generateConstructorHeader();
489: }
490: }
491: }
492:
493: public void visitJumpInsn(final int opcode, final Label label) {
494: flushInsns();
495: cv.visitJumpInsn(opcode, label);
496: }
497:
498: public void visitLabel(final Label label) {
499: insns.add(new Insn(label));
500: }
501:
502: public void visitLdcInsn(final Object cst) {
503: int size;
504: if (cst instanceof Double || cst instanceof Long) {
505: size = 2;
506: } else {
507: size = 1;
508: }
509: updateStack(size);
510: insns.add(new Insn(cst));
511: }
512:
513: public void visitIincInsn(final int var, final int increment) {
514: // no stack change
515: insns.add(new Insn(var, increment));
516: }
517:
518: public void visitTableSwitchInsn(final int min, final int max,
519: final Label dflt, final Label labels[]) {
520: flushInsns();
521: cv.visitTableSwitchInsn(min, max, dflt, labels);
522: }
523:
524: public void visitLookupSwitchInsn(final Label dflt,
525: final int keys[], final Label labels[]) {
526: flushInsns();
527: cv.visitLookupSwitchInsn(dflt, keys, labels);
528: }
529:
530: public void visitMultiANewArrayInsn(final String desc,
531: final int dims) {
532: updateStack(1 - dims);
533: insns.add(new Insn(desc, dims));
534: }
535:
536: public void visitTryCatchBlock(final Label start, final Label end,
537: final Label handler, final String type) {
538: flushInsns();
539: super .visitTryCatchBlock(start, end, handler, type);
540: }
541:
542: public void visitMaxs(final int maxStack, final int maxLocals) {
543: flushInsns();
544: if (header != null) {
545: cv.visitLabel(header);
546: generateMethodHeader();
547: cv.visitJumpInsn(Constants.GOTO, start);
548: }
549: cv.visitMaxs(maxStack, maxLocals);
550: }
551:
552: public void visitLocalVariable(final String name,
553: final String desc, final Label start, final Label end,
554: final int index) {
555: flushInsns();
556: super .visitLocalVariable(name, desc, start, end, index);
557: }
558:
559: public void visitLineNumber(final int line, final Label start) {
560: flushInsns();
561: super .visitLineNumber(line, start);
562: }
563:
564: // OTHER METHODS //
565: // --------------//
566:
567: /**
568: * Generates a header which is added at the begining of the the visited
569: * method. The default implementation of this method does nothing.
570: */
571: protected void generateMethodHeader() {
572: // does nothing
573: }
574:
575: protected void generateConstructorHeader() {
576: // does nothing
577: }
578:
579: /**
580: * Visits field access instruction on 'this'.
581: *
582: * @param opcode GETFIELD or PUTFIELD
583: * @param owner internal name of the owner class of the field
584: * @param name name of the field
585: * @param desc type descriptor of te field
586: * @param pushThisInsn index in {@link #insns insns} of the ALOAD 0
587: * instruction that pushed the 'this' value which is, at this bytecode
588: * instruction, at the top (for GETFIELD) or just under the top (for
589: * PUTFIELD) of the stack.
590: */
591: protected void visitThisFieldInsn(final int opcode,
592: final String owner, final String name, final String desc,
593: final int pushThisInsn) {
594: // keeps the field access instructions on 'this' unchanged
595: insns.add(new Insn(opcode, owner, name, desc));
596: }
597:
598: /**
599: * Makes {@link #cv cv} visit the instructions stored in {@link #insns
600: * insns}, and then clear this list, as well as the stack. This method
601: * is used when the end of method is encountered, but also each time
602: * a jump or return statement is visited, because we can not easily know
603: * the stack state at the next instruction (this is possible but
604: * complicated: we prefer to keep the algorithm simple. The cost of this
605: * simplification is that some field accesses on 'this' will not be
606: * statically detected, and will therefore be replaced by calls to
607: * accessor methods, which is less efficient but safe).
608: */
609: protected void flushInsns() {
610: int n = insns.size();
611: for (int i = 0; i < n; ++i) {
612: ((Insn) insns.get(i)).accept(cv);
613: }
614: insns.clear();
615: stack.clear();
616: cam.getLogger().log(BasicLevel.DEBUG, "stack=" + stack);
617: }
618:
619: /**
620: * Pushes some <tt>null</tt> elements on the stack, or pops some
621: * elements.
622: * @param n the number of elements to be pushed (if n is positive), or
623: * to be popped (if n is negative).
624: */
625: private void updateStack(final int n) {
626: int size = stack.size();
627: if (n > 0) {
628: for (int i = 0; i < n; ++i) {
629: stack.add(null);
630: cam.getLogger().log(BasicLevel.DEBUG, "stack=" + stack);
631: }
632: } else {
633: int m = -n;
634: if (m >= size) {
635: stack.clear();
636: cam.getLogger().log(BasicLevel.DEBUG, "stack=" + stack);
637: } else {
638: for (int i = 0; i < m; ++i) {
639: stack.remove(--size);
640: cam.getLogger().log(BasicLevel.DEBUG,
641: "stack=" + stack);
642: }
643: }
644: }
645: }
646: }
647:
648: /**
649: * The method code modifier used for all non static methods. This code modifier
650: * replaces accesses to 'this' fields by accesses to the 'state' object, and
651: * replaces accesses to other fields by calls to accessor methods. It also
652: * adds a header to the method to call speedoReadIntention or speedoWriteIntention.
653: */
654: class OptimisticCodeAccessorModifier extends
655: DefaultCodeAccessorModifier {
656:
657: /**
658: * Constructs a new {@link OptimisticCodeAccessorModifier}.
659: *
660: * @param desc the descriptor of the PESSIMISTIC method, and not of
661: * the optimistic one
662: * @param cv the code visitor to be used to generate the modified method
663: */
664: public OptimisticCodeAccessorModifier(final String desc,
665: final CodeVisitor cv, String name, FieldAccessModifier cam) {
666: super (desc, true, cv, name, cam);
667: }
668:
669: // OVERRIDEN METHODS //
670: // ------------------//
671:
672: public void visitVarInsn(final int opcode, final int var) {
673: // shifts local variables that are just after the original actual
674: // parameters (ie without counting the added formal paramter), to
675: // make room for the added actual parameter
676: super .visitVarInsn(opcode, var >= params ? var + 1 : var);
677: }
678:
679: public void visitIincInsn(final int var, final int increment) {
680: // shifts local variables that are just after the original actual
681: // parameters (ie without counting the added formal paramter), to
682: // make room for the added actual parameter
683: super .visitIincInsn(var >= params ? var + 1 : var, increment);
684: }
685:
686: public void visitMaxs(final int maxStack, final int maxLocals) {
687: boolean allUnmodified = true;
688: for (int i = 0; allUnmodified && i < cam.nbfields; i++) {
689: allUnmodified &= readFields[i / 64] == 0
690: & writtenFields[i / 64] == 0;
691: }
692: if (allUnmodified) {
693: super .visitMaxs(maxStack, maxLocals + (cam.nbfields / 64)
694: + 1);
695: } else {
696: super .visitMaxs(Math.max(maxStack, 4), maxLocals
697: + (cam.nbfields / 64) + 1);
698: }
699: }
700:
701: protected void generateConstructorHeader() {
702: cam
703: .getLogger()
704: .log(BasicLevel.DEBUG,
705: "Create reference state after the super in the constructor");
706: flushInsns();
707:
708: //if (speedoGetReferenceState() == null) {
709: cv.visitVarInsn(Constants.ALOAD, 0);
710: cv.visitMethodInsn(Constants.INVOKEVIRTUAL, cam.owner,
711: "speedoGetReferenceState",
712: "()Lorg/objectweb/speedo/mim/api/StateItf;");
713: Label afterSetState = new Label();
714: cv.visitJumpInsn(Constants.IFNONNULL, afterSetState);
715:
716: //creating the reference state by calling
717: // 'speedoSetReferenceState(speedoCreateState())'
718: cv.visitVarInsn(Constants.ALOAD, 0);
719: cv.visitVarInsn(Constants.ALOAD, 0);
720: cv.visitMethodInsn(Constants.INVOKEVIRTUAL, cam.owner,
721: "speedoCreateState",
722: "()Lorg/objectweb/speedo/mim/api/StateItf;");
723: cv.visitMethodInsn(Constants.INVOKEVIRTUAL, cam.owner,
724: "speedoSetReferenceState",
725: "(Lorg/objectweb/speedo/mim/api/StateItf;)V");
726:
727: cv.visitLabel(afterSetState);
728:
729: //Load the reference state in a local variable
730: cv.visitVarInsn(Constants.ALOAD, 0);
731: cv.visitMethodInsn(Constants.INVOKEVIRTUAL, cam.owner,
732: "speedoGetReferenceState",
733: "()Lorg/objectweb/speedo/mim/api/StateItf;");
734: cv.visitTypeInsn(Constants.CHECKCAST, NamingRules
735: .fieldsName(cam.owner));
736: cv.visitVarInsn(Constants.ASTORE, params);
737: }
738:
739: protected void generateMethodHeader() {
740: boolean allUnmodified = true;
741: boolean hasWritten = false;
742: for (int i = 0; allUnmodified && i < cam.nbfields; i++) {
743: hasWritten |= writtenFields[i / 64] != 0;
744: allUnmodified &= readFields[i / 64] == 0
745: & writtenFields[i / 64] == 0;
746: }
747: cam.getLogger().log(BasicLevel.DEBUG,
748: "allUnmodified=" + allUnmodified);
749: if (allUnmodified) {
750: return;
751: }
752: // generate code corresponding to
753: // 'XXXFields state = speedoRead/WriteIntention(fieldIds);'
754: cv.visitVarInsn(Constants.ALOAD, 0);
755: // we always use readFields, even for a writeIntention
756: // (fields that are written but not read do not need to be loaded
757: // from the database before being used)
758:
759: // define the long[] value such as new long[]{56L, 45L, 45646163}
760: Util.visitIntConstant(cv, readFields.length);
761: cv.visitIntInsn(Constants.NEWARRAY, Constants.T_LONG);
762: StringBuffer sb = new StringBuffer();
763: sb.append(hasWritten ? "speedoWriteIntention"
764: : "speedoReadIntention");
765: sb.append("(new long[]{");
766: String sep = "";
767: for (int i = 0; i < readFields.length; i++) {
768: cv.visitInsn(Constants.DUP);
769: Util.visitIntConstant(cv, i);
770: Util
771: .visitLongConstant(cv, readFields[i]
772: | writtenFields[i]);
773: sb.append(sep);
774: sep = ", ";
775: sb.append(readFields[i] | writtenFields[i]);
776: sb.append("L");
777: cv.visitInsn(Constants.LASTORE);
778: }
779: sb.append(")");
780: cam.getLogger().log(BasicLevel.INFO, sb.toString());
781: cv.visitMethodInsn(Constants.INVOKEVIRTUAL, cam.owner,
782: hasWritten ? "speedoWriteIntention"
783: : "speedoReadIntention",
784: "([J)Lorg/objectweb/speedo/mim/api/StateItf;");
785: //Store return value
786: cv.visitTypeInsn(Constants.CHECKCAST, NamingRules
787: .fieldsName(cam.owner));
788: cv.visitVarInsn(Constants.ASTORE, params);
789: }
790:
791: protected void visitThisFieldInsn(final int opcode,
792: final String owner, final String name, final String desc,
793: final int pushThisInsn) {
794: // replaces the field access instructions on 'this' by field
795: // access instructions on the state object
796: cam.getLogger().log(BasicLevel.DEBUG,
797: "replace ALOAD0 at " + pushThisInsn);
798: insns.set(pushThisInsn,
799: new Insn(false, Constants.ALOAD, params));
800: insns.add(new Insn(opcode, cam.fieldsOwner, name, desc));
801: }
802: }
803:
804: /**
805: * Object representation of a sequential bytecode instruction. A sequential
806: * bytecode instruction is any instruction that is not a jump, a return,
807: * a ret or a switch instruction. A single class is used to represent
808: * several types of bytecode instruction, in order to minimize the size of
809: * the outer class.
810: */
811: class Insn {
812:
813: // the sequential bytecode instruction types
814: final static int INSN = 1;
815: final static int INT_INSN = 2;
816: final static int VAR_INSN = 3;
817: final static int TYPE_INSN = 4;
818: final static int FIELD_INSN = 5;
819: final static int METH_INSN = 6;
820: final static int LABEL_INSN = 7;
821: final static int LDC_INSN = 8;
822: final static int IINC_INSN = 9;
823: final static int MANA_INSN = 10;
824:
825: /**
826: * Type of this instruction
827: */
828: private int type;
829:
830: /**
831: * Opcode of this instruction
832: */
833: private int opcode;
834:
835: // operand(s) of this instruction
836:
837: private int int1, int2;
838: private String str1, str2, str3;
839: private Object obj;
840:
841: public Insn(final int opcode) {
842: this .type = INSN;
843: this .opcode = opcode;
844: }
845:
846: public Insn(final boolean intInsn, final int opcode, final int val) {
847: this .type = intInsn ? INT_INSN : VAR_INSN;
848: this .opcode = opcode;
849: this .int1 = val;
850: }
851:
852: public Insn(final int opcode, final String desc) {
853: this .type = TYPE_INSN;
854: this .opcode = opcode;
855: this .str1 = desc;
856: }
857:
858: public Insn(final int opcode, final String owner,
859: final String name, final String desc) {
860: this .type = opcode <= Constants.PUTFIELD ? FIELD_INSN
861: : METH_INSN;
862: this .opcode = opcode;
863: this .str1 = owner;
864: this .str2 = name;
865: this .str3 = desc;
866: }
867:
868: public Insn(final Label label) {
869: this .type = LABEL_INSN;
870: this .obj = label;
871: }
872:
873: public Insn(final Object cst) {
874: this .type = LDC_INSN;
875: this .obj = cst;
876: }
877:
878: public Insn(final int var, final int inc) {
879: this .type = IINC_INSN;
880: this .int1 = var;
881: this .int2 = inc;
882: }
883:
884: public Insn(final String desc, final int dims) {
885: this .type = MANA_INSN;
886: this .str1 = desc;
887: this .int1 = dims;
888: }
889:
890: /**
891: * Makes the given code visitor visit this instruction.
892: *
893: * @param cv a code visitor
894: */
895: public void accept(final CodeVisitor cv) {
896: switch (type) {
897: case INSN:
898: cv.visitInsn(opcode);
899: break;
900: case INT_INSN:
901: cv.visitIntInsn(opcode, int1);
902: break;
903: case VAR_INSN:
904: cv.visitVarInsn(opcode, int1);
905: break;
906: case TYPE_INSN:
907: cv.visitTypeInsn(opcode, str1);
908: break;
909: case FIELD_INSN:
910: cv.visitFieldInsn(opcode, str1, str2, str3);
911: break;
912: case METH_INSN:
913: cv.visitMethodInsn(opcode, str1, str2, str3);
914: break;
915: case LABEL_INSN:
916: cv.visitLabel((Label) obj);
917: break;
918: case LDC_INSN:
919: cv.visitLdcInsn(obj);
920: break;
921: case IINC_INSN:
922: cv.visitIincInsn(int1, int2);
923: break;
924: default:
925: cv.visitMultiANewArrayInsn(str1, int1);
926: }
927: }
928: }
|