~ubuntu-branches/ubuntu/trusty/proguard/trusty

« back to all changes in this revision

Viewing changes to src/proguard/classfile/editor/CodeAttributeEditor.java

  • Committer: Bazaar Package Importer
  • Author(s): Sam Clegg
  • Date: 2008-05-15 10:39:48 UTC
  • mfrom: (1.2.1 upstream) (3.1.3 intrepid)
  • Revision ID: james.westby@ubuntu.com-20080515103948-nipob41uk77osuu2
* New upstream release
* Fix build of ant task (Closes: #459829)
  Thanks to Hans van Kranenburg <debian@knorrie.org>

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * ProGuard -- shrinking, optimization, obfuscation, and preverification
 
3
 *             of Java bytecode.
 
4
 *
 
5
 * Copyright (c) 2002-2007 Eric Lafortune (eric@graphics.cornell.edu)
 
6
 *
 
7
 * This program is free software; you can redistribute it and/or modify it
 
8
 * under the terms of the GNU General Public License as published by the Free
 
9
 * Software Foundation; either version 2 of the License, or (at your option)
 
10
 * any later version.
 
11
 *
 
12
 * This program is distributed in the hope that it will be useful, but WITHOUT
 
13
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 
14
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 
15
 * more details.
 
16
 *
 
17
 * You should have received a copy of the GNU General Public License along
 
18
 * with this program; if not, write to the Free Software Foundation, Inc.,
 
19
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 
20
 */
 
21
package proguard.classfile.editor;
 
22
 
 
23
import proguard.classfile.*;
 
24
import proguard.classfile.attribute.*;
 
25
import proguard.classfile.attribute.preverification.*;
 
26
import proguard.classfile.attribute.preverification.visitor.*;
 
27
import proguard.classfile.attribute.visitor.*;
 
28
import proguard.classfile.instruction.*;
 
29
import proguard.classfile.instruction.visitor.InstructionVisitor;
 
30
import proguard.classfile.util.SimplifiedVisitor;
 
31
 
 
32
/**
 
33
 * This AttributeVisitor accumulates specified changes to code, and then applies
 
34
 * these accumulated changes to the code attributes that it visits.
 
35
 *
 
36
 * @author Eric Lafortune
 
37
 */
 
38
public class CodeAttributeEditor
 
39
extends      SimplifiedVisitor
 
40
implements   AttributeVisitor,
 
41
             InstructionVisitor,
 
42
             ExceptionInfoVisitor,
 
43
             StackMapFrameVisitor,
 
44
             VerificationTypeVisitor,
 
45
             LineNumberInfoVisitor,
 
46
             LocalVariableInfoVisitor,
 
47
             LocalVariableTypeInfoVisitor
 
48
{
 
49
    //*
 
50
    private static final boolean DEBUG = false;
 
51
    /*/
 
52
    private static       boolean DEBUG = true;
 
53
    //*/
 
54
 
 
55
    private boolean updateFrameSizes;
 
56
 
 
57
    private int     codeLength;
 
58
    private boolean modified;
 
59
    private boolean simple;
 
60
 
 
61
    /*private*/public Instruction[]    preInsertions  = new Instruction[ClassConstants.TYPICAL_CODE_LENGTH];
 
62
    /*private*/public Instruction[]    replacements   = new Instruction[ClassConstants.TYPICAL_CODE_LENGTH];
 
63
    /*private*/public Instruction[]    postInsertions = new Instruction[ClassConstants.TYPICAL_CODE_LENGTH];
 
64
    /*private*/public boolean[]        deleted        = new boolean[ClassConstants.TYPICAL_CODE_LENGTH];
 
65
 
 
66
    private int[]   instructionOffsetMap = new int[ClassConstants.TYPICAL_CODE_LENGTH];
 
67
    private int     newOffset;
 
68
    private boolean lengthIncreased;
 
69
 
 
70
    private int expectedStackMapFrameOffset;
 
71
 
 
72
    private final StackSizeUpdater    stackSizeUpdater    = new StackSizeUpdater();
 
73
    private final VariableSizeUpdater variableSizeUpdater = new VariableSizeUpdater();
 
74
    private final InstructionWriter   instructionWriter   = new InstructionWriter();
 
75
 
 
76
 
 
77
    public CodeAttributeEditor()
 
78
    {
 
79
        this(true);
 
80
    }
 
81
 
 
82
 
 
83
    public CodeAttributeEditor(boolean updateFrameSizes)
 
84
    {
 
85
        this.updateFrameSizes = updateFrameSizes;
 
86
    }
 
87
 
 
88
 
 
89
    /**
 
90
     * Resets the accumulated code changes.
 
91
     * @param codeLength the length of the code that will be edited next.
 
92
     */
 
93
    public void reset(int codeLength)
 
94
    {
 
95
        this.codeLength = codeLength;
 
96
 
 
97
        // Try to reuse the previous arrays.
 
98
        if (preInsertions.length < codeLength)
 
99
        {
 
100
            preInsertions  = new Instruction[codeLength];
 
101
            replacements   = new Instruction[codeLength];
 
102
            postInsertions = new Instruction[codeLength];
 
103
            deleted        = new boolean[codeLength];
 
104
        }
 
105
        else
 
106
        {
 
107
            for (int index = 0; index < codeLength; index++)
 
108
            {
 
109
                preInsertions[index]  = null;
 
110
                replacements[index]   = null;
 
111
                postInsertions[index] = null;
 
112
                deleted[index]        = false;
 
113
            }
 
114
        }
 
115
 
 
116
        modified = false;
 
117
        simple   = true;
 
118
 
 
119
    }
 
120
 
 
121
 
 
122
    /**
 
123
     * Remembers to place the given instruction right before the instruction
 
124
     * at the given offset.
 
125
     * @param instructionOffset the offset of the instruction.
 
126
     * @param instruction       the new instruction.
 
127
     */
 
128
    public void insertBeforeInstruction(int instructionOffset, Instruction instruction)
 
129
    {
 
130
        if (instructionOffset < 0 ||
 
131
            instructionOffset >= codeLength)
 
132
        {
 
133
            throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]");
 
134
        }
 
135
 
 
136
        preInsertions[instructionOffset] = instruction;
 
137
 
 
138
        modified = true;
 
139
        simple   = false;
 
140
 
 
141
    }
 
142
 
 
143
 
 
144
    /**
 
145
     * Remembers to replace the instruction at the given offset by the given
 
146
     * instruction.
 
147
     * @param instructionOffset the offset of the instruction to be replaced.
 
148
     * @param instruction       the new instruction.
 
149
     */
 
150
    public void replaceInstruction(int instructionOffset, Instruction instruction)
 
151
    {
 
152
        if (instructionOffset < 0 ||
 
153
            instructionOffset >= codeLength)
 
154
        {
 
155
            throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]");
 
156
        }
 
157
 
 
158
        replacements[instructionOffset] = instruction;
 
159
 
 
160
        modified = true;
 
161
    }
 
