View Javadoc

1   /*
2    * Copyright 2005 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at 
7    * 
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software 
11   * distributed under the License is distributed on an "AS IS" BASIS, 
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
13   * See the License for the specific language governing permissions and 
14   * limitations under the License.
15   */
16  
17  
18  package org.apache.jdo.impl.enhancer.classfile;
19  
20  import java.util.Stack;
21  import java.util.Vector;
22  import java.io.*;
23  
24  /***
25   * Subtype of ClassAttribute which describes the "Code" attribute
26   * associated with a method.
27   */
28  public class CodeAttribute extends ClassAttribute {
29      public final static String expectedAttrName = "Code";
30  
31      /* The java class file contents defining this code attribute.
32         If non-null, this must be disassembled before the remaining 
33         fields of this instance may be accessed. */
34      private byte theDataBytes[];
35  
36      /* The maximum number of stack entries used by this method */
37      private int maxStack;
38  
39      /* The maximum number of local variables used by this method */
40      private int maxLocals;
41  
42      /* The java VM byte code sequence for this method - null for native
43         and abstract methods */
44      private byte theCodeBytes[];
45  
46      /* The instruction sequence for this method - initially derived from
47         the byte code array, but may later be modified */
48      private Insn theCode;
49  
50      /* The exception ranges and handlers which apply to the code in this
51         method */
52      private ExceptionTable exceptionTable;
53  
54      /* The attributes which apply to this method */
55      private AttributeVector codeAttributes;
56  
57      /* The method environment used for decompiling this code attribute */
58      CodeEnv codeEnv;
59  
60      /* public accessors */
61  
62      /***
63       * Return the maximum number of stack entries used by this method
64       */
65      public int stackUsed() {
66          makeValid();
67          return maxStack;
68      }
69  
70      /***
71       * Set the maximum number of stack entries used by this method
72       */
73      public void setStackUsed(int used) {
74          makeValid();
75          maxStack = used;
76      }
77  
78      /***
79       * Return the maximum number of local variables used by this method
80       */
81      public int localsUsed() {
82          makeValid();
83          return maxLocals;
84      }
85  
86      /***
87       * Set the maximum number of local variables used by this method
88       */
89      public void setLocalsUsed(int used) {
90          makeValid();
91          maxLocals = used;
92      }
93  
94      /***
95       * Return the java VM byte code sequence for this method - null for
96       * native and abstract methods
97       */
98      public byte[] byteCodes() {
99          makeValid();
100         return theCodeBytes;
101     }
102 
103     /***
104      * Return the instruction sequence for this method - initially derived
105      * from the byte code array, but may later be modified
106      */
107     public Insn theCode() {
108         makeValid();
109         if (theCode == null && codeEnv != null) {
110             buildInstructions(codeEnv);
111         }
112         return theCode;
113     }
114 
115     /***
116      * Install the instruction sequence for this method - the byte code array
117      * is later updated.
118      */
119     public void setTheCode(Insn insn) {
120         makeValid();
121         if (insn != null && insn.opcode() != Insn.opc_target)
122             throw new InsnError(
123                 "The initial instruction in all methods must be a target");
124         theCode = insn;
125     }
126 
127     /***
128      * Return the exception ranges and handlers which apply to the code in
129      * this method.
130      */
131     public ExceptionTable exceptionHandlers() {
132         makeValid();
133         return exceptionTable;
134     }
135 
136     /***
137      * Return the attributes which apply to this code
138      */
139     public AttributeVector attributes() {
140         makeValid();
141         return codeAttributes;
142     }
143 
144     /***
145      * Constructs a CodeAttribute object for construction from scratch
146      */
147     public CodeAttribute(ConstUtf8 attrName,
148                          int maxStack, int maxLocals,
149                          Insn code, 
150                          ExceptionTable excTable,
151                          AttributeVector codeAttrs) {
152         this(attrName, maxStack, maxLocals, code, null, /* byteCodes */
153              excTable, codeAttrs, null /* CodeEnv */ );
154     }
155 
156     /***
157      * Constructs a CodeAttribute object 
158      */
159     public CodeAttribute(ConstUtf8 attrName,
160                          int maxStack, int maxLocals,
161                          Insn code, byte[] codeBytes,
162                          ExceptionTable excTable,
163                          AttributeVector codeAttrs,
164                          CodeEnv codeEnv) {
165         super(attrName);
166         this.maxStack = maxStack;
167         this.maxLocals = maxLocals;
168         theCode = code;
169         theCodeBytes = codeBytes;
170         exceptionTable = excTable;
171         codeAttributes = codeAttrs;
172         this.codeEnv = codeEnv;
173     }
174 
175     /***
176      * Constructs a CodeAttribute object for later disassembly
177      */
178     public CodeAttribute(ConstUtf8 attrName, byte[] dataBytes, CodeEnv codeEnv) {
179         super(attrName);
180         this.theDataBytes = dataBytes;
181         this.codeEnv = codeEnv;
182     }
183 
184     /***
185      * Compares this instance with another for structural equality.
186      */
187     //@olsen: added method
188     public boolean isEqual(Stack msg, Object obj) {
189         if (!(obj instanceof CodeAttribute)) {
190             msg.push("obj/obj.getClass() = "
191                      + (obj == null ? null : obj.getClass()));
192             msg.push("this.getClass() = "
193                      + this.getClass());
194             return false;
195         }
196         CodeAttribute other = (CodeAttribute)obj;
197 
198         if (!super.isEqual(msg, other)) {
199             return false;
200         }
201 
202         if (this.stackUsed() != other.stackUsed()) {
203             msg.push(String.valueOf("stackUsed() = "
204                                     + other.stackUsed()));
205             msg.push(String.valueOf("stackUsed() = "
206                                     + this.stackUsed()));
207             return false;
208         }
209         if (this.localsUsed() != other.localsUsed()) {
210             msg.push(String.valueOf("localsUsed() = "
211                                     + other.localsUsed()));
212             msg.push(String.valueOf("localsUsed() = "
213                                     + this.localsUsed()));
214             return false;
215         }
216 
217         // iterate over the instructions
218         Insn theCode1 = this.theCode();
219         Insn theCode2 = other.theCode();
220         while (theCode1 != null && theCode2 != null) {
221             // ignore targets (ignore line numbers)
222             if (theCode1.opcode() == Insn.opc_target) {
223                 theCode1 = theCode1.next();
224                 continue;
225             }
226             if (theCode2.opcode() == Insn.opc_target) {
227                 theCode2 = theCode2.next();
228                 continue;
229             }
230             if (!theCode1.isEqual(msg, theCode2)) {
231                 msg.push("theCode()[i] = " + String.valueOf(theCode2));
232                 msg.push("theCode()[i] = " + String.valueOf(theCode1));
233                 return false;
234             }
235             theCode1 = theCode1.next();
236             theCode2 = theCode2.next();
237         }
238         if (theCode1 == null ^ theCode2 == null) {
239             msg.push("theCode()[i] = " + String.valueOf(theCode2));
240             msg.push("theCode()[i] = " + String.valueOf(theCode1));
241             return false;
242         }
243 
244         if (!this.exceptionHandlers().isEqual(msg, other.exceptionHandlers())) {
245             msg.push(String.valueOf("exceptionHandlers() = "
246                                     + other.exceptionHandlers()));
247             msg.push(String.valueOf("exceptionHandlers() = "
248                                     + this.exceptionHandlers()));
249             return false;
250         }
251         if (!this.attributes().isEqual(msg, other.attributes())) {
252             msg.push(String.valueOf("attributes() = "
253                                     + other.attributes()));
254             msg.push(String.valueOf("attributes() = "
255                                     + this.attributes()));
256             return false;
257         }
258         return true;
259     }
260 
261     /* package local methods *//package-summary/html">class="comment"> package local methods *//package-summary.html">/* package local methods *//package-summary.html">class="comment"> package local methods */
262 
263     static CodeAttribute read(ConstUtf8 attrName,
264                               DataInputStream data, ConstantPool pool)
265         throws IOException {
266         int maxStack = data.readUnsignedShort();
267         int maxLocals = data.readUnsignedShort();
268         int codeLength = data.readInt();
269         byte codeBytes[] = new byte[codeLength];
270         data.readFully(codeBytes);
271         Insn code = null;
272         CodeEnv codeEnv = new CodeEnv(pool);
273 
274         ExceptionTable excTable = ExceptionTable.read(data, codeEnv);
275     
276         AttributeVector codeAttrs = 
277             AttributeVector.readAttributes(data, codeEnv);
278 
279         return new CodeAttribute(attrName, maxStack, maxLocals, code, codeBytes,
280                                  excTable, codeAttrs, codeEnv);
281     } 
282 
283     /* This version reads the attribute into a byte array for later 
284        consumption */
285     static CodeAttribute read(ConstUtf8 attrName, int attrLength,
286                               DataInputStream data, ConstantPool pool)
287         throws IOException {
288         byte dataBytes[] = new byte[attrLength];
289         data.readFully(dataBytes);
290         return new CodeAttribute(attrName, dataBytes, new CodeEnv(pool));
291     } 
292 
293     void write(DataOutputStream out) throws IOException {
294         out.writeShort(attrName().getIndex());
295         if (theDataBytes == null) {
296             buildInstructionBytes();
297             ByteArrayOutputStream baos = new ByteArrayOutputStream();
298             DataOutputStream tmpOut = new DataOutputStream(baos);
299             tmpOut.writeShort(maxStack);
300             tmpOut.writeShort(maxLocals);
301             tmpOut.writeInt(theCodeBytes.length);
302             tmpOut.write(theCodeBytes, 0, theCodeBytes.length);
303             exceptionTable.write(tmpOut);
304             codeAttributes.write(tmpOut);
305 
306             tmpOut.flush();
307             byte tmpBytes[] = baos.toByteArray();
308             out.writeInt(tmpBytes.length);
309             out.write(tmpBytes, 0, tmpBytes.length);
310         } else {
311             out.writeInt(theDataBytes.length);
312             out.write(theDataBytes, 0, theDataBytes.length);
313         }
314     }
315 
316     void print(PrintStream out, int indent) {
317         makeValid();
318         ClassPrint.spaces(out, indent);
319         out.print("Code:");
320         out.print(" max_stack = " + Integer.toString(maxStack));
321         out.print(" max_locals = " + Integer.toString(maxLocals));
322         out.println(" Exceptions:");
323         exceptionTable.print(out, indent+2);
324         ClassPrint.spaces(out, indent);
325         out.println("Code Attributes:");
326         codeAttributes.print(out, indent+2);
327 
328         Insn insn = theCode();
329         if (insn != null) {
330             ClassPrint.spaces(out, indent);
331             out.println("Instructions:");
332             while (insn != null) {
333                 insn.print(out, indent+2);
334                 insn = insn.next();
335             }
336         }
337     }
338 
339     /***
340      *  Assign offsets to instructions and return the number of bytes.
341      *  theCode must be non-null.
342      */
343     private int resolveOffsets() {
344         Insn insn = theCode;
345         int currPC = 0;
346         while (insn != null) {
347             currPC = insn.resolveOffset(currPC);
348             insn = insn.next();
349         }
350         return currPC;
351     }
352 
353     int codeSize() {
354         makeValid();
355         return theCodeBytes.length;
356     }
357 
358     /***
359      * Derive the instruction list from the instruction byte codes
360      */
361     private void buildInstructions(CodeEnv codeEnv) {
362         if (theCodeBytes != null) {
363             InsnReadEnv insnEnv = new InsnReadEnv(theCodeBytes, codeEnv);
364             theCode = insnEnv.getTarget(0);
365             Insn currInsn = theCode;
366 
367             /* First, create instructions */
368             while (insnEnv.more()) {
369                 Insn newInsn = Insn.read(insnEnv);
370                 currInsn.setNext(newInsn);
371                 currInsn = newInsn;
372             }
373 
374             /* Now, insert targets */
375             InsnTarget targ;
376             currInsn = theCode;
377             Insn prevInsn = null;
378             while (currInsn != null) {
379                 int off = currInsn.offset();
380 
381                 /* We always insert a target a 0 to start so ignore that one */
382                 if (off > 0) {
383                     targ = codeEnv.findTarget(off);
384                     if (targ != null)
385                         prevInsn.setNext(targ);
386                 }
387                 prevInsn = currInsn;
388                 currInsn = currInsn.next();
389             }
390 
391             /* And follow up with a final target if needed */
392             targ = codeEnv.findTarget(insnEnv.currentPC());
393             if (targ != null)
394                 prevInsn.setNext(targ);
395         }
396     }
397 
398     /***
399      * Derive the instruction byte codes from the instruction list
400      * This should also recompute stack and variables but for now we
401      * assume that this isn't needed
402      */
403     private void buildInstructionBytes() {
404         if (theCode != null) {
405             /* Make sure instructions have correct offsets */
406             int size = resolveOffsets();
407             theCodeBytes = new byte[size];
408 
409             Insn insn = theCode;
410             int index = 0;
411             while (insn != null) {
412                 index = insn.store(theCodeBytes, index);
413                 insn = insn.next();
414             }
415         }
416     }
417 
418     /*** If theDataBytes is non-null, disassemble this code attribute
419      *  from the data bytes. */
420     private void makeValid() {
421         if (theDataBytes != null) {
422             DataInputStream dis = new DataInputStream(
423 		new ByteArrayInputStream(theDataBytes));
424             try {
425                 maxStack = dis.readUnsignedShort();
426                 maxLocals = dis.readUnsignedShort();
427                 int codeLength = dis.readInt();
428                 theCodeBytes = new byte[codeLength];
429                 dis.readFully(theCodeBytes);
430                 exceptionTable = ExceptionTable.read(dis, codeEnv);
431                 codeAttributes = AttributeVector.readAttributes(dis, codeEnv);
432             } catch (java.io.IOException ioe) {
433                 throw new ClassFormatError("IOException while reading code attribute");
434             }
435 
436             theDataBytes = null;
437         }
438     }
439 }
440