blob: 4dd3ee6878e6217a2c2c28a7968d6a4c4dd4e262 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
20 * CA 95054 USA or visit www.sun.com if you need additional information or
21 * have any questions.
22 */
23
24/*
25 * An extension of BinaryCode that allows code to be printed.
26 * Includes printing of disassembled byte codes, exception info,
27 * local variable and line number info.
28 *
29 */
30
31package ilib;
32
33import java.io.ByteArrayInputStream;
34import java.io.CharArrayWriter;
35import java.io.DataInputStream;
36import java.io.IOException;
37import java.io.PrintWriter;
38import java.io.PrintStream;
39import java.util.Map;
40import java.util.HashMap;
41
42class InjectBytecodes implements RuntimeConstants {
43
44 private final ClassReaderWriter c;
45 private final PrintStream output;
46 private final int length;
47 private final int[] map;
48 private final byte[] widening;
49 private final Injector[] before = new Injector[256];
50 private final Injector[] after = new Injector[256];
51 private final String className;
52 private final String methodName;
53 private final Map<Integer,byte[]> snippets = new HashMap<Integer,byte[]>();
54
55 private int pos;
56 private int newPos;
57
58 private class Span {
59 final int delta;
60 final int target;
61 final int newDelta;
62 final int newTarget;
63
64 Span(int delta) {
65 this.delta = delta;
66 this.target = pos + delta;
67 this.newTarget = map[target];
68 this.newDelta = newTarget - newPos;
69 }
70 }
71
72 /**
73 * Constructor
74 */
75 InjectBytecodes(ClassReaderWriter c, int length,
76 String className, String methodName) {
77 this.c = c;
78 this.output = System.out;
79 this.length = length;
80 this.map = new int[length + 1];
81 this.widening = new byte[length + 1];
82 this.className = className;
83 this.methodName = methodName;
84 c.markLocalPositionStart();
85 for (int i = 0; i <= length; ++i) {
86 map[i] = i;
87 }
88 }
89
90 public void inject(int at, byte[] newCode) {
91 snippets.put(new Integer(at), newCode);
92 trace("external ");
93 inject(at, newCode.length);
94 }
95
96 private void inject(int at, int len) {
97 if (Inject.verbose) {
98 traceln("Injecting " + len + " at " + at);
99 }
100 for (int i = at; i <= length; ++i) {
101 map[i] += len;
102 }
103 }
104
105 private void widen(int at, int len) {
106 int delta = len - widening[at];
107 if (Inject.verbose) {
108 traceln();
109 traceln("Widening to " + len + " at " + at);
110 }
111 inject(c.localPosition(), delta); // inject at end of instruction
112 widening[at] = (byte)len; // mark at beginning of instruction
113 }
114
115 public void injectBefore(int code, Injector inj) {
116 before[code] = inj;
117 }
118
119 public void injectAfter(int code, Injector inj) {
120 after[code] = inj;
121 }
122
123 private void trace(String str) {
124 if (Inject.verbose) {
125 output.print(str);
126 }
127 }
128
129 private void traceln(String str) {
130 if (Inject.verbose) {
131 output.println(str);
132 }
133 }
134
135 private void traceln() {
136 if (Inject.verbose) {
137 output.println();
138 }
139 }
140
141 private void trace(int i) {
142 if (Inject.verbose) {
143 output.print(i);
144 }
145 }
146
147 /**
148 * Print an integer so that it takes 'length' characters in
149 * the output. Temporary until formatting code is stable.
150 */
151 private void traceFixedWidthInt(int x, int length) {
152 if (Inject.verbose) {
153 CharArrayWriter baStream = new CharArrayWriter();
154 PrintWriter pStream = new PrintWriter(baStream);
155 pStream.print(x);
156 String str = baStream.toString();
157 for (int cnt = length - str.length(); cnt > 0; --cnt)
158 trace(" ");
159 trace(str);
160 }
161 }
162
163 void adjustOffsets() throws IOException {
164 if (Inject.verbose) {
165 traceln();
166 traceln("Method " + methodName);
167 traceln();
168 }
169 c.rewind();
170 while (c.localPosition() < length) {
171 insertAtInstruction();
172 }
173 trace("Searching for adjustments...");
174 c.rewind();
175 while (c.localPosition() < length) {
176 if (!adjustInstruction()) {
177 c.rewind();
178 traceln();
179 traceln("Restarting adjustments after change...");
180 }
181 }
182 // write the new bytecodes
183 traceln();
184 traceln();
185 trace("Writing new code...");
186 c.rewind();
187 while (c.localPosition() < length) {
188 writeInstruction();
189 }
190 if (!snippets.isEmpty()) {
191 throw new Error("not all snippets written");
192 }
193 }
194
195 /**
196 * Walk one instruction inserting instrumentation at specified instructions
197 */
198 private void insertAtInstruction() throws IOException {
199 pos = c.localPosition();
200 int opcode = c.readU1();
201 if (opcode == opc_wide) {
202 // no support for instrumenting wide instructions
203 int wopcode = c.readU1();
204 int lvIndex = c.readU2();
205 switch (wopcode) {
206 case opc_aload: case opc_astore:
207 case opc_fload: case opc_fstore:
208 case opc_iload: case opc_istore:
209 case opc_lload: case opc_lstore:
210 case opc_dload: case opc_dstore:
211 case opc_ret:
212 break;
213
214 case opc_iinc:
215 c.readS2();
216 break;
217 default:
218 throw new Error("Invalid wide opcode: " + wopcode);
219 }
220 } else {
221 Injector inj;
222
223 inj = before[opcode];
224 if (inj != null) {
225 inject(pos, inj.bytecodes(className, methodName, pos));
226 }
227
228 switch (opcode) {
229 case opc_tableswitch:{
230 int header = (pos+1+3) & (~3); // 4byte boundry
231 c.skip(header - (pos+1)); // skip old padding
232
233 c.readU4();
234 int low = c.readU4();
235 int high = c.readU4();
236 c.skip((high+1-low) * 4);
237 break;
238 }
239
240 case opc_lookupswitch:{
241 int header = (pos+1+3) & (~3); // 4byte boundry
242 c.skip(header - (pos+1)); // skip padding
243
244 c.readU4();
245 int npairs = c.readU4();
246 c.skip(npairs * 8);
247 break;
248 }
249
250 default: {
251 int instrLen = opcLengths[opcode];
252 c.skip(instrLen-1);
253 }
254 }
255 inj = after[opcode];
256 if (inj != null) {
257 pos = c.localPosition();
258 inject(pos, inj.bytecodes(className, methodName, pos));
259 }
260 }
261 }
262
263 /**
264 * Walk one instruction adjusting for insertions
265 */
266 private boolean adjustInstruction() throws IOException {
267 pos = c.localPosition();
268 newPos = map[pos];
269 int opcode = c.readU1();
270 if (Inject.verbose) {
271 traceln();
272 traceFixedWidthInt(pos, 4);
273 traceFixedWidthInt(newPos, 4);
274 trace(" ");
275 }
276 if (opcode == opc_wide) {
277 int wopcode = c.readU1();
278 int lvIndex = c.readU2();
279 if (Inject.verbose) {
280 trace(opcNames[wopcode] + "_w ");
281 }
282 switch (wopcode) {
283 case opc_aload: case opc_astore:
284 case opc_fload: case opc_fstore:
285 case opc_iload: case opc_istore:
286 case opc_lload: case opc_lstore:
287 case opc_dload: case opc_dstore:
288 case opc_ret:
289 trace(lvIndex);
290 break;
291
292 case opc_iinc:
293 int constVal = c.readS2();
294 if (Inject.verbose) {
295 trace(lvIndex + " " + constVal);
296 }
297 break;
298 default:
299 throw new Error("Invalid wide opcode: " + wopcode);
300 }
301 } else {
302 if (Inject.verbose) {
303 trace(opcNames[opcode]);
304 }
305 switch (opcode) {
306
307 case opc_tableswitch:{
308 int widened = widening[pos];
309 int header = (pos+1+3) & (~3); // 4byte boundry
310 int newHeader = (newPos+1+3) & (~3); // 4byte boundry
311
312 c.skip(header - (pos+1)); // skip old padding
313
314 Span defaultSkip = new Span(c.readU4());
315 int low = c.readU4();
316 int high = c.readU4();
317 if (Inject.verbose) {
318 trace(" " + low + " to " + high);
319 trace(": default= [was] " + defaultSkip.target);
320 trace(" [now] " + defaultSkip.newTarget);
321 for (int i = low; i <= high; ++i) {
322 Span jump = new Span(c.readU4());
323 traceln("");
324 trace('\t');
325 traceFixedWidthInt(i, 5);
326 trace(": " + jump.newTarget);
327 }
328 } else {
329 c.skip((high+1-low) * 4);
330 }
331 int newPadding = newHeader - newPos;
332 int oldPadding = header - pos;
333 int deltaPadding = newPadding - oldPadding;
334 if (widened != deltaPadding) {
335 widen(pos, deltaPadding);
336 return false; // cause restart
337 }
338 break;
339 }
340
341 case opc_lookupswitch:{
342 int widened = widening[pos];
343 int header = (pos+1+3) & (~3); // 4byte boundry
344 int newHeader = (newPos+1+3) & (~3); // 4byte boundry
345
346 c.skip(header - (pos+1)); // skip old padding
347
348 Span defaultSkip = new Span(c.readU4());
349 int npairs = c.readU4();
350 if (Inject.verbose) {
351 trace(" npairs: " + npairs);
352 trace(": default= [was] " + defaultSkip.target);
353 trace(" [now] " + defaultSkip.newTarget);
354 for (int i = 0; i< npairs; ++i) {
355 int match = c.readU4();
356 Span jump = new Span(c.readU4());
357 traceln("");
358 trace('\t');
359 traceFixedWidthInt(match, 5);
360 trace(": " + jump.newTarget);
361 }
362 } else {
363 c.skip(npairs * 8);
364 }
365 int newPadding = newHeader - newPos;
366 int oldPadding = header - pos;
367 int deltaPadding = newPadding - oldPadding;
368 if (widened != deltaPadding) {
369 widen(pos, deltaPadding);
370 return false; // cause restart
371 }
372 break;
373 }
374
375 case opc_jsr: case opc_goto:
376 case opc_ifeq: case opc_ifge: case opc_ifgt:
377 case opc_ifle: case opc_iflt: case opc_ifne:
378 case opc_if_icmpeq: case opc_if_icmpne: case opc_if_icmpge:
379 case opc_if_icmpgt: case opc_if_icmple: case opc_if_icmplt:
380 case opc_if_acmpeq: case opc_if_acmpne:
381 case opc_ifnull: case opc_ifnonnull: {
382 int widened = widening[pos];
383 Span jump = new Span(c.readS2());
384 if (widened == 0) { // not yet widened
385 int newDelta = jump.newDelta;
386 if ((newDelta < -32768) || (newDelta > 32767)) {
387 switch (opcode) {
388 case opc_jsr: case opc_goto:
389 widen(pos, 2); // will convert to wide
390 break;
391 default:
392 widen(pos, 5); // will inject goto_w
393 break;
394 }
395 return false; // cause restart
396 }
397 }
398 if (Inject.verbose) {
399 trace(" [was] " + jump.target + " ==> " +
400 " [now] " + jump.newTarget);
401 }
402 break;
403 }
404
405 case opc_jsr_w:
406 case opc_goto_w: {
407 Span jump = new Span(c.readU4());
408 if (Inject.verbose) {
409 trace(" [was] " + jump.target +
410 " [now] " + jump.newTarget);
411 }
412 break;
413 }
414
415 default: {
416 int instrLen = opcLengths[opcode];
417 c.skip(instrLen-1);
418 break;
419 }
420 }
421 }
422 return true; // successful return
423 }
424
425
426 /**
427 * Walk one instruction writing the transformed instruction.
428 */
429 private void writeInstruction() throws IOException {
430 pos = c.localPosition();
431 newPos = map[pos];
432 byte[] newCode = snippets.remove(new Integer(pos));
433 if (newCode != null) {
434 traceln();
435 traceFixedWidthInt(pos, 4);
436 trace(" ... -- Inserting new code");
437 c.writeBytes(newCode);
438 }
439 int opcode = c.readU1();
440 if (Inject.verbose) {
441 traceln();
442 traceFixedWidthInt(pos, 4);
443 traceFixedWidthInt(newPos, 4);
444 trace(" ");
445 }
446 if (opcode == opc_wide) {
447 int wopcode = c.readU1();
448 int lvIndex = c.readU2();
449 if (Inject.verbose) {
450 trace(opcNames[wopcode] + "_w ");
451 }
452 c.writeU1(opcode);
453 c.writeU1(wopcode);
454 c.writeU2(lvIndex);
455 switch (wopcode) {
456 case opc_aload: case opc_astore:
457 case opc_fload: case opc_fstore:
458 case opc_iload: case opc_istore:
459 case opc_lload: case opc_lstore:
460 case opc_dload: case opc_dstore:
461 case opc_ret:
462 trace(lvIndex);
463 break;
464
465 case opc_iinc:
466 int constVal = c.readS2();
467 c.writeU2(constVal); // ??? U vs S
468 if (Inject.verbose) {
469 trace(lvIndex + " " + constVal);
470 }
471 break;
472 default:
473 throw new Error("Invalid wide opcode: " + wopcode);
474 }
475 } else {
476 if (Inject.verbose) {
477 trace(opcNames[opcode]);
478 }
479 switch (opcode) {
480
481 case opc_tableswitch:{
482 int header = (pos+1+3) & (~3); // 4byte boundry
483 int newHeader = (newPos+1+3) & (~3); // 4byte boundry
484
485 c.skip(header - (pos+1)); // skip old padding
486
487 Span defaultSkip = new Span(c.readU4());
488 int low = c.readU4();
489 int high = c.readU4();
490
491 c.writeU1(opcode); // copy instruction
492 for (int i = newPos+1; i < newHeader; ++i) {
493 c.writeU1(0); // write new padding
494 }
495 c.writeU4(defaultSkip.newDelta);
496 c.writeU4(low);
497 c.writeU4(high);
498
499 if (Inject.verbose) {
500 trace(" " + low + " to " + high);
501 trace(": default= [was] " + defaultSkip.target);
502 trace(" [now] " + defaultSkip.newTarget);
503 }
504 for (int i = low; i <= high; ++i) {
505 Span jump = new Span(c.readU4());
506 c.writeU4(jump.newDelta);
507 if (Inject.verbose) {
508 traceln("");
509 trace('\t');
510 traceFixedWidthInt(i, 5);
511 trace(": " + jump.newTarget);
512 }
513 }
514 break;
515 }
516
517 case opc_lookupswitch:{
518 int header = (pos+1+3) & (~3); // 4byte boundry
519 int newHeader = (newPos+1+3) & (~3); // 4byte boundry
520
521 c.skip(header - (pos+1)); // skip old padding
522
523 Span defaultSkip = new Span(c.readU4());
524 int npairs = c.readU4();
525 if (Inject.verbose) {
526 trace(" npairs: " + npairs);
527 trace(": default= [was] " + defaultSkip.target);
528 trace(" [now] " + defaultSkip.newTarget);
529 }
530 c.writeU1(opcode); // copy instruction
531 for (int i = newPos+1; i < newHeader; ++i) {
532 c.writeU1(0); // write new padding
533 }
534 c.writeU4(defaultSkip.newDelta);
535 c.writeU4(npairs);
536 for (int i = 0; i< npairs; ++i) {
537 int match = c.readU4();
538 Span jump = new Span(c.readU4());
539 c.writeU4(match);
540 c.writeU4(jump.newDelta);
541 if (Inject.verbose) {
542 traceln("");
543 trace('\t');
544 traceFixedWidthInt(match, 5);
545 trace(": " + jump.newTarget);
546 }
547 }
548 break;
549 }
550
551 case opc_jsr: case opc_goto:
552 case opc_ifeq: case opc_ifge: case opc_ifgt:
553 case opc_ifle: case opc_iflt: case opc_ifne:
554 case opc_if_icmpeq: case opc_if_icmpne: case opc_if_icmpge:
555 case opc_if_icmpgt: case opc_if_icmple: case opc_if_icmplt:
556 case opc_if_acmpeq: case opc_if_acmpne:
557 case opc_ifnull: case opc_ifnonnull: {
558 int widened = widening[pos];
559 Span jump = new Span(c.readS2());
560 int newOpcode = opcode; // default to unchanged
561 if (widened == 0) { // not widened
562 c.writeU1(opcode); // rewrite instruction
563 c.writeU2(jump.newDelta);
564 } else if (widened == 2) { // wide form
565 switch (opcode) {
566 case opc_jsr:
567 newOpcode = opc_jsr_w;
568 break;
569 case opc_goto:
570 newOpcode = opc_jsr_w;
571 break;
572 default:
573 throw new Error("unexpected opcode: " +
574 opcode);
575 }
576 c.writeU1(newOpcode); // write wide instruction
577 c.writeU4(jump.newDelta); // write new and wide delta
578 } else if (widened == 5) { // insert goto_w
579 switch (opcode) {
580 case opc_ifeq:
581 newOpcode = opc_ifne;
582 break;
583 case opc_ifge:
584 newOpcode = opc_iflt;
585 break;
586 case opc_ifgt:
587 newOpcode = opc_ifle;
588 break;
589 case opc_ifle:
590 newOpcode = opc_ifgt;
591 break;
592 case opc_iflt:
593 newOpcode = opc_ifge;
594 break;
595 case opc_ifne:
596 newOpcode = opc_ifeq;
597 break;
598 case opc_if_icmpeq:
599 newOpcode = opc_if_icmpne;
600 break;
601 case opc_if_icmpne:
602 newOpcode = opc_if_icmpeq;
603 break;
604 case opc_if_icmpge:
605 newOpcode = opc_if_icmplt;
606 break;
607 case opc_if_icmpgt:
608 newOpcode = opc_if_icmple;
609 break;
610 case opc_if_icmple:
611 newOpcode = opc_if_icmpgt;
612 break;
613 case opc_if_icmplt:
614 newOpcode = opc_if_icmpge;
615 break;
616 case opc_if_acmpeq:
617 newOpcode = opc_if_acmpne;
618 break;
619 case opc_if_acmpne:
620 newOpcode = opc_if_acmpeq;
621 break;
622 case opc_ifnull:
623 newOpcode = opc_ifnonnull;
624 break;
625 case opc_ifnonnull:
626 newOpcode = opc_ifnull;
627 break;
628 default:
629 throw new Error("unexpected opcode: " +
630 opcode);
631 }
632 c.writeU1(newOpcode); // write inverse branch
633 c.writeU2(3 + 5); // beyond if and goto_w
634 c.writeU1(opc_goto_w);// add a goto_w
635 c.writeU4(jump.newDelta); // write new and wide delta
636 } else {
637 throw new Error("unexpected widening");
638 }
639
640 if (Inject.verbose) {
641 trace(" [was] " + jump.target + " ==> " +
642 opcNames[newOpcode] +
643 " [now] " + jump.newTarget);
644 }
645 break;
646 }
647
648 case opc_jsr_w:
649 case opc_goto_w: {
650 Span jump = new Span(c.readU4());
651 c.writeU1(opcode); // instruction itself
652 c.writeU4(jump.newDelta);
653 if (Inject.verbose) {
654 trace(" [was] " + jump.target +
655 " [now] " + jump.newTarget);
656 }
657 break;
658 }
659
660 default: {
661 int instrLen = opcLengths[opcode];
662 c.writeU1(opcode); // instruction itself
663 c.copy(instrLen-1);
664 }
665 }
666 }
667 }
668
669 /**
670 * Copy the exception table for this method code
671 */
672 void copyExceptionTable() throws IOException {
673 int tableLength = c.copyU2(); // exception table len
674 if (tableLength > 0) {
675 traceln();
676 traceln("Exception table:");
677 traceln(" from:old/new to:old/new target:old/new type");
678 for (int tcnt = tableLength; tcnt > 0; --tcnt) {
679 int startPC = c.readU2();
680 int newStartPC = map[startPC];
681 c.writeU2(newStartPC);
682 int endPC = c.readU2();
683 int newEndPC = map[endPC];
684 c.writeU2(newEndPC);
685 int handlerPC = c.readU2();
686 int newHandlerPC = map[handlerPC];
687 c.writeU2(newHandlerPC);
688 int catchType = c.copyU2();
689 if (Inject.verbose) {
690 traceFixedWidthInt(startPC, 6);
691 traceFixedWidthInt(newStartPC, 6);
692 traceFixedWidthInt(endPC, 6);
693 traceFixedWidthInt(newEndPC, 6);
694 traceFixedWidthInt(handlerPC, 6);
695 traceFixedWidthInt(newHandlerPC, 6);
696 trace(" ");
697 if (catchType == 0)
698 traceln("any");
699 else {
700 traceln("" + catchType);
701 }
702 }
703 }
704 }
705 }
706
707 /**
708 * Copy the line number table for this method code
709 */
710 void copyLineNumberAttr() throws IOException {
711 // name index already read
712 c.copy(4); // attr len
713 int tableLength = c.copyU2(); // line table len
714 if (tableLength > 0) {
715 if (Inject.verbose) {
716 traceln();
717 traceln("Line numbers for method " + methodName);
718 }
719 for (int tcnt = tableLength; tcnt > 0; --tcnt) {
720 int startPC = c.readU2();
721 int newStartPC = map[startPC];
722 c.writeU2(newStartPC);
723 int lineNumber = c.copyU2();
724 if (Inject.verbose) {
725 traceln(" line " + lineNumber +
726 ": [was] " + startPC +
727 " [now] " + newStartPC);
728 }
729 }
730 }
731 }
732
733 /**
734 * Copy the local variable table for this method code
735 */
736 void copyLocalVarAttr() throws IOException {
737 // name index already read
738 c.copy(4); // attr len
739 int tableLength = c.copyU2(); // local var table len
740 if (tableLength > 0) {
741 if (Inject.verbose) {
742 traceln();
743 traceln("Local variables for method " + methodName);
744 }
745 for (int tcnt = tableLength; tcnt > 0; --tcnt) {
746 int startPC = c.readU2();
747 int newStartPC = map[startPC];
748 c.writeU2(newStartPC);
749 int length = c.readU2();
750 int endPC = startPC + length;
751 int newEndPC = map[endPC];
752 int newLength = newEndPC - newStartPC;
753 c.writeU2(newLength);
754 int nameIndex = c.copyU2();
755 int descriptorIndex = c.copyU2();
756 int index = c.copyU2();
757 if (Inject.verbose) {
758 trace(" ");
759 trace(descriptorIndex);
760 trace(" ");
761 trace(nameIndex);
762 traceln(" pc= [was] " + startPC + " [now] " + newStartPC +
763 ", length= [was] " + length + " [now] " + newLength +
764 ", slot=" + index);
765 }
766 }
767 }
768 }
769}