162
 
 
163
 
 
164
    /**
 
165
     * Remembers to place the given instruction right after the instruction
 
166
     * at the given offset.
 
167
     * @param instructionOffset the offset of the instruction.
 
168
     * @param instruction       the new instruction.
 
169
     */
 
170
    public void insertAfterInstruction(int instructionOffset, Instruction instruction)
 
171
    {
 
172
        if (instructionOffset < 0 ||
 
173
            instructionOffset >= codeLength)
 
174
        {
 
175
            throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]");
 
176
        }
 
177
 
 
178
        postInsertions[instructionOffset] = instruction;
 
179
 
 
180
        modified = true;
 
181
        simple   = false;
 
182
    }
 
183
 
 
184
 
 
185
    /**
 
186
     * Remembers to delete the instruction at the given offset.
 
187
     * @param instructionOffset the offset of the instruction to be deleted.
 
188
     */
 
189
    public void deleteInstruction(int instructionOffset)
 
190
    {
 
191
        if (instructionOffset < 0 ||
 
192
            instructionOffset >= codeLength)
 
193
        {
 
194
            throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]");
 
195
        }
 
196
 
 
197
        deleted[instructionOffset] = true;
 
198
 
 
199
        modified = true;
 
200
        simple   = false;
 
201
    }
 
202
 
 
203
 
 
204
    /**
 
205
     * Remembers not to delete the instruction at the given offset.
 
206
     * @param instructionOffset the offset of the instruction not to be deleted.
 
207
     */
 
208
    public void undeleteInstruction(int instructionOffset)
 
209
    {
 
210
        if (instructionOffset < 0 ||
 
211
            instructionOffset >= codeLength)
 
212
        {
 
213
            throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]");
 
214
        }
 
215
 
 
216
        deleted[instructionOffset] = false;
 
217
    }
 
218
 
 
219
 
 
220
    /**
 
221
     * Returns whether the instruction at the given offset has been modified
 
222
     * in any way.
 
223
     */
 
224
    public boolean isModified(int instructionOffset)
 
225
    {
 
226
        return preInsertions[instructionOffset]  != null ||
 
227
               replacements[instructionOffset]   != null ||
 
228
               postInsertions[instructionOffset] != null ||
 
229
               deleted[instructionOffset];
 
230
    }
 
231
 
 
232
 
 
233
    // Implementations for AttributeVisitor.
 
234
 
 
235
    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
 
236
 
 
237
 
 
238
    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
 
