2
2
* ProGuard -- shrinking, optimization, obfuscation, and preverification
5
* Copyright (c) 2002-2012 Eric Lafortune (eric@graphics.cornell.edu)
5
* Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu)
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
59
60
private static final int INVALID = -1;
62
private boolean allowExternalExceptionHandlers;
63
private final boolean allowExternalExceptionHandlers;
64
private final boolean shrinkInstructions;
64
66
private int maximumCodeLength;
65
67
private int codeLength;
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();
86
88
* Creates a new CodeAttributeComposer that doesn't allow external exception
89
* handlers and that automatically shrinks instructions.
89
91
public CodeAttributeComposer()
96
* Creates a new CodeAttributeComposer that optionally allows external
98
* Creates a new CodeAttributeComposer.
99
* @param allowExternalExceptionHandlers specifies whether exception
100
* handlers can lie outside the code
101
* fragment in which exceptions are
103
* @param shrinkInstructions specifies whether instructions
104
* should automatically be shrunk
105
* before being written.
99
public CodeAttributeComposer(boolean allowExternalExceptionHandlers)
107
public CodeAttributeComposer(boolean allowExternalExceptionHandlers,
108
boolean shrinkInstructions)
101
110
this.allowExternalExceptionHandlers = allowExternalExceptionHandlers;
111
this.shrinkInstructions = shrinkInstructions;
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
123
138
public void beginCodeFragment(int maximumCodeFragmentLength)
129
144
throw new IllegalArgumentException("Maximum number of code fragment levels exceeded ["+level+"]");
132
// // TODO: Figure out some length.
135
// // Prepare for possible widening of instructions.
136
// instructionWriter.reset(2 * maximumCodeFragmentLength);
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;
142
152
ensureCodeLength(maximumCodeLength);
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)
184
if (shrinkInstructions)
186
instruction = instruction.shrink();
174
191
println("["+codeLength+"] <- ", instruction.toString(oldInstructionOffset));
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);
180
197
ensureCodeLength(newCodeLength);
182
199
// Remember the old offset of the appended instruction.
183
200
oldInstructionOffsets[codeLength] = oldInstructionOffset;
185
// Write the instruction.
186
// instruction.accept(null,
188
// new CodeAttribute(0, 0, 0, 0, code, 0, null, 0, null),
190
// instructionWriter);
191
instruction.write(code, codeLength);
193
202
// Fill out the new offset of the appended instruction.
194
203
instructionOffsetMap[level][oldInstructionOffset] = codeLength;
205
// Write the instruction. The instruction writer may widen it later on,
207
instruction.accept(null,
209
new CodeAttribute(0, 0, 0, 0, code, 0, null, 0, null),
212
//instruction.write(code, codeLength);
196
214
// Continue appending at the next instruction offset.
197
215
codeLength = newCodeLength;
211
229
println("["+codeLength+"] <- ", "[" + oldInstructionOffset + "] (label)");
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);
235
// Remember the old offset of the following instruction.
236
oldInstructionOffsets[codeLength] = oldInstructionOffset;
238
// Fill out the new offset of the following instruction.
215
239
instructionOffsetMap[level][oldInstructionOffset] = codeLength;
244
* Appends the given instruction without defined offsets.
245
* @param instructions the instructions to be appended.
247
public void appendInstructions(Instruction[] instructions)
249
for (int index = 0; index < instructions.length; index++)
251
appendInstruction(instructions[index]);
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.
264
public void appendInstruction(Instruction instruction)
266
if (shrinkInstructions)
268
instruction = instruction.shrink();
273
println("["+codeLength+"] <- ", instruction.toString());
276
// Make sure the code array is large enough.
277
int newCodeLength = codeLength + instruction.length(codeLength);
279
ensureCodeLength(newCodeLength);
281
// Write the instruction. The instruction writer may widen it later on,
283
instruction.accept(null,
285
new CodeAttribute(0, 0, 0, 0, code, 0, null, 0, null),
288
//instruction.write(code, codeLength);
290
// Continue appending at the next instruction offset.
291
codeLength = newCodeLength;
220
296
* Appends the given exception to the exception table.
221
297
* @param exceptionInfo the exception to be appended.
249
// Make sure there is sufficient space in the exception table.
250
if (exceptionTable.length <= exceptionTableLength)
252
ExceptionInfo[] newExceptionTable = new ExceptionInfo[exceptionTableLength+1];
253
System.arraycopy(exceptionTable, 0, newExceptionTable, 0, exceptionTableLength);
254
exceptionTable = newExceptionTable;
257
325
// Add the exception.
258
exceptionTable[exceptionTableLength++] = exceptionInfo;
327
(ExceptionInfo[])ArrayUtil.add(exceptionTable,
328
exceptionTableLength++,
283
354
// Adapt the instruction for its new offset.
284
355
instruction.accept(null, null, null, instructionOffset, this);
286
// Write the instruction back.
287
// instruction.accept(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,
361
new CodeAttribute(0, 0, 0, 0, code, 0, null, 0, null),
364
//instruction.write(code, codeLength);
294
366
// Don't remap this instruction again.
295
367
oldInstructionOffsets[instructionOffset] = -1;
383
455
// Remap the line number table and the local variable table.
384
456
codeAttribute.attributesAccept(clazz, method, this);
386
// Remap the exception table.
458
// Remap the exception table (done before).
387
459
//codeAttribute.exceptionsAccept(clazz, method, this);
389
461
// Remove exceptions with empty code blocks (done before).
391
463
// removeEmptyExceptions(codeAttribute.exceptionTable,
392
464
// codeAttribute.u2exceptionTableLength);
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);
461
533
public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
463
535
// Adjust the branch offset.
464
branchInstruction.branchOffset = remapBranchOffset(offset,
465
branchInstruction.branchOffset);
536
branchInstruction.branchOffset = newBranchOffset(offset,
537
branchInstruction.branchOffset);
469
541
public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction)
471
543
// Adjust the default jump offset.
472
switchInstruction.defaultOffset = remapBranchOffset(offset,
473
switchInstruction.defaultOffset);
544
switchInstruction.defaultOffset = newBranchOffset(offset,
545
switchInstruction.defaultOffset);
475
547
// Adjust the jump offsets.
476
remapJumpOffsets(offset,
477
switchInstruction.jumpOffsets);
548
updateJumpOffsets(offset,
549
switchInstruction.jumpOffsets);
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);
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) :
503
575
public void visitAnyStackMapFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrame stackMapFrame)
505
577
// Remap the stack map frame offset.
506
int stackMapFrameOffset = remapInstructionOffset(offset);
578
int stackMapFrameOffset = newInstructionOffset(offset);
508
580
int offsetDelta = stackMapFrameOffset;
559
631
public void visitUninitializedType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, UninitializedType uninitializedType)
561
633
// Remap the offset of the 'new' instruction.
562
uninitializedType.u2newInstructionOffset = remapInstructionOffset(uninitializedType.u2newInstructionOffset);
634
uninitializedType.u2newInstructionOffset = newInstructionOffset(uninitializedType.u2newInstructionOffset);
568
640
public void visitLineNumberInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberInfo lineNumberInfo)
570
642
// Remap the code offset.
571
lineNumberInfo.u2startPC = remapInstructionOffset(lineNumberInfo.u2startPC);
643
lineNumberInfo.u2startPC = newInstructionOffset(lineNumberInfo.u2startPC);
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);
584
657
localVariableInfo.u2startPC = startPC;
585
658
localVariableInfo.u2length = endPC - startPC;
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);
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;
614
byte[] newCode = new byte[newCodeLength];
615
System.arraycopy(code, 0, newCode, 0, codeLength);
688
code = ArrayUtil.extendArray(code, newCodeLength);
689
oldInstructionOffsets = ArrayUtil.extendArray(oldInstructionOffsets, newCodeLength);
618
int[] newOldInstructionOffsets = new int[newCodeLength];
619
System.arraycopy(oldInstructionOffsets, 0, newOldInstructionOffsets, 0, codeLength);
620
oldInstructionOffsets = newOldInstructionOffsets;
691
instructionWriter.extend(newCodeLength);
626
697
* Adjusts the given jump offsets for the instruction at the given offset.
628
private void remapJumpOffsets(int offset, int[] jumpOffsets)
699
private void updateJumpOffsets(int offset, int[] jumpOffsets)
630
701
for (int index = 0; index < jumpOffsets.length; index++)
632
jumpOffsets[index] = remapBranchOffset(offset, jumpOffsets[index]);
703
jumpOffsets[index] = newBranchOffset(offset, jumpOffsets[index]);
638
709
* Computes the new branch offset for the instruction at the given new offset
639
710
* with the given old branch offset.
641
private int remapBranchOffset(int newInstructionOffset, int branchOffset)
712
private int newBranchOffset(int newInstructionOffset, int oldBranchOffset)
643
714
if (newInstructionOffset < 0 ||
644
715
newInstructionOffset > codeLength)
649
720
int oldInstructionOffset = oldInstructionOffsets[newInstructionOffset];
651
return remapInstructionOffset(oldInstructionOffset + branchOffset) -
652
remapInstructionOffset(oldInstructionOffset);
722
return newInstructionOffset(oldInstructionOffset + oldBranchOffset) -
723
newInstructionOffset(oldInstructionOffset);
657
728
* Computes the new instruction offset for the instruction at the given old
660
private int remapInstructionOffset(int oldInstructionOffset)
731
private int newInstructionOffset(int oldInstructionOffset)
662
733
if (oldInstructionOffset < 0 ||
663
734
oldInstructionOffset > codeFragmentLengths[level])
682
753
private boolean remappableExceptionHandler(int oldInstructionOffset)
755
// Can we index in the array?
684
756
if (oldInstructionOffset > codeFragmentLengths[level])
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];