// // Copyright (C) 2013 United States Government as represented by the // Administrator of the National Aeronautics and Space Administration // (NASA). All Rights Reserved. // // This software is distributed under the NASA Open Source Agreement // (NOSA), version 1.3. The NOSA has been approved by the Open Source // Initiative. See the file NOSA-1.3-JPF at the top of the distribution // directory tree for the complete NOSA document. // // THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY // KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT // LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO // SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR // A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT // THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT // DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE. // package gov.nasa.jpf.util; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import gov.nasa.jpf.vm.ClassParseException; /** * common root for classes that read classes from binary data */ public class BinaryClassSource { protected byte[] data; protected int pos; // temp index value during parsing protected int pc; // bytecode pos relative to method code start protected int[] posStack; protected int top; protected BinaryClassSource (byte[] data, int pos){ this.data = data; this.pos = pos; } protected BinaryClassSource (File file) throws ClassParseException { FileInputStream is = null; try { is = new FileInputStream(file); long len = file.length(); if (len > Integer.MAX_VALUE || len <= 0){ // classfile of size > 2GB not supported error("cannot read file of size: " + len); } data = new byte[(int)len]; readData(is); } catch (FileNotFoundException fnfx) { error("classfile not found: " + file.getPath()); } finally { if (is != null) { try { is.close(); } catch (IOException iox) { error("failed to close file: " + file.getPath()); } } } } protected void readData (InputStream is) throws ClassParseException { try { int nRead = 0; while (nRead < data.length){ int n = is.read(data, nRead, (data.length - nRead)); if (n < 0){ error("premature end of dex file: " + data.length + '/' + nRead); } nRead += n; } } catch (IOException iox){ error("failed to read dex file"); } } public void stopParsing(){ throw new BailOut(); } protected void error(String msg) throws ClassParseException { throw new ClassParseException(msg); } /** * obtain current classfile data. This is mainly provided to allow * on-the-fly classfile instrumentation with 3rd party libraries * * BEWARE - this is not a copy, i.e. any modification of the returned data * might cause the parsing to fail. */ public byte[] getData(){ return data; } public int getPos(){ return pos; } // for selective parsing public void setPos (int newPos){ pos = newPos; } public void pushPos(){ if (posStack == null){ posStack = new int[4]; posStack[0] = pos; top = 0; } else { top++; if (top == posStack.length){ int[] newStack = new int[posStack.length * 2]; System.arraycopy(posStack, 0, newStack, 0, posStack.length); posStack = newStack; } posStack[top] = pos; } } public void popPos(){ if (top >= 0){ pos = posStack[top]; top--; } } //--- the low level type specific read methods public static String readModifiedUTF8String( byte[] data, int pos, int len) throws ClassParseException { int n = 0; // the number of chars in buf char[] buf = new char[len]; // it can't be more, but it can be less chars // \u0001 - \u007f : single byte chars: 0xxxxxxx // \u0000 and \u0080 - \u07ff : double byte chars: 110xxxxx, 10xxxxxx // \u0800 - \uffff : tripple byte chars: 1110xxxx, 10xxxxxx, 10xxxxxx int max = pos+len; for (int i=pos; i<max; i++){ int c = data[i] & 0xff; if ((c & 0x80) == 0){ // single byte char 0xxxxxxx buf[n++] = (char)c; } else { if ((c & 0x40) != 0){ // 11xxxxxx // for the sake of efficiency, we don't check for the trailing zero bit in the marker, // we just mask it out if ((c & 0x20) == 0) { // 110xxxxx - double byte char buf[n++] = (char) (((c & 0x1f) << 6) | (data[++i] & 0x3f)); } else { // 1110xxxx - tripple byte char buf[n++] = (char) (((c & 0x0f) << 12) | ((data[++i] & 0x3f) << 6) | (data[++i] & 0x3f)); } } else { throw new ClassParseException("malformed modified UTF-8 input: "); } } } return new String(buf, 0, n); } public final int readByte(){ return data[pos++]; } public final int readUByte(){ return (data[pos++] & 0xff); } public final byte[] read (int n){ byte[] b = new byte[n]; System.arraycopy(data,pos,b,0,n); pos += n; return b; } //--- debugging protected void dumpData (int startPos, int nBytes){ System.out.printf("%d +%d: [", startPos, nBytes); for (int i=0; i<nBytes; i++){ System.out.printf("%02X ", data[startPos+i]); } System.out.println(']'); } protected String dataToString (int startPos, int nBytes){ StringBuilder sb = new StringBuilder(); int i1 = startPos + nBytes; for (int i=startPos; i<i1; i++){ sb.append( Integer.toHexString(data[i])); sb.append(' '); } return sb.toString(); } }