239
    {
 
240
//        DEBUG =
 
241
//            clazz.getName().equals("abc/Def") &&
 
242
//            method.getName(clazz).equals("abc");
 
243
 
 
244
        // TODO: Remove this when the code has stabilized.
 
245
        // Catch any unexpected exceptions from the actual visiting method.
 
246
        try
 
247
        {
 
248
            // Process the code.
 
249
            visitCodeAttribute0(clazz, method, codeAttribute);
 
250
        }
 
251
        catch (RuntimeException ex)
 
252
        {
 
253
            System.err.println("Unexpected error while editing code:");
 
254
            System.err.println("  Class       = ["+clazz.getName()+"]");
 
255
            System.err.println("  Method      = ["+method.getName(clazz)+method.getDescriptor(clazz)+"]");
 
256
            System.err.println("  Exception   = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")");
 
257
 
 
258
            throw ex;
 
259
        }
 
260
    }
 
261
 
 
262
 
 
263
    public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute)
 
264
    {
 
265
        if (DEBUG)
 
266
        {
 
267
            System.out.println("CodeAttributeEditor: ["+clazz.getName()+"."+method.getName(clazz)+"]");
 
268
        }
 
269
 
 
270
        // Avoid doing any work if nothing is changing anyway.
 
271
        if (!modified)
 
272
        {
 
273
            return;
 
274
        }
 
275
 
 
276
        // Check if we can perform a faster simple replacement of instructions.
 
277
        if (canPerformSimpleReplacements(codeAttribute))
 
278
        {
 
279
            // Simply overwrite the instructions.
 
280
            performSimpleReplacements(codeAttribute);
 
281
 
 
282
            // Update the maximum stack size and local variable frame size.
 
283
            updateFrameSizes(clazz, method, codeAttribute);
 
284
        }
 
285
        else
 
286
        {
 
287
            // Move and remap the instructions.
 
288
            codeAttribute.u4codeLength =
 
289
                updateInstructions(clazz, method, codeAttribute);
 
290
 
 
291
            // Remap the exception table.
 
292
            codeAttribute.exceptionsAccept(clazz, method, this);
 
293
 
 
294
            // Remove exceptions with empty code blocks.
 
295
            codeAttribute.u2exceptionTableLength =
 
296
                removeEmptyExceptions(codeAttribute.exceptionTable,
 
297
                                      codeAttribute.u2exceptionTableLength);
 
298
 
 
299
            // Update the maximum stack size and local variable frame size.
 
300
            updateFrameSizes(clazz, method, codeAttribute);
 
301
 
 
302
            // Remap the line number table and the local variable table.
 
303
            codeAttribute.attributesAccept(clazz, method, this);
 
304
 
 
305
            // Make sure instructions are widened if necessary.
 
306
            instructionWriter.visitCodeAttribute(clazz, method, codeAttribute);
 
307
        }
 
308
    }
 
309
 
 
310
 
 
311
    private void updateFrameSizes(Clazz clazz, Method method, CodeAttribute codeAttribute)
 
312
    {
 
313
        if (updateFrameSizes)
 
314
        {
 
315
            stackSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute);
 
316
            variableSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute);
 
317
        }
 
318
    }
 
319
 
 
320
 
 
321
    public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute)
 
322
    {
 
323
        // Remap all stack map entries.
 
324
        expectedStackMapFrameOffset = -1;
 
325
        stackMapAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this);
 
326
    }
 
327
 
 
328
 
 
329
    public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute)
 
330
    {
 
331
        // Remap all stack map table entries.
 
332
        expectedStackMapFrameOffset = 0;
 
333
        stackMapTableAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this);
 
334
    }
 
335
 
 
336
 
 
337
    public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute)
 
338
    {
 
339
        // Remap all line number table entries.
 
340
        lineNumberTableAttribute.lineNumbersAccept(clazz, method, codeAttribute, this);
 
341
 
 
342
        // Remove line numbers with empty code blocks.
 
343
        lineNumberTableAttribute.u2lineNumberTableLength =
 
344
           removeEmptyLineNumbers(lineNumberTableAttribute.lineNumberTable,
 
345
                                  lineNumberTableAttribute.u2lineNumberTableLength,
 
346
                                  codeAttribute.u4codeLength);
 
347
    }
 
348
 
 
349
 
 
350
    public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
 
351
    {
 
352
        // Remap all local variable table entries.
 
353
        localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
 
354
 
 
355
        // Remove local variables with empty code blocks.
 
356
        localVariableTableAttribute.u2localVariableTableLength =
 
357
            removeEmptyLocalVariables(localVariableTableAttribute.localVariableTable,
 
358
                                      localVariableTableAttribute.u2localVariableTableLength,
 
359
                                      codeAttribute.u2maxLocals);
 
360
    }
 
361
 
 
362
 
 
363
    public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
 
364
    {
 
365
        // Remap all local variable table entries.
 
366
        localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
 
367
 
 
368
        // Remove local variables with empty code blocks.
 
369
        localVariableTypeTableAttribute.u2localVariableTypeTableLength =
 
370
            removeEmptyLocalVariableTypes(localVariableTypeTableAttribute.localVariableTypeTable,
 
371
                                          localVariableTypeTableAttribute.u2localVariableTypeTableLength,
 
372
                                          codeAttribute.u2maxLocals);
 
373
    }
 
