2 * Copyright (C) 2014, United States Government, as represented by the
3 * Administrator of the National Aeronautics and Space Administration.
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
10 * http://www.apache.org/licenses/LICENSE-2.0.
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.
19 package gov.nasa.jpf.util;
21 import gov.nasa.jpf.vm.ClassParseException;
23 import java.io.FileInputStream;
24 import java.io.FileNotFoundException;
25 import java.io.IOException;
26 import java.io.InputStream;
29 * common root for classes that read classes from binary data
31 public class BinaryClassSource {
33 protected byte[] data;
35 protected int pos; // temp index value during parsing
36 protected int pc; // bytecode pos relative to method code start
38 protected int[] posStack;
41 protected ByteReader byteReader;
43 //--------------------------------------------------------- variable endian support
45 public interface ByteReader {
56 void makeLittleEndian (short[] data);
59 public class LittleEndianReader implements ByteReader {
62 public final int peekI2 () {
64 return (data[idx++] & 0xff) | (data[idx] << 8);
68 public final int peekU2 () {
70 return (data[idx++] & 0xff) | ((data[idx] & 0xff)<< 8);
74 public final int peekI4 () {
76 byte[] data = BinaryClassSource.this.data;
77 return (data[idx++] & 0xff) | ((data[idx++] & 0xff) << 8) | ((data[idx++] & 0xff) << 16) | (data[idx] << 24);
81 public final long peekU4 () {
83 byte[] data = BinaryClassSource.this.data;
84 return (data[idx++] & 0xff) | ((data[idx++] & 0xff) << 8) | ((data[idx++] & 0xff) << 16) | ((data[idx] & 0xff) << 24);
89 public final int readI2 () {
92 return (data[idx++] & 0xff) | (data[idx] << 8);
96 public final int readU2 () {
99 return (data[idx++] & 0xff) | ((data[idx] & 0xff)<< 8);
103 public final int readI4 () {
106 byte[] data = BinaryClassSource.this.data;
108 return (data[idx++] & 0xff) | ((data[idx++] & 0xff) << 8) | ((data[idx++] & 0xff) << 16) | (data[idx] << 24);
112 public final long readU4 () {
115 byte[] data = BinaryClassSource.this.data;
117 return (data[idx++] & 0xff) | ((data[idx++] & 0xff) << 8) | ((data[idx++] & 0xff) << 16) | ((data[idx] & 0xff) << 24);
121 public final void makeLittleEndian (short[] data){
122 // nothing - we already are
127 public class BigEndianReader implements ByteReader {
130 public final int peekI2 () {
132 return (data[idx++] << 8) | (data[idx] & 0xff);
136 public final int peekU2 () {
138 return ((data[idx++] & 0xff) << 8) | (data[idx] & 0xff);
142 public final int peekI4 () {
144 byte[] data = BinaryClassSource.this.data;
146 return (data[idx++] << 24) | ((data[idx++] & 0xff) << 16) | ((data[idx++] & 0xff) << 8) | (data[idx] & 0xff);
150 public final long peekU4 () {
152 byte[] data = BinaryClassSource.this.data;
154 return ((data[idx++] & 0xff) << 24) | ((data[idx++] & 0xff) << 16) | ((data[idx++] & 0xff) << 8) | (data[idx] & 0xff);
159 public final int readI2 () {
162 return (data[idx++] << 8) | (data[idx] & 0xff);
166 public final int readU2 () {
169 return ((data[idx++] & 0xff) << 8) | (data[idx] & 0xff);
173 public final int readI4 () {
176 byte[] data = BinaryClassSource.this.data;
178 return (data[idx++] << 24) | ((data[idx++] & 0xff) << 16) | ((data[idx++] & 0xff) << 8) | (data[idx] & 0xff);
182 public final long readU4 () {
185 byte[] data = BinaryClassSource.this.data;
187 return ((data[idx++] & 0xff) << 24) | ((data[idx++] & 0xff) << 16) | ((data[idx++] & 0xff) << 8) | (data[idx] & 0xff);
191 public final void makeLittleEndian (short[] data){
192 for (int i=0; i<data.length; i++){
194 s = (short) (((s & 0xFF00) >> 8) | (s << 8));
200 //----------------------------------- BinaryClassSource methods
203 protected BinaryClassSource (byte[] data, int pos){
207 this.byteReader = initializeByteReader();
210 protected BinaryClassSource (File file) throws ClassParseException {
211 FileInputStream is = null;
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);
218 data = new byte[(int)len];
221 } catch (FileNotFoundException fnfx) {
222 error("classfile not found: " + file.getPath());
228 } catch (IOException iox) {
229 error("failed to close file: " + file.getPath());
234 this.byteReader = initializeByteReader();
237 protected ByteReader initializeByteReader(){
238 // Java classfiles are big endian
239 return new BigEndianReader();
242 protected void readData (InputStream is) throws ClassParseException {
246 while (nRead < data.length){
247 int n = is.read(data, nRead, (data.length - nRead));
249 error("premature end of dex file: " + data.length + '/' + nRead);
254 } catch (IOException iox){
255 error("failed to read dex file");
259 public void stopParsing(){
263 protected void error(String msg) throws ClassParseException {
264 throw new ClassParseException(msg);
268 * obtain current classfile data. This is mainly provided to allow
269 * on-the-fly classfile instrumentation with 3rd party libraries
271 * BEWARE - this is not a copy, i.e. any modification of the returned data
272 * might cause the parsing to fail.
274 public byte[] getData(){
282 public boolean hasMoreData(){
283 return pos < data.length;
286 // for selective parsing
287 public void setPos (int newPos){
291 public void pushPos(){
292 if (posStack == null){
293 posStack = new int[4];
298 if (top == posStack.length){
299 int[] newStack = new int[posStack.length * 2];
300 System.arraycopy(posStack, 0, newStack, 0, posStack.length);
307 public void popPos(){
314 //--- the low level type specific read methods
316 public static String readModifiedUTF8String( byte[] data, int pos, int len) throws ClassParseException {
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
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
326 for (int i=pos; i<max; i++){
327 int c = data[i] & 0xff;
328 if ((c & 0x80) == 0){ // single byte char 0xxxxxxx
332 if ((c & 0x40) != 0){ // 11xxxxxx
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));
339 } else { // 1110xxxx - tripple byte char
340 buf[n++] = (char) (((c & 0x0f) << 12) | ((data[++i] & 0x3f) << 6) | (data[++i] & 0x3f));
344 throw new ClassParseException("malformed modified UTF-8 input: ");
349 return new String(buf, 0, n);
352 public final int readByte(){
356 public final int readUByte(){
357 return (data[pos++] & 0xff);
360 public final byte[] read (int n){
361 byte[] b = new byte[n];
362 System.arraycopy(data,pos,b,0,n);
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++];
372 return new String(buf);
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]);
381 System.out.println(']');
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]));
392 return sb.toString();