Fixes null captured parameters
[jpf-core.git] / src / main / gov / nasa / jpf / util / BinaryClassSource.java
1 /*
2  * Copyright (C) 2014, United States Government, as represented by the
3  * Administrator of the National Aeronautics and Space Administration.
4  * All rights reserved.
5  *
6  * The Java Pathfinder core (jpf-core) platform is licensed under the
7  * Apache License, Version 2.0 (the "License"); you may not use this file except
8  * in compliance with the License. You may obtain a copy of the License at
9  * 
10  *        http://www.apache.org/licenses/LICENSE-2.0. 
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and 
16  * limitations under the License.
17  */
18
19 package gov.nasa.jpf.util;
20
21 import gov.nasa.jpf.vm.ClassParseException;
22 import java.io.File;
23 import java.io.FileInputStream;
24 import java.io.FileNotFoundException;
25 import java.io.IOException;
26 import java.io.InputStream;
27
28 /**
29  * common root for classes that read classes from binary data
30  */
31 public class BinaryClassSource {
32
33   protected byte[] data;
34   
35   protected int pos; // temp index value during parsing
36   protected int pc; // bytecode pos relative to method code start
37   
38   protected int[] posStack;
39   protected int top;
40   
41   protected ByteReader byteReader;
42   
43   //--------------------------------------------------------- variable endian support 
44   
45   public interface ByteReader {
46     int peekI2();
47     int peekU2();
48     int peekI4();
49     long peekU4();
50     
51     int readI2();
52     int readU2();
53     int readI4();
54     long readU4();
55     
56     void makeLittleEndian (short[] data);
57   }
58
59   public class LittleEndianReader implements ByteReader {
60     
61     @Override
62         public final int peekI2 () {
63       int idx = pos;
64       return (data[idx++] & 0xff) | (data[idx] << 8);
65     }
66
67     @Override
68         public final int peekU2 () {
69       int idx = pos;
70       return (data[idx++] & 0xff) | ((data[idx] & 0xff)<< 8);
71     }
72     
73     @Override
74         public final int peekI4 () {
75       int idx = pos;
76       byte[] data = BinaryClassSource.this.data;
77       return (data[idx++] & 0xff) | ((data[idx++] & 0xff) << 8) | ((data[idx++] & 0xff) << 16) | (data[idx] << 24);
78     }
79
80     @Override
81         public final long peekU4 () {
82       int idx = pos;
83       byte[] data = BinaryClassSource.this.data;
84       return (data[idx++] & 0xff) | ((data[idx++] & 0xff) << 8) | ((data[idx++] & 0xff) << 16) | ((data[idx] & 0xff) << 24);
85     }
86     
87     
88     @Override
89         public final int readI2 () {
90       int idx = pos;
91       pos += 2;
92       return (data[idx++] & 0xff) | (data[idx] << 8);
93     }
94
95     @Override
96         public final int readU2 () {
97       int idx = pos;
98       pos += 2;
99       return (data[idx++] & 0xff) | ((data[idx] & 0xff)<< 8);
100     }
101     
102     @Override
103         public final int readI4 () {
104       int idx = pos;
105       pos += 4;
106       byte[] data = BinaryClassSource.this.data;
107
108       return (data[idx++] & 0xff) | ((data[idx++] & 0xff) << 8) | ((data[idx++] & 0xff) << 16) | (data[idx] << 24);
109     }
110
111     @Override
112         public final long readU4 () {
113       int idx = pos;
114       pos += 4;
115       byte[] data = BinaryClassSource.this.data;
116
117       return (data[idx++] & 0xff) | ((data[idx++] & 0xff) << 8) | ((data[idx++] & 0xff) << 16) | ((data[idx] & 0xff) << 24);
118     }
119     
120     @Override
121         public final void makeLittleEndian (short[] data){
122       // nothing - we already are
123     }
124   }
125
126   
127   public class BigEndianReader implements ByteReader {
128     
129     @Override
130         public final int peekI2 () {
131       int idx = pos;
132       return (data[idx++] << 8) | (data[idx] & 0xff);
133     }
134
135     @Override
136         public final int peekU2 () {
137       int idx = pos;
138       return ((data[idx++] & 0xff) << 8) | (data[idx] & 0xff);
139     }
140     
141     @Override
142         public final int peekI4 () {
143       int idx = pos;
144       byte[] data = BinaryClassSource.this.data;
145
146       return (data[idx++] << 24) | ((data[idx++] & 0xff) << 16) | ((data[idx++] & 0xff) << 8) | (data[idx] & 0xff);
147     }
148
149     @Override
150         public final long peekU4 () {
151       int idx = pos;
152       byte[] data = BinaryClassSource.this.data;
153
154       return ((data[idx++] & 0xff) << 24) | ((data[idx++] & 0xff) << 16) | ((data[idx++] & 0xff) << 8) | (data[idx] & 0xff);
155     }
156
157     
158     @Override
159         public final int readI2 () {
160       int idx = pos;
161       pos += 2;
162       return (data[idx++] << 8) | (data[idx] & 0xff);
163     }
164
165     @Override
166         public final int readU2 () {
167       int idx = pos;
168       pos += 2;
169       return ((data[idx++] & 0xff) << 8) | (data[idx] & 0xff);
170     }
171     
172     @Override
173         public final int readI4 () {
174       int idx = pos;
175       pos += 4;
176       byte[] data = BinaryClassSource.this.data;
177
178       return (data[idx++] << 24) | ((data[idx++] & 0xff) << 16) | ((data[idx++] & 0xff) << 8) | (data[idx] & 0xff);
179     }
180
181     @Override
182         public final long readU4 () {
183       int idx = pos;
184       pos += 4;
185       byte[] data = BinaryClassSource.this.data;
186
187       return ((data[idx++] & 0xff) << 24) | ((data[idx++] & 0xff) << 16) | ((data[idx++] & 0xff) << 8) | (data[idx] & 0xff);
188     }
189     
190     @Override
191         public final void makeLittleEndian (short[] data){
192       for (int i=0; i<data.length; i++){
193         short s = data[i];
194         s = (short) (((s & 0xFF00) >> 8) | (s << 8));
195         data[i] = s;
196       }
197     }
198   }
199   
200   //----------------------------------- BinaryClassSource methods
201
202   
203   protected BinaryClassSource (byte[] data, int pos){
204    this.data = data;
205    this.pos = pos;
206    
207    this.byteReader = initializeByteReader();
208   }
209   
210   protected BinaryClassSource (File file) throws ClassParseException {
211     FileInputStream is = null;
212     try {
213       is = new FileInputStream(file);
214       long len = file.length();
215       if (len > Integer.MAX_VALUE || len <= 0){   // classfile of size > 2GB not supported
216         error("cannot read file of size: " + len);
217       }
218       data = new byte[(int)len];
219       readData(is);
220       
221     } catch (FileNotFoundException fnfx) {
222       error("classfile not found: " + file.getPath());
223
224     } finally {
225       if (is != null) {
226         try {
227           is.close();
228         } catch (IOException iox) {
229           error("failed to close file: " + file.getPath());
230         }
231       }
232     }
233     
234     this.byteReader = initializeByteReader();
235   }
236   
237   protected ByteReader initializeByteReader(){
238     // Java classfiles are big endian
239     return new BigEndianReader();
240   }
241   
242   protected void readData (InputStream is) throws ClassParseException {
243     try {
244       int nRead = 0;
245
246       while (nRead < data.length){
247         int n = is.read(data, nRead, (data.length - nRead));
248         if (n < 0){
249           error("premature end of dex file: " + data.length + '/' + nRead);
250         }
251         nRead += n;
252       }
253
254     } catch (IOException iox){
255       error("failed to read dex file");
256     }
257   }
258   
259   public void stopParsing(){
260     throw new BailOut();
261   }
262
263   protected void error(String msg) throws ClassParseException {
264     throw new ClassParseException(msg);
265   }
266   
267   /**
268    * obtain current classfile data. This is mainly provided to allow
269    * on-the-fly classfile instrumentation with 3rd party libraries
270    * 
271    * BEWARE - this is not a copy, i.e. any modification of the returned data
272    * might cause the parsing to fail.
273    */
274   public byte[] getData(){
275     return data;
276   }
277   
278   public int getPos(){
279     return pos;
280   }
281   
282   public boolean hasMoreData(){
283     return pos < data.length;
284   }
285   
286   // for selective parsing
287   public void setPos (int newPos){
288     pos = newPos;
289   }
290   
291   public void pushPos(){
292     if (posStack == null){
293       posStack = new int[4];
294       posStack[0] = pos;
295       top = 0;
296     } else {
297       top++;
298       if (top == posStack.length){
299         int[] newStack = new int[posStack.length * 2];
300         System.arraycopy(posStack, 0, newStack, 0, posStack.length);
301         posStack = newStack;
302       }
303       posStack[top] = pos;
304     }
305   }
306   
307   public void popPos(){
308     if (top >= 0){
309       pos = posStack[top];
310       top--;
311     }
312   }
313   
314   //--- the low level type specific read methods
315   
316   public static String readModifiedUTF8String( byte[] data, int pos, int len) throws ClassParseException {
317     
318     int n = 0; // the number of chars in buf
319     char[] buf = new char[len]; // it can't be more, but it can be less chars
320     
321     // \u0001 - \u007f             : single byte chars:  0xxxxxxx
322     // \u0000 and \u0080 - \u07ff  : double byte chars:  110xxxxx, 10xxxxxx
323     // \u0800 - \uffff             : tripple byte chars: 1110xxxx, 10xxxxxx, 10xxxxxx
324     
325     int max = pos+len;
326     for (int i=pos; i<max; i++){
327       int c = data[i] & 0xff;
328       if ((c & 0x80) == 0){ // single byte char  0xxxxxxx
329         buf[n++] = (char)c;
330         
331       } else {
332         if ((c & 0x40) != 0){      // 11xxxxxx
333           
334           // for the sake of efficiency, we don't check for the trailing zero bit in the marker,
335           // we just mask it out
336           if ((c & 0x20) == 0) {   // 110xxxxx - double byte char
337             buf[n++] = (char) (((c & 0x1f) << 6) | (data[++i] & 0x3f));
338             
339           } else {                 // 1110xxxx - tripple byte char
340             buf[n++] = (char) (((c & 0x0f) << 12) | ((data[++i] & 0x3f) << 6) | (data[++i] & 0x3f));
341           }
342           
343         } else {
344           throw new ClassParseException("malformed modified UTF-8 input: ");
345         }
346       }
347     }
348     
349     return new String(buf, 0, n);
350   }
351   
352   public final int readByte(){
353     return data[pos++];
354   }
355
356   public final int readUByte(){
357     return (data[pos++] & 0xff);
358   }
359   
360   public final byte[] read (int n){
361     byte[] b = new byte[n];
362     System.arraycopy(data,pos,b,0,n);
363     pos += n;
364     return b;
365   }
366   
367   public String readByteString(int nChars){
368     char[] buf = new char[nChars];
369     for (int i=0; i<nChars; i++){
370       buf[i] = (char)data[pos++];
371     }
372     return new String(buf);
373   }
374   
375   //--- debugging
376   protected void dumpData (int startPos, int nBytes){
377     System.out.printf("%d +%d: [", startPos, nBytes);
378     for (int i=0; i<nBytes; i++){
379       System.out.printf("%02X ", data[startPos+i]);
380     }
381     System.out.println(']');
382   }
383     
384   protected String dataToString (int startPos, int nBytes){
385     StringBuilder sb = new StringBuilder();
386     int i1 = startPos + nBytes;
387     for (int i=startPos; i<i1; i++){
388       sb.append( Integer.toHexString(data[i]));
389       sb.append(' ');
390     }
391
392     return sb.toString();
393   }
394 }