374
 
 
375
 
 
376
    /**
 
377
     * Checks if it is possible to modifies the given code without having to
 
378
     * update any offsets.
 
379
     * @param codeAttribute the code to be changed.
 
380
     * @return the new code length.
 
381
     */
 
382
    private boolean canPerformSimpleReplacements(CodeAttribute codeAttribute)
 
383
    {
 
384
        if (!simple)
 
385
        {
 
386
            return false;
 
387
        }
 
388
 
 
389
        byte[] code       = codeAttribute.code;
 
390
        int    codeLength = codeAttribute.u4codeLength;
 
391
 
 
392
        // Go over all replacement instructions.
 
393
        for (int offset = 0; offset < codeLength; offset++)
 
394
        {
 
395
            // Check if the replacement instruction, if any, has a different
 
396
            // length than the original instruction.
 
397
            Instruction replacementInstruction = replacements[offset];
 
398
            if (replacementInstruction != null &&
 
399
                replacementInstruction.length(offset) !=
 
400
                    InstructionFactory.create(code, offset).length(offset))
 
401
            {
 
402
                return false;
 
403
            }
 
404
        }
 
405
 
 
406
        return true;
 
407
    }
 
408
 
 
409
 
 
410
    /**
 
411
     * Modifies the given code without updating any offsets.
 
412
     * @param codeAttribute the code to be changed.
 
413
     */
 
414
    private void performSimpleReplacements(CodeAttribute codeAttribute)
 
415
    {
 
416
        int codeLength = codeAttribute.u4codeLength;
 
417
 
 
418
        // Go over all replacement instructions.
 
419
        for (int offset = 0; offset < codeLength; offset++)
 
420
        {
 
421
            // Overwrite the original instruction with the replacement
 
422
            // instruction if any.
 
423
            Instruction replacementInstruction = replacements[offset];
 
424
            if (replacementInstruction != null)
 
425
            {
 
426
                replacementInstruction.write(codeAttribute, offset);
 
427
 
 
428
                if (DEBUG)
 
429
                {
 
430
                    System.out.println("  Replaced "+replacementInstruction.toString(newOffset));
 
431
                }
 
432
            }
 
433
        }
 
434
    }
 
435
 
 
436
 
 
437
    /**
 
438
     * Modifies the given code based on the previously specified changes.
 
439
     * @param clazz         the class file of the code to be changed.
 
440
     * @param method        the method of the code to be changed.
 
441
     * @param codeAttribute the code to be changed.
 
442
     * @return the new code length.
 
443
     */
 
444
    private int updateInstructions(Clazz    clazz,
 
445
                                   Method   method,
 
446
                                   CodeAttribute codeAttribute)
 
447
    {
 
448
        byte[] oldCode   = codeAttribute.code;
 
449
        int    oldLength = codeAttribute.u4codeLength;
 
450
 
 
451
        // Make sure there is a sufficiently large instruction offset map.
 
452
        if (instructionOffsetMap.length < oldLength + 1)
 
453
        {
 
454
            instructionOffsetMap = new int[oldLength + 1];
 
455
        }
 
456
 
 
457
        // Fill out the instruction offset map.
 
458
        int newLength = mapInstructions(oldCode,
 
459
                                        oldLength);
 
460
 
 
461
        // Create a new code array if necessary.
 
462
        if (lengthIncreased)
 
463
        {
 
464
            codeAttribute.code = new byte[newLength];
 
465
        }
 
466
 
 
467
        // Prepare for possible widening of instructions.
 
468
        instructionWriter.reset(newLength);
 
469
 
 
470
        // Move the instructions into the new code array.
 
471
        moveInstructions(clazz,
 
472
                         method,
 
473
                         codeAttribute,
 
474
                         oldCode,
 
475
                         oldLength);
 
476
 
 
477
        // We can return the new length.
 
478
        return newLength;
 
479
    }
 
480
 
 
481
 
 
482
    /**
 
483
     * Fills out the instruction offset map for the given code block.
 
484
     * @param oldCode   the instructions to be moved.
 
485
     * @param oldLength the code length.
 
486
     * @return the new code length.
 
487
     */
 
488
    private int mapInstructions(byte[] oldCode, int oldLength)
 
489
    {
 
490
        // Start mapping instructions at the beginning.
 
491
        newOffset       = 0;
 
492
        lengthIncreased = false;
 
493
 
 
494
        int oldOffset = 0;
 
495
        do
 
496
        {
 
497
            // Get the next instruction.
 
498
            Instruction instruction = InstructionFactory.create(oldCode, oldOffset);
 
499
 
 
500
            // Compute the mapping of the instruction.
 
501
            mapInstruction(oldOffset, instruction);
 
502
 
 
503
            oldOffset += instruction.length(oldOffset);
 
504
 
 
505
            if (newOffset > oldOffset)
 
506
            {
 
507
                lengthIncreased = true;
 
508
            }
 
509
        }
 
510
        while (oldOffset < oldLength);
 
511
 
 
512
        // Also add an entry for the first offset after the code.
 
513
        instructionOffsetMap[oldOffset] = newOffset;
 
514
 
 
515
        return newOffset;
 
516
    }
 
