~ubuntu-branches/ubuntu/wily/proguard/wily

« back to all changes in this revision

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

  • Committer: Package Import Robot
  • Author(s): Emmanuel Bourg
  • Date: 2014-04-10 13:58:11 UTC
  • mfrom: (1.2.5)
  • Revision ID: package-import@ubuntu.com-20140410135811-ddwzt2avu94rnolt
Tags: 4.11-1
* Team upload.
* New upstream release
* Removed the non-free documentation from the package (Closes: #719706)
* Removed the pre-built jars from the upstream tarball
* debian/control:
  - The package is now co-maintained with the Java Team
  - Standards-Version updated to 3.9.5 (no changes)
  - Added the Vcs-* fields
  - Added the Homepage field
* Switch to debhelper level 9
* Use XZ compression for the upstream tarball

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
 * ProGuard -- shrinking, optimization, obfuscation, and preverification
3
3
 *             of Java bytecode.
4
4
 *
5
 
 * Copyright (c) 2002-2012 Eric Lafortune (eric@graphics.cornell.edu)
 
5
 * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu)
6
6
 *
7
7
 * This program is free software; you can redistribute it and/or modify it
8
8
 * under the terms of the GNU General Public License as published by the Free
28
28
import proguard.classfile.instruction.*;
29
29
import proguard.classfile.instruction.visitor.InstructionVisitor;
30
30
import proguard.classfile.util.SimplifiedVisitor;
 
31
import proguard.util.ArrayUtil;
31
32
 
32
33
import java.util.Arrays;
33
34
 
59
60
    private static final int INVALID        = -1;
60
61
 
61
62
 
62
 
    private boolean allowExternalExceptionHandlers;
 
63
    private final boolean allowExternalExceptionHandlers;
 
64
    private final boolean shrinkInstructions;
63
65
 
64
66
    private int maximumCodeLength;
65
67
    private int codeLength;
79
81
 
80
82
    private final StackSizeUpdater    stackSizeUpdater    = new StackSizeUpdater();
81
83
    private final VariableSizeUpdater variableSizeUpdater = new VariableSizeUpdater();
82
 
//    private final InstructionWriter   instructionWriter   = new InstructionWriter();
 
84
    private final InstructionWriter   instructionWriter   = new InstructionWriter();
83
85
 
84
86
 
85
87
    /**
86
88
     * Creates a new CodeAttributeComposer that doesn't allow external exception
87
 
     * handlers.
 
89
     * handlers and that automatically shrinks instructions.
88
90
     */
89
91
    public CodeAttributeComposer()
90
92
    {
91
 
        this(false);
 
93
        this(false, true);
92
94
    }
93
95
 
94
96
 
95
97
    /**
96
 
     * Creates a new CodeAttributeComposer that optionally allows external
97
 
     * exception handlers.
 
98
     * Creates a new CodeAttributeComposer.
 
99
     * @param allowExternalExceptionHandlers specifies whether exception
 
100
     *                                       handlers can lie outside the code
 
101
     *                                       fragment in which exceptions are
 
102
     *                                       defined.
 
103
     * @param shrinkInstructions             specifies whether instructions
 
104
     *                                       should automatically be shrunk
 
105
     *                                       before being written.
98
106
     */
99
 
    public CodeAttributeComposer(boolean allowExternalExceptionHandlers)
 
107
    public CodeAttributeComposer(boolean allowExternalExceptionHandlers,
 
108
                                 boolean shrinkInstructions)
100
109
    {
101
110
        this.allowExternalExceptionHandlers = allowExternalExceptionHandlers;
 
111
        this.shrinkInstructions             = shrinkInstructions;
102
112
    }
103
113
 
104
114
 
111
121
        codeLength           = 0;
112
122
        exceptionTableLength = 0;
113
123
        level                = -1;
 
124
 
 
125
        instructionWriter.reset(ClassConstants.TYPICAL_CODE_LENGTH);
114
126
    }
115
127
 
116
128
 
118
130
     * Starts a new code fragment. Branch instructions that are added are
119
131
     * assumed to be relative within such code fragments.
120
132
     * @param maximumCodeFragmentLength the maximum length of the code that will
121
 
     *                                  be added as part of this fragment.
 
133
     *                                  be added as part of this fragment (more
 
134
     *                                  precisely, the maximum old instruction
 
135
     *                                  offset or label that is specified, plus
 
136
     *                                  one).
