1 /*
   2  * Copyright (c) 2005, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * 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 
  31 package ilib;
  32 
  33 import java.io.ByteArrayInputStream;
  34 import java.io.CharArrayWriter;
  35 import java.io.DataInputStream;
  36 import java.io.IOException;
  37 import java.io.PrintWriter;
  38 import java.io.PrintStream;
  39 import java.util.Map;
  40 import java.util.HashMap;
  41 
  42 class 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 }