517
 
 
518
 
 
519
    /**
 
520
     * Fills out the instruction offset map for the given instruction.
 
521
     * @param oldOffset   the instruction's old offset.
 
522
     * @param instruction the instruction to be moved.
 
523
     */
 
524
    private void mapInstruction(int         oldOffset,
 
525
                                Instruction instruction)
 
526
    {
 
527
        instructionOffsetMap[oldOffset] = newOffset;
 
528
 
 
529
        // Account for the pre-inserted instruction, if any.
 
530
        Instruction preInstruction = preInsertions[oldOffset];
 
531
        if (preInstruction != null)
 
532
        {
 
533
            newOffset += preInstruction.length(newOffset);
 
534
        }
 
535
 
 
536
        // Account for the replacement instruction, or for the current
 
537
        // instruction, if it shouldn't be  deleted.
 
538
        Instruction replacementInstruction = replacements[oldOffset];
 
539
        if (replacementInstruction != null)
 
540
        {
 
541
            newOffset += replacementInstruction.length(newOffset);
 
542
        }
 
543
        else if (!deleted[oldOffset])
 
544
        {
 
545
            // Note that the instruction's length may change at its new offset,
 
546
            // e.g. if it is a switch instruction.
 
547
            newOffset += instruction.length(newOffset);
 
548
        }
 
549
 
 
550
        // Account for the post-inserted instruction, if any.
 
551
        Instruction postInstruction = postInsertions[oldOffset];
 
552
        if (postInstruction != null)
 
553
        {
 
554
            newOffset += postInstruction.length(newOffset);
 
555
        }
 
556
    }
 
557
 
 
558
 
 
559
    /**
 
560
     * Moves the given code block to the new offsets.
 
561
     * @param clazz         the class file of the code to be changed.
 
562
     * @param method        the method of the code to be changed.
 
563
     * @param codeAttribute the code to be changed.
 
564
     * @param oldCode       the original code to be moved.
 
565
     * @param oldLength     the original code length.
 
566
     */
 
567
    private void moveInstructions(Clazz         clazz,
 
568
                                  Method        method,
 
569
                                  CodeAttribute codeAttribute,
 
570
                                  byte[]        oldCode,
 
571
                                  int           oldLength)
 
572
    {
 
573
        // Start writing instructions at the beginning.
 
574
        newOffset = 0;
 
575
 
 
576
        int oldOffset = 0;
 
577
        do
 
578
        {
 
579
            // Get the next instruction.
 
580
            Instruction instruction = InstructionFactory.create(oldCode, oldOffset);
 
581
 
 
582
            // Move the instruction to its new offset.
 
583
            moveInstruction(clazz,
 
584
                            method,
 
585
                            codeAttribute,
 
586
                            oldOffset,
 
587
                            instruction);
 
588
 
 
589
            oldOffset += instruction.length(oldOffset);
 
590
        }
 
591
        while (oldOffset < oldLength);
 
592
    }
 
593
 
 
594
 
 
595
    /**
 
596
     * Moves the given instruction to its new offset.
 
597
     * @param clazz         the class file of the code to be changed.
 
598
     * @param method        the method of the code to be changed.
 
599
     * @param codeAttribute the code to be changed.
 
600
     * @param oldOffset     the original instruction offset.
 
601
     * @param instruction   the original instruction.
 
602
     */
 
603
    private void moveInstruction(Clazz         clazz,
 
604
                                 Method        method,
 
605
                                 CodeAttribute codeAttribute,
 
606
                                 int           oldOffset,
 
607
                                 Instruction   instruction)
 