122
137
     */
123
138
    public void beginCodeFragment(int maximumCodeFragmentLength)
124
139
    {
129
144
            throw new IllegalArgumentException("Maximum number of code fragment levels exceeded ["+level+"]");
130
145
        }
131
146
 
132
 
//        // TODO: Figure out some length.
133
 
//        if (level == 0)
134
 
//        {
135
 
//            // Prepare for possible widening of instructions.
136
 
//            instructionWriter.reset(2 * maximumCodeFragmentLength);
137
 
//        }
138
 
 
139
147
        // Make sure there is sufficient space for adding the code fragment.
 
148
        // It's only a rough initial estimate for the code length, not even
 
149
        // necessarily a length expressed in bytes.
140
150
        maximumCodeLength += maximumCodeFragmentLength;
141
151
 
142
152
        ensureCodeLength(maximumCodeLength);
161
171
 
162
172
    /**
163
173
     * Appends the given instruction with the given old offset.
 
174
     * Branch instructions must fit, for instance by enabling automatic
 
175
     * shrinking of instructions.
164
176
     * @param oldInstructionOffset the old offset of the instruction, to which
165
177
     *                             branches and other references in the current
166
178
     *                             code fragment are pointing.
169
181
    public void appendInstruction(int         oldInstructionOffset,
170
182
                                  Instruction instruction)
171
183
    {
 
184
        if (shrinkInstructions)
 
185
        {
 
186
            instruction = instruction.shrink();
 
187
        }
 
188
 
172
189
        if (DEBUG)
173
190
        {
174
191
            println("["+codeLength+"] <- ", instruction.toString(oldInstructionOffset));
175
192
        }
176
193
 
177
 
        // Make sure the code array is large enough.
 
194
        // Make sure the code and offset arrays are large enough.
178
195
        int newCodeLength = codeLength + instruction.length(codeLength);
179
196
 
180
197
        ensureCodeLength(newCodeLength);
182
199
        // Remember the old offset of the appended instruction.
183
200
        oldInstructionOffsets[codeLength] = oldInstructionOffset;
184
201
 
185
 
        // Write the instruction.
186
 
//        instruction.accept(null,
187
 
//                           null,
188
 
//                           new CodeAttribute(0, 0, 0, 0, code, 0, null, 0, null),
189
 
//                           codeLength,
190
 
//                           instructionWriter);
191
 
        instruction.write(code, codeLength);
192
 
 
193
202
        // Fill out the new offset of the appended instruction.
194
203
        instructionOffsetMap[level][oldInstructionOffset] = codeLength;
195
204
 
 
205
        // Write the instruction. The instruction writer may widen it later on,
 
206
        // if necessary.
 
207
        instruction.accept(null,
 
208
                           null,
 
209
                           new CodeAttribute(0, 0, 0, 0, code, 0, null, 0, null),
 
210
                           codeLength,
 
211
                           instructionWriter);
 
212
        //instruction.write(code, codeLength);
 
213
 
196
214
        // Continue appending at the next instruction offset.
197
215
        codeLength = newCodeLength;
198
216
    }
211
229
            println("["+codeLength+"] <- ", "[" + oldInstructionOffset + "] (label)");
212
230
        }
213
231
 
214
 
        // Fill out the new offset of the appended instruction.
 
232
        // Make sure the code and offset arrays are large enough.
 
233
        ensureCodeLength(codeLength + 1);
 
234
 
 
235
        // Remember the old offset of the following instruction.
 
236
        oldInstructionOffsets[codeLength] = oldInstructionOffset;
 
237
 
 
238
        // Fill out the new offset of the following instruction.
215
239
        instructionOffsetMap[level][oldInstructionOffset] = codeLength;
216
240
    }
217
241
 
218
242
 
219
243
    /**
 
244
     * Appends the given instruction without defined offsets.
 
245
     * @param instructions the instructions to be appended.
 
246
     */
 
247
    public void appendInstructions(Instruction[] instructions)
 
248
    {
 
249
        for (int index = 0; index < instructions.length; index++)
 
250
        {
 
251
            appendInstruction(instructions[index]);
 
252
        }
 
253
    }
 
