001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: * The Original Software is NetBeans. The Initial Developer of the Original
026: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
027: * Microsystems, Inc. All Rights Reserved.
028: *
029: * If you wish your version of this file to be governed by only the CDDL
030: * or only the GPL Version 2, indicate your decision by adding
031: * "[Contributor] elects to include this software in this distribution
032: * under the [CDDL or GPL Version 2] license." If you do not indicate a
033: * single choice of license, a recipient has the option to distribute
034: * your version of this file under either the CDDL, the GPL Version 2 or
035: * to extend the choice of license to its licensees as provided above.
036: * However, if you add GPL Version 2 code and therefore, elected the GPL
037: * Version 2 license, then the option applies only if the new code is
038: * made subject to such option by the copyright holder.
039: */
040:
041: package org.netbeans.lib.profiler.instrumentation;
042:
043: import org.netbeans.lib.profiler.classfile.ClassInfo;
044: import org.netbeans.lib.profiler.client.RuntimeProfilingPoint;
045: import java.util.Stack;
046:
047: /**
048: * Base class for a number of classes performing concrete method instrumentation injections. Contains core functionality
049: * for injection of arbitrary bytecodes at a given location in a method and subsequent rewriting of bytecodes. Also
050: * supports appending bytecodes to the existing bytecodes, and extending the method's exception table.
051: *
052: * @author Misha Dmitriev
053: */
054: public abstract class Injector extends SingleMethodScaner {
055: //~ Inner Classes ------------------------------------------------------------------------------------------------------------
056:
057: //-------------------------------------- Helper classes ------------------------------------------
058: private abstract static class ChangeItem {
059: //~ Instance fields ------------------------------------------------------------------------------------------------------
060:
061: int bci;
062:
063: //~ Constructors ---------------------------------------------------------------------------------------------------------
064:
065: ChangeItem(int bci) {
066: this .bci = bci;
067: }
068:
069: //~ Methods --------------------------------------------------------------------------------------------------------------
070:
071: abstract void handleCodeChange(Injector r);
072:
073: void relocate(int breakBCI, int delta) {
074: if (bci > breakBCI) {
075: bci += delta;
076: }
077: }
078: }
079:
080: private static class ChangeJumpWiden extends ChangeItem {
081: //~ Instance fields ------------------------------------------------------------------------------------------------------
082:
083: int delta; // New length of instruction at bci
084:
085: //~ Constructors ---------------------------------------------------------------------------------------------------------
086:
087: ChangeJumpWiden(int bci, int delta) {
088: super (bci);
089: this .delta = delta;
090: }
091:
092: //~ Methods --------------------------------------------------------------------------------------------------------------
093:
094: // Callback to do instruction
095: void handleCodeChange(Injector r) {
096: r.handleJumpWiden(bci, delta);
097: }
098: }
099:
100: private static class ChangeSwitchPadding extends ChangeItem {
101: //~ Instance fields ------------------------------------------------------------------------------------------------------
102:
103: boolean isLookupSwitch;
104: int padding;
105:
106: //~ Constructors ---------------------------------------------------------------------------------------------------------
107:
108: ChangeSwitchPadding(int bci, int padding, boolean isLookupSwitch) {
109: super (bci);
110: this .padding = padding;
111: this .isLookupSwitch = isLookupSwitch;
112: }
113:
114: //~ Methods --------------------------------------------------------------------------------------------------------------
115:
116: // Callback to do instruction
117: void handleCodeChange(Injector r) {
118: r.handleSwitchPadding(bci, padding, isLookupSwitch);
119: }
120: }
121:
122: //~ Static fields/initializers -----------------------------------------------------------------------------------------------
123:
124: private static final int MAX_SHORT = ((1 << 15) - 1);
125: private static final int MIN_SHORT = (-(1 << 15));
126:
127: // Increment to possible stack depth -- two instrumentation arguments
128: private static final int STACK_INCREMENT = 2;
129: private static Stack changes = new Stack(); // Stack of additional changes to bytecodes that may be caused by our main injection change
130: private static byte[] _overwrite = new byte[3]; // Stores overwritten bytes for shrunken instructions
131:
132: // The following array is re-used,to avoid excessive object allocations - which means that the class is not multi thread safe
133: private static byte[] reusableExcTable = new byte[100];
134:
135: // ProfilerRuntime.profilePointHit instrumentation
136: private static final byte[] injProfilePointHitCode = new byte[] {
137: opc_sipush, 0, 0, (byte) opc_invokestatic, 0, 0, 0, 0 };
138: private static final int injProfilePointHitIDCodeIdx = 1;
139: private static final int injProfilePointHitMethodIdx = 4;
140:
141: //~ Instance fields ----------------------------------------------------------------------------------------------------------
142:
143: protected byte[] exceptionTable; // Current updateable copy of exception table (what is in MethodInfo Code attribute after exception_table_length)
144: protected int baseCPoolCount; // The "base" constant pool index, after which the set of entries containing the calls that
145: // we inject, plus supporting entries, are located.
146: protected int excTableEntryCount;
147: protected int maxLocals;
148: protected int maxStack;
149: protected int origBytecodesLength;
150: protected int origExcTableEntryCount;
151: private boolean changeTypeIsInjectNewInstr;
152: private boolean injectionBindsToFollowingInstruction;
153:
154: //~ Constructors -------------------------------------------------------------------------------------------------------------
155:
156: // ---------------------------------------- Protected methods -------------------------------------------------
157: protected Injector() {
158: }
159:
160: /**
161: * Creates a new Injector for the specified method. Subsequently one can call injectCodeAndRewrite() and other
162: * methods below as many times as needed.
163: */
164: protected Injector(ClassInfo clazz, int methodIdx) {
165: super (clazz, methodIdx);
166: origBytecodesLength = bytecodesLength;
167: maxStack = getU2(origMethodInfo, bytecodesStartIdx - 8);
168: maxLocals = getU2(origMethodInfo, bytecodesStartIdx - 6);
169: initExceptionTable();
170: origExcTableEntryCount = excTableEntryCount;
171: }
172:
173: //~ Methods ------------------------------------------------------------------------------------------------------------------
174:
175: // ------------------------------------ Public abstract interface ---------------------------------------------
176:
177: /** In concrete subclasses, should do all the real work on method instrumentation and return the updated packed methodInfo */
178: public abstract byte[] instrumentMethod();
179:
180: protected void addExceptionTableEntry(int startPC, int endPC,
181: int handlerPC, int typeCPIndex) {
182: int pos = excTableEntryCount * 8;
183: putU2(exceptionTable, pos, startPC);
184: putU2(exceptionTable, pos + 2, endPC);
185: putU2(exceptionTable, pos + 4, handlerPC);
186: putU2(exceptionTable, pos + 6, typeCPIndex);
187: excTableEntryCount++;
188: }
189:
190: protected void appendCode(byte[] appendedBytes,
191: int appendedBytesCount) {
192: System.arraycopy(appendedBytes, 0, bytecodes, bytecodesLength,
193: appendedBytesCount);
194: bytecodesLength += appendedBytesCount;
195: }
196:
197: protected byte[] createPackedMethodInfo() {
198: int diff = (bytecodesLength - origBytecodesLength)
199: + ((excTableEntryCount - origExcTableEntryCount) * 8);
200: byte[] ret = new byte[origMethodInfo.length + diff];
201: System.arraycopy(origMethodInfo, 0, ret, 0, bytecodesStartIdx);
202: System.arraycopy(bytecodes, 0, ret, bytecodesStartIdx,
203: bytecodesLength);
204:
205: int attrLength = getU4(origMethodInfo, bytecodesStartIdx - 12);
206: attrLength += diff;
207: putU4(ret, bytecodesStartIdx - 12, attrLength); // Patch the attribute_length in Code attribute
208: putU4(ret, bytecodesStartIdx - 4, bytecodesLength); // Patch the code_length in Code attribute
209: System.arraycopy(origMethodInfo, bytecodesStartIdx
210: + origBytecodesLength, ret, bytecodesStartIdx
211: + bytecodesLength, origMethodInfo.length
212: - bytecodesStartIdx - origBytecodesLength);
213:
214: int excTableOldStart = clazz
215: .getExceptionTableStartOffsetInMethodInfo(methodIdx);
216: int excTableNewStart = excTableOldStart
217: + (bytecodesLength - origBytecodesLength);
218: int excTableOldLen = origExcTableEntryCount * 8;
219: int excTableNewLen = excTableEntryCount * 8;
220:
221: if (excTableOldLen != excTableNewLen) {
222: System.arraycopy(ret, excTableNewStart + excTableOldLen,
223: ret, excTableNewStart + excTableNewLen,
224: origMethodInfo.length - excTableOldStart
225: - excTableOldLen);
226: putU2(ret, excTableNewStart, excTableEntryCount);
227: }
228:
229: System.arraycopy(exceptionTable, 0, ret, excTableNewStart + 2,
230: excTableNewLen);
231:
232: // FIXME: need to update linenumber table and localvariable table as well
233: putU2(ret, bytecodesStartIdx - 8, maxStack + STACK_INCREMENT);
234: putU2(ret, bytecodesStartIdx - 6, maxLocals);
235:
236: return ret;
237: }
238:
239: /**
240: * Injects the specified raw bytes array at the specified location.
241: * The last parameter indicates whether the injected code should be bound to the following (if true) or previous (if false) bytecode
242: * instruction. When binding to the following instruction, all jumps that target that instruction will target the injected code after
243: * rewriting. When binding to the previous instruction x, all jumps that target the instruction x+1 will still target that instruction
244: * after rewriting (i.e. not target our injected code, as in the first case).
245: */
246: protected void injectCodeAndRewrite(byte[] injectedBytes,
247: int injectedBytesCount, int injectionPos,
248: boolean injectionBindsToFollowingInstruction) {
249: this .injectionBindsToFollowingInstruction = injectionBindsToFollowingInstruction;
250: relocateCode(injectionPos, 0, injectedBytesCount, true);
251: // Copy the bytecodes to inject into the method
252: System.arraycopy(injectedBytes, 0, bytecodes, injectionPos,
253: injectedBytesCount);
254: // Now handle the additional changes that could be caused by the injection
255: handleCodeChanges();
256: }
257:
258: /**
259: * Profiling points MUST BE already SORTED by BCI in ascending order and only relevant to this method.
260: *
261: * @param points sorted array of Profiling points
262: * @param ppHitCPMethodIdx
263: */
264: protected void insertProfilingPoints(
265: RuntimeProfilingPoint[] points, int ppHitCPMethodIdx) {
266: for (int i = 0; i < points.length; i++) {
267: RuntimeProfilingPoint point = points[i];
268: assert (i == 0)
269: || (point.getBci() >= points[i - 1].getBci());
270:
271: int ppbci = point.getBci()
272: + (i * injProfilePointHitCode.length);
273: injectProfilePointHit(point, ppbci, ppHitCPMethodIdx);
274: maxStack = Math.max(maxStack, 4);
275: }
276: }
277:
278: /** Called by the ChangeJumpWiden class */
279: void handleJumpWiden(int bci, int delta) {
280: int ilen = rcInstrLen(bci);
281:
282: if (ilen != 3) {
283: return; // Request already handled
284: //Above: assert(code_at(bci) == Bytecodes::_goto_w || code_at(bci) == Bytecodes::_jsr_w, "sanity check");
285: }
286:
287: //assert(ilen == 3, "check length");
288: relocateCode(bci, 3, 2, false);
289:
290: int bc = bytecodes[bci] & 0xff;
291:
292: switch (bc) {
293: case opc_goto:
294: bytecodes[bci] = (byte) opc_goto_w;
295:
296: break;
297: case opc_jsr:
298: bytecodes[bci] = (byte) opc_jsr_w;
299:
300: break;
301: default:
302: System.err
303: .println("*** Profiler Engine: error - should not reach here in handleJumpWiden!"); // NOI18N
304: }
305:
306: // If it's a forward jump, add 2 for the widening.
307: if (delta > 0) {
308: delta += 2;
309: }
310:
311: putInt(bci + 1, delta);
312: }
313:
314: /** If a change item is recorded for bci, with the same is_lookup_switch flag value, returns the associated padding, else -1. */
315: private int getOrigSwitchPadding(int bci, boolean isLookupSwitch) {
316: for (int k = 0; k < changes.size(); k++) {
317: ChangeItem ci = (ChangeItem) changes.elementAt(k);
318:
319: if (ci instanceof ChangeSwitchPadding) {
320: ChangeSwitchPadding csp = (ChangeSwitchPadding) ci;
321:
322: if ((csp.isLookupSwitch == isLookupSwitch)
323: && (csp.bci == bci)) {
324: return csp.padding;
325: }
326: }
327: }
328:
329: return -1;
330: }
331:
332: /**
333: * The current instruction at bci is a jump; one of its offsets starts at "offset" and is a short if isShort is true,
334: * and an integer otherwise. If the jump crosses breakBCI, change the span of the jump by delta.
335: */
336: private void changeJump(int bci, int offset, boolean isShort,
337: int breakBCI, int delta) {
338: int bciDelta = (isShort) ? getShort(offset) : getInt(offset);
339: int targ = bci + bciDelta;
340:
341: boolean doRewrite = false;
342:
343: // Injection of a completely new instruction and widening of an existing instruction should be handled differently
344: if (changeTypeIsInjectNewInstr) {
345: if (breakBCI == 0) {
346: return; // No jump to the code injected at offset 0 can be made in the rewrited code
347: // The most reliable way to verify the correctness of the statements below, is to draw a picture, something like
348: // 1 a
349: // <--- breakBCI = 2
350: // 2 b
351: // <--- breakBCI = 3
352: // 3 goto
353: // <--- breakBCI = 4
354: // 4 c
355: // 5 d
356: //
357: // and then consider all the combinations of bci = 3 with breakBCI = 2, 3, 4 and targ = 1,2,4,5 - what should happen in each case.
358: // For example:
359: // bci = 3, breakBCI = 2, injectionBindsToFollowingInstruction = true
360: // targ = 1 - should rewrite jump argument
361: // targ = 2 - should rewrite jump argument (because we will need to jump to the injected code, rather than old instruction at b at 2)
362: // targ = 4, 5 - don't need to rewrite jump argument
363: // ... and so on
364: }
365:
366: if (injectionBindsToFollowingInstruction) {
367: if (((bci < breakBCI) && (targ > breakBCI))
368: || ((bci >= breakBCI) && (targ <= breakBCI))) {
369: doRewrite = true;
370: }
371: } else { // Injected code binds to previous instruction, e.g. the traceObjAlloc() call that follows a constructor call
372:
373: if (((bci < breakBCI) && (targ >= breakBCI))
374: || ((bci >= breakBCI) && (targ < breakBCI))) {
375: doRewrite = true;
376: }
377: }
378: } else {
379: if (((bci <= breakBCI) && (targ > breakBCI))
380: || ((bci >= breakBCI) && (targ < breakBCI))) {
381: doRewrite = true;
382: }
383: }
384:
385: if (doRewrite) {
386: int newDelta = (bciDelta > 0) ? (bciDelta + delta)
387: : (bciDelta - delta);
388:
389: if (isShort
390: && ((newDelta > MAX_SHORT) || (newDelta < MIN_SHORT))) {
391: changes.push(new ChangeJumpWiden(bci, newDelta));
392: } else if (isShort) {
393: putShort(offset, (short) newDelta);
394: } else {
395: putInt(offset, newDelta);
396: }
397: }
398: }
399:
400: /** Changes all jumps crossing breakBCI by delta. May enqueue things on the changes stack */
401: private void changeJumps(int breakBCI, int delta) {
402: int bci = 0;
403: int bc;
404:
405: // Now, adjust any affected instructions.
406: while (bci < bytecodesLength) {
407: bc = (bytecodes[bci] & 0xFF);
408:
409: if (((bc >= opc_ifeq) && (bc <= opc_if_acmpne))
410: || (bc == opc_ifnull) || (bc == opc_ifnonnull)
411: || (bc == opc_goto) || (bc == opc_jsr)) {
412: changeJump(bci, bci + 1, true, breakBCI, delta);
413: } else {
414: switch (bc) {
415: case opc_goto_w:
416: case opc_jsr_w:
417: changeJump(bci, bci + 1, false, breakBCI, delta);
418:
419: break;
420: case opc_tableswitch:
421: case opc_lookupswitch: {
422: int recPad = getOrigSwitchPadding(bci,
423: (bc != opc_tableswitch));
424: int oldPad = (recPad != -1) ? recPad
425: : (align(bci + 1) - (bci + 1));
426:
427: if (bci > breakBCI) {
428: int new_bci = bci + delta;
429: int newPad = align(new_bci + 1) - (new_bci + 1);
430:
431: // Do we need to check the padding?
432: if (newPad != oldPad) {
433: if (recPad == -1) {
434: changes.push(new ChangeSwitchPadding(
435: bci, oldPad,
436: (bc != opc_tableswitch)));
437: }
438: }
439: }
440:
441: // Then the rest, which depends on the kind of switch.
442: if (bc == opc_tableswitch) {
443: changeJump(bci, bci + 1 + oldPad, false,
444: breakBCI, delta);
445:
446: // We cannot use the Bytecode_tableswitch abstraction, since the padding might not be correct.
447: int lo = getInt(bci + 1 + oldPad + (4 * 1));
448: int hi = getInt(bci + 1 + oldPad + (4 * 2));
449: int n = hi - lo + 1;
450:
451: for (int k = 0; k < n; k++) {
452: changeJump(bci, bci + 1 + oldPad
453: + (4 * (k + 3)), false, breakBCI,
454: delta);
455: }
456:
457: // Special next-bci calculation here...
458: bci += (1 + oldPad + ((n + 3) * 4));
459:
460: continue;
461: } else {
462: changeJump(bci, bci + 1 + oldPad, false,
463: breakBCI, delta);
464:
465: // We cannot use the Bytecode_lookupswitch abstraction, since the padding might not be correct.
466: int npairs = getInt(bci + 1 + oldPad + (4 * 1));
467:
468: for (int k = 0; k < npairs; k++) {
469: changeJump(bci, bci + 1 + oldPad
470: + (4 * (2 + (2 * k) + 1)), false,
471: breakBCI, delta);
472: }
473:
474: // Special next-bci calculation here...
475: bci += (1 + oldPad + ((2 + (npairs * 2)) * 4));
476:
477: continue;
478: }
479: }
480: default:
481: break;
482: }
483: }
484:
485: bci += opcodeLength(bci);
486: }
487: }
488:
489: //--------------------------------------- Private implementation -------------------------------------------
490: private void handleCodeChanges() {
491: while (!changes.empty()) {
492: // Inv: everything is aligned.
493: ChangeItem ci = (ChangeItem) changes.pop();
494: ci.handleCodeChange(this );
495: }
496: }
497:
498: /** Handle lookup/table switch instructions. Called be ChangeSwitchPad class */
499: private void handleSwitchPadding(int bci, int oldPad,
500: boolean isLookupSwitch) {
501: int ilen = rcInstrLen(bci);
502: int newPad = align(bci + 1) - (bci + 1);
503: int padDelta = newPad - oldPad;
504:
505: if (padDelta != 0) {
506: int len;
507:
508: if (!isLookupSwitch) {
509: int low = getInt(bci + 1 + oldPad + 4);
510: int high = getInt(bci + 1 + oldPad + 8);
511: len = high - low + 1 + 3; // 3 for default, hi, lo.
512: } else {
513: int npairs = getInt(bci + 1 + oldPad + 4);
514: len = (npairs * 2) + 2; // 2 for default, npairs.
515: }
516:
517: // Because "relocateCode" does a "changeJumps" loop, which parses instructions to determine their length,
518: // we need to call that before messing with the current instruction. Since it may also overwrite the current
519: // instruction when moving down, remember the possibly overwritten part.
520: relocateCode(bci, ilen, padDelta, false); // Relocate the code following the instruction...
521:
522: if (padDelta < 0) { // Move the shrunken instruction down.
523: System.arraycopy(bytecodes, bci + 1 + oldPad,
524: bytecodes, bci + 1 + newPad, (len * 4)
525: + padDelta);
526: System.arraycopy(_overwrite, 0, bytecodes, bci + 1
527: + newPad + (len * 4) + padDelta, -padDelta);
528: } else { // Move the expanded instruction up.
529: System.arraycopy(bytecodes, bci + 1 + oldPad,
530: bytecodes, bci + 1 + newPad, len * 4);
531: }
532: }
533: }
534:
535: private void initExceptionTable() {
536: int startOfs = clazz
537: .getExceptionTableStartOffsetInMethodInfo(methodIdx);
538: excTableEntryCount = getU2(origMethodInfo, startOfs);
539:
540: int len = excTableEntryCount * 8;
541:
542: if (reusableExcTable.length < (len + 40)) {
543: reusableExcTable = new byte[(len * 2) + 40]; // Can add at least 8*5 = 40 entries.
544: }
545:
546: System.arraycopy(origMethodInfo, startOfs + 2,
547: reusableExcTable, 0, len);
548: exceptionTable = reusableExcTable;
549: }
550:
551: private void injectProfilePointHit(RuntimeProfilingPoint point,
552: int bci, int ppHitCPMethodIdx) {
553: // Prepare the profilePointHit(char id, char flags) code packet that is to be injected
554: putU2(injProfilePointHitCode, injProfilePointHitMethodIdx,
555: ppHitCPMethodIdx + baseCPoolCount);
556: putU2(injProfilePointHitCode, injProfilePointHitIDCodeIdx,
557: point.getId());
558:
559: injectCodeAndRewrite(injProfilePointHitCode,
560: injProfilePointHitCode.length, bci, true);
561: }
562:
563: /**
564: * We need a special instruction size method, since lookupswitches and tableswitches might not be
565: * properly aligned during relocation
566: */
567: private int rcInstrLen(int bci) {
568: int bc = bytecodes[bci] & 0xFF;
569:
570: switch (bc) {
571: // In the case of switch instructions, see if we have the original padding recorded.
572: case opc_tableswitch:
573: case opc_lookupswitch: {
574: int pad = getOrigSwitchPadding(bci,
575: (bc == opc_lookupswitch));
576:
577: if (pad == -1) {
578: return opcodeLength(bci);
579: }
580:
581: // Otherwise, depends on the switch type.
582: switch (bc) {
583: case opc_tableswitch: {
584: int lo = getInt(bci + 1 + pad + (4 * 1));
585: int hi = getInt(bci + 1 + pad + (4 * 2));
586: int n = hi - lo + 1;
587:
588: return 1 + pad + (4 * (3 + n));
589: }
590: case opc_lookupswitch: {
591: int npairs = getInt(bci + 1 + pad + (4 * 1));
592:
593: return 1 + pad + (4 * (2 + (2 * npairs)));
594: }
595: }
596: }
597: }
598:
599: return opcodeLength(bci);
600: }
601:
602: /**
603: * The input is interpreted depending on the injectNewInstr flag value.
604: * If it's false, instruction at bci, whose size is iLen, is changing size by delta.
605: * If it's true, we inject some instructions bytes of length delta at bci.
606: * Reallocate, move code, recalculate jumps, and enqueue more change items as necessary.
607: * Note that currently it's assumed that delta is a multiple of 4 - in this way we avoid additional problems with changed
608: * paddings in 'switch' instructions.
609: */
610: private void relocateCode(int bci, int iLen, int delta,
611: boolean injectNewInstr) {
612: changeTypeIsInjectNewInstr = injectNewInstr; // Other methods need to know whether we are injecting or widening an instruction
613:
614: // Change jumps before doing the copying; this routine requires aligned switches. If we inject something in
615: // the very beginning of the method, changing jumps is not needed.
616: if (bci > 0) {
617: changeJumps(bci, delta);
618: }
619:
620: // In case we have shrunken a tableswitch/lookupswitch statement, we store the last
621: // bytes that get overwritten. We have to copy the bytes after the changeJumps method
622: // has been called, since it is likely to update last offset in a tableswitch/lookupswitch
623: if (delta < 0) {
624: //assert(delta>=-3, "we cannot overwrite more than 3 bytes");
625: System.arraycopy(bytecodes, bci + iLen + delta, _overwrite,
626: 0, -delta);
627: }
628:
629: int nextBCI = bci + iLen; // Needed for instruction widening; ok for injection, since iLen == 0 in that case
630: System.arraycopy(bytecodes, nextBCI, bytecodes,
631: nextBCI + delta, bytecodesLength - nextBCI);
632: bytecodesLength += delta;
633:
634: updateExceptionTable(bci, delta);
635:
636: // We currently don't support the following updates - they are used only by debuggers.
637: // updateLineNumberTable(injectionPos, delta);
638: // updateLocalVariableTable(injectionPos, delta);
639: // updateLocalVariableTypeTable(injectionPos, delta);
640:
641: // Relocate the bcis of changes in the pending change stack
642: for (int j = 0; j < changes.size(); j++) {
643: ChangeItem ci = (ChangeItem) changes.elementAt(j);
644: ci.relocate(bci, delta);
645: }
646: }
647:
648: private void updateExceptionTable(int injectionPos,
649: int injectedBytesCount) {
650: int pos = 0;
651:
652: for (int i = 0; i < excTableEntryCount; i++) {
653: int startPC = getU2(exceptionTable, pos);
654: int endPC = getU2(exceptionTable, pos + 2);
655: int handlerPC = getU2(exceptionTable, pos + 4);
656:
657: if (startPC > injectionPos) {
658: startPC += injectedBytesCount;
659: endPC += injectedBytesCount;
660: putU2(exceptionTable, pos, startPC);
661: putU2(exceptionTable, pos + 2, endPC);
662: } else if (injectionPos < endPC) {
663: endPC += injectedBytesCount;
664: putU2(exceptionTable, pos + 2, endPC);
665: }
666:
667: if (handlerPC > injectionPos) {
668: handlerPC += injectedBytesCount;
669: putU2(exceptionTable, pos + 4, handlerPC);
670: }
671:
672: pos += 8;
673: }
674: }
675: }
|