608
    {
 
609
        // Remap and insert the pre-inserted instruction, if any.
 
610
        Instruction preInstruction = preInsertions[oldOffset];
 
611
        if (preInstruction != null)
 
612
        {
 
613
            // Remap the instruction.
 
614
            preInstruction.accept(clazz, method, codeAttribute, oldOffset, this);
 
615
 
 
616
            if (DEBUG)
 
617
            {
 
618
                System.out.println("  Pre-inserted  "+preInstruction.toString(newOffset));
 
619
            }
 
620
 
 
621
            newOffset += preInstruction.length(newOffset);
 
622
        }
 
623
 
 
624
        // Remap and insert the replacement instruction, or the current
 
625
        // instruction, if it shouldn't be deleted.
 
626
        Instruction replacementInstruction = replacements[oldOffset];
 
627
        if (replacementInstruction != null)
 
628
        {
 
629
            // Remap the instruction.
 
630
            replacementInstruction.accept(clazz, method, codeAttribute, oldOffset, this);
 
631
 
 
632
            if (DEBUG)
 
633
            {
 
634
                System.out.println("  Replaced      "+replacementInstruction.toString(newOffset));
 
635
            }
 
636
 
 
637
            newOffset += replacementInstruction.length(newOffset);
 
638
        }
 
639
        else if (!deleted[oldOffset])
 
640
        {
 
641
            // Remap the instruction.
 
642
            instruction.accept(clazz, method, codeAttribute, oldOffset, this);
 
643
 
 
644
            if (DEBUG)
 
645
            {
 
646
                System.out.println("  Copied        "+instruction.toString(newOffset));
 
647
            }
 
648
 
 
649
            newOffset += instruction.length(newOffset);
 
650
        }
 
651
 
 
652
        // Remap and insert the post-inserted instruction, if any.
 
653
        Instruction postInstruction = postInsertions[oldOffset];
 
654
        if (postInstruction != null)
 
655
        {
 
656
            // Remap the instruction.
 
657
            postInstruction.accept(clazz, method, codeAttribute, oldOffset, this);
 
658
 
 
659
            if (DEBUG)
 
660
            {
 
661
                System.out.println("  Post-inserted "+postInstruction.toString(newOffset));
 
662
            }
 
663
 
 
664
            newOffset += postInstruction.length(newOffset);
 
665
        }
 
666
    }
 
667
 
 
668
 
 
669
    // Implementations for InstructionVisitor.
 
670
 
 
671
    public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
 
672
    {
 
673
        // Write out the instruction.
 
674
        instructionWriter.visitSimpleInstruction(clazz,
 
675
                                                 method,
 
676
                                                 codeAttribute,
 
677
                                                 newOffset,
 
678
                                                 simpleInstruction);
 
679
    }
 
680
 
 
681
 
 
682
    public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
 
683
    {
 
684
        // Write out the instruction.
 
685
        instructionWriter.visitConstantInstruction(clazz,
 
686
                                                   method,
 
687
                                                   codeAttribute,
 
688
                                                   newOffset,
 
689
                                                   constantInstruction);
 
690
    }
 
691
 
 
692
 
 
693
    public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
 
694
    {
 
695
        // Write out the instruction.
 
696
        instructionWriter.visitVariableInstruction(clazz,
 
697
                                                   method,
 
698
                                                   codeAttribute,
 
699
                                                   newOffset,
 
700
                                                   variableInstruction);
 
701
    }
 
702
 
 
703
 
 
704
    public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
 
705
    {
 
706
        // Adjust the branch offset.
 
707
        branchInstruction.branchOffset = remapBranchOffset(offset,
 
708
                                                           branchInstruction.branchOffset);
 
709
 
 
710
        // Write out the instruction.
 
711
        instructionWriter.visitBranchInstruction(clazz,
 
712
                                                 method,
 
713
                                                 codeAttribute,
 
714
                                                 newOffset,
 
715
                                                 branchInstruction);
 
716
    }
 
717
 
 
718
 
 
719
    public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction)
 
720
    {
 
721
        // Adjust the default jump offset.
 
722
        tableSwitchInstruction.defaultOffset = remapBranchOffset(offset,
 
723
                                                                 tableSwitchInstruction.defaultOffset);
 
724
 
 
725
        // Adjust the jump offsets.
 
726
        remapJumpOffsets(offset,
 
727
                         tableSwitchInstruction.jumpOffsets);
 
728
 
 
729
        // Write out the instruction.
 
730
        instructionWriter.visitTableSwitchInstruction(clazz,
 
731
                                                      method,
 
732
                                                      codeAttribute,
 
733
                                                      newOffset,
 
734
                                                      tableSwitchInstruction);
 
735
    }
 
736
 
 
737
 
 
738
    public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)
 
739
    {
 
740
        // Adjust the default jump offset.
 
741
        lookUpSwitchInstruction.defaultOffset = remapBranchOffset(offset,
 
742
                                                                  lookUpSwitchInstruction.defaultOffset);
 
743
 
 
744
        // Adjust the jump offsets.
 
745
        remapJumpOffsets(offset,
 
746
                         lookUpSwitchInstruction.jumpOffsets);
 
747
 
 
748
        // Write out the instruction.
 
749
        instructionWriter.visitLookUpSwitchInstruction(clazz,
 
750
                                                       method,
 
751
                                                       codeAttribute,
 
752
                                                       newOffset,
 
753
                                                       lookUpSwitchInstruction);
 
754
    }
 
755
 
 
756
 
 
757
    // Implementations for ExceptionInfoVisitor.
 
758
 
 
759
    public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
 
760
    {
 
761
        // Remap the code offsets. Note that the instruction offset map also has
 
762
        // an entry for the first offset after the code, for u2endPC.
 
763
        exceptionInfo.u2startPC   = remapInstructionOffset(exceptionInfo.u2startPC);
 
764
        exceptionInfo.u2endPC     = remapInstructionOffset(exceptionInfo.u2endPC);
 
765
        exceptionInfo.u2handlerPC = remapInstructionOffset(exceptionInfo.u2handlerPC);
 
766
    }
 
