View Javadoc

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