254
 
 
255
 
 
256
    /**
 
257
     * Appends the given instruction without a defined offset.
 
258
     * Branch instructions should have a label, to allow computing the
 
259
     * new relative offset.
 
260
     * Branch instructions must fit, for instance by enabling automatic
 
261
     * shrinking of instructions.
 
262
     * @param instruction the instruction to be appended.
 
263
     */
 
264
    public void appendInstruction(Instruction instruction)
 
265
    {
 
266
        if (shrinkInstructions)
 
267
        {
 
268
            instruction = instruction.shrink();
 
269
        }
 
270
 
 
271
        if (DEBUG)
 
272
        {
 
273
            println("["+codeLength+"] <- ", instruction.toString());
 
274
        }
 
275
 
 
276
        // Make sure the code array is large enough.
 
277
        int newCodeLength = codeLength + instruction.length(codeLength);
 
278
 
 
279
        ensureCodeLength(newCodeLength);
 
280
 
 
281
        // Write the instruction. The instruction writer may widen it later on,
 
282
        // if necessary.
 
283
        instruction.accept(null,
 
284
                           null,
 
285
                           new CodeAttribute(0, 0, 0, 0, code, 0, null, 0, null),
 
286
                           codeLength,
 
287
                           instructionWriter);
 
288
        //instruction.write(code, codeLength);
 
289
 
 
290
        // Continue appending at the next instruction offset.
 
291
        codeLength = newCodeLength;
 
292
    }
 
293
 
 
294
 
 
295
    /**
220
296
     * Appends the given exception to the exception table.
221
297
     * @param exceptionInfo the exception to be appended.
222
298
     */
246
322
            return;
247
323
        }
248
324
 
249
 
        // Make sure there is sufficient space in the exception table.
250
 
        if (exceptionTable.length <= exceptionTableLength)
251
 
        {
252
 
            ExceptionInfo[] newExceptionTable = new ExceptionInfo[exceptionTableLength+1];
253
 
            System.arraycopy(exceptionTable, 0, newExceptionTable, 0, exceptionTableLength);
254
 
            exceptionTable = newExceptionTable;
255
 
        }
256
 
 
257
325
        // Add the exception.
258
 
        exceptionTable[exceptionTableLength++] = exceptionInfo;
 
326
        exceptionTable =
 
327
            (ExceptionInfo[])ArrayUtil.add(exceptionTable,
 
328
                                           exceptionTableLength++,
 
329
                                           exceptionInfo);
259
330
    }
260
331
 
261
332
 
283
354
                // Adapt the instruction for its new offset.
284
355
                instruction.accept(null, null, null, instructionOffset, this);
285
356
 
286
 
                // Write the instruction back.
287
 
//                instruction.accept(null,
288
 
//                                   null,
289
 
//                                   new CodeAttribute(0, 0, 0, 0, code, 0, null, 0, null),
290
 
//                                   instructionOffset,
291
 
//                                   instructionWriter);
292
 
                instruction.write(code, instructionOffset);
 
357
                // Write the instruction back. The instruction writer may still
 
358
                // widen it later on, if necessary.
 
359
                instruction.accept(null,
 
360
                                   null,
 
361
                                   new CodeAttribute(0, 0, 0, 0, code, 0, null, 0, null),
 
362
                                   instructionOffset,
 
363
                                   instructionWriter);
 
364
                //instruction.write(code, codeLength);
293
365
 
294
366
                // Don't remap this instruction again.
295
367
                oldInstructionOffsets[instructionOffset] = -1;
317
389
                {
318
390
                    if (remappableExceptionHandler(handlerPC))
319
391
                    {
320
 
                        exceptionInfo.u2handlerPC = remapInstructionOffset(handlerPC);
 
392
                        exceptionInfo.u2handlerPC = newInstructionOffset(handlerPC);
321
393
                    }
322
394
                    else if (level == 0)
323
395
                    {
383
455
        // Remap  the line number table and the local variable table.
384
456
        codeAttribute.attributesAccept(clazz, method, this);
385
457
 
386
 
        // Remap the exception table.
 
458
        // Remap the exception table (done before).
387
459
        //codeAttribute.exceptionsAccept(clazz, method, this);
388
460
 
389
461
        // Remove exceptions with empty code blocks (done before).
391
463
        //    removeEmptyExceptions(codeAttribute.exceptionTable,
392
464
        //                          codeAttribute.u2exceptionTableLength);
393
465
 
394
 
//        // Make sure instructions are widened if necessary.
395
 
//        instructionWriter.visitCodeAttribute(clazz, method, codeAttribute);
 
466
        // Make sure instructions are widened if necessary.
 
467
        instructionWriter.visitCodeAttribute(clazz, method, codeAttribute);
396
468
 
397
469
        level--;
398
470
    }
461
533
    public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
462
534
    {
463
535
        // Adjust the branch offset.
464
 
        branchInstruction.branchOffset = remapBranchOffset(offset,
465
 
                                                           branchInstruction.branchOffset);
 
536
        branchInstruction.branchOffset = newBranchOffset(offset,
 
537
                                                         branchInstruction.branchOffset);
466
538
    }
467
539
 
468
540
 
469
541
    public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction)