767
 
 
768
 
 
769
    // Implementations for StackMapFrameVisitor.
 
770
 
 
771
    public void visitAnyStackMapFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrame stackMapFrame)
 
772
    {
 
773
        // Remap the stack map frame offset.
 
774
        int stackMapFrameOffset = remapInstructionOffset(offset);
 
775
 
 
776
        int offsetDelta = stackMapFrameOffset;
 
777
 
 
778
        // Compute the offset delta if the frame is part of a stack map frame
 
779
        // table (for JDK 6.0) instead of a stack map (for Java Micro Edition).
 
780
        if (expectedStackMapFrameOffset >= 0)
 
781
        {
 
782
            offsetDelta -= expectedStackMapFrameOffset;
 
783
 
 
784
            expectedStackMapFrameOffset = stackMapFrameOffset + 1;
 
785
        }
 
786
 
 
787
        stackMapFrame.u2offsetDelta = offsetDelta;
 
788
    }
 
789
 
 
790
 
 
791
    public void visitSameOneFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameOneFrame sameOneFrame)
 
792
    {
 
793
        // Remap the stack map frame offset.
 
794
        visitAnyStackMapFrame(clazz, method, codeAttribute, offset, sameOneFrame);
 
795
 
 
796
        // Remap the verification type offset.
 
797
        sameOneFrame.stackItemAccept(clazz, method, codeAttribute, offset, this);
 
798
    }
 
799
 
 
800
 
 
801
    public void visitMoreZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, MoreZeroFrame moreZeroFrame)
 
802
    {
 
803
        // Remap the stack map frame offset.
 
804
        visitAnyStackMapFrame(clazz, method, codeAttribute, offset, moreZeroFrame);
 
805
 
 
806
        // Remap the verification type offsets.
 
807
        moreZeroFrame.additionalVariablesAccept(clazz, method, codeAttribute, offset, this);
 
808
    }
 
809
 
 
810
 
 
811
    public void visitFullFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FullFrame fullFrame)
 
812
    {
 
813
        // Remap the stack map frame offset.
 
814
        visitAnyStackMapFrame(clazz, method, codeAttribute, offset, fullFrame);
 
815
 
 
816
        // Remap the verification type offsets.
 
817
        fullFrame.variablesAccept(clazz, method, codeAttribute, offset, this);
 
818
        fullFrame.stackAccept(clazz, method, codeAttribute, offset, this);
 
819
    }
 
820
 
 
821
 
 
822
    // Implementations for VerificationTypeVisitor.
 
823
 
 
824
    public void visitAnyVerificationType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VerificationType verificationType) {}
 
825
 
 
826
 
 
827
    public void visitUninitializedType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, UninitializedType uninitializedType)
 
828
    {
 
829
        // Remap the offset of the 'new' instruction.
 
830
        uninitializedType.u2newInstructionOffset = remapInstructionOffset(uninitializedType.u2newInstructionOffset);
 
831
    }
 
832
 
 
833
 
 
834
    // Implementations for LineNumberInfoVisitor.
 
835
 
 
836
    public void visitLineNumberInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberInfo lineNumberInfo)
 
837
    {
 
838
        // Remap the code offset.
 
839
        lineNumberInfo.u2startPC = remapInstructionOffset(lineNumberInfo.u2startPC);
 
840
    }
 
841
 
 
842
 
 
843
    // Implementations for LocalVariableInfoVisitor.
 
844
 
 
845
    public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo)
 
846
    {
 
847
        // Remap the code offset and length.
 
848
        // TODO: The local variable frame might not be strictly preserved.
 
849
        localVariableInfo.u2length  = remapBranchOffset(localVariableInfo.u2startPC,
 
850
                                                        localVariableInfo.u2length);
 
851
        localVariableInfo.u2startPC = remapInstructionOffset(localVariableInfo.u2startPC);
 
852
    }
 
853
 
 
854
 
 
855
    // Implementations for LocalVariableTypeInfoVisitor.
 
856
 
 
857
    public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo)
 
858
    {
 
859
        // Remap the code offset and length.
 
860
        // TODO: The local variable frame might not be strictly preserved.
 
861
        localVariableTypeInfo.u2length  = remapBranchOffset(localVariableTypeInfo.u2startPC,
 
862
                                                            localVariableTypeInfo.u2length);
 
863
        localVariableTypeInfo.u2startPC = remapInstructionOffset(localVariableTypeInfo.u2startPC);
 
864
    }
 
865
 
 
866
 
 
867
    // Small utility methods.
 
868
 
 
869
    /**
 
870
     * Adjusts the given jump offsets for the instruction at the given offset.
 
871
     */
 
872
    private void remapJumpOffsets(int offset, int[] jumpOffsets)
 
873
    {
 
874
        for (int index = 0; index < jumpOffsets.length; index++)
 
875
        {
 
876
            jumpOffsets[index] = remapBranchOffset(offset, jumpOffsets[index]);
 
877
        }
 
878
    }
 
