Mercurial > lasercutter
diff src/clojure/asm/MethodWriter.java @ 10:ef7dbbd6452c
added clojure source goodness
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sat, 21 Aug 2010 06:25:44 -0400 |
parents | |
children |
line wrap: on
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/clojure/asm/MethodWriter.java Sat Aug 21 06:25:44 2010 -0400 1.3 @@ -0,0 +1,3029 @@ 1.4 +/*** 1.5 + * ASM: a very small and fast Java bytecode manipulation framework 1.6 + * Copyright (c) 2000-2005 INRIA, France Telecom 1.7 + * All rights reserved. 1.8 + * 1.9 + * Redistribution and use in source and binary forms, with or without 1.10 + * modification, are permitted provided that the following conditions 1.11 + * are met: 1.12 + * 1. Redistributions of source code must retain the above copyright 1.13 + * notice, this list of conditions and the following disclaimer. 1.14 + * 2. Redistributions in binary form must reproduce the above copyright 1.15 + * notice, this list of conditions and the following disclaimer in the 1.16 + * documentation and/or other materials provided with the distribution. 1.17 + * 3. Neither the name of the copyright holders nor the names of its 1.18 + * contributors may be used to endorse or promote products derived from 1.19 + * this software without specific prior written permission. 1.20 + * 1.21 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 1.22 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1.23 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1.24 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 1.25 + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 1.26 + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 1.27 + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 1.28 + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 1.29 + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 1.30 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 1.31 + * THE POSSIBILITY OF SUCH DAMAGE. 1.32 + */ 1.33 +package clojure.asm; 1.34 + 1.35 +/** 1.36 + * A {@link MethodVisitor} that generates methods in bytecode form. Each visit 1.37 + * method of this class appends the bytecode corresponding to the visited 1.38 + * instruction to a byte vector, in the order these methods are called. 1.39 + * 1.40 + * @author Eric Bruneton 1.41 + * @author Eugene Kuleshov 1.42 + */ 1.43 +class MethodWriter implements MethodVisitor{ 1.44 + 1.45 +/** 1.46 + * Pseudo access flag used to denote constructors. 1.47 + */ 1.48 +final static int ACC_CONSTRUCTOR = 262144; 1.49 + 1.50 +/** 1.51 + * Frame has exactly the same locals as the previous stack map frame and 1.52 + * number of stack items is zero. 1.53 + */ 1.54 +final static int SAME_FRAME = 0; // to 63 (0-3f) 1.55 + 1.56 +/** 1.57 + * Frame has exactly the same locals as the previous stack map frame and 1.58 + * number of stack items is 1 1.59 + */ 1.60 +final static int SAME_LOCALS_1_STACK_ITEM_FRAME = 64; // to 127 (40-7f) 1.61 + 1.62 +/** 1.63 + * Reserved for future use 1.64 + */ 1.65 +final static int RESERVED = 128; 1.66 + 1.67 +/** 1.68 + * Frame has exactly the same locals as the previous stack map frame and 1.69 + * number of stack items is 1. Offset is bigger then 63; 1.70 + */ 1.71 +final static int SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED = 247; // f7 1.72 + 1.73 +/** 1.74 + * Frame where current locals are the same as the locals in the previous 1.75 + * frame, except that the k last locals are absent. The value of k is given 1.76 + * by the formula 251-frame_type. 1.77 + */ 1.78 +final static int CHOP_FRAME = 248; // to 250 (f8-fA) 1.79 + 1.80 +/** 1.81 + * Frame has exactly the same locals as the previous stack map frame and 1.82 + * number of stack items is zero. Offset is bigger then 63; 1.83 + */ 1.84 +final static int SAME_FRAME_EXTENDED = 251; // fb 1.85 + 1.86 +/** 1.87 + * Frame where current locals are the same as the locals in the previous 1.88 + * frame, except that k additional locals are defined. The value of k is 1.89 + * given by the formula frame_type-251. 1.90 + */ 1.91 +final static int APPEND_FRAME = 252; // to 254 // fc-fe 1.92 + 1.93 +/** 1.94 + * Full frame 1.95 + */ 1.96 +final static int FULL_FRAME = 255; // ff 1.97 + 1.98 +/** 1.99 + * Indicates that the stack map frames must be recomputed from scratch. In 1.100 + * this case the maximum stack size and number of local variables is also 1.101 + * recomputed from scratch. 1.102 + * 1.103 + * @see #compute 1.104 + */ 1.105 +private final static int FRAMES = 0; 1.106 + 1.107 +/** 1.108 + * Indicates that the maximum stack size and number of local variables must 1.109 + * be automatically computed. 1.110 + * 1.111 + * @see #compute 1.112 + */ 1.113 +private final static int MAXS = 1; 1.114 + 1.115 +/** 1.116 + * Indicates that nothing must be automatically computed. 1.117 + * 1.118 + * @see #compute 1.119 + */ 1.120 +private final static int NOTHING = 2; 1.121 + 1.122 +/** 1.123 + * Next method writer (see {@link ClassWriter#firstMethod firstMethod}). 1.124 + */ 1.125 +MethodWriter next; 1.126 + 1.127 +/** 1.128 + * The class writer to which this method must be added. 1.129 + */ 1.130 +ClassWriter cw; 1.131 + 1.132 +/** 1.133 + * Access flags of this method. 1.134 + */ 1.135 +private int access; 1.136 + 1.137 +/** 1.138 + * The index of the constant pool item that contains the name of this 1.139 + * method. 1.140 + */ 1.141 +private int name; 1.142 + 1.143 +/** 1.144 + * The index of the constant pool item that contains the descriptor of this 1.145 + * method. 1.146 + */ 1.147 +private int desc; 1.148 + 1.149 +/** 1.150 + * The descriptor of this method. 1.151 + */ 1.152 +private String descriptor; 1.153 + 1.154 +/** 1.155 + * The signature of this method. 1.156 + */ 1.157 +String signature; 1.158 + 1.159 +/** 1.160 + * If not zero, indicates that the code of this method must be copied from 1.161 + * the ClassReader associated to this writer in <code>cw.cr</code>. More 1.162 + * precisely, this field gives the index of the first byte to copied from 1.163 + * <code>cw.cr.b</code>. 1.164 + */ 1.165 +int classReaderOffset; 1.166 + 1.167 +/** 1.168 + * If not zero, indicates that the code of this method must be copied from 1.169 + * the ClassReader associated to this writer in <code>cw.cr</code>. More 1.170 + * precisely, this field gives the number of bytes to copied from 1.171 + * <code>cw.cr.b</code>. 1.172 + */ 1.173 +int classReaderLength; 1.174 + 1.175 +/** 1.176 + * Number of exceptions that can be thrown by this method. 1.177 + */ 1.178 +int exceptionCount; 1.179 + 1.180 +/** 1.181 + * The exceptions that can be thrown by this method. More precisely, this 1.182 + * array contains the indexes of the constant pool items that contain the 1.183 + * internal names of these exception classes. 1.184 + */ 1.185 +int[] exceptions; 1.186 + 1.187 +/** 1.188 + * The annotation default attribute of this method. May be <tt>null</tt>. 1.189 + */ 1.190 +private ByteVector annd; 1.191 + 1.192 +/** 1.193 + * The runtime visible annotations of this method. May be <tt>null</tt>. 1.194 + */ 1.195 +private AnnotationWriter anns; 1.196 + 1.197 +/** 1.198 + * The runtime invisible annotations of this method. May be <tt>null</tt>. 1.199 + */ 1.200 +private AnnotationWriter ianns; 1.201 + 1.202 +/** 1.203 + * The runtime visible parameter annotations of this method. May be 1.204 + * <tt>null</tt>. 1.205 + */ 1.206 +private AnnotationWriter[] panns; 1.207 + 1.208 +/** 1.209 + * The runtime invisible parameter annotations of this method. May be 1.210 + * <tt>null</tt>. 1.211 + */ 1.212 +private AnnotationWriter[] ipanns; 1.213 + 1.214 +/** 1.215 + * The non standard attributes of the method. 1.216 + */ 1.217 +private Attribute attrs; 1.218 + 1.219 +/** 1.220 + * The bytecode of this method. 1.221 + */ 1.222 +private ByteVector code = new ByteVector(); 1.223 + 1.224 +/** 1.225 + * Maximum stack size of this method. 1.226 + */ 1.227 +private int maxStack; 1.228 + 1.229 +/** 1.230 + * Maximum number of local variables for this method. 1.231 + */ 1.232 +private int maxLocals; 1.233 + 1.234 +/** 1.235 + * Number of stack map frames in the StackMapTable attribute. 1.236 + */ 1.237 +private int frameCount; 1.238 + 1.239 +/** 1.240 + * The StackMapTable attribute. 1.241 + */ 1.242 +private ByteVector stackMap; 1.243 + 1.244 +/** 1.245 + * The offset of the last frame that was written in the StackMapTable 1.246 + * attribute. 1.247 + */ 1.248 +private int previousFrameOffset; 1.249 + 1.250 +/** 1.251 + * The last frame that was written in the StackMapTable attribute. 1.252 + * 1.253 + * @see #frame 1.254 + */ 1.255 +private int[] previousFrame; 1.256 + 1.257 +/** 1.258 + * Index of the next element to be added in {@link #frame}. 1.259 + */ 1.260 +private int frameIndex; 1.261 + 1.262 +/** 1.263 + * The current stack map frame. The first element contains the offset of the 1.264 + * instruction to which the frame corresponds, the second element is the 1.265 + * number of locals and the third one is the number of stack elements. The 1.266 + * local variables start at index 3 and are followed by the operand stack 1.267 + * values. In summary frame[0] = offset, frame[1] = nLocal, frame[2] = 1.268 + * nStack, frame[3] = nLocal. All types are encoded as integers, with the 1.269 + * same format as the one used in {@link Label}, but limited to BASE types. 1.270 + */ 1.271 +private int[] frame; 1.272 + 1.273 +/** 1.274 + * Number of elements in the exception handler list. 1.275 + */ 1.276 +private int handlerCount; 1.277 + 1.278 +/** 1.279 + * The first element in the exception handler list. 1.280 + */ 1.281 +private Handler firstHandler; 1.282 + 1.283 +/** 1.284 + * The last element in the exception handler list. 1.285 + */ 1.286 +private Handler lastHandler; 1.287 + 1.288 +/** 1.289 + * Number of entries in the LocalVariableTable attribute. 1.290 + */ 1.291 +private int localVarCount; 1.292 + 1.293 +/** 1.294 + * The LocalVariableTable attribute. 1.295 + */ 1.296 +private ByteVector localVar; 1.297 + 1.298 +/** 1.299 + * Number of entries in the LocalVariableTypeTable attribute. 1.300 + */ 1.301 +private int localVarTypeCount; 1.302 + 1.303 +/** 1.304 + * The LocalVariableTypeTable attribute. 1.305 + */ 1.306 +private ByteVector localVarType; 1.307 + 1.308 +/** 1.309 + * Number of entries in the LineNumberTable attribute. 1.310 + */ 1.311 +private int lineNumberCount; 1.312 + 1.313 +/** 1.314 + * The LineNumberTable attribute. 1.315 + */ 1.316 +private ByteVector lineNumber; 1.317 + 1.318 +/** 1.319 + * The non standard attributes of the method's code. 1.320 + */ 1.321 +private Attribute cattrs; 1.322 + 1.323 +/** 1.324 + * Indicates if some jump instructions are too small and need to be resized. 1.325 + */ 1.326 +private boolean resize; 1.327 + 1.328 +/** 1.329 + * Indicates if the instructions contain at least one JSR instruction. 1.330 + */ 1.331 +private boolean jsr; 1.332 + 1.333 +// ------------------------------------------------------------------------ 1.334 + 1.335 +/* 1.336 + * Fields for the control flow graph analysis algorithm (used to compute the 1.337 + * maximum stack size). A control flow graph contains one node per "basic 1.338 + * block", and one edge per "jump" from one basic block to another. Each 1.339 + * node (i.e., each basic block) is represented by the Label object that 1.340 + * corresponds to the first instruction of this basic block. Each node also 1.341 + * stores the list of its successors in the graph, as a linked list of Edge 1.342 + * objects. 1.343 + */ 1.344 + 1.345 +/** 1.346 + * Indicates what must be automatically computed. 1.347 + * 1.348 + * @see FRAMES 1.349 + * @see MAXS 1.350 + * @see NOTHING 1.351 + */ 1.352 +private int compute; 1.353 + 1.354 +/** 1.355 + * A list of labels. This list is the list of basic blocks in the method, 1.356 + * i.e. a list of Label objects linked to each other by their 1.357 + * {@link Label#successor} field, in the order they are visited by 1.358 + * {@link visitLabel}, and starting with the first basic block. 1.359 + */ 1.360 +private Label labels; 1.361 + 1.362 +/** 1.363 + * The previous basic block. 1.364 + */ 1.365 +private Label previousBlock; 1.366 + 1.367 +/** 1.368 + * The current basic block. 1.369 + */ 1.370 +private Label currentBlock; 1.371 + 1.372 +/** 1.373 + * The (relative) stack size after the last visited instruction. This size 1.374 + * is relative to the beginning of the current basic block, i.e., the true 1.375 + * stack size after the last visited instruction is equal to the 1.376 + * {@link Label#inputStackTop beginStackSize} of the current basic block 1.377 + * plus <tt>stackSize</tt>. 1.378 + */ 1.379 +private int stackSize; 1.380 + 1.381 +/** 1.382 + * The (relative) maximum stack size after the last visited instruction. 1.383 + * This size is relative to the beginning of the current basic block, i.e., 1.384 + * the true maximum stack size after the last visited instruction is equal 1.385 + * to the {@link Label#inputStackTop beginStackSize} of the current basic 1.386 + * block plus <tt>stackSize</tt>. 1.387 + */ 1.388 +private int maxStackSize; 1.389 + 1.390 +// ------------------------------------------------------------------------ 1.391 +// Constructor 1.392 +// ------------------------------------------------------------------------ 1.393 + 1.394 +/** 1.395 + * Constructs a new {@link MethodWriter}. 1.396 + * 1.397 + * @param cw the class writer in which the method must be added. 1.398 + * @param access the method's access flags (see {@link Opcodes}). 1.399 + * @param name the method's name. 1.400 + * @param desc the method's descriptor (see {@link Type}). 1.401 + * @param signature the method's signature. May be <tt>null</tt>. 1.402 + * @param exceptions the internal names of the method's exceptions. May be 1.403 + * <tt>null</tt>. 1.404 + * @param computeMaxs <tt>true</tt> if the maximum stack size and number 1.405 + * of local variables must be automatically computed. 1.406 + * @param computeFrames <tt>true</tt> if the stack map tables must be 1.407 + * recomputed from scratch. 1.408 + */ 1.409 +MethodWriter( 1.410 + final ClassWriter cw, 1.411 + final int access, 1.412 + final String name, 1.413 + final String desc, 1.414 + final String signature, 1.415 + final String[] exceptions, 1.416 + final boolean computeMaxs, 1.417 + final boolean computeFrames){ 1.418 + if(cw.firstMethod == null) 1.419 + { 1.420 + cw.firstMethod = this; 1.421 + } 1.422 + else 1.423 + { 1.424 + cw.lastMethod.next = this; 1.425 + } 1.426 + cw.lastMethod = this; 1.427 + this.cw = cw; 1.428 + this.access = access; 1.429 + this.name = cw.newUTF8(name); 1.430 + this.desc = cw.newUTF8(desc); 1.431 + this.descriptor = desc; 1.432 + this.signature = signature; 1.433 + if(exceptions != null && exceptions.length > 0) 1.434 + { 1.435 + exceptionCount = exceptions.length; 1.436 + this.exceptions = new int[exceptionCount]; 1.437 + for(int i = 0; i < exceptionCount; ++i) 1.438 + { 1.439 + this.exceptions[i] = cw.newClass(exceptions[i]); 1.440 + } 1.441 + } 1.442 + this.compute = computeFrames ? FRAMES : (computeMaxs ? MAXS : NOTHING); 1.443 + if(computeMaxs || computeFrames) 1.444 + { 1.445 + if(computeFrames && name.equals("<init>")) 1.446 + { 1.447 + this.access |= ACC_CONSTRUCTOR; 1.448 + } 1.449 + // updates maxLocals 1.450 + int size = getArgumentsAndReturnSizes(descriptor) >> 2; 1.451 + if((access & Opcodes.ACC_STATIC) != 0) 1.452 + { 1.453 + --size; 1.454 + } 1.455 + maxLocals = size; 1.456 + // creates and visits the label for the first basic block 1.457 + labels = new Label(); 1.458 + labels.status |= Label.PUSHED; 1.459 + visitLabel(labels); 1.460 + } 1.461 +} 1.462 + 1.463 +// ------------------------------------------------------------------------ 1.464 +// Implementation of the MethodVisitor interface 1.465 +// ------------------------------------------------------------------------ 1.466 + 1.467 +public AnnotationVisitor visitAnnotationDefault(){ 1.468 + annd = new ByteVector(); 1.469 + return new AnnotationWriter(cw, false, annd, null, 0); 1.470 +} 1.471 + 1.472 +public AnnotationVisitor visitAnnotation( 1.473 + final String desc, 1.474 + final boolean visible){ 1.475 + ByteVector bv = new ByteVector(); 1.476 + // write type, and reserve space for values count 1.477 + bv.putShort(cw.newUTF8(desc)).putShort(0); 1.478 + AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2); 1.479 + if(visible) 1.480 + { 1.481 + aw.next = anns; 1.482 + anns = aw; 1.483 + } 1.484 + else 1.485 + { 1.486 + aw.next = ianns; 1.487 + ianns = aw; 1.488 + } 1.489 + return aw; 1.490 +} 1.491 + 1.492 +public AnnotationVisitor visitParameterAnnotation( 1.493 + final int parameter, 1.494 + final String desc, 1.495 + final boolean visible){ 1.496 + ByteVector bv = new ByteVector(); 1.497 + // write type, and reserve space for values count 1.498 + bv.putShort(cw.newUTF8(desc)).putShort(0); 1.499 + AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2); 1.500 + if(visible) 1.501 + { 1.502 + if(panns == null) 1.503 + { 1.504 + panns = new AnnotationWriter[Type.getArgumentTypes(descriptor).length]; 1.505 + } 1.506 + aw.next = panns[parameter]; 1.507 + panns[parameter] = aw; 1.508 + } 1.509 + else 1.510 + { 1.511 + if(ipanns == null) 1.512 + { 1.513 + ipanns = new AnnotationWriter[Type.getArgumentTypes(descriptor).length]; 1.514 + } 1.515 + aw.next = ipanns[parameter]; 1.516 + ipanns[parameter] = aw; 1.517 + } 1.518 + return aw; 1.519 +} 1.520 + 1.521 +public void visitAttribute(final Attribute attr){ 1.522 + if(attr.isCodeAttribute()) 1.523 + { 1.524 + attr.next = cattrs; 1.525 + cattrs = attr; 1.526 + } 1.527 + else 1.528 + { 1.529 + attr.next = attrs; 1.530 + attrs = attr; 1.531 + } 1.532 +} 1.533 + 1.534 +public void visitCode(){ 1.535 +} 1.536 + 1.537 +public void visitFrame( 1.538 + final int type, 1.539 + final int nLocal, 1.540 + final Object[] local, 1.541 + final int nStack, 1.542 + final Object[] stack){ 1.543 + if(compute == FRAMES) 1.544 + { 1.545 + return; 1.546 + } 1.547 + 1.548 + if(type == Opcodes.F_NEW) 1.549 + { 1.550 + startFrame(code.length, nLocal, nStack); 1.551 + for(int i = 0; i < nLocal; ++i) 1.552 + { 1.553 + if(local[i] instanceof String) 1.554 + { 1.555 + frame[frameIndex++] = Frame.OBJECT 1.556 + | cw.addType((String) local[i]); 1.557 + } 1.558 + else if(local[i] instanceof Integer) 1.559 + { 1.560 + frame[frameIndex++] = ((Integer) local[i]).intValue(); 1.561 + } 1.562 + else 1.563 + { 1.564 + frame[frameIndex++] = Frame.UNINITIALIZED 1.565 + | cw.addUninitializedType("", 1.566 + ((Label) local[i]).position); 1.567 + } 1.568 + } 1.569 + for(int i = 0; i < nStack; ++i) 1.570 + { 1.571 + if(stack[i] instanceof String) 1.572 + { 1.573 + frame[frameIndex++] = Frame.OBJECT 1.574 + | cw.addType((String) stack[i]); 1.575 + } 1.576 + else if(stack[i] instanceof Integer) 1.577 + { 1.578 + frame[frameIndex++] = ((Integer) stack[i]).intValue(); 1.579 + } 1.580 + else 1.581 + { 1.582 + frame[frameIndex++] = Frame.UNINITIALIZED 1.583 + | cw.addUninitializedType("", 1.584 + ((Label) stack[i]).position); 1.585 + } 1.586 + } 1.587 + endFrame(); 1.588 + } 1.589 + else 1.590 + { 1.591 + int delta; 1.592 + if(stackMap == null) 1.593 + { 1.594 + stackMap = new ByteVector(); 1.595 + delta = code.length; 1.596 + } 1.597 + else 1.598 + { 1.599 + delta = code.length - previousFrameOffset - 1; 1.600 + } 1.601 + 1.602 + switch(type) 1.603 + { 1.604 + case Opcodes.F_FULL: 1.605 + stackMap.putByte(FULL_FRAME) 1.606 + .putShort(delta) 1.607 + .putShort(nLocal); 1.608 + for(int i = 0; i < nLocal; ++i) 1.609 + { 1.610 + writeFrameType(local[i]); 1.611 + } 1.612 + stackMap.putShort(nStack); 1.613 + for(int i = 0; i < nStack; ++i) 1.614 + { 1.615 + writeFrameType(stack[i]); 1.616 + } 1.617 + break; 1.618 + case Opcodes.F_APPEND: 1.619 + stackMap.putByte(SAME_FRAME_EXTENDED + nLocal) 1.620 + .putShort(delta); 1.621 + for(int i = 0; i < nLocal; ++i) 1.622 + { 1.623 + writeFrameType(local[i]); 1.624 + } 1.625 + break; 1.626 + case Opcodes.F_CHOP: 1.627 + stackMap.putByte(SAME_FRAME_EXTENDED - nLocal) 1.628 + .putShort(delta); 1.629 + break; 1.630 + case Opcodes.F_SAME: 1.631 + if(delta < 64) 1.632 + { 1.633 + stackMap.putByte(delta); 1.634 + } 1.635 + else 1.636 + { 1.637 + stackMap.putByte(SAME_FRAME_EXTENDED).putShort(delta); 1.638 + } 1.639 + break; 1.640 + case Opcodes.F_SAME1: 1.641 + if(delta < 64) 1.642 + { 1.643 + stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME + delta); 1.644 + } 1.645 + else 1.646 + { 1.647 + stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) 1.648 + .putShort(delta); 1.649 + } 1.650 + writeFrameType(stack[0]); 1.651 + break; 1.652 + } 1.653 + 1.654 + previousFrameOffset = code.length; 1.655 + ++frameCount; 1.656 + } 1.657 +} 1.658 + 1.659 +public void visitInsn(final int opcode){ 1.660 + // adds the instruction to the bytecode of the method 1.661 + code.putByte(opcode); 1.662 + // update currentBlock 1.663 + // Label currentBlock = this.currentBlock; 1.664 + if(currentBlock != null) 1.665 + { 1.666 + if(compute == FRAMES) 1.667 + { 1.668 + currentBlock.frame.execute(opcode, 0, null, null); 1.669 + } 1.670 + else 1.671 + { 1.672 + // updates current and max stack sizes 1.673 + int size = stackSize + Frame.SIZE[opcode]; 1.674 + if(size > maxStackSize) 1.675 + { 1.676 + maxStackSize = size; 1.677 + } 1.678 + stackSize = size; 1.679 + } 1.680 + // if opcode == ATHROW or xRETURN, ends current block (no successor) 1.681 + if((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) 1.682 + || opcode == Opcodes.ATHROW) 1.683 + { 1.684 + noSuccessor(); 1.685 + } 1.686 + } 1.687 +} 1.688 + 1.689 +public void visitIntInsn(final int opcode, final int operand){ 1.690 + // Label currentBlock = this.currentBlock; 1.691 + if(currentBlock != null) 1.692 + { 1.693 + if(compute == FRAMES) 1.694 + { 1.695 + currentBlock.frame.execute(opcode, operand, null, null); 1.696 + } 1.697 + else if(opcode != Opcodes.NEWARRAY) 1.698 + { 1.699 + // updates current and max stack sizes only for NEWARRAY 1.700 + // (stack size variation = 0 for BIPUSH or SIPUSH) 1.701 + int size = stackSize + 1; 1.702 + if(size > maxStackSize) 1.703 + { 1.704 + maxStackSize = size; 1.705 + } 1.706 + stackSize = size; 1.707 + } 1.708 + } 1.709 + // adds the instruction to the bytecode of the method 1.710 + if(opcode == Opcodes.SIPUSH) 1.711 + { 1.712 + code.put12(opcode, operand); 1.713 + } 1.714 + else 1.715 + { // BIPUSH or NEWARRAY 1.716 + code.put11(opcode, operand); 1.717 + } 1.718 +} 1.719 + 1.720 +public void visitVarInsn(final int opcode, final int var){ 1.721 + // Label currentBlock = this.currentBlock; 1.722 + if(currentBlock != null) 1.723 + { 1.724 + if(compute == FRAMES) 1.725 + { 1.726 + currentBlock.frame.execute(opcode, var, null, null); 1.727 + } 1.728 + else 1.729 + { 1.730 + // updates current and max stack sizes 1.731 + if(opcode == Opcodes.RET) 1.732 + { 1.733 + // no stack change, but end of current block (no successor) 1.734 + currentBlock.status |= Label.RET; 1.735 + // save 'stackSize' here for future use 1.736 + // (see {@link #findSubroutineSuccessors}) 1.737 + currentBlock.inputStackTop = stackSize; 1.738 + noSuccessor(); 1.739 + } 1.740 + else 1.741 + { // xLOAD or xSTORE 1.742 + int size = stackSize + Frame.SIZE[opcode]; 1.743 + if(size > maxStackSize) 1.744 + { 1.745 + maxStackSize = size; 1.746 + } 1.747 + stackSize = size; 1.748 + } 1.749 + } 1.750 + } 1.751 + if(compute != NOTHING) 1.752 + { 1.753 + // updates max locals 1.754 + int n; 1.755 + if(opcode == Opcodes.LLOAD || opcode == Opcodes.DLOAD 1.756 + || opcode == Opcodes.LSTORE || opcode == Opcodes.DSTORE) 1.757 + { 1.758 + n = var + 2; 1.759 + } 1.760 + else 1.761 + { 1.762 + n = var + 1; 1.763 + } 1.764 + if(n > maxLocals) 1.765 + { 1.766 + maxLocals = n; 1.767 + } 1.768 + } 1.769 + // adds the instruction to the bytecode of the method 1.770 + if(var < 4 && opcode != Opcodes.RET) 1.771 + { 1.772 + int opt; 1.773 + if(opcode < Opcodes.ISTORE) 1.774 + { 1.775 + /* ILOAD_0 */ 1.776 + opt = 26 + ((opcode - Opcodes.ILOAD) << 2) + var; 1.777 + } 1.778 + else 1.779 + { 1.780 + /* ISTORE_0 */ 1.781 + opt = 59 + ((opcode - Opcodes.ISTORE) << 2) + var; 1.782 + } 1.783 + code.putByte(opt); 1.784 + } 1.785 + else if(var >= 256) 1.786 + { 1.787 + code.putByte(196 /* WIDE */).put12(opcode, var); 1.788 + } 1.789 + else 1.790 + { 1.791 + code.put11(opcode, var); 1.792 + } 1.793 + if(opcode >= Opcodes.ISTORE && compute == FRAMES && handlerCount > 0) 1.794 + { 1.795 + visitLabel(new Label()); 1.796 + } 1.797 +} 1.798 + 1.799 +public void visitTypeInsn(final int opcode, final String desc){ 1.800 + Item i = cw.newClassItem(desc); 1.801 + // Label currentBlock = this.currentBlock; 1.802 + if(currentBlock != null) 1.803 + { 1.804 + if(compute == FRAMES) 1.805 + { 1.806 + currentBlock.frame.execute(opcode, code.length, cw, i); 1.807 + } 1.808 + else if(opcode == Opcodes.NEW) 1.809 + { 1.810 + // updates current and max stack sizes only if opcode == NEW 1.811 + // (no stack change for ANEWARRAY, CHECKCAST, INSTANCEOF) 1.812 + int size = stackSize + 1; 1.813 + if(size > maxStackSize) 1.814 + { 1.815 + maxStackSize = size; 1.816 + } 1.817 + stackSize = size; 1.818 + } 1.819 + } 1.820 + // adds the instruction to the bytecode of the method 1.821 + code.put12(opcode, i.index); 1.822 +} 1.823 + 1.824 +public void visitFieldInsn( 1.825 + final int opcode, 1.826 + final String owner, 1.827 + final String name, 1.828 + final String desc){ 1.829 + Item i = cw.newFieldItem(owner, name, desc); 1.830 + // Label currentBlock = this.currentBlock; 1.831 + if(currentBlock != null) 1.832 + { 1.833 + if(compute == FRAMES) 1.834 + { 1.835 + currentBlock.frame.execute(opcode, 0, cw, i); 1.836 + } 1.837 + else 1.838 + { 1.839 + int size; 1.840 + // computes the stack size variation 1.841 + char c = desc.charAt(0); 1.842 + switch(opcode) 1.843 + { 1.844 + case Opcodes.GETSTATIC: 1.845 + size = stackSize + (c == 'D' || c == 'J' ? 2 : 1); 1.846 + break; 1.847 + case Opcodes.PUTSTATIC: 1.848 + size = stackSize + (c == 'D' || c == 'J' ? -2 : -1); 1.849 + break; 1.850 + case Opcodes.GETFIELD: 1.851 + size = stackSize + (c == 'D' || c == 'J' ? 1 : 0); 1.852 + break; 1.853 + // case Constants.PUTFIELD: 1.854 + default: 1.855 + size = stackSize + (c == 'D' || c == 'J' ? -3 : -2); 1.856 + break; 1.857 + } 1.858 + // updates current and max stack sizes 1.859 + if(size > maxStackSize) 1.860 + { 1.861 + maxStackSize = size; 1.862 + } 1.863 + stackSize = size; 1.864 + } 1.865 + } 1.866 + // adds the instruction to the bytecode of the method 1.867 + code.put12(opcode, i.index); 1.868 +} 1.869 + 1.870 +public void visitMethodInsn( 1.871 + final int opcode, 1.872 + final String owner, 1.873 + final String name, 1.874 + final String desc){ 1.875 + boolean itf = opcode == Opcodes.INVOKEINTERFACE; 1.876 + Item i = cw.newMethodItem(owner, name, desc, itf); 1.877 + int argSize = i.intVal; 1.878 + // Label currentBlock = this.currentBlock; 1.879 + if(currentBlock != null) 1.880 + { 1.881 + if(compute == FRAMES) 1.882 + { 1.883 + currentBlock.frame.execute(opcode, 0, cw, i); 1.884 + } 1.885 + else 1.886 + { 1.887 + /* 1.888 + * computes the stack size variation. In order not to recompute 1.889 + * several times this variation for the same Item, we use the 1.890 + * intVal field of this item to store this variation, once it 1.891 + * has been computed. More precisely this intVal field stores 1.892 + * the sizes of the arguments and of the return value 1.893 + * corresponding to desc. 1.894 + */ 1.895 + if(argSize == 0) 1.896 + { 1.897 + // the above sizes have not been computed yet, 1.898 + // so we compute them... 1.899 + argSize = getArgumentsAndReturnSizes(desc); 1.900 + // ... and we save them in order 1.901 + // not to recompute them in the future 1.902 + i.intVal = argSize; 1.903 + } 1.904 + int size; 1.905 + if(opcode == Opcodes.INVOKESTATIC) 1.906 + { 1.907 + size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1; 1.908 + } 1.909 + else 1.910 + { 1.911 + size = stackSize - (argSize >> 2) + (argSize & 0x03); 1.912 + } 1.913 + // updates current and max stack sizes 1.914 + if(size > maxStackSize) 1.915 + { 1.916 + maxStackSize = size; 1.917 + } 1.918 + stackSize = size; 1.919 + } 1.920 + } 1.921 + // adds the instruction to the bytecode of the method 1.922 + if(itf) 1.923 + { 1.924 + if(argSize == 0) 1.925 + { 1.926 + argSize = getArgumentsAndReturnSizes(desc); 1.927 + i.intVal = argSize; 1.928 + } 1.929 + code.put12(Opcodes.INVOKEINTERFACE, i.index).put11(argSize >> 2, 0); 1.930 + } 1.931 + else 1.932 + { 1.933 + code.put12(opcode, i.index); 1.934 + } 1.935 +} 1.936 + 1.937 +public void visitJumpInsn(final int opcode, final Label label){ 1.938 + Label nextInsn = null; 1.939 + // Label currentBlock = this.currentBlock; 1.940 + if(currentBlock != null) 1.941 + { 1.942 + if(compute == FRAMES) 1.943 + { 1.944 + currentBlock.frame.execute(opcode, 0, null, null); 1.945 + // 'label' is the target of a jump instruction 1.946 + label.getFirst().status |= Label.TARGET; 1.947 + // adds 'label' as a successor of this basic block 1.948 + addSuccessor(Edge.NORMAL, label); 1.949 + if(opcode != Opcodes.GOTO) 1.950 + { 1.951 + // creates a Label for the next basic block 1.952 + nextInsn = new Label(); 1.953 + } 1.954 + } 1.955 + else 1.956 + { 1.957 + if(opcode == Opcodes.JSR) 1.958 + { 1.959 + jsr = true; 1.960 + currentBlock.status |= Label.JSR; 1.961 + addSuccessor(stackSize + 1, label); 1.962 + // creates a Label for the next basic block 1.963 + nextInsn = new Label(); 1.964 + /* 1.965 + * note that, by construction in this method, a JSR block 1.966 + * has at least two successors in the control flow graph: 1.967 + * the first one leads the next instruction after the JSR, 1.968 + * while the second one leads to the JSR target. 1.969 + */ 1.970 + } 1.971 + else 1.972 + { 1.973 + // updates current stack size (max stack size unchanged 1.974 + // because stack size variation always negative in this 1.975 + // case) 1.976 + stackSize += Frame.SIZE[opcode]; 1.977 + addSuccessor(stackSize, label); 1.978 + } 1.979 + } 1.980 + } 1.981 + // adds the instruction to the bytecode of the method 1.982 + if((label.status & Label.RESOLVED) != 0 1.983 + && label.position - code.length < Short.MIN_VALUE) 1.984 + { 1.985 + /* 1.986 + * case of a backward jump with an offset < -32768. In this case we 1.987 + * automatically replace GOTO with GOTO_W, JSR with JSR_W and IFxxx 1.988 + * <l> with IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx is the 1.989 + * "opposite" opcode of IFxxx (i.e., IFNE for IFEQ) and where <l'> 1.990 + * designates the instruction just after the GOTO_W. 1.991 + */ 1.992 + if(opcode == Opcodes.GOTO) 1.993 + { 1.994 + code.putByte(200); // GOTO_W 1.995 + } 1.996 + else if(opcode == Opcodes.JSR) 1.997 + { 1.998 + code.putByte(201); // JSR_W 1.999 + } 1.1000 + else 1.1001 + { 1.1002 + // if the IF instruction is transformed into IFNOT GOTO_W the 1.1003 + // next instruction becomes the target of the IFNOT instruction 1.1004 + if(nextInsn != null) 1.1005 + { 1.1006 + nextInsn.status |= Label.TARGET; 1.1007 + } 1.1008 + code.putByte(opcode <= 166 1.1009 + ? ((opcode + 1) ^ 1) - 1 1.1010 + : opcode ^ 1); 1.1011 + code.putShort(8); // jump offset 1.1012 + code.putByte(200); // GOTO_W 1.1013 + } 1.1014 + label.put(this, code, code.length - 1, true); 1.1015 + } 1.1016 + else 1.1017 + { 1.1018 + /* 1.1019 + * case of a backward jump with an offset >= -32768, or of a forward 1.1020 + * jump with, of course, an unknown offset. In these cases we store 1.1021 + * the offset in 2 bytes (which will be increased in 1.1022 + * resizeInstructions, if needed). 1.1023 + */ 1.1024 + code.putByte(opcode); 1.1025 + label.put(this, code, code.length - 1, false); 1.1026 + } 1.1027 + if(currentBlock != null) 1.1028 + { 1.1029 + if(nextInsn != null) 1.1030 + { 1.1031 + // if the jump instruction is not a GOTO, the next instruction 1.1032 + // is also a successor of this instruction. Calling visitLabel 1.1033 + // adds the label of this next instruction as a successor of the 1.1034 + // current block, and starts a new basic block 1.1035 + visitLabel(nextInsn); 1.1036 + } 1.1037 + if(opcode == Opcodes.GOTO) 1.1038 + { 1.1039 + noSuccessor(); 1.1040 + } 1.1041 + } 1.1042 +} 1.1043 + 1.1044 +public void visitLabel(final Label label){ 1.1045 + // resolves previous forward references to label, if any 1.1046 + resize |= label.resolve(this, code.length, code.data); 1.1047 + // updates currentBlock 1.1048 + if((label.status & Label.DEBUG) != 0) 1.1049 + { 1.1050 + return; 1.1051 + } 1.1052 + if(compute == FRAMES) 1.1053 + { 1.1054 + if(currentBlock != null) 1.1055 + { 1.1056 + if(label.position == currentBlock.position) 1.1057 + { 1.1058 + // successive labels, do not start a new basic block 1.1059 + currentBlock.status |= (label.status & Label.TARGET); 1.1060 + label.frame = currentBlock.frame; 1.1061 + return; 1.1062 + } 1.1063 + // ends current block (with one new successor) 1.1064 + addSuccessor(Edge.NORMAL, label); 1.1065 + } 1.1066 + // begins a new current block 1.1067 + currentBlock = label; 1.1068 + if(label.frame == null) 1.1069 + { 1.1070 + label.frame = new Frame(); 1.1071 + label.frame.owner = label; 1.1072 + } 1.1073 + // updates the basic block list 1.1074 + if(previousBlock != null) 1.1075 + { 1.1076 + if(label.position == previousBlock.position) 1.1077 + { 1.1078 + previousBlock.status |= (label.status & Label.TARGET); 1.1079 + label.frame = previousBlock.frame; 1.1080 + currentBlock = previousBlock; 1.1081 + return; 1.1082 + } 1.1083 + previousBlock.successor = label; 1.1084 + } 1.1085 + previousBlock = label; 1.1086 + } 1.1087 + else if(compute == MAXS) 1.1088 + { 1.1089 + if(currentBlock != null) 1.1090 + { 1.1091 + // ends current block (with one new successor) 1.1092 + currentBlock.outputStackMax = maxStackSize; 1.1093 + addSuccessor(stackSize, label); 1.1094 + } 1.1095 + // begins a new current block 1.1096 + currentBlock = label; 1.1097 + // resets the relative current and max stack sizes 1.1098 + stackSize = 0; 1.1099 + maxStackSize = 0; 1.1100 + // updates the basic block list 1.1101 + if(previousBlock != null) 1.1102 + { 1.1103 + previousBlock.successor = label; 1.1104 + } 1.1105 + previousBlock = label; 1.1106 + } 1.1107 +} 1.1108 + 1.1109 +public void visitLdcInsn(final Object cst){ 1.1110 + Item i = cw.newConstItem(cst); 1.1111 + // Label currentBlock = this.currentBlock; 1.1112 + if(currentBlock != null) 1.1113 + { 1.1114 + if(compute == FRAMES) 1.1115 + { 1.1116 + currentBlock.frame.execute(Opcodes.LDC, 0, cw, i); 1.1117 + } 1.1118 + else 1.1119 + { 1.1120 + int size; 1.1121 + // computes the stack size variation 1.1122 + if(i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE) 1.1123 + { 1.1124 + size = stackSize + 2; 1.1125 + } 1.1126 + else 1.1127 + { 1.1128 + size = stackSize + 1; 1.1129 + } 1.1130 + // updates current and max stack sizes 1.1131 + if(size > maxStackSize) 1.1132 + { 1.1133 + maxStackSize = size; 1.1134 + } 1.1135 + stackSize = size; 1.1136 + } 1.1137 + } 1.1138 + // adds the instruction to the bytecode of the method 1.1139 + int index = i.index; 1.1140 + if(i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE) 1.1141 + { 1.1142 + code.put12(20 /* LDC2_W */, index); 1.1143 + } 1.1144 + else if(index >= 256) 1.1145 + { 1.1146 + code.put12(19 /* LDC_W */, index); 1.1147 + } 1.1148 + else 1.1149 + { 1.1150 + code.put11(Opcodes.LDC, index); 1.1151 + } 1.1152 +} 1.1153 + 1.1154 +public void visitIincInsn(final int var, final int increment){ 1.1155 + if(currentBlock != null) 1.1156 + { 1.1157 + if(compute == FRAMES) 1.1158 + { 1.1159 + currentBlock.frame.execute(Opcodes.IINC, var, null, null); 1.1160 + } 1.1161 + } 1.1162 + if(compute != NOTHING) 1.1163 + { 1.1164 + // updates max locals 1.1165 + int n = var + 1; 1.1166 + if(n > maxLocals) 1.1167 + { 1.1168 + maxLocals = n; 1.1169 + } 1.1170 + } 1.1171 + // adds the instruction to the bytecode of the method 1.1172 + if((var > 255) || (increment > 127) || (increment < -128)) 1.1173 + { 1.1174 + code.putByte(196 /* WIDE */) 1.1175 + .put12(Opcodes.IINC, var) 1.1176 + .putShort(increment); 1.1177 + } 1.1178 + else 1.1179 + { 1.1180 + code.putByte(Opcodes.IINC).put11(var, increment); 1.1181 + } 1.1182 +} 1.1183 + 1.1184 +public void visitTableSwitchInsn( 1.1185 + final int min, 1.1186 + final int max, 1.1187 + final Label dflt, 1.1188 + final Label labels[]){ 1.1189 + // adds the instruction to the bytecode of the method 1.1190 + int source = code.length; 1.1191 + code.putByte(Opcodes.TABLESWITCH); 1.1192 + code.length += (4 - code.length % 4) % 4; 1.1193 + dflt.put(this, code, source, true); 1.1194 + code.putInt(min).putInt(max); 1.1195 + for(int i = 0; i < labels.length; ++i) 1.1196 + { 1.1197 + labels[i].put(this, code, source, true); 1.1198 + } 1.1199 + // updates currentBlock 1.1200 + visitSwitchInsn(dflt, labels); 1.1201 +} 1.1202 + 1.1203 +public void visitLookupSwitchInsn( 1.1204 + final Label dflt, 1.1205 + final int keys[], 1.1206 + final Label labels[]){ 1.1207 + // adds the instruction to the bytecode of the method 1.1208 + int source = code.length; 1.1209 + code.putByte(Opcodes.LOOKUPSWITCH); 1.1210 + code.length += (4 - code.length % 4) % 4; 1.1211 + dflt.put(this, code, source, true); 1.1212 + code.putInt(labels.length); 1.1213 + for(int i = 0; i < labels.length; ++i) 1.1214 + { 1.1215 + code.putInt(keys[i]); 1.1216 + labels[i].put(this, code, source, true); 1.1217 + } 1.1218 + // updates currentBlock 1.1219 + visitSwitchInsn(dflt, labels); 1.1220 +} 1.1221 + 1.1222 +private void visitSwitchInsn(final Label dflt, final Label[] labels){ 1.1223 + // Label currentBlock = this.currentBlock; 1.1224 + if(currentBlock != null) 1.1225 + { 1.1226 + if(compute == FRAMES) 1.1227 + { 1.1228 + currentBlock.frame.execute(Opcodes.LOOKUPSWITCH, 0, null, null); 1.1229 + // adds current block successors 1.1230 + addSuccessor(Edge.NORMAL, dflt); 1.1231 + dflt.getFirst().status |= Label.TARGET; 1.1232 + for(int i = 0; i < labels.length; ++i) 1.1233 + { 1.1234 + addSuccessor(Edge.NORMAL, labels[i]); 1.1235 + labels[i].getFirst().status |= Label.TARGET; 1.1236 + } 1.1237 + } 1.1238 + else 1.1239 + { 1.1240 + // updates current stack size (max stack size unchanged) 1.1241 + --stackSize; 1.1242 + // adds current block successors 1.1243 + addSuccessor(stackSize, dflt); 1.1244 + for(int i = 0; i < labels.length; ++i) 1.1245 + { 1.1246 + addSuccessor(stackSize, labels[i]); 1.1247 + } 1.1248 + } 1.1249 + // ends current block 1.1250 + noSuccessor(); 1.1251 + } 1.1252 +} 1.1253 + 1.1254 +public void visitMultiANewArrayInsn(final String desc, final int dims){ 1.1255 + Item i = cw.newClassItem(desc); 1.1256 + // Label currentBlock = this.currentBlock; 1.1257 + if(currentBlock != null) 1.1258 + { 1.1259 + if(compute == FRAMES) 1.1260 + { 1.1261 + currentBlock.frame.execute(Opcodes.MULTIANEWARRAY, dims, cw, i); 1.1262 + } 1.1263 + else 1.1264 + { 1.1265 + // updates current stack size (max stack size unchanged because 1.1266 + // stack size variation always negative or null) 1.1267 + stackSize += 1 - dims; 1.1268 + } 1.1269 + } 1.1270 + // adds the instruction to the bytecode of the method 1.1271 + code.put12(Opcodes.MULTIANEWARRAY, i.index).putByte(dims); 1.1272 +} 1.1273 + 1.1274 +public void visitTryCatchBlock( 1.1275 + final Label start, 1.1276 + final Label end, 1.1277 + final Label handler, 1.1278 + final String type){ 1.1279 + ++handlerCount; 1.1280 + Handler h = new Handler(); 1.1281 + h.start = start; 1.1282 + h.end = end; 1.1283 + h.handler = handler; 1.1284 + h.desc = type; 1.1285 + h.type = type != null ? cw.newClass(type) : 0; 1.1286 + if(lastHandler == null) 1.1287 + { 1.1288 + firstHandler = h; 1.1289 + } 1.1290 + else 1.1291 + { 1.1292 + lastHandler.next = h; 1.1293 + } 1.1294 + lastHandler = h; 1.1295 +} 1.1296 + 1.1297 +public void visitLocalVariable( 1.1298 + final String name, 1.1299 + final String desc, 1.1300 + final String signature, 1.1301 + final Label start, 1.1302 + final Label end, 1.1303 + final int index){ 1.1304 + if(signature != null) 1.1305 + { 1.1306 + if(localVarType == null) 1.1307 + { 1.1308 + localVarType = new ByteVector(); 1.1309 + } 1.1310 + ++localVarTypeCount; 1.1311 + localVarType.putShort(start.position) 1.1312 + .putShort(end.position - start.position) 1.1313 + .putShort(cw.newUTF8(name)) 1.1314 + .putShort(cw.newUTF8(signature)) 1.1315 + .putShort(index); 1.1316 + } 1.1317 + if(localVar == null) 1.1318 + { 1.1319 + localVar = new ByteVector(); 1.1320 + } 1.1321 + ++localVarCount; 1.1322 + localVar.putShort(start.position) 1.1323 + .putShort(end.position - start.position) 1.1324 + .putShort(cw.newUTF8(name)) 1.1325 + .putShort(cw.newUTF8(desc)) 1.1326 + .putShort(index); 1.1327 + if(compute != NOTHING) 1.1328 + { 1.1329 + // updates max locals 1.1330 + char c = desc.charAt(0); 1.1331 + int n = index + (c == 'J' || c == 'D' ? 2 : 1); 1.1332 + if(n > maxLocals) 1.1333 + { 1.1334 + maxLocals = n; 1.1335 + } 1.1336 + } 1.1337 +} 1.1338 + 1.1339 +public void visitLineNumber(final int line, final Label start){ 1.1340 + if(lineNumber == null) 1.1341 + { 1.1342 + lineNumber = new ByteVector(); 1.1343 + } 1.1344 + ++lineNumberCount; 1.1345 + lineNumber.putShort(start.position); 1.1346 + lineNumber.putShort(line); 1.1347 +} 1.1348 + 1.1349 +public void visitMaxs(final int maxStack, final int maxLocals){ 1.1350 + if(compute == FRAMES) 1.1351 + { 1.1352 + // completes the control flow graph with exception handler blocks 1.1353 + Handler handler = firstHandler; 1.1354 + while(handler != null) 1.1355 + { 1.1356 + Label l = handler.start.getFirst(); 1.1357 + Label h = handler.handler.getFirst(); 1.1358 + Label e = handler.end.getFirst(); 1.1359 + // computes the kind of the edges to 'h' 1.1360 + String t = handler.desc == null 1.1361 + ? "java/lang/Throwable" 1.1362 + : handler.desc; 1.1363 + int kind = Frame.OBJECT | cw.addType(t); 1.1364 + // h is an exception handler 1.1365 + h.status |= Label.TARGET; 1.1366 + // adds 'h' as a successor of labels between 'start' and 'end' 1.1367 + while(l != e) 1.1368 + { 1.1369 + // creates an edge to 'h' 1.1370 + Edge b = new Edge(); 1.1371 + b.info = kind; 1.1372 + b.successor = h; 1.1373 + // adds it to the successors of 'l' 1.1374 + b.next = l.successors; 1.1375 + l.successors = b; 1.1376 + // goes to the next label 1.1377 + l = l.successor; 1.1378 + } 1.1379 + handler = handler.next; 1.1380 + } 1.1381 + 1.1382 + // creates and visits the first (implicit) frame 1.1383 + Frame f = labels.frame; 1.1384 + Type[] args = Type.getArgumentTypes(descriptor); 1.1385 + f.initInputFrame(cw, access, args, this.maxLocals); 1.1386 + visitFrame(f); 1.1387 + 1.1388 + /* 1.1389 + * fix point algorithm: mark the first basic block as 'changed' 1.1390 + * (i.e. put it in the 'changed' list) and, while there are changed 1.1391 + * basic blocks, choose one, mark it as unchanged, and update its 1.1392 + * successors (which can be changed in the process). 1.1393 + */ 1.1394 + int max = 0; 1.1395 + Label changed = labels; 1.1396 + while(changed != null) 1.1397 + { 1.1398 + // removes a basic block from the list of changed basic blocks 1.1399 + Label l = changed; 1.1400 + changed = changed.next; 1.1401 + l.next = null; 1.1402 + f = l.frame; 1.1403 + // a reacheable jump target must be stored in the stack map 1.1404 + if((l.status & Label.TARGET) != 0) 1.1405 + { 1.1406 + l.status |= Label.STORE; 1.1407 + } 1.1408 + // all visited labels are reacheable, by definition 1.1409 + l.status |= Label.REACHABLE; 1.1410 + // updates the (absolute) maximum stack size 1.1411 + int blockMax = f.inputStack.length + l.outputStackMax; 1.1412 + if(blockMax > max) 1.1413 + { 1.1414 + max = blockMax; 1.1415 + } 1.1416 + // updates the successors of the current basic block 1.1417 + Edge e = l.successors; 1.1418 + while(e != null) 1.1419 + { 1.1420 + Label n = e.successor.getFirst(); 1.1421 + boolean change = f.merge(cw, n.frame, e.info); 1.1422 + if(change && n.next == null) 1.1423 + { 1.1424 + // if n has changed and is not already in the 'changed' 1.1425 + // list, adds it to this list 1.1426 + n.next = changed; 1.1427 + changed = n; 1.1428 + } 1.1429 + e = e.next; 1.1430 + } 1.1431 + } 1.1432 + this.maxStack = max; 1.1433 + 1.1434 + // visits all the frames that must be stored in the stack map 1.1435 + Label l = labels; 1.1436 + while(l != null) 1.1437 + { 1.1438 + f = l.frame; 1.1439 + if((l.status & Label.STORE) != 0) 1.1440 + { 1.1441 + visitFrame(f); 1.1442 + } 1.1443 + if((l.status & Label.REACHABLE) == 0) 1.1444 + { 1.1445 + // finds start and end of dead basic block 1.1446 + Label k = l.successor; 1.1447 + int start = l.position; 1.1448 + int end = (k == null ? code.length : k.position) - 1; 1.1449 + // if non empty basic block 1.1450 + if(end >= start) 1.1451 + { 1.1452 + // replaces instructions with NOP ... NOP ATHROW 1.1453 + for(int i = start; i < end; ++i) 1.1454 + { 1.1455 + code.data[i] = Opcodes.NOP; 1.1456 + } 1.1457 + code.data[end] = (byte) Opcodes.ATHROW; 1.1458 + // emits a frame for this unreachable block 1.1459 + startFrame(start, 0, 1); 1.1460 + frame[frameIndex++] = Frame.OBJECT 1.1461 + | cw.addType("java/lang/Throwable"); 1.1462 + endFrame(); 1.1463 + } 1.1464 + } 1.1465 + l = l.successor; 1.1466 + } 1.1467 + } 1.1468 + else if(compute == MAXS) 1.1469 + { 1.1470 + // completes the control flow graph with exception handler blocks 1.1471 + Handler handler = firstHandler; 1.1472 + while(handler != null) 1.1473 + { 1.1474 + Label l = handler.start; 1.1475 + Label h = handler.handler; 1.1476 + Label e = handler.end; 1.1477 + // adds 'h' as a successor of labels between 'start' and 'end' 1.1478 + while(l != e) 1.1479 + { 1.1480 + // creates an edge to 'h' 1.1481 + Edge b = new Edge(); 1.1482 + b.info = Edge.EXCEPTION; 1.1483 + b.successor = h; 1.1484 + // adds it to the successors of 'l' 1.1485 + if((l.status & Label.JSR) != 0) 1.1486 + { 1.1487 + // if l is a JSR block, adds b after the first two edges 1.1488 + // to preserve the hypothesis about JSR block successors 1.1489 + // order (see {@link #visitJumpInsn}) 1.1490 + b.next = l.successors.next.next; 1.1491 + l.successors.next.next = b; 1.1492 + } 1.1493 + else 1.1494 + { 1.1495 + b.next = l.successors; 1.1496 + l.successors = b; 1.1497 + } 1.1498 + // goes to the next label 1.1499 + l = l.successor; 1.1500 + } 1.1501 + handler = handler.next; 1.1502 + } 1.1503 + 1.1504 + if(jsr) 1.1505 + { 1.1506 + // completes the control flow graph with the RET successors 1.1507 + /* 1.1508 + * first step: finds the subroutines. This step determines, for 1.1509 + * each basic block, to which subroutine(s) it belongs, and 1.1510 + * stores this set as a bit set in the {@link Label#status} 1.1511 + * field. Subroutines are numbered with powers of two, from 1.1512 + * 0x1000 to 0x80000000 (so there must be at most 20 subroutines 1.1513 + * in a method). 1.1514 + */ 1.1515 + // finds the basic blocks that belong to the "main" subroutine 1.1516 + int id = 0x1000; 1.1517 + findSubroutine(labels, id); 1.1518 + // finds the basic blocks that belong to the real subroutines 1.1519 + Label l = labels; 1.1520 + while(l != null) 1.1521 + { 1.1522 + if((l.status & Label.JSR) != 0) 1.1523 + { 1.1524 + // the subroutine is defined by l's TARGET, not by l 1.1525 + Label subroutine = l.successors.next.successor; 1.1526 + // if this subroutine does not have an id yet... 1.1527 + if((subroutine.status & ~0xFFF) == 0) 1.1528 + { 1.1529 + // ...assigns it a new id and finds its basic blocks 1.1530 + id = id << 1; 1.1531 + findSubroutine(subroutine, id); 1.1532 + } 1.1533 + } 1.1534 + l = l.successor; 1.1535 + } 1.1536 + // second step: finds the successors of RET blocks 1.1537 + findSubroutineSuccessors(0x1000, new Label[10], 0); 1.1538 + } 1.1539 + 1.1540 + /* 1.1541 + * control flow analysis algorithm: while the block stack is not 1.1542 + * empty, pop a block from this stack, update the max stack size, 1.1543 + * compute the true (non relative) begin stack size of the 1.1544 + * successors of this block, and push these successors onto the 1.1545 + * stack (unless they have already been pushed onto the stack). 1.1546 + * Note: by hypothesis, the {@link Label#inputStackTop} of the 1.1547 + * blocks in the block stack are the true (non relative) beginning 1.1548 + * stack sizes of these blocks. 1.1549 + */ 1.1550 + int max = 0; 1.1551 + Label stack = labels; 1.1552 + while(stack != null) 1.1553 + { 1.1554 + // pops a block from the stack 1.1555 + Label l = stack; 1.1556 + stack = stack.next; 1.1557 + // computes the true (non relative) max stack size of this block 1.1558 + int start = l.inputStackTop; 1.1559 + int blockMax = start + l.outputStackMax; 1.1560 + // updates the global max stack size 1.1561 + if(blockMax > max) 1.1562 + { 1.1563 + max = blockMax; 1.1564 + } 1.1565 + // analyses the successors of the block 1.1566 + Edge b = l.successors; 1.1567 + if((l.status & Label.JSR) != 0) 1.1568 + { 1.1569 + // ignores the first edge of JSR blocks (virtual successor) 1.1570 + b = b.next; 1.1571 + } 1.1572 + while(b != null) 1.1573 + { 1.1574 + l = b.successor; 1.1575 + // if this successor has not already been pushed... 1.1576 + if((l.status & Label.PUSHED) == 0) 1.1577 + { 1.1578 + // computes its true beginning stack size... 1.1579 + l.inputStackTop = b.info == Edge.EXCEPTION ? 1 : start 1.1580 + + b.info; 1.1581 + // ...and pushes it onto the stack 1.1582 + l.status |= Label.PUSHED; 1.1583 + l.next = stack; 1.1584 + stack = l; 1.1585 + } 1.1586 + b = b.next; 1.1587 + } 1.1588 + } 1.1589 + this.maxStack = max; 1.1590 + } 1.1591 + else 1.1592 + { 1.1593 + this.maxStack = maxStack; 1.1594 + this.maxLocals = maxLocals; 1.1595 + } 1.1596 +} 1.1597 + 1.1598 +public void visitEnd(){ 1.1599 +} 1.1600 + 1.1601 +// ------------------------------------------------------------------------ 1.1602 +// Utility methods: control flow analysis algorithm 1.1603 +// ------------------------------------------------------------------------ 1.1604 + 1.1605 +/** 1.1606 + * Computes the size of the arguments and of the return value of a method. 1.1607 + * 1.1608 + * @param desc the descriptor of a method. 1.1609 + * @return the size of the arguments of the method (plus one for the 1.1610 + * implicit this argument), argSize, and the size of its return 1.1611 + * value, retSize, packed into a single int i = 1.1612 + * <tt>(argSize << 2) | retSize</tt> (argSize is therefore equal 1.1613 + * to <tt>i >> 2</tt>, and retSize to <tt>i & 0x03</tt>). 1.1614 + */ 1.1615 +static int getArgumentsAndReturnSizes(final String desc){ 1.1616 + int n = 1; 1.1617 + int c = 1; 1.1618 + while(true) 1.1619 + { 1.1620 + char car = desc.charAt(c++); 1.1621 + if(car == ')') 1.1622 + { 1.1623 + car = desc.charAt(c); 1.1624 + return n << 2 1.1625 + | (car == 'V' ? 0 : (car == 'D' || car == 'J' ? 2 : 1)); 1.1626 + } 1.1627 + else if(car == 'L') 1.1628 + { 1.1629 + while(desc.charAt(c++) != ';') 1.1630 + { 1.1631 + } 1.1632 + n += 1; 1.1633 + } 1.1634 + else if(car == '[') 1.1635 + { 1.1636 + while((car = desc.charAt(c)) == '[') 1.1637 + { 1.1638 + ++c; 1.1639 + } 1.1640 + if(car == 'D' || car == 'J') 1.1641 + { 1.1642 + n -= 1; 1.1643 + } 1.1644 + } 1.1645 + else if(car == 'D' || car == 'J') 1.1646 + { 1.1647 + n += 2; 1.1648 + } 1.1649 + else 1.1650 + { 1.1651 + n += 1; 1.1652 + } 1.1653 + } 1.1654 +} 1.1655 + 1.1656 +/** 1.1657 + * Adds a successor to the {@link #currentBlock currentBlock} block. 1.1658 + * 1.1659 + * @param info information about the control flow edge to be added. 1.1660 + * @param successor the successor block to be added to the current block. 1.1661 + */ 1.1662 +private void addSuccessor(final int info, final Label successor){ 1.1663 + // creates and initializes an Edge object... 1.1664 + Edge b = new Edge(); 1.1665 + b.info = info; 1.1666 + b.successor = successor; 1.1667 + // ...and adds it to the successor list of the currentBlock block 1.1668 + b.next = currentBlock.successors; 1.1669 + currentBlock.successors = b; 1.1670 +} 1.1671 + 1.1672 +/** 1.1673 + * Ends the current basic block. This method must be used in the case where 1.1674 + * the current basic block does not have any successor. 1.1675 + */ 1.1676 +private void noSuccessor(){ 1.1677 + if(compute == FRAMES) 1.1678 + { 1.1679 + Label l = new Label(); 1.1680 + l.frame = new Frame(); 1.1681 + l.frame.owner = l; 1.1682 + l.resolve(this, code.length, code.data); 1.1683 + previousBlock.successor = l; 1.1684 + previousBlock = l; 1.1685 + } 1.1686 + else 1.1687 + { 1.1688 + currentBlock.outputStackMax = maxStackSize; 1.1689 + } 1.1690 + currentBlock = null; 1.1691 +} 1.1692 + 1.1693 +/** 1.1694 + * Finds the basic blocks that belong to a given subroutine, and marks these 1.1695 + * blocks as belonging to this subroutine (by using {@link Label#status} as 1.1696 + * a bit set (see {@link #visitMaxs}). This recursive method follows the 1.1697 + * control flow graph to find all the blocks that are reachable from the 1.1698 + * given block WITHOUT following any JSR target. 1.1699 + * 1.1700 + * @param block a block that belongs to the subroutine 1.1701 + * @param id the id of this subroutine 1.1702 + */ 1.1703 +private void findSubroutine(final Label block, final int id){ 1.1704 + // if 'block' is already marked as belonging to subroutine 'id', returns 1.1705 + if((block.status & id) != 0) 1.1706 + { 1.1707 + return; 1.1708 + } 1.1709 + // marks 'block' as belonging to subroutine 'id' 1.1710 + block.status |= id; 1.1711 + // calls this method recursively on each successor, except JSR targets 1.1712 + Edge e = block.successors; 1.1713 + while(e != null) 1.1714 + { 1.1715 + // if 'block' is a JSR block, then 'block.successors.next' leads 1.1716 + // to the JSR target (see {@link #visitJumpInsn}) and must therefore 1.1717 + // not be followed 1.1718 + if((block.status & Label.JSR) == 0 || e != block.successors.next) 1.1719 + { 1.1720 + findSubroutine(e.successor, id); 1.1721 + } 1.1722 + e = e.next; 1.1723 + } 1.1724 +} 1.1725 + 1.1726 +/** 1.1727 + * Finds the successors of the RET blocks of the specified subroutine, and 1.1728 + * of any nested subroutine it calls. 1.1729 + * 1.1730 + * @param id id of the subroutine whose RET block successors must be found. 1.1731 + * @param JSRs the JSR blocks that were followed to reach this subroutine. 1.1732 + * @param nJSRs number of JSR blocks in the JSRs array. 1.1733 + */ 1.1734 +private void findSubroutineSuccessors( 1.1735 + final int id, 1.1736 + final Label[] JSRs, 1.1737 + final int nJSRs){ 1.1738 + // iterates over all the basic blocks... 1.1739 + Label l = labels; 1.1740 + while(l != null) 1.1741 + { 1.1742 + // for those that belong to subroutine 'id'... 1.1743 + if((l.status & id) != 0) 1.1744 + { 1.1745 + if((l.status & Label.JSR) != 0) 1.1746 + { 1.1747 + // finds the subroutine to which 'l' leads by following the 1.1748 + // second edge of l.successors (see {@link #visitJumpInsn}) 1.1749 + int nId = l.successors.next.successor.status & ~0xFFF; 1.1750 + if(nId != id) 1.1751 + { 1.1752 + // calls this method recursively with l pushed onto the 1.1753 + // JSRs stack to find the successors of the RET blocks 1.1754 + // of this nested subroutine 'nId' 1.1755 + JSRs[nJSRs] = l; 1.1756 + findSubroutineSuccessors(nId, JSRs, nJSRs + 1); 1.1757 + } 1.1758 + } 1.1759 + else if((l.status & Label.RET) != 0) 1.1760 + { 1.1761 + /* 1.1762 + * finds the JSR block in the JSRs stack that corresponds to 1.1763 + * this RET block, and updates the successors of this RET 1.1764 + * block accordingly. This corresponding JSR is the one that 1.1765 + * leads to the subroutine to which the RET block belongs. 1.1766 + * But the RET block can belong to several subroutines (if a 1.1767 + * nested subroutine returns to its parent subroutine 1.1768 + * implicitely, without a RET). So, in fact, the JSR that 1.1769 + * corresponds to this RET is the first block in the JSRs 1.1770 + * stack, starting from the bottom of the stack, that leads 1.1771 + * to a subroutine to which the RET block belongs. 1.1772 + */ 1.1773 + for(int i = 0; i < nJSRs; ++i) 1.1774 + { 1.1775 + int JSRstatus = JSRs[i].successors.next.successor.status; 1.1776 + if(((JSRstatus & ~0xFFF) & (l.status & ~0xFFF)) != 0) 1.1777 + { 1.1778 + Edge e = new Edge(); 1.1779 + e.info = l.inputStackTop; 1.1780 + e.successor = JSRs[i].successors.successor; 1.1781 + e.next = l.successors; 1.1782 + l.successors = e; 1.1783 + break; 1.1784 + } 1.1785 + } 1.1786 + } 1.1787 + } 1.1788 + l = l.successor; 1.1789 + } 1.1790 +} 1.1791 + 1.1792 +// ------------------------------------------------------------------------ 1.1793 +// Utility methods: stack map frames 1.1794 +// ------------------------------------------------------------------------ 1.1795 + 1.1796 +/** 1.1797 + * Visits a frame that has been computed from scratch. 1.1798 + * 1.1799 + * @param f the frame that must be visited. 1.1800 + */ 1.1801 +private void visitFrame(final Frame f){ 1.1802 + int i, t; 1.1803 + int nTop = 0; 1.1804 + int nLocal = 0; 1.1805 + int nStack = 0; 1.1806 + int[] locals = f.inputLocals; 1.1807 + int[] stacks = f.inputStack; 1.1808 + // computes the number of locals (ignores TOP types that are just after 1.1809 + // a LONG or a DOUBLE, and all trailing TOP types) 1.1810 + for(i = 0; i < locals.length; ++i) 1.1811 + { 1.1812 + t = locals[i]; 1.1813 + if(t == Frame.TOP) 1.1814 + { 1.1815 + ++nTop; 1.1816 + } 1.1817 + else 1.1818 + { 1.1819 + nLocal += nTop + 1; 1.1820 + nTop = 0; 1.1821 + } 1.1822 + if(t == Frame.LONG || t == Frame.DOUBLE) 1.1823 + { 1.1824 + ++i; 1.1825 + } 1.1826 + } 1.1827 + // computes the stack size (ignores TOP types that are just after 1.1828 + // a LONG or a DOUBLE) 1.1829 + for(i = 0; i < stacks.length; ++i) 1.1830 + { 1.1831 + t = stacks[i]; 1.1832 + ++nStack; 1.1833 + if(t == Frame.LONG || t == Frame.DOUBLE) 1.1834 + { 1.1835 + ++i; 1.1836 + } 1.1837 + } 1.1838 + // visits the frame and its content 1.1839 + startFrame(f.owner.position, nLocal, nStack); 1.1840 + for(i = 0; nLocal > 0; ++i, --nLocal) 1.1841 + { 1.1842 + t = locals[i]; 1.1843 + frame[frameIndex++] = t; 1.1844 + if(t == Frame.LONG || t == Frame.DOUBLE) 1.1845 + { 1.1846 + ++i; 1.1847 + } 1.1848 + } 1.1849 + for(i = 0; i < stacks.length; ++i) 1.1850 + { 1.1851 + t = stacks[i]; 1.1852 + frame[frameIndex++] = t; 1.1853 + if(t == Frame.LONG || t == Frame.DOUBLE) 1.1854 + { 1.1855 + ++i; 1.1856 + } 1.1857 + } 1.1858 + endFrame(); 1.1859 +} 1.1860 + 1.1861 +/** 1.1862 + * Starts the visit of a stack map frame. 1.1863 + * 1.1864 + * @param offset the offset of the instruction to which the frame 1.1865 + * corresponds. 1.1866 + * @param nLocal the number of local variables in the frame. 1.1867 + * @param nStack the number of stack elements in the frame. 1.1868 + */ 1.1869 +private void startFrame(final int offset, final int nLocal, final int nStack){ 1.1870 + int n = 3 + nLocal + nStack; 1.1871 + if(frame == null || frame.length < n) 1.1872 + { 1.1873 + frame = new int[n]; 1.1874 + } 1.1875 + frame[0] = offset; 1.1876 + frame[1] = nLocal; 1.1877 + frame[2] = nStack; 1.1878 + frameIndex = 3; 1.1879 +} 1.1880 + 1.1881 +/** 1.1882 + * Checks if the visit of the current frame {@link #frame} is finished, and 1.1883 + * if yes, write it in the StackMapTable attribute. 1.1884 + */ 1.1885 +private void endFrame(){ 1.1886 + if(previousFrame != null) 1.1887 + { // do not write the first frame 1.1888 + if(stackMap == null) 1.1889 + { 1.1890 + stackMap = new ByteVector(); 1.1891 + } 1.1892 + writeFrame(); 1.1893 + ++frameCount; 1.1894 + } 1.1895 + previousFrame = frame; 1.1896 + frame = null; 1.1897 +} 1.1898 + 1.1899 +/** 1.1900 + * Compress and writes the current frame {@link #frame} in the StackMapTable 1.1901 + * attribute. 1.1902 + */ 1.1903 +private void writeFrame(){ 1.1904 + int clocalsSize = frame[1]; 1.1905 + int cstackSize = frame[2]; 1.1906 + if((cw.version & 0xFFFF) < Opcodes.V1_6) 1.1907 + { 1.1908 + stackMap.putShort(frame[0]).putShort(clocalsSize); 1.1909 + writeFrameTypes(3, 3 + clocalsSize); 1.1910 + stackMap.putShort(cstackSize); 1.1911 + writeFrameTypes(3 + clocalsSize, 3 + clocalsSize + cstackSize); 1.1912 + return; 1.1913 + } 1.1914 + int localsSize = previousFrame[1]; 1.1915 + int type = FULL_FRAME; 1.1916 + int k = 0; 1.1917 + int delta; 1.1918 + if(frameCount == 0) 1.1919 + { 1.1920 + delta = frame[0]; 1.1921 + } 1.1922 + else 1.1923 + { 1.1924 + delta = frame[0] - previousFrame[0] - 1; 1.1925 + } 1.1926 + if(cstackSize == 0) 1.1927 + { 1.1928 + k = clocalsSize - localsSize; 1.1929 + switch(k) 1.1930 + { 1.1931 + case-3: 1.1932 + case-2: 1.1933 + case-1: 1.1934 + type = CHOP_FRAME; 1.1935 + localsSize = clocalsSize; 1.1936 + break; 1.1937 + case 0: 1.1938 + type = delta < 64 ? SAME_FRAME : SAME_FRAME_EXTENDED; 1.1939 + break; 1.1940 + case 1: 1.1941 + case 2: 1.1942 + case 3: 1.1943 + type = APPEND_FRAME; 1.1944 + break; 1.1945 + } 1.1946 + } 1.1947 + else if(clocalsSize == localsSize && cstackSize == 1) 1.1948 + { 1.1949 + type = delta < 63 1.1950 + ? SAME_LOCALS_1_STACK_ITEM_FRAME 1.1951 + : SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED; 1.1952 + } 1.1953 + if(type != FULL_FRAME) 1.1954 + { 1.1955 + // verify if locals are the same 1.1956 + int l = 3; 1.1957 + for(int j = 0; j < localsSize; j++) 1.1958 + { 1.1959 + if(frame[l] != previousFrame[l]) 1.1960 + { 1.1961 + type = FULL_FRAME; 1.1962 + break; 1.1963 + } 1.1964 + l++; 1.1965 + } 1.1966 + } 1.1967 + switch(type) 1.1968 + { 1.1969 + case SAME_FRAME: 1.1970 + stackMap.putByte(delta); 1.1971 + break; 1.1972 + case SAME_LOCALS_1_STACK_ITEM_FRAME: 1.1973 + stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME + delta); 1.1974 + writeFrameTypes(3 + clocalsSize, 4 + clocalsSize); 1.1975 + break; 1.1976 + case SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED: 1.1977 + stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) 1.1978 + .putShort(delta); 1.1979 + writeFrameTypes(3 + clocalsSize, 4 + clocalsSize); 1.1980 + break; 1.1981 + case SAME_FRAME_EXTENDED: 1.1982 + stackMap.putByte(SAME_FRAME_EXTENDED).putShort(delta); 1.1983 + break; 1.1984 + case CHOP_FRAME: 1.1985 + stackMap.putByte(SAME_FRAME_EXTENDED + k).putShort(delta); 1.1986 + break; 1.1987 + case APPEND_FRAME: 1.1988 + stackMap.putByte(SAME_FRAME_EXTENDED + k).putShort(delta); 1.1989 + writeFrameTypes(3 + localsSize, 3 + clocalsSize); 1.1990 + break; 1.1991 + // case FULL_FRAME: 1.1992 + default: 1.1993 + stackMap.putByte(FULL_FRAME) 1.1994 + .putShort(delta) 1.1995 + .putShort(clocalsSize); 1.1996 + writeFrameTypes(3, 3 + clocalsSize); 1.1997 + stackMap.putShort(cstackSize); 1.1998 + writeFrameTypes(3 + clocalsSize, 3 + clocalsSize + cstackSize); 1.1999 + } 1.2000 +} 1.2001 + 1.2002 +/** 1.2003 + * Writes some types of the current frame {@link #frame} into the 1.2004 + * StackMapTableAttribute. This method converts types from the format used 1.2005 + * in {@link Label} to the format used in StackMapTable attributes. In 1.2006 + * particular, it converts type table indexes to constant pool indexes. 1.2007 + * 1.2008 + * @param start index of the first type in {@link #frame} to write. 1.2009 + * @param end index of last type in {@link #frame} to write (exclusive). 1.2010 + */ 1.2011 +private void writeFrameTypes(final int start, final int end){ 1.2012 + for(int i = start; i < end; ++i) 1.2013 + { 1.2014 + int t = frame[i]; 1.2015 + int d = t & Frame.DIM; 1.2016 + if(d == 0) 1.2017 + { 1.2018 + int v = t & Frame.BASE_VALUE; 1.2019 + switch(t & Frame.BASE_KIND) 1.2020 + { 1.2021 + case Frame.OBJECT: 1.2022 + stackMap.putByte(7) 1.2023 + .putShort(cw.newClass(cw.typeTable[v].strVal1)); 1.2024 + break; 1.2025 + case Frame.UNINITIALIZED: 1.2026 + stackMap.putByte(8).putShort(cw.typeTable[v].intVal); 1.2027 + break; 1.2028 + default: 1.2029 + stackMap.putByte(v); 1.2030 + } 1.2031 + } 1.2032 + else 1.2033 + { 1.2034 + StringBuffer buf = new StringBuffer(); 1.2035 + d >>= 28; 1.2036 + while(d-- > 0) 1.2037 + { 1.2038 + buf.append('['); 1.2039 + } 1.2040 + if((t & Frame.BASE_KIND) == Frame.OBJECT) 1.2041 + { 1.2042 + buf.append('L'); 1.2043 + buf.append(cw.typeTable[t & Frame.BASE_VALUE].strVal1); 1.2044 + buf.append(';'); 1.2045 + } 1.2046 + else 1.2047 + { 1.2048 + switch(t & 0xF) 1.2049 + { 1.2050 + case 1: 1.2051 + buf.append('I'); 1.2052 + break; 1.2053 + case 2: 1.2054 + buf.append('F'); 1.2055 + break; 1.2056 + case 3: 1.2057 + buf.append('D'); 1.2058 + break; 1.2059 + case 9: 1.2060 + buf.append('Z'); 1.2061 + break; 1.2062 + case 10: 1.2063 + buf.append('B'); 1.2064 + break; 1.2065 + case 11: 1.2066 + buf.append('C'); 1.2067 + break; 1.2068 + case 12: 1.2069 + buf.append('S'); 1.2070 + break; 1.2071 + default: 1.2072 + buf.append('J'); 1.2073 + } 1.2074 + } 1.2075 + stackMap.putByte(7).putShort(cw.newClass(buf.toString())); 1.2076 + } 1.2077 + } 1.2078 +} 1.2079 + 1.2080 +private void writeFrameType(final Object type){ 1.2081 + if(type instanceof String) 1.2082 + { 1.2083 + stackMap.putByte(7).putShort(cw.newClass((String) type)); 1.2084 + } 1.2085 + else if(type instanceof Integer) 1.2086 + { 1.2087 + stackMap.putByte(((Integer) type).intValue()); 1.2088 + } 1.2089 + else 1.2090 + { 1.2091 + stackMap.putByte(8).putShort(((Label) type).position); 1.2092 + } 1.2093 +} 1.2094 + 1.2095 +// ------------------------------------------------------------------------ 1.2096 +// Utility methods: dump bytecode array 1.2097 +// ------------------------------------------------------------------------ 1.2098 + 1.2099 +/** 1.2100 + * Returns the size of the bytecode of this method. 1.2101 + * 1.2102 + * @return the size of the bytecode of this method. 1.2103 + */ 1.2104 +final int getSize(){ 1.2105 + if(classReaderOffset != 0) 1.2106 + { 1.2107 + return 6 + classReaderLength; 1.2108 + } 1.2109 + if(resize) 1.2110 + { 1.2111 + // replaces the temporary jump opcodes introduced by Label.resolve. 1.2112 + resizeInstructions(); 1.2113 + } 1.2114 + int size = 8; 1.2115 + if(code.length > 0) 1.2116 + { 1.2117 + cw.newUTF8("Code"); 1.2118 + size += 18 + code.length + 8 * handlerCount; 1.2119 + if(localVar != null) 1.2120 + { 1.2121 + cw.newUTF8("LocalVariableTable"); 1.2122 + size += 8 + localVar.length; 1.2123 + } 1.2124 + if(localVarType != null) 1.2125 + { 1.2126 + cw.newUTF8("LocalVariableTypeTable"); 1.2127 + size += 8 + localVarType.length; 1.2128 + } 1.2129 + if(lineNumber != null) 1.2130 + { 1.2131 + cw.newUTF8("LineNumberTable"); 1.2132 + size += 8 + lineNumber.length; 1.2133 + } 1.2134 + if(stackMap != null) 1.2135 + { 1.2136 + boolean zip = (cw.version & 0xFFFF) >= Opcodes.V1_6; 1.2137 + cw.newUTF8(zip ? "StackMapTable" : "StackMap"); 1.2138 + size += 8 + stackMap.length; 1.2139 + } 1.2140 + if(cattrs != null) 1.2141 + { 1.2142 + size += cattrs.getSize(cw, 1.2143 + code.data, 1.2144 + code.length, 1.2145 + maxStack, 1.2146 + maxLocals); 1.2147 + } 1.2148 + } 1.2149 + if(exceptionCount > 0) 1.2150 + { 1.2151 + cw.newUTF8("Exceptions"); 1.2152 + size += 8 + 2 * exceptionCount; 1.2153 + } 1.2154 + if((access & Opcodes.ACC_SYNTHETIC) != 0 1.2155 + && (cw.version & 0xffff) < Opcodes.V1_5) 1.2156 + { 1.2157 + cw.newUTF8("Synthetic"); 1.2158 + size += 6; 1.2159 + } 1.2160 + if((access & Opcodes.ACC_DEPRECATED) != 0) 1.2161 + { 1.2162 + cw.newUTF8("Deprecated"); 1.2163 + size += 6; 1.2164 + } 1.2165 + if(signature != null) 1.2166 + { 1.2167 + cw.newUTF8("Signature"); 1.2168 + cw.newUTF8(signature); 1.2169 + size += 8; 1.2170 + } 1.2171 + if(annd != null) 1.2172 + { 1.2173 + cw.newUTF8("AnnotationDefault"); 1.2174 + size += 6 + annd.length; 1.2175 + } 1.2176 + if(anns != null) 1.2177 + { 1.2178 + cw.newUTF8("RuntimeVisibleAnnotations"); 1.2179 + size += 8 + anns.getSize(); 1.2180 + } 1.2181 + if(ianns != null) 1.2182 + { 1.2183 + cw.newUTF8("RuntimeInvisibleAnnotations"); 1.2184 + size += 8 + ianns.getSize(); 1.2185 + } 1.2186 + if(panns != null) 1.2187 + { 1.2188 + cw.newUTF8("RuntimeVisibleParameterAnnotations"); 1.2189 + size += 7 + 2 * panns.length; 1.2190 + for(int i = panns.length - 1; i >= 0; --i) 1.2191 + { 1.2192 + size += panns[i] == null ? 0 : panns[i].getSize(); 1.2193 + } 1.2194 + } 1.2195 + if(ipanns != null) 1.2196 + { 1.2197 + cw.newUTF8("RuntimeInvisibleParameterAnnotations"); 1.2198 + size += 7 + 2 * ipanns.length; 1.2199 + for(int i = ipanns.length - 1; i >= 0; --i) 1.2200 + { 1.2201 + size += ipanns[i] == null ? 0 : ipanns[i].getSize(); 1.2202 + } 1.2203 + } 1.2204 + if(attrs != null) 1.2205 + { 1.2206 + size += attrs.getSize(cw, null, 0, -1, -1); 1.2207 + } 1.2208 + return size; 1.2209 +} 1.2210 + 1.2211 +/** 1.2212 + * Puts the bytecode of this method in the given byte vector. 1.2213 + * 1.2214 + * @param out the byte vector into which the bytecode of this method must be 1.2215 + * copied. 1.2216 + */ 1.2217 +final void put(final ByteVector out){ 1.2218 + out.putShort(access).putShort(name).putShort(desc); 1.2219 + if(classReaderOffset != 0) 1.2220 + { 1.2221 + out.putByteArray(cw.cr.b, classReaderOffset, classReaderLength); 1.2222 + return; 1.2223 + } 1.2224 + int attributeCount = 0; 1.2225 + if(code.length > 0) 1.2226 + { 1.2227 + ++attributeCount; 1.2228 + } 1.2229 + if(exceptionCount > 0) 1.2230 + { 1.2231 + ++attributeCount; 1.2232 + } 1.2233 + if((access & Opcodes.ACC_SYNTHETIC) != 0 1.2234 + && (cw.version & 0xffff) < Opcodes.V1_5) 1.2235 + { 1.2236 + ++attributeCount; 1.2237 + } 1.2238 + if((access & Opcodes.ACC_DEPRECATED) != 0) 1.2239 + { 1.2240 + ++attributeCount; 1.2241 + } 1.2242 + if(signature != null) 1.2243 + { 1.2244 + ++attributeCount; 1.2245 + } 1.2246 + if(annd != null) 1.2247 + { 1.2248 + ++attributeCount; 1.2249 + } 1.2250 + if(anns != null) 1.2251 + { 1.2252 + ++attributeCount; 1.2253 + } 1.2254 + if(ianns != null) 1.2255 + { 1.2256 + ++attributeCount; 1.2257 + } 1.2258 + if(panns != null) 1.2259 + { 1.2260 + ++attributeCount; 1.2261 + } 1.2262 + if(ipanns != null) 1.2263 + { 1.2264 + ++attributeCount; 1.2265 + } 1.2266 + if(attrs != null) 1.2267 + { 1.2268 + attributeCount += attrs.getCount(); 1.2269 + } 1.2270 + out.putShort(attributeCount); 1.2271 + if(code.length > 0) 1.2272 + { 1.2273 + int size = 12 + code.length + 8 * handlerCount; 1.2274 + if(localVar != null) 1.2275 + { 1.2276 + size += 8 + localVar.length; 1.2277 + } 1.2278 + if(localVarType != null) 1.2279 + { 1.2280 + size += 8 + localVarType.length; 1.2281 + } 1.2282 + if(lineNumber != null) 1.2283 + { 1.2284 + size += 8 + lineNumber.length; 1.2285 + } 1.2286 + if(stackMap != null) 1.2287 + { 1.2288 + size += 8 + stackMap.length; 1.2289 + } 1.2290 + if(cattrs != null) 1.2291 + { 1.2292 + size += cattrs.getSize(cw, 1.2293 + code.data, 1.2294 + code.length, 1.2295 + maxStack, 1.2296 + maxLocals); 1.2297 + } 1.2298 + out.putShort(cw.newUTF8("Code")).putInt(size); 1.2299 + out.putShort(maxStack).putShort(maxLocals); 1.2300 + out.putInt(code.length).putByteArray(code.data, 0, code.length); 1.2301 + out.putShort(handlerCount); 1.2302 + if(handlerCount > 0) 1.2303 + { 1.2304 + Handler h = firstHandler; 1.2305 + while(h != null) 1.2306 + { 1.2307 + out.putShort(h.start.position) 1.2308 + .putShort(h.end.position) 1.2309 + .putShort(h.handler.position) 1.2310 + .putShort(h.type); 1.2311 + h = h.next; 1.2312 + } 1.2313 + } 1.2314 + attributeCount = 0; 1.2315 + if(localVar != null) 1.2316 + { 1.2317 + ++attributeCount; 1.2318 + } 1.2319 + if(localVarType != null) 1.2320 + { 1.2321 + ++attributeCount; 1.2322 + } 1.2323 + if(lineNumber != null) 1.2324 + { 1.2325 + ++attributeCount; 1.2326 + } 1.2327 + if(stackMap != null) 1.2328 + { 1.2329 + ++attributeCount; 1.2330 + } 1.2331 + if(cattrs != null) 1.2332 + { 1.2333 + attributeCount += cattrs.getCount(); 1.2334 + } 1.2335 + out.putShort(attributeCount); 1.2336 + if(localVar != null) 1.2337 + { 1.2338 + out.putShort(cw.newUTF8("LocalVariableTable")); 1.2339 + out.putInt(localVar.length + 2).putShort(localVarCount); 1.2340 + out.putByteArray(localVar.data, 0, localVar.length); 1.2341 + } 1.2342 + if(localVarType != null) 1.2343 + { 1.2344 + out.putShort(cw.newUTF8("LocalVariableTypeTable")); 1.2345 + out.putInt(localVarType.length + 2).putShort(localVarTypeCount); 1.2346 + out.putByteArray(localVarType.data, 0, localVarType.length); 1.2347 + } 1.2348 + if(lineNumber != null) 1.2349 + { 1.2350 + out.putShort(cw.newUTF8("LineNumberTable")); 1.2351 + out.putInt(lineNumber.length + 2).putShort(lineNumberCount); 1.2352 + out.putByteArray(lineNumber.data, 0, lineNumber.length); 1.2353 + } 1.2354 + if(stackMap != null) 1.2355 + { 1.2356 + boolean zip = (cw.version & 0xFFFF) >= Opcodes.V1_6; 1.2357 + out.putShort(cw.newUTF8(zip ? "StackMapTable" : "StackMap")); 1.2358 + out.putInt(stackMap.length + 2).putShort(frameCount); 1.2359 + out.putByteArray(stackMap.data, 0, stackMap.length); 1.2360 + } 1.2361 + if(cattrs != null) 1.2362 + { 1.2363 + cattrs.put(cw, code.data, code.length, maxLocals, maxStack, out); 1.2364 + } 1.2365 + } 1.2366 + if(exceptionCount > 0) 1.2367 + { 1.2368 + out.putShort(cw.newUTF8("Exceptions")) 1.2369 + .putInt(2 * exceptionCount + 2); 1.2370 + out.putShort(exceptionCount); 1.2371 + for(int i = 0; i < exceptionCount; ++i) 1.2372 + { 1.2373 + out.putShort(exceptions[i]); 1.2374 + } 1.2375 + } 1.2376 + if((access & Opcodes.ACC_SYNTHETIC) != 0 1.2377 + && (cw.version & 0xffff) < Opcodes.V1_5) 1.2378 + { 1.2379 + out.putShort(cw.newUTF8("Synthetic")).putInt(0); 1.2380 + } 1.2381 + if((access & Opcodes.ACC_DEPRECATED) != 0) 1.2382 + { 1.2383 + out.putShort(cw.newUTF8("Deprecated")).putInt(0); 1.2384 + } 1.2385 + if(signature != null) 1.2386 + { 1.2387 + out.putShort(cw.newUTF8("Signature")) 1.2388 + .putInt(2) 1.2389 + .putShort(cw.newUTF8(signature)); 1.2390 + } 1.2391 + if(annd != null) 1.2392 + { 1.2393 + out.putShort(cw.newUTF8("AnnotationDefault")); 1.2394 + out.putInt(annd.length); 1.2395 + out.putByteArray(annd.data, 0, annd.length); 1.2396 + } 1.2397 + if(anns != null) 1.2398 + { 1.2399 + out.putShort(cw.newUTF8("RuntimeVisibleAnnotations")); 1.2400 + anns.put(out); 1.2401 + } 1.2402 + if(ianns != null) 1.2403 + { 1.2404 + out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations")); 1.2405 + ianns.put(out); 1.2406 + } 1.2407 + if(panns != null) 1.2408 + { 1.2409 + out.putShort(cw.newUTF8("RuntimeVisibleParameterAnnotations")); 1.2410 + AnnotationWriter.put(panns, out); 1.2411 + } 1.2412 + if(ipanns != null) 1.2413 + { 1.2414 + out.putShort(cw.newUTF8("RuntimeInvisibleParameterAnnotations")); 1.2415 + AnnotationWriter.put(ipanns, out); 1.2416 + } 1.2417 + if(attrs != null) 1.2418 + { 1.2419 + attrs.put(cw, null, 0, -1, -1, out); 1.2420 + } 1.2421 +} 1.2422 + 1.2423 +// ------------------------------------------------------------------------ 1.2424 +// Utility methods: instruction resizing (used to handle GOTO_W and JSR_W) 1.2425 +// ------------------------------------------------------------------------ 1.2426 + 1.2427 +/** 1.2428 + * Resizes and replaces the temporary instructions inserted by 1.2429 + * {@link Label#resolve} for wide forward jumps, while keeping jump offsets 1.2430 + * and instruction addresses consistent. This may require to resize other 1.2431 + * existing instructions, or even to introduce new instructions: for 1.2432 + * example, increasing the size of an instruction by 2 at the middle of a 1.2433 + * method can increases the offset of an IFEQ instruction from 32766 to 1.2434 + * 32768, in which case IFEQ 32766 must be replaced with IFNEQ 8 GOTO_W 1.2435 + * 32765. This, in turn, may require to increase the size of another jump 1.2436 + * instruction, and so on... All these operations are handled automatically 1.2437 + * by this method. <p> <i>This method must be called after all the method 1.2438 + * that is being built has been visited</i>. In particular, the 1.2439 + * {@link Label Label} objects used to construct the method are no longer 1.2440 + * valid after this method has been called. 1.2441 + */ 1.2442 +private void resizeInstructions(){ 1.2443 + byte[] b = code.data; // bytecode of the method 1.2444 + int u, v, label; // indexes in b 1.2445 + int i, j; // loop indexes 1.2446 + /* 1.2447 + * 1st step: As explained above, resizing an instruction may require to 1.2448 + * resize another one, which may require to resize yet another one, and 1.2449 + * so on. The first step of the algorithm consists in finding all the 1.2450 + * instructions that need to be resized, without modifying the code. 1.2451 + * This is done by the following "fix point" algorithm: 1.2452 + * 1.2453 + * Parse the code to find the jump instructions whose offset will need 1.2454 + * more than 2 bytes to be stored (the future offset is computed from 1.2455 + * the current offset and from the number of bytes that will be inserted 1.2456 + * or removed between the source and target instructions). For each such 1.2457 + * instruction, adds an entry in (a copy of) the indexes and sizes 1.2458 + * arrays (if this has not already been done in a previous iteration!). 1.2459 + * 1.2460 + * If at least one entry has been added during the previous step, go 1.2461 + * back to the beginning, otherwise stop. 1.2462 + * 1.2463 + * In fact the real algorithm is complicated by the fact that the size 1.2464 + * of TABLESWITCH and LOOKUPSWITCH instructions depends on their 1.2465 + * position in the bytecode (because of padding). In order to ensure the 1.2466 + * convergence of the algorithm, the number of bytes to be added or 1.2467 + * removed from these instructions is over estimated during the previous 1.2468 + * loop, and computed exactly only after the loop is finished (this 1.2469 + * requires another pass to parse the bytecode of the method). 1.2470 + */ 1.2471 + int[] allIndexes = new int[0]; // copy of indexes 1.2472 + int[] allSizes = new int[0]; // copy of sizes 1.2473 + boolean[] resize; // instructions to be resized 1.2474 + int newOffset; // future offset of a jump instruction 1.2475 + 1.2476 + resize = new boolean[code.length]; 1.2477 + 1.2478 + // 3 = loop again, 2 = loop ended, 1 = last pass, 0 = done 1.2479 + int state = 3; 1.2480 + do 1.2481 + { 1.2482 + if(state == 3) 1.2483 + { 1.2484 + state = 2; 1.2485 + } 1.2486 + u = 0; 1.2487 + while(u < b.length) 1.2488 + { 1.2489 + int opcode = b[u] & 0xFF; // opcode of current instruction 1.2490 + int insert = 0; // bytes to be added after this instruction 1.2491 + 1.2492 + switch(ClassWriter.TYPE[opcode]) 1.2493 + { 1.2494 + case ClassWriter.NOARG_INSN: 1.2495 + case ClassWriter.IMPLVAR_INSN: 1.2496 + u += 1; 1.2497 + break; 1.2498 + case ClassWriter.LABEL_INSN: 1.2499 + if(opcode > 201) 1.2500 + { 1.2501 + // converts temporary opcodes 202 to 217, 218 and 1.2502 + // 219 to IFEQ ... JSR (inclusive), IFNULL and 1.2503 + // IFNONNULL 1.2504 + opcode = opcode < 218 ? opcode - 49 : opcode - 20; 1.2505 + label = u + readUnsignedShort(b, u + 1); 1.2506 + } 1.2507 + else 1.2508 + { 1.2509 + label = u + readShort(b, u + 1); 1.2510 + } 1.2511 + newOffset = getNewOffset(allIndexes, allSizes, u, label); 1.2512 + if(newOffset < Short.MIN_VALUE 1.2513 + || newOffset > Short.MAX_VALUE) 1.2514 + { 1.2515 + if(!resize[u]) 1.2516 + { 1.2517 + if(opcode == Opcodes.GOTO 1.2518 + || opcode == Opcodes.JSR) 1.2519 + { 1.2520 + // two additional bytes will be required to 1.2521 + // replace this GOTO or JSR instruction with 1.2522 + // a GOTO_W or a JSR_W 1.2523 + insert = 2; 1.2524 + } 1.2525 + else 1.2526 + { 1.2527 + // five additional bytes will be required to 1.2528 + // replace this IFxxx <l> instruction with 1.2529 + // IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx 1.2530 + // is the "opposite" opcode of IFxxx (i.e., 1.2531 + // IFNE for IFEQ) and where <l'> designates 1.2532 + // the instruction just after the GOTO_W. 1.2533 + insert = 5; 1.2534 + } 1.2535 + resize[u] = true; 1.2536 + } 1.2537 + } 1.2538 + u += 3; 1.2539 + break; 1.2540 + case ClassWriter.LABELW_INSN: 1.2541 + u += 5; 1.2542 + break; 1.2543 + case ClassWriter.TABL_INSN: 1.2544 + if(state == 1) 1.2545 + { 1.2546 + // true number of bytes to be added (or removed) 1.2547 + // from this instruction = (future number of padding 1.2548 + // bytes - current number of padding byte) - 1.2549 + // previously over estimated variation = 1.2550 + // = ((3 - newOffset%4) - (3 - u%4)) - u%4 1.2551 + // = (-newOffset%4 + u%4) - u%4 1.2552 + // = -(newOffset & 3) 1.2553 + newOffset = getNewOffset(allIndexes, allSizes, 0, u); 1.2554 + insert = -(newOffset & 3); 1.2555 + } 1.2556 + else if(!resize[u]) 1.2557 + { 1.2558 + // over estimation of the number of bytes to be 1.2559 + // added to this instruction = 3 - current number 1.2560 + // of padding bytes = 3 - (3 - u%4) = u%4 = u & 3 1.2561 + insert = u & 3; 1.2562 + resize[u] = true; 1.2563 + } 1.2564 + // skips instruction 1.2565 + u = u + 4 - (u & 3); 1.2566 + u += 4 * (readInt(b, u + 8) - readInt(b, u + 4) + 1) + 12; 1.2567 + break; 1.2568 + case ClassWriter.LOOK_INSN: 1.2569 + if(state == 1) 1.2570 + { 1.2571 + // like TABL_INSN 1.2572 + newOffset = getNewOffset(allIndexes, allSizes, 0, u); 1.2573 + insert = -(newOffset & 3); 1.2574 + } 1.2575 + else if(!resize[u]) 1.2576 + { 1.2577 + // like TABL_INSN 1.2578 + insert = u & 3; 1.2579 + resize[u] = true; 1.2580 + } 1.2581 + // skips instruction 1.2582 + u = u + 4 - (u & 3); 1.2583 + u += 8 * readInt(b, u + 4) + 8; 1.2584 + break; 1.2585 + case ClassWriter.WIDE_INSN: 1.2586 + opcode = b[u + 1] & 0xFF; 1.2587 + if(opcode == Opcodes.IINC) 1.2588 + { 1.2589 + u += 6; 1.2590 + } 1.2591 + else 1.2592 + { 1.2593 + u += 4; 1.2594 + } 1.2595 + break; 1.2596 + case ClassWriter.VAR_INSN: 1.2597 + case ClassWriter.SBYTE_INSN: 1.2598 + case ClassWriter.LDC_INSN: 1.2599 + u += 2; 1.2600 + break; 1.2601 + case ClassWriter.SHORT_INSN: 1.2602 + case ClassWriter.LDCW_INSN: 1.2603 + case ClassWriter.FIELDORMETH_INSN: 1.2604 + case ClassWriter.TYPE_INSN: 1.2605 + case ClassWriter.IINC_INSN: 1.2606 + u += 3; 1.2607 + break; 1.2608 + case ClassWriter.ITFMETH_INSN: 1.2609 + u += 5; 1.2610 + break; 1.2611 + // case ClassWriter.MANA_INSN: 1.2612 + default: 1.2613 + u += 4; 1.2614 + break; 1.2615 + } 1.2616 + if(insert != 0) 1.2617 + { 1.2618 + // adds a new (u, insert) entry in the allIndexes and 1.2619 + // allSizes arrays 1.2620 + int[] newIndexes = new int[allIndexes.length + 1]; 1.2621 + int[] newSizes = new int[allSizes.length + 1]; 1.2622 + System.arraycopy(allIndexes, 1.2623 + 0, 1.2624 + newIndexes, 1.2625 + 0, 1.2626 + allIndexes.length); 1.2627 + System.arraycopy(allSizes, 0, newSizes, 0, allSizes.length); 1.2628 + newIndexes[allIndexes.length] = u; 1.2629 + newSizes[allSizes.length] = insert; 1.2630 + allIndexes = newIndexes; 1.2631 + allSizes = newSizes; 1.2632 + if(insert > 0) 1.2633 + { 1.2634 + state = 3; 1.2635 + } 1.2636 + } 1.2637 + } 1.2638 + if(state < 3) 1.2639 + { 1.2640 + --state; 1.2641 + } 1.2642 + } while(state != 0); 1.2643 + 1.2644 + // 2nd step: 1.2645 + // copies the bytecode of the method into a new bytevector, updates the 1.2646 + // offsets, and inserts (or removes) bytes as requested. 1.2647 + 1.2648 + ByteVector newCode = new ByteVector(code.length); 1.2649 + 1.2650 + u = 0; 1.2651 + while(u < code.length) 1.2652 + { 1.2653 + int opcode = b[u] & 0xFF; 1.2654 + switch(ClassWriter.TYPE[opcode]) 1.2655 + { 1.2656 + case ClassWriter.NOARG_INSN: 1.2657 + case ClassWriter.IMPLVAR_INSN: 1.2658 + newCode.putByte(opcode); 1.2659 + u += 1; 1.2660 + break; 1.2661 + case ClassWriter.LABEL_INSN: 1.2662 + if(opcode > 201) 1.2663 + { 1.2664 + // changes temporary opcodes 202 to 217 (inclusive), 218 1.2665 + // and 219 to IFEQ ... JSR (inclusive), IFNULL and 1.2666 + // IFNONNULL 1.2667 + opcode = opcode < 218 ? opcode - 49 : opcode - 20; 1.2668 + label = u + readUnsignedShort(b, u + 1); 1.2669 + } 1.2670 + else 1.2671 + { 1.2672 + label = u + readShort(b, u + 1); 1.2673 + } 1.2674 + newOffset = getNewOffset(allIndexes, allSizes, u, label); 1.2675 + if(resize[u]) 1.2676 + { 1.2677 + // replaces GOTO with GOTO_W, JSR with JSR_W and IFxxx 1.2678 + // <l> with IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx is 1.2679 + // the "opposite" opcode of IFxxx (i.e., IFNE for IFEQ) 1.2680 + // and where <l'> designates the instruction just after 1.2681 + // the GOTO_W. 1.2682 + if(opcode == Opcodes.GOTO) 1.2683 + { 1.2684 + newCode.putByte(200); // GOTO_W 1.2685 + } 1.2686 + else if(opcode == Opcodes.JSR) 1.2687 + { 1.2688 + newCode.putByte(201); // JSR_W 1.2689 + } 1.2690 + else 1.2691 + { 1.2692 + newCode.putByte(opcode <= 166 1.2693 + ? ((opcode + 1) ^ 1) - 1 1.2694 + : opcode ^ 1); 1.2695 + newCode.putShort(8); // jump offset 1.2696 + newCode.putByte(200); // GOTO_W 1.2697 + // newOffset now computed from start of GOTO_W 1.2698 + newOffset -= 3; 1.2699 + } 1.2700 + newCode.putInt(newOffset); 1.2701 + } 1.2702 + else 1.2703 + { 1.2704 + newCode.putByte(opcode); 1.2705 + newCode.putShort(newOffset); 1.2706 + } 1.2707 + u += 3; 1.2708 + break; 1.2709 + case ClassWriter.LABELW_INSN: 1.2710 + label = u + readInt(b, u + 1); 1.2711 + newOffset = getNewOffset(allIndexes, allSizes, u, label); 1.2712 + newCode.putByte(opcode); 1.2713 + newCode.putInt(newOffset); 1.2714 + u += 5; 1.2715 + break; 1.2716 + case ClassWriter.TABL_INSN: 1.2717 + // skips 0 to 3 padding bytes 1.2718 + v = u; 1.2719 + u = u + 4 - (v & 3); 1.2720 + // reads and copies instruction 1.2721 + newCode.putByte(Opcodes.TABLESWITCH); 1.2722 + newCode.length += (4 - newCode.length % 4) % 4; 1.2723 + label = v + readInt(b, u); 1.2724 + u += 4; 1.2725 + newOffset = getNewOffset(allIndexes, allSizes, v, label); 1.2726 + newCode.putInt(newOffset); 1.2727 + j = readInt(b, u); 1.2728 + u += 4; 1.2729 + newCode.putInt(j); 1.2730 + j = readInt(b, u) - j + 1; 1.2731 + u += 4; 1.2732 + newCode.putInt(readInt(b, u - 4)); 1.2733 + for(; j > 0; --j) 1.2734 + { 1.2735 + label = v + readInt(b, u); 1.2736 + u += 4; 1.2737 + newOffset = getNewOffset(allIndexes, allSizes, v, label); 1.2738 + newCode.putInt(newOffset); 1.2739 + } 1.2740 + break; 1.2741 + case ClassWriter.LOOK_INSN: 1.2742 + // skips 0 to 3 padding bytes 1.2743 + v = u; 1.2744 + u = u + 4 - (v & 3); 1.2745 + // reads and copies instruction 1.2746 + newCode.putByte(Opcodes.LOOKUPSWITCH); 1.2747 + newCode.length += (4 - newCode.length % 4) % 4; 1.2748 + label = v + readInt(b, u); 1.2749 + u += 4; 1.2750 + newOffset = getNewOffset(allIndexes, allSizes, v, label); 1.2751 + newCode.putInt(newOffset); 1.2752 + j = readInt(b, u); 1.2753 + u += 4; 1.2754 + newCode.putInt(j); 1.2755 + for(; j > 0; --j) 1.2756 + { 1.2757 + newCode.putInt(readInt(b, u)); 1.2758 + u += 4; 1.2759 + label = v + readInt(b, u); 1.2760 + u += 4; 1.2761 + newOffset = getNewOffset(allIndexes, allSizes, v, label); 1.2762 + newCode.putInt(newOffset); 1.2763 + } 1.2764 + break; 1.2765 + case ClassWriter.WIDE_INSN: 1.2766 + opcode = b[u + 1] & 0xFF; 1.2767 + if(opcode == Opcodes.IINC) 1.2768 + { 1.2769 + newCode.putByteArray(b, u, 6); 1.2770 + u += 6; 1.2771 + } 1.2772 + else 1.2773 + { 1.2774 + newCode.putByteArray(b, u, 4); 1.2775 + u += 4; 1.2776 + } 1.2777 + break; 1.2778 + case ClassWriter.VAR_INSN: 1.2779 + case ClassWriter.SBYTE_INSN: 1.2780 + case ClassWriter.LDC_INSN: 1.2781 + newCode.putByteArray(b, u, 2); 1.2782 + u += 2; 1.2783 + break; 1.2784 + case ClassWriter.SHORT_INSN: 1.2785 + case ClassWriter.LDCW_INSN: 1.2786 + case ClassWriter.FIELDORMETH_INSN: 1.2787 + case ClassWriter.TYPE_INSN: 1.2788 + case ClassWriter.IINC_INSN: 1.2789 + newCode.putByteArray(b, u, 3); 1.2790 + u += 3; 1.2791 + break; 1.2792 + case ClassWriter.ITFMETH_INSN: 1.2793 + newCode.putByteArray(b, u, 5); 1.2794 + u += 5; 1.2795 + break; 1.2796 + // case MANA_INSN: 1.2797 + default: 1.2798 + newCode.putByteArray(b, u, 4); 1.2799 + u += 4; 1.2800 + break; 1.2801 + } 1.2802 + } 1.2803 + 1.2804 + // recomputes the stack map frames 1.2805 + if(frameCount > 0) 1.2806 + { 1.2807 + if(compute == FRAMES) 1.2808 + { 1.2809 + frameCount = 0; 1.2810 + stackMap = null; 1.2811 + previousFrame = null; 1.2812 + frame = null; 1.2813 + Frame f = new Frame(); 1.2814 + f.owner = labels; 1.2815 + Type[] args = Type.getArgumentTypes(descriptor); 1.2816 + f.initInputFrame(cw, access, args, maxLocals); 1.2817 + visitFrame(f); 1.2818 + Label l = labels; 1.2819 + while(l != null) 1.2820 + { 1.2821 + /* 1.2822 + * here we need the original label position. getNewOffset 1.2823 + * must therefore never have been called for this label. 1.2824 + */ 1.2825 + u = l.position - 3; 1.2826 + if((l.status & Label.STORE) != 0 || (u >= 0 && resize[u])) 1.2827 + { 1.2828 + getNewOffset(allIndexes, allSizes, l); 1.2829 + // TODO update offsets in UNINITIALIZED values 1.2830 + visitFrame(l.frame); 1.2831 + } 1.2832 + l = l.successor; 1.2833 + } 1.2834 + } 1.2835 + else 1.2836 + { 1.2837 + /* 1.2838 + * Resizing an existing stack map frame table is really hard. 1.2839 + * Not only the table must be parsed to update the offets, but 1.2840 + * new frames may be needed for jump instructions that were 1.2841 + * inserted by this method. And updating the offsets or 1.2842 + * inserting frames can change the format of the following 1.2843 + * frames, in case of packed frames. In practice the whole table 1.2844 + * must be recomputed. For this the frames are marked as 1.2845 + * potentially invalid. This will cause the whole class to be 1.2846 + * reread and rewritten with the COMPUTE_FRAMES option (see the 1.2847 + * ClassWriter.toByteArray method). This is not very efficient 1.2848 + * but is much easier and requires much less code than any other 1.2849 + * method I can think of. 1.2850 + */ 1.2851 + cw.invalidFrames = true; 1.2852 + } 1.2853 + } 1.2854 + // updates the exception handler block labels 1.2855 + Handler h = firstHandler; 1.2856 + while(h != null) 1.2857 + { 1.2858 + getNewOffset(allIndexes, allSizes, h.start); 1.2859 + getNewOffset(allIndexes, allSizes, h.end); 1.2860 + getNewOffset(allIndexes, allSizes, h.handler); 1.2861 + h = h.next; 1.2862 + } 1.2863 + // updates the instructions addresses in the 1.2864 + // local var and line number tables 1.2865 + for(i = 0; i < 2; ++i) 1.2866 + { 1.2867 + ByteVector bv = i == 0 ? localVar : localVarType; 1.2868 + if(bv != null) 1.2869 + { 1.2870 + b = bv.data; 1.2871 + u = 0; 1.2872 + while(u < bv.length) 1.2873 + { 1.2874 + label = readUnsignedShort(b, u); 1.2875 + newOffset = getNewOffset(allIndexes, allSizes, 0, label); 1.2876 + writeShort(b, u, newOffset); 1.2877 + label += readUnsignedShort(b, u + 2); 1.2878 + newOffset = getNewOffset(allIndexes, allSizes, 0, label) 1.2879 + - newOffset; 1.2880 + writeShort(b, u + 2, newOffset); 1.2881 + u += 10; 1.2882 + } 1.2883 + } 1.2884 + } 1.2885 + if(lineNumber != null) 1.2886 + { 1.2887 + b = lineNumber.data; 1.2888 + u = 0; 1.2889 + while(u < lineNumber.length) 1.2890 + { 1.2891 + writeShort(b, u, getNewOffset(allIndexes, 1.2892 + allSizes, 1.2893 + 0, 1.2894 + readUnsignedShort(b, u))); 1.2895 + u += 4; 1.2896 + } 1.2897 + } 1.2898 + // updates the labels of the other attributes 1.2899 + Attribute attr = cattrs; 1.2900 + while(attr != null) 1.2901 + { 1.2902 + Label[] labels = attr.getLabels(); 1.2903 + if(labels != null) 1.2904 + { 1.2905 + for(i = labels.length - 1; i >= 0; --i) 1.2906 + { 1.2907 + getNewOffset(allIndexes, allSizes, labels[i]); 1.2908 + } 1.2909 + } 1.2910 + attr = attr.next; 1.2911 + } 1.2912 + 1.2913 + // replaces old bytecodes with new ones 1.2914 + code = newCode; 1.2915 +} 1.2916 + 1.2917 +/** 1.2918 + * Reads an unsigned short value in the given byte array. 1.2919 + * 1.2920 + * @param b a byte array. 1.2921 + * @param index the start index of the value to be read. 1.2922 + * @return the read value. 1.2923 + */ 1.2924 +static int readUnsignedShort(final byte[] b, final int index){ 1.2925 + return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF); 1.2926 +} 1.2927 + 1.2928 +/** 1.2929 + * Reads a signed short value in the given byte array. 1.2930 + * 1.2931 + * @param b a byte array. 1.2932 + * @param index the start index of the value to be read. 1.2933 + * @return the read value. 1.2934 + */ 1.2935 +static short readShort(final byte[] b, final int index){ 1.2936 + return (short) (((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF)); 1.2937 +} 1.2938 + 1.2939 +/** 1.2940 + * Reads a signed int value in the given byte array. 1.2941 + * 1.2942 + * @param b a byte array. 1.2943 + * @param index the start index of the value to be read. 1.2944 + * @return the read value. 1.2945 + */ 1.2946 +static int readInt(final byte[] b, final int index){ 1.2947 + return ((b[index] & 0xFF) << 24) | ((b[index + 1] & 0xFF) << 16) 1.2948 + | ((b[index + 2] & 0xFF) << 8) | (b[index + 3] & 0xFF); 1.2949 +} 1.2950 + 1.2951 +/** 1.2952 + * Writes a short value in the given byte array. 1.2953 + * 1.2954 + * @param b a byte array. 1.2955 + * @param index where the first byte of the short value must be written. 1.2956 + * @param s the value to be written in the given byte array. 1.2957 + */ 1.2958 +static void writeShort(final byte[] b, final int index, final int s){ 1.2959 + b[index] = (byte) (s >>> 8); 1.2960 + b[index + 1] = (byte) s; 1.2961 +} 1.2962 + 1.2963 +/** 1.2964 + * Computes the future value of a bytecode offset. <p> Note: it is possible 1.2965 + * to have several entries for the same instruction in the <tt>indexes</tt> 1.2966 + * and <tt>sizes</tt>: two entries (index=a,size=b) and (index=a,size=b') 1.2967 + * are equivalent to a single entry (index=a,size=b+b'). 1.2968 + * 1.2969 + * @param indexes current positions of the instructions to be resized. Each 1.2970 + * instruction must be designated by the index of its <i>last</i> 1.2971 + * byte, plus one (or, in other words, by the index of the <i>first</i> 1.2972 + * byte of the <i>next</i> instruction). 1.2973 + * @param sizes the number of bytes to be <i>added</i> to the above 1.2974 + * instructions. More precisely, for each i < <tt>len</tt>, 1.2975 + * <tt>sizes</tt>[i] bytes will be added at the end of the 1.2976 + * instruction designated by <tt>indexes</tt>[i] or, if 1.2977 + * <tt>sizes</tt>[i] is negative, the <i>last</i> |<tt>sizes[i]</tt>| 1.2978 + * bytes of the instruction will be removed (the instruction size 1.2979 + * <i>must not</i> become negative or null). 1.2980 + * @param begin index of the first byte of the source instruction. 1.2981 + * @param end index of the first byte of the target instruction. 1.2982 + * @return the future value of the given bytecode offset. 1.2983 + */ 1.2984 +static int getNewOffset( 1.2985 + final int[] indexes, 1.2986 + final int[] sizes, 1.2987 + final int begin, 1.2988 + final int end){ 1.2989 + int offset = end - begin; 1.2990 + for(int i = 0; i < indexes.length; ++i) 1.2991 + { 1.2992 + if(begin < indexes[i] && indexes[i] <= end) 1.2993 + { 1.2994 + // forward jump 1.2995 + offset += sizes[i]; 1.2996 + } 1.2997 + else if(end < indexes[i] && indexes[i] <= begin) 1.2998 + { 1.2999 + // backward jump 1.3000 + offset -= sizes[i]; 1.3001 + } 1.3002 + } 1.3003 + return offset; 1.3004 +} 1.3005 + 1.3006 +/** 1.3007 + * Updates the offset of the given label. 1.3008 + * 1.3009 + * @param indexes current positions of the instructions to be resized. Each 1.3010 + * instruction must be designated by the index of its <i>last</i> 1.3011 + * byte, plus one (or, in other words, by the index of the <i>first</i> 1.3012 + * byte of the <i>next</i> instruction). 1.3013 + * @param sizes the number of bytes to be <i>added</i> to the above 1.3014 + * instructions. More precisely, for each i < <tt>len</tt>, 1.3015 + * <tt>sizes</tt>[i] bytes will be added at the end of the 1.3016 + * instruction designated by <tt>indexes</tt>[i] or, if 1.3017 + * <tt>sizes</tt>[i] is negative, the <i>last</i> |<tt>sizes[i]</tt>| 1.3018 + * bytes of the instruction will be removed (the instruction size 1.3019 + * <i>must not</i> become negative or null). 1.3020 + * @param label the label whose offset must be updated. 1.3021 + */ 1.3022 +static void getNewOffset( 1.3023 + final int[] indexes, 1.3024 + final int[] sizes, 1.3025 + final Label label){ 1.3026 + if((label.status & Label.RESIZED) == 0) 1.3027 + { 1.3028 + label.position = getNewOffset(indexes, sizes, 0, label.position); 1.3029 + label.status |= Label.RESIZED; 1.3030 + } 1.3031 +} 1.3032 +}