470
542
    {
471
543
        // Adjust the default jump offset.
472
 
        switchInstruction.defaultOffset = remapBranchOffset(offset,
473
 
                                                            switchInstruction.defaultOffset);
 
544
        switchInstruction.defaultOffset = newBranchOffset(offset,
 
545
                                                          switchInstruction.defaultOffset);
474
546
 
475
547
        // Adjust the jump offsets.
476
 
        remapJumpOffsets(offset,
477
 
                         switchInstruction.jumpOffsets);
 
548
        updateJumpOffsets(offset,
 
549
                          switchInstruction.jumpOffsets);
478
550
    }
479
551
 
480
552
 
484
556
    {
485
557
        // Remap the code offsets. Note that the instruction offset map also has
486
558
        // an entry for the first offset after the code, for u2endPC.
487
 
        exceptionInfo.u2startPC = remapInstructionOffset(exceptionInfo.u2startPC);
488
 
        exceptionInfo.u2endPC   = remapInstructionOffset(exceptionInfo.u2endPC);
 
559
        exceptionInfo.u2startPC = newInstructionOffset(exceptionInfo.u2startPC);
 
560
        exceptionInfo.u2endPC   = newInstructionOffset(exceptionInfo.u2endPC);
489
561
 
490
562
        // See if we can remap the handler right away. Unmapped exception
491
563
        // handlers are negated, in order to mark them as external.
493
565
        exceptionInfo.u2handlerPC =
494
566
            !allowExternalExceptionHandlers ||
495
567
            remappableExceptionHandler(handlerPC) ?
496
 
                remapInstructionOffset(handlerPC) :
 
568
                newInstructionOffset(handlerPC) :
497
569
                -handlerPC;
498
570
    }
499
571
 
503
575
    public void visitAnyStackMapFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrame stackMapFrame)
504
576
    {
505
577
        // Remap the stack map frame offset.
506
 
        int stackMapFrameOffset = remapInstructionOffset(offset);
 
578
        int stackMapFrameOffset = newInstructionOffset(offset);
507
579
 
508
580
        int offsetDelta = stackMapFrameOffset;
509
581
 
559
631
    public void visitUninitializedType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, UninitializedType uninitializedType)
560
632
    {
561
633
        // Remap the offset of the 'new' instruction.
562
 
        uninitializedType.u2newInstructionOffset = remapInstructionOffset(uninitializedType.u2newInstructionOffset);
 
634
        uninitializedType.u2newInstructionOffset = newInstructionOffset(uninitializedType.u2newInstructionOffset);
563
635
    }
564
636
 
565
637
 
568
640
    public void visitLineNumberInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberInfo lineNumberInfo)
569
641
    {
570
642
        // Remap the code offset.
571
 
        lineNumberInfo.u2startPC = remapInstructionOffset(lineNumberInfo.u2startPC);
 
643
        lineNumberInfo.u2startPC = newInstructionOffset(lineNumberInfo.u2startPC);
572
644
    }
573
645
 
574
646
 