879
 
 
880
 
 
881
    /**
 
882
     * Computes the new branch offset for the instruction at the given offset
 
883
     * with the given branch offset.
 
884
     */
 
885
    private int remapBranchOffset(int offset, int branchOffset)
 
886
    {
 
887
        return remapInstructionOffset(offset + branchOffset) - newOffset;
 
888
    }
 
889
 
 
890
 
 
891
    /**
 
892
     * Computes the new instruction offset for the instruction at the given offset.
 
893
     */
 
894
    private int remapInstructionOffset(int offset)
 
895
    {
 
896
        if (offset < 0 ||
 
897
            offset > codeLength)
 
898
        {
 
899
            throw new IllegalArgumentException("Invalid instruction offset ["+offset+"] in code with length ["+codeLength+"]");
 
900
        }
 
901
 
 
902
        return instructionOffsetMap[offset];
 
903
    }
 
904
 
 
905
 
 
906
    /**
 
907
     * Returns the given list of exceptions, without the ones that have empty
 
908
     * code blocks.
 
909
     */
 
910
    private int removeEmptyExceptions(ExceptionInfo[] exceptionInfos,
 
911
                                      int             exceptionInfoCount)
 
912
    {
 
913
        // Overwrite all empty exceptions.
 
914
        int newIndex = 0;
 
915
        for (int index = 0; index < exceptionInfoCount; index++)
 
916
        {
 
917
            ExceptionInfo exceptionInfo = exceptionInfos[index];
 
918
            if (exceptionInfo.u2startPC < exceptionInfo.u2endPC)
 
919
            {
 
920
                exceptionInfos[newIndex++] = exceptionInfo;
 
921
            }
 
922
        }
 
923
 
 
924
        return newIndex;
 
925
    }
 
926
 
 
927
 
 
928
    /**
 
929
     * Returns the given list of line numbers, without the ones that have empty
 
930
     * code blocks or that exceed the code size.
 
931
     */
 
932
    private int removeEmptyLineNumbers(LineNumberInfo[] lineNumberInfos,
 
933
                                       int              lineNumberInfoCount,
 
934
                                       int              codeLength)
 
935
    {
 
936
        // Overwrite all empty line number entries.
 
937
        int newIndex = 0;
 
938
        for (int index = 0; index < lineNumberInfoCount; index++)
 
939
        {
 
940
            LineNumberInfo lineNumberInfo = lineNumberInfos[index];
 
941
            int startPC = lineNumberInfo.u2startPC;
 
942
            if (startPC < codeLength &&
 
943
                (index == 0 || startPC > lineNumberInfos[index-1].u2startPC))
 
944
            {
 
945
                lineNumberInfos[newIndex++] = lineNumberInfo;
 
946
            }
 
947
        }
 
948
 
 
949
        return newIndex;
 
950
    }
 
951
 
 
952
 
 
953
    /**
 
954
     * Returns the given list of local variables, without the ones that have empty
 
955
     * code blocks or that exceed the actual number of local variables.
 
956
     */
 
957
    private int removeEmptyLocalVariables(LocalVariableInfo[] localVariableInfos,
 
958
                                          int                 localVariableInfoCount,
 
959
                                          int                 maxLocals)
 
960
    {
 
961
        // Overwrite all empty local variable entries.
 
962
        int newIndex = 0;
 
963
        for (int index = 0; index < localVariableInfoCount; index++)
 
964
        {
 
965
            LocalVariableInfo localVariableInfo = localVariableInfos[index];
 
966
            if (localVariableInfo.u2length > 0 &&
 
967
                localVariableInfo.u2index < maxLocals)
 
968
            {
 
969
                localVariableInfos[newIndex++] = localVariableInfo;
 
970
            }
 
971
        }
 
972
 
 
973
        return newIndex;
 
974
    }
 
975
 
 
976
 
 
977
    /**
 
978
     * Returns the given list of local variable types, without the ones that
 
979
     * have empty code blocks or that exceed the actual number of local variables.
 
980
     */
 
981
    private int removeEmptyLocalVariableTypes(LocalVariableTypeInfo[] localVariableTypeInfos,
 
982
                                              int                     localVariableTypeInfoCount,
 
983
                                              int                     maxLocals)
 
984
    {
 
985
        // Overwrite all empty local variable type entries.
 
986
        int newIndex = 0;
 
987
        for (int index = 0; index < localVariableTypeInfoCount; index++)
 
988
        {
 
989
            LocalVariableTypeInfo localVariableTypeInfo = localVariableTypeInfos[index];
 
990
            if (localVariableTypeInfo.u2length > 0 &&
 
991
                localVariableTypeInfo.u2index < maxLocals)
 
992
            {
 
993
                localVariableTypeInfos[newIndex++] = localVariableTypeInfo;
 
994
            }
 
995
        }
 
996
 
 
997
        return newIndex;
 
998
    }
 
999
}