001: /* Copyright (C) 2004 - 2007 db4objects Inc. http://www.db4o.com
002:
003: This file is part of the db4o open source object database.
004:
005: db4o is free software; you can redistribute it and/or modify it under
006: the terms of version 2 of the GNU General Public License as published
007: by the Free Software Foundation and as clarified by db4objects' GPL
008: interpretation policy, available at
009: http://www.db4o.com/about/company/legalpolicies/gplinterpretation/
010: Alternatively you can write to db4objects, Inc., 1900 S Norfolk Street,
011: Suite 350, San Mateo, CA 94403, USA.
012:
013: db4o is distributed in the hope that it will be useful, but WITHOUT ANY
014: WARRANTY; without even the implied warranty of MERCHANTABILITY or
015: FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
016: for more details.
017:
018: You should have received a copy of the GNU General Public License along
019: with this program; if not, write to the Free Software Foundation, Inc.,
020: 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
021: package EDU.purdue.cs.bloat.inline;
022:
023: import java.util.*;
024:
025: import EDU.purdue.cs.bloat.editor.*;
026: import EDU.purdue.cs.bloat.util.*;
027:
028: /**
029: * Inlines the code of non-virtual method call sites. These sites include calls
030: * to static methods and certain uses of the <tt>invokespecial</tt> method.
031: * There are certain metrics that can be set to effect where and how inlining is
032: * performed.
033: */
034: public class Inline {
035: public static boolean DEBUG = false;
036:
037: private int maxCodeSize; // Max number of instructions in method
038:
039: private int maxCallDepth; // Max of height of call stack
040:
041: private boolean inlineExceptions; // Inline methods that throw exceptions
042:
043: private InlineContext context;
044:
045: private Map editors; // Maps MemberRefs to their MethodEditors
046:
047: /**
048: * Size of the largest method that can be inlined
049: */
050: public static int CALLEE_SIZE = 100000;
051:
052: private static void db(final String s) {
053: if (Inline.DEBUG) {
054: System.out.println(s);
055: }
056: }
057:
058: /**
059: * Constructor. By default the first-level calls are only inlined one level
060: * deep, there is no max size on methods to inline, and methods that may
061: * throw exceptions are inlined.
062: *
063: * @param maxCodeSize
064: * The maximum number of instructions a method can grow to.
065: */
066: public Inline(final InlineContext context, final int maxCodeSize) {
067: this .context = context;
068: this .maxCodeSize = maxCodeSize;
069: this .maxCallDepth = 1;
070: this .inlineExceptions = true;
071:
072: editors = new HashMap();
073: }
074:
075: /**
076: * Sets the maximum of size of a method that will be inlined. No method
077: * larger than this will be inlined.
078: */
079: public void setMaxInlineSize(final int maxInlineSize) {
080: }
081:
082: /**
083: * Sets the maximum number of nested calls we inline.
084: */
085: public void setMaxCallDepth(final int maxCallDepth) {
086: this .maxCallDepth = maxCallDepth;
087: }
088:
089: /**
090: * Sets whether or not methods that may throw exceptions (that is, have a
091: * non-empty "throws" declaration) are inlined.
092: */
093: public void setInlineExceptions(final boolean inlineExceptions) {
094: this .inlineExceptions = inlineExceptions;
095: }
096:
097: /**
098: * Scans a method and inlines non-virtual method calls according to this
099: * <tt>Inline</tt>'s metrics.
100: */
101: public void inline(final MethodEditor method) {
102: // Go through the method and look for calls to inline
103: StackHeightCounter stackHeight = new StackHeightCounter(method);
104: List code = method.code();
105: boolean firstCall = true;
106: for (int i = 0; i < code.size(); i++) {
107: final Object o = code.get(i);
108: if (o instanceof Instruction) {
109: final Instruction inst = (Instruction) o;
110: if ((inst.opcodeClass() == Opcode.opcx_invokestatic)
111: || (inst.opcodeClass() == Opcode.opcx_invokespecial)) {
112: final MemberRef callee = (MemberRef) inst.operand();
113: final Stack callStack = new Stack();
114: callStack.add(method.memberRef());
115:
116: Inline.db(" Call: " + inst);
117:
118: stackHeight.handle(inst);
119: final int expectedHeight = stackHeight.height();
120: stackHeight.unhandle(inst);
121:
122: final int j = i;
123: i = inline(method, callee, i, callStack,
124: stackHeight, firstCall);
125:
126: if (j == i) {
127: // Call was not inlined, add it to the stack
128: stackHeight.handle(inst);
129: Inline.db(" " + i + "." + stackHeight.height()
130: + ") " + inst);
131: }
132:
133: final int newHeight = stackHeight.height();
134: // If an exception is thrown as the last thing in a method
135: // newHeight will equal 0. Let's let this one slide.
136: Assert.isTrue((newHeight == 0)
137: || (newHeight == expectedHeight),
138: "Inlining did not get the stack heights right: "
139: + "Expected " + expectedHeight
140: + ", got " + newHeight);
141:
142: } else {
143: stackHeight.handle(inst);
144: Inline.db(" " + i + "." + stackHeight.height()
145: + ") " + inst);
146: }
147:
148: if (inst.isInvoke()) {
149: firstCall = false;
150: }
151:
152: } else if (o instanceof Label) {
153: final Label label = (Label) o;
154: stackHeight.handle(label);
155: Inline
156: .db(" "
157: + i
158: + "."
159: + stackHeight.height()
160: + ") "
161: + label
162: + (label.startsBlock() ? " (starts block)"
163: : ""));
164: }
165:
166: }
167:
168: method.setCode(code);
169:
170: if (Inline.DEBUG) {
171: stackHeight = new StackHeightCounter(method);
172: Inline.db("\nNew Code for "
173: + method.declaringClass().name() + "."
174: + method.name() + method.type());
175: code = method.code();
176: for (int j = 0; j < code.size(); j++) {
177: if (code.get(j) instanceof Label) {
178: final Label label = (Label) code.get(j);
179:
180: stackHeight.handle(label);
181:
182: final Iterator tryCatches = method.tryCatches()
183: .iterator();
184: while (tryCatches.hasNext()) {
185: final TryCatch tryCatch = (TryCatch) tryCatches
186: .next();
187: if (tryCatch.start().equals(label)) {
188: System.out
189: .println(" Begin protected region");
190:
191: }
192:
193: if (tryCatch.end().equals(label)) {
194: System.out.println(" End protected region");
195:
196: }
197:
198: // A Label can both end a protected region and begin
199: // catch
200: // block
201:
202: if (tryCatch.handler().equals(label)) {
203: System.out.println(" Catch "
204: + tryCatch.type());
205: }
206:
207: }
208:
209: System.out.println(" "
210: + j
211: + "."
212: + stackHeight.height()
213: + ") "
214: + label
215: + (label.startsBlock() ? " (starts block)"
216: : ""));
217: } else {
218: final Instruction inst = (Instruction) code.get(j);
219: stackHeight.handle(inst);
220: System.out
221: .println(" " + j + "."
222: + stackHeight.height() + ") "
223: + code.get(j));
224: }
225: }
226:
227: // Print try-catch information
228: final Iterator tryCatches = method.tryCatches().iterator();
229: System.out.println("Exception information:");
230: while (tryCatches.hasNext()) {
231: final TryCatch tryCatch = (TryCatch) tryCatches.next();
232: System.out.println(" " + tryCatch);
233: }
234: System.out.println("");
235: }
236:
237: }
238:
239: /**
240: * Helper method that does most of the work. By calling this method
241: * recursively, we can inline more than one call deep.
242: *
243: * @param caller
244: * The original caller that got all of this started. Into this
245: * method we insert the code.
246: * @param callee
247: * The method to be inlined
248: * @param index
249: * Where in caller we insert inlined code
250: * @param callStack
251: * A stack of <tt>MemberRef</tt>s that represent the inlined
252: * methods that call other methods. It is used to detect
253: * recursion.
254: *
255: * @return The index into the caller's code array of the instruction
256: * following the last inlinined instruction. Start looking here
257: * after inline returns.
258: */
259: private int inline(final MethodEditor caller,
260: final MemberRef callee, int index, final Stack callStack,
261: final StackHeightCounter stackHeight, boolean firstCall) {
262:
263: Instruction newInst = null;
264:
265: // Do we ignore the method being inlined?
266: if (context.ignoreMethod(callee)) {
267: Inline.db(" Can't inline " + callee + ": it's ignored");
268: return (index++);
269: }
270:
271: // Can we inline this method
272: if (callStack.size() > maxCallDepth) {
273: Inline.db(" Can't inline " + callee + ": max call depth ("
274: + maxCallDepth + ") reached");
275: return (index++);
276:
277: } else if (callStack.contains(callee)) {
278: Inline.db(" Can't inline recursive call to " + callee);
279: return (index++);
280: }
281:
282: // Make sure we're not inlining the static-ized version of a
283: // method in the call stack.
284: String name = callee.name();
285: final int b = name.indexOf("$$BLOAT");
286: if (b != -1) {
287: name = name.substring(0, b);
288:
289: // Get rid of first parameter
290: final Type[] oldParams = callee.type().paramTypes();
291: final StringBuffer sb = new StringBuffer("(");
292: for (int p = 1; p < oldParams.length; p++) {
293: sb.append(oldParams[p].descriptor());
294: }
295: sb.append(")" + callee.type().returnType());
296: final Type newType = Type.getType(sb.toString());
297:
298: final MemberRef unBloated = new MemberRef(callee
299: .declaringClass(), new NameAndType(name, newType));
300:
301: if (callStack.contains(unBloated)) {
302: Inline.db(" Can't inline recursive call to " + callee);
303: return (index++);
304: }
305: }
306:
307: final List code = caller.code();
308: if (code.size() > maxCodeSize) {
309: Inline.db(" Can't inline " + callee + ": max code size ("
310: + maxCodeSize + ") reached");
311: return (index++);
312: }
313:
314: MethodEditor calleeMethod = null;
315: try {
316: calleeMethod = context.editMethod(callee);
317:
318: } catch (final NoSuchMethodException ex) {
319: System.err.println("Couldn't find method " + callee);
320: ex.printStackTrace(System.err);
321: System.exit(1);
322: }
323:
324: if (calleeMethod.isNative()) {
325: Inline.db(" Can't inline " + callee
326: + ": it's a native method");
327: return (index++);
328: }
329:
330: if (calleeMethod.isSynchronized()) {
331: Inline.db(" Can't inline " + callee
332: + ": it's synchronized");
333: return (index++);
334: }
335:
336: if (!inlineExceptions
337: && (calleeMethod.methodInfo().exceptionTypes().length > 0)) {
338: Inline.db(" Can't inline " + callee
339: + ": it may throw an exception");
340: return (index++);
341: }
342:
343: if (calleeMethod.code().size() > Inline.CALLEE_SIZE) {
344: Inline.db(" Can't inline " + callee + ": it's too big");
345: return (index++);
346: }
347:
348: // Methods that catch exceptions are problematic. When an
349: // exception is thrown, it clears the stack. Ordinarily this
350: // isn't a problem. However, now the stack of the caller is
351: // cleared in addition to the stack of the callee. This is bad.
352: // The callee might catch the exception and deal with it.
353: // However, the stack has still been cleared. This really messes
354: // things up for the code that appears after the inlined method.
355: // So, if a method catches an exception, we can only inline it if
356: // the stack contains nothing but the parameters to the call.
357: if (calleeMethod.tryCatches().size() > 0) {
358: if (stackHeight.height() > callee.type().stackHeight()) {
359: Inline
360: .db(" Can't inline "
361: + callee
362: + ": It catches an exception and there's stuff on the "
363: + "stack");
364: return (index++);
365: }
366: }
367:
368: // If the callee method catches any of the same exceptions as the
369: // protected region that we are currently in, then we can't inline
370: // the method.
371: final Iterator tryCatches0 = calleeMethod.tryCatches()
372: .iterator();
373: while (tryCatches0.hasNext()) {
374: final TryCatch tc1 = (TryCatch) tryCatches0.next();
375: final Iterator iter = stackHeight.tryCatches().iterator();
376: while (iter.hasNext()) {
377: final TryCatch tc2 = (TryCatch) iter.next();
378: final Type t1 = tc1.type();
379: final Type t2 = tc2.type();
380: if ((t1 != null) && (t2 != null) && t1.equals(t2)) {
381: Inline.db(" Can't inline " + callee
382: + ": It catches the same type "
383: + tc1.type().className()
384: + " as the current protected region");
385: return (index++);
386: }
387: }
388: }
389:
390: // If the caller is a constructor and this is the first
391: // invokespecial we've seen in this callee method, we can inline
392: // calls to the constructors of superclasses and other
393: // constructors in this method. So, if this IS the first call in
394: // a method which IS a constructor, we can inline it.
395: if (calleeMethod.isConstructor()
396: && (!firstCall || !caller.isConstructor())) {
397:
398: Inline.db(" Can't inline " + callee
399: + ": It calls a normal constructor");
400: return (index++);
401: }
402:
403: // Local variables are problematic. We cannot simply map the
404: // callee's variables to new variables in the caller because we
405: // cannot precisely determine the width of the variable the first
406: // time we see it. (For instance, Nate's generated code might use
407: // a local variable as a non-wide initially and then use it as a
408: // wide later.)
409:
410: // Okay, we going to inline. Remove the calling instruction.
411: final Instruction call = (Instruction) code.remove(index--);
412: Inline.db(" Removing call: " + call);
413: Assert.isTrue((call.opcodeClass() == Opcode.opcx_invokestatic)
414: || (call.opcodeClass() == Opcode.opcx_invokespecial),
415: "Removing the wrong call instruction:" + call);
416: callStack.push(callee);
417:
418: Inline.db(" Inlining call (" + callStack.size() + ") to "
419: + callee.declaringClass() + "." + callee.name()
420: + callee.type());
421: context.getInlineStats().noteInlined();
422:
423: // First we have to pop the arguments off the stack and store them
424: // into the local variables. Remember that wide types occupy two
425: // local variables.
426: final Mapper mapper = new Mapper(caller);
427: Type[] paramTypes = callee.type().indexedParamTypes();
428: if (!calleeMethod.isStatic()) {
429: // Constructors (and any other special methods we're inlining)
430: // have a "this" pointer where static methods do not.
431: final Type[] newParams = new Type[paramTypes.length + 1];
432: newParams[0] = callee.declaringClass();
433:
434: for (int i = 0; i < paramTypes.length; i++) {
435: newParams[i + 1] = paramTypes[i];
436: }
437: paramTypes = newParams;
438: }
439:
440: final LocalVariable[] params = new LocalVariable[paramTypes.length];
441:
442: Inline.db(" Indexed params:");
443: for (int i = 0; i < params.length; i++) {
444: params[i] = calleeMethod.paramAt(i);
445: Inline.db(" "
446: + i
447: + ": "
448: + params[i]
449: + (params[i] != null ? " " + params[i].type() + " "
450: : ""));
451: }
452:
453: for (int i = params.length - 1; i >= 0; i--) {
454: // Map the local variables containing the arguments to new
455: // local variables.
456: final LocalVariable param = params[i];
457: final Type paramType = params[i].type();
458:
459: if (param.type() == null) {
460: continue;
461: }
462:
463: Inline.db(" Param " + i + ": " + param + " of type "
464: + paramType);
465:
466: final LocalVariable newVar = mapper.map(param, paramType);
467:
468: int opcode;
469:
470: if (paramType.isReference()) {
471: opcode = Opcode.opcx_astore;
472:
473: } else {
474: switch (paramType.typeCode()) {
475: case Type.BOOLEAN_CODE:
476: case Type.BYTE_CODE:
477: case Type.CHARACTER_CODE:
478: case Type.SHORT_CODE:
479: opcode = Opcode.opcx_istore;
480: break;
481:
482: case Type.DOUBLE_CODE:
483: opcode = Opcode.opcx_dstore;
484: break;
485:
486: case Type.LONG_CODE:
487: opcode = Opcode.opcx_lstore;
488: break;
489:
490: case Type.FLOAT_CODE:
491: opcode = Opcode.opcx_fstore;
492: break;
493:
494: case Type.INTEGER_CODE:
495: opcode = Opcode.opcx_istore;
496: break;
497:
498: default:
499: throw new IllegalArgumentException("What's a "
500: + paramType + "doing as a method "
501: + "parameter");
502: }
503: }
504:
505: newInst = new Instruction(opcode, newVar);
506: code.add(++index, newInst);
507: stackHeight.handle(newInst);
508: Inline.db(" " + index + "." + stackHeight.height() + "> "
509: + newInst);
510: }
511:
512: // Before we mess with the code, we have to patch up the try-catch
513: // information from the inlined method to the caller method.
514: final Iterator tryCatches = calleeMethod.tryCatches()
515: .iterator();
516: while (tryCatches.hasNext()) {
517: final TryCatch tryCatch = (TryCatch) tryCatches.next();
518:
519: final Label start = mapper.map(tryCatch.start());
520: final Label end = mapper.map(tryCatch.end());
521: final Label handler = mapper.map(tryCatch.handler());
522:
523: final TryCatch newTryCatch = new TryCatch(start, end,
524: handler, tryCatch.type());
525: caller.addTryCatch(newTryCatch);
526:
527: // db("Try-catch");
528: // db(" Before: " + tryCatch.start() + "\t" + tryCatch.end() +
529: // "\t" + tryCatch.handler());
530: // db(" After: " + newTryCatch.start() + "\t" + newTryCatch.end()
531: // + "\t" + newTryCatch.handler());
532: }
533:
534: // Go through the code in the callee method and inline it. Handle
535: // any calls by making a recursive call to this method. Copy each
536: // instruction to the method in which it is being inlined. Along
537: // the way convert references to local variables to their mapped
538: // values. Also remove return instructions. Replace them with
539: // loads as necessary.
540:
541: final List inlineCode = calleeMethod.code();
542:
543: // We don't want to introduce a new end label because it confuses
544: // BLOAT during CFG construction. We designate the end label as
545: // starting a new block in hopes that it will solve problems with
546: // CFG construction.
547: final Object last = inlineCode.get(inlineCode.size() - 1);
548: boolean addEndLabel;
549: Label endLabel;
550: if (last instanceof Label) {
551: endLabel = mapper.map((Label) last);
552: addEndLabel = false;
553:
554: } else {
555: endLabel = caller.newLabel();
556: addEndLabel = true;
557: }
558: endLabel.setStartsBlock(true);
559:
560: firstCall = true;
561:
562: for (int j = 0; j < inlineCode.size(); j++) {
563: final Object o = inlineCode.get(j);
564:
565: if (o instanceof Label) {
566: final Label label = (Label) o;
567: final Label newLabel = mapper.map(label);
568:
569: code.add(++index, newLabel);
570: stackHeight.handle(newLabel);
571: Inline.db(" "
572: + index
573: + "."
574: + stackHeight.height()
575: + "> "
576: + newLabel
577: + (newLabel.startsBlock() ? " (starts block)"
578: : ""));
579: continue;
580: }
581:
582: Assert.isTrue(o instanceof Instruction, "What is a " + o
583: + " doing in the instruction stream?");
584:
585: final Instruction inst = (Instruction) inlineCode.get(j);
586: Object operand = inst.operand();
587: final int opcode = inst.opcodeClass();
588:
589: if (operand instanceof LocalVariable) {
590: // Map local variable in the callee method to local
591: // variables in the caller method.
592: final LocalVariable local = mapper.map(
593: (LocalVariable) operand,
594: (inst.category() == 2 ? true : false));
595: operand = local;
596:
597: } else if (operand instanceof Label) {
598: // Map labels in the callee method to labels in the caller
599: // method.
600: final Label label = mapper.map((Label) operand);
601: operand = label;
602:
603: } else if (operand instanceof IncOperand) {
604: // Map the local being incremented
605: final IncOperand inc = (IncOperand) operand;
606: final LocalVariable newLocal = mapper.map(inc.var(),
607: Type.INTEGER);
608: operand = new IncOperand(newLocal, inc.incr());
609:
610: } else if (operand instanceof Switch) {
611: // We have to patch up the Labels involved with the Switch
612: final Switch oldSwitch = (Switch) operand;
613:
614: final Label newDefault = mapper.map(oldSwitch
615: .defaultTarget());
616:
617: final Label[] oldTargets = oldSwitch.targets();
618: final Label[] newTargets = new Label[oldTargets.length];
619: for (int i = 0; i < newTargets.length; i++) {
620: final Label newTarget = mapper.map(oldTargets[i]);
621: newTargets[i] = newTarget;
622: }
623:
624: operand = new Switch(newDefault, newTargets, oldSwitch
625: .values());
626: }
627:
628: if (inst.isReturn()) {
629: // Insert a jump to the end of the inlined method. Any
630: // return value will be on top of the stack. This is where
631: // we want it.
632: newInst = new Instruction(Opcode.opcx_goto, endLabel);
633: code.add(++index, newInst);
634: stackHeight.handle(newInst);
635: Inline.db(" " + index + "." + stackHeight.height()
636: + "> " + newInst);
637:
638: } else if ((inst.opcodeClass() == Opcode.opcx_invokestatic)
639: || (inst.opcodeClass() == Opcode.opcx_invokespecial)) {
640: // Make a recursive call. Note that this must be done after
641: // we add the call instruction above. But we only want to
642: // visit the instruction with the stackHeight if the call was
643: // not inlined.
644: newInst = new Instruction(opcode, operand);
645: code.add(++index, newInst);
646:
647: stackHeight.handle(newInst);
648: final int expectedHeight = stackHeight.height();
649: stackHeight.unhandle(newInst);
650:
651: final MemberRef nestedCall = (MemberRef) inst.operand();
652: final int oldIndex = index;
653: index = inline(caller, nestedCall, index, callStack,
654: stackHeight, firstCall);
655:
656: if (index == oldIndex) {
657: stackHeight.handle(newInst);
658: Inline.db(" " + index + "." + stackHeight.height()
659: + "> " + newInst);
660: }
661:
662: final int newHeight = stackHeight.height();
663: Assert.isTrue((newHeight == 0)
664: || (newHeight == expectedHeight),
665: "Inlining did not get the stack heights right: "
666: + "Expected " + expectedHeight
667: + ", got " + newHeight);
668:
669: } else {
670: // Add the instruction
671: newInst = new Instruction(opcode, operand);
672: code.add(++index, newInst);
673: stackHeight.handle(newInst);
674: Inline.db(" " + index + "." + stackHeight.height()
675: + "> " + newInst);
676: }
677:
678: // We want to do this after we've made any recursive calls to
679: // inline.
680: if (inst.isInvoke()) {
681: firstCall = false;
682: }
683:
684: }
685:
686: if (addEndLabel) {
687: // Done inlining. Add end label.
688: code.add(++index, endLabel);
689: stackHeight.handle(endLabel);
690: Inline
691: .db(" "
692: + index
693: + "."
694: + stackHeight.height()
695: + "> "
696: + endLabel
697: + (endLabel.startsBlock() ? " (starts block)"
698: : ""));
699: }
700:
701: caller.setDirty(true);
702: callStack.pop();
703: return (index);
704:
705: }
706: }
707:
708: /**
709: * Utility class for mapping local variables and labels. Note that when mapping
710: * local variables we have to be careful. We can't assume that a variable will
711: * retain its "wideness" throughout the method. I learned this one the hard way.
712: * So, we have to keep a constant difference between the mapped variables.
713: */
714: class Mapper {
715: private Map varsMap; // Maps local variables
716:
717: private Map labelsMap; // Maps labels
718:
719: private MethodEditor method; // Method into which things are mapped
720:
721: private int offset; // Start numbering new locals here
722:
723: private static void db(final String s) {
724: if (Inline.DEBUG) {
725: System.out.println(s);
726: }
727: }
728:
729: /**
730: * Constructor.
731: */
732: public Mapper(final MethodEditor method) {
733: this .method = method;
734: varsMap = new HashMap();
735: labelsMap = new HashMap();
736: offset = method.maxLocals() + 1;
737: }
738:
739: public Label map(final Label label) {
740: Label newLabel = (Label) labelsMap.get(label);
741: if (newLabel == null) {
742: newLabel = this .method.newLabel();
743: newLabel.setStartsBlock(label.startsBlock());
744: labelsMap.put(label, newLabel);
745: Mapper
746: .db(" "
747: + label
748: + " -> "
749: + newLabel
750: + (newLabel.startsBlock() ? " (starts block)"
751: : ""));
752: }
753: return (newLabel);
754: }
755:
756: public LocalVariable map(final LocalVariable var, final Type type) {
757: LocalVariable newVar = (LocalVariable) varsMap.get(var);
758: if (newVar == null) {
759: newVar = this .method.localAt(var.index() + offset);
760: // newVar = this.method.newLocal(type);
761: varsMap.put(var, newVar);
762: Mapper.db(" " + var + " (" + var.index() + ") -> "
763: + newVar + "(" + var.index() + "+" + offset + ")"
764: + (type.isWide() ? " (" + type + ")" : ""));
765: }
766: return (newVar);
767: }
768:
769: public LocalVariable map(final LocalVariable var,
770: final boolean isWide) {
771: LocalVariable newVar = (LocalVariable) varsMap.get(var);
772: if (newVar == null) {
773: newVar = this .method.localAt(var.index() + offset);
774: // newVar = this.method.newLocal(isWide);
775: varsMap.put(var, newVar);
776: Mapper.db(" " + var + " (" + var.index() + ") -> "
777: + newVar + "(" + var.index() + "+" + offset + ")"
778: + (isWide ? " (wide)" : ""));
779: }
780: return (newVar);
781: }
782:
783: }
|