578
650
    {
579
651
        // Remap the code offset and length.
580
652
        // TODO: The local variable frame might not be strictly preserved.
581
 
        int startPC = remapInstructionOffset(localVariableInfo.u2startPC);
582
 
        int endPC   = remapInstructionOffset(localVariableInfo.u2startPC + localVariableInfo.u2length);
 
653
        int startPC = newInstructionOffset(localVariableInfo.u2startPC);
 
654
        int endPC   = newInstructionOffset(localVariableInfo.u2startPC +
 
655
                                           localVariableInfo.u2length);
583
656
 
584
657
        localVariableInfo.u2startPC = startPC;
585
658
        localVariableInfo.u2length  = endPC - startPC;
591
664
    {
592
665
        // Remap the code offset and length.
593
666
        // TODO: The local variable frame might not be strictly preserved.
594
 
        int startPC = remapInstructionOffset(localVariableTypeInfo.u2startPC);
595
 
        int endPC   = remapInstructionOffset(localVariableTypeInfo.u2startPC + localVariableTypeInfo.u2length);
 
667
        int startPC = newInstructionOffset(localVariableTypeInfo.u2startPC);
 
668
        int endPC   = newInstructionOffset(localVariableTypeInfo.u2startPC +
 
669
                                           localVariableTypeInfo.u2length);
596
670
 
597
671
        localVariableTypeInfo.u2startPC = startPC;
598
672
        localVariableTypeInfo.u2length  = endPC - startPC;
611
685
            // Add 20% to avoid extending the arrays too often.
612
686
            newCodeLength = newCodeLength * 6 / 5;
613
687
 
614
 
            byte[] newCode = new byte[newCodeLength];
615
 
            System.arraycopy(code, 0, newCode, 0, codeLength);
616
 
            code = newCode;
 
688
            code                  = ArrayUtil.extendArray(code,                  newCodeLength);
 
689
            oldInstructionOffsets = ArrayUtil.extendArray(oldInstructionOffsets, newCodeLength);
617
690
 
618
 
            int[] newOldInstructionOffsets = new int[newCodeLength];
619
 
            System.arraycopy(oldInstructionOffsets, 0, newOldInstructionOffsets, 0, codeLength);
620
 
            oldInstructionOffsets = newOldInstructionOffsets;
 
691
            instructionWriter.extend(newCodeLength);
621
692
        }
622
693
    }
623
694
 
625
696
    /**
626
697
     * Adjusts the given jump offsets for the instruction at the given offset.
627
698
     */
628
 
    private void remapJumpOffsets(int offset, int[] jumpOffsets)
 
699
    private void updateJumpOffsets(int offset, int[] jumpOffsets)
629
700
    {
630
701
        for (int index = 0; index < jumpOffsets.length; index++)
631
702
        {
632
 
            jumpOffsets[index] = remapBranchOffset(offset, jumpOffsets[index]);
 
703
            jumpOffsets[index] = newBranchOffset(offset, jumpOffsets[index]);
633
704
        }
634
705
    }
635
706
 
638
709
     * Computes the new branch offset for the instruction at the given new offset
639
710
     * with the given old branch offset.
640
711
     */
641
 
    private int remapBranchOffset(int newInstructionOffset, int branchOffset)
 
712
    private int newBranchOffset(int newInstructionOffset, int oldBranchOffset)
642
713
    {
643
714
        if (newInstructionOffset < 0 ||
644
715
            newInstructionOffset > codeLength)
648
719
 
649
720
        int oldInstructionOffset = oldInstructionOffsets[newInstructionOffset];
650
721
 
651
 
        return remapInstructionOffset(oldInstructionOffset + branchOffset) -
652
 
               remapInstructionOffset(oldInstructionOffset);
 
722
        return newInstructionOffset(oldInstructionOffset + oldBranchOffset) -
 
723
               newInstructionOffset(oldInstructionOffset);
653
724
    }
654
725
 
655
726
 
657
728
     * Computes the new instruction offset for the instruction at the given old
658
729
     * offset.
659
730
     */
660
 
    private int remapInstructionOffset(int oldInstructionOffset)
 
731
    private int newInstructionOffset(int oldInstructionOffset)
661
732
    {
662
733
        if (oldInstructionOffset < 0 ||
663
734
            oldInstructionOffset > codeFragmentLengths[level])
681
752
     */
682
753
    private boolean remappableExceptionHandler(int oldInstructionOffset)
683
754
    {
 
755
        // Can we index in the array?
684
756
        if (oldInstructionOffset > codeFragmentLengths[level])
685
757
        {
686
758
            return false;
687
759
        }
688
760
 
 
761
        // Do we have a valid new instruction offset, but not yet right after
 
762
        // the code? That offset is only labeled for mapping try blocks, not
 
763
        // for mapping handlers.
689
764
        int newInstructionOffset =
690
765
            instructionOffsetMap[level][oldInstructionOffset];
691
766