mirror of
https://github.com/pxb1988/dex2jar.git
synced 2024-11-27 07:00:51 +00:00
merge 0.0.9.10 to default
This commit is contained in:
commit
e4a43f67d1
@ -110,7 +110,11 @@ public class FileUtils {
|
||||
}
|
||||
|
||||
private static void doFind(File dir, Set<String> exts, List<File> list, boolean r) {
|
||||
for (File f : dir.listFiles()) {
|
||||
File[] fs = dir.listFiles();
|
||||
if (fs == null) {
|
||||
return;
|
||||
}
|
||||
for (File f : fs) {
|
||||
if (f.isFile()) {
|
||||
String name = f.getName();
|
||||
for (String ext : exts) {
|
||||
|
@ -0,0 +1,255 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
package org.apache.xmlgraphics.image.codec.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A subclass of <code>SeekableStream</code> that may be used to wrap
|
||||
* a regular <code>InputStream</code>. Seeking backwards is supported
|
||||
* by means of an in-memory cache. For greater efficiency,
|
||||
* <code>FileCacheSeekableStream</code> should be used in
|
||||
* circumstances that allow the creation of a temporary file.
|
||||
*
|
||||
* <p> The <code>mark()</code> and <code>reset()</code> methods are
|
||||
* supported.
|
||||
*
|
||||
* <p><b> This class is not a committed part of the JAI API. It may
|
||||
* be removed or changed in future releases of JAI.</b>
|
||||
*/
|
||||
public final class MemoryCacheSeekableStream extends SeekableStream {
|
||||
|
||||
/** The source input stream. */
|
||||
private InputStream src;
|
||||
|
||||
/** Position of first unread byte. */
|
||||
private long pointer = 0;
|
||||
|
||||
/** Log_2 of the sector size. */
|
||||
private static final int SECTOR_SHIFT = 9;
|
||||
|
||||
/** The sector size. */
|
||||
private static final int SECTOR_SIZE = 1 << SECTOR_SHIFT;
|
||||
|
||||
/** A mask to determine the offset within a sector. */
|
||||
private static final int SECTOR_MASK = SECTOR_SIZE - 1;
|
||||
|
||||
/** A Vector of source sectors. */
|
||||
private List data = new ArrayList();
|
||||
|
||||
/** Number of sectors stored. */
|
||||
int sectors = 0;
|
||||
|
||||
/** Number of bytes read. */
|
||||
int length = 0;
|
||||
|
||||
/** True if we've previously reached the end of the source stream */
|
||||
boolean foundEOS = false;
|
||||
|
||||
/**
|
||||
* Constructs a <code>MemoryCacheSeekableStream</code> that takes
|
||||
* its source data from a regular <code>InputStream</code>.
|
||||
* Seeking backwards is supported by means of an in-memory cache.
|
||||
*/
|
||||
public MemoryCacheSeekableStream(InputStream src) {
|
||||
this.src = src;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that at least <code>pos</code> bytes are cached,
|
||||
* or the end of the source is reached. The return value
|
||||
* is equal to the smaller of <code>pos</code> and the
|
||||
* length of the source stream.
|
||||
*/
|
||||
private long readUntil(long pos) throws IOException {
|
||||
// We've already got enough data cached
|
||||
if (pos < length) {
|
||||
return pos;
|
||||
}
|
||||
// pos >= length but length isn't getting any bigger, so return it
|
||||
if (foundEOS) {
|
||||
return length;
|
||||
}
|
||||
|
||||
int sector = (int)(pos >> SECTOR_SHIFT);
|
||||
|
||||
// First unread sector
|
||||
int startSector = length >> SECTOR_SHIFT;
|
||||
|
||||
// Read sectors until the desired sector
|
||||
for (int i = startSector; i <= sector; i++) {
|
||||
byte[] buf = new byte[SECTOR_SIZE];
|
||||
data.add(buf);
|
||||
|
||||
// Read up to SECTOR_SIZE bytes
|
||||
int len = SECTOR_SIZE;
|
||||
int off = 0;
|
||||
while (len > 0) {
|
||||
int nbytes = src.read(buf, off, len);
|
||||
// Found the end-of-stream
|
||||
if (nbytes == -1) {
|
||||
foundEOS = true;
|
||||
return length;
|
||||
}
|
||||
off += nbytes;
|
||||
len -= nbytes;
|
||||
|
||||
// Record new data length
|
||||
length += nbytes;
|
||||
}
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> since all
|
||||
* <code>MemoryCacheSeekableStream</code> instances support seeking
|
||||
* backwards.
|
||||
*/
|
||||
public boolean canSeekBackwards() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current offset in this file.
|
||||
*
|
||||
* @return the offset from the beginning of the file, in bytes,
|
||||
* at which the next read occurs.
|
||||
*/
|
||||
public long getFilePointer() {
|
||||
return pointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the file-pointer offset, measured from the beginning of this
|
||||
* file, at which the next read occurs.
|
||||
*
|
||||
* @param pos the offset position, measured in bytes from the
|
||||
* beginning of the file, at which to set the file
|
||||
* pointer.
|
||||
* @exception IOException if <code>pos</code> is less than
|
||||
* <code>0</code> or if an I/O error occurs.
|
||||
*/
|
||||
public void seek(long pos) throws IOException {
|
||||
if (pos < 0) {
|
||||
throw new IOException("MemoryCacheSeekableStream0");
|
||||
}
|
||||
pointer = pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the next byte of data from the input stream. The value byte is
|
||||
* returned as an <code>int</code> in the range <code>0</code> to
|
||||
* <code>255</code>. If no byte is available because the end of the stream
|
||||
* has been reached, the value <code>-1</code> is returned. This method
|
||||
* blocks until input data is available, the end of the stream is detected,
|
||||
* or an exception is thrown.
|
||||
*
|
||||
* @return the next byte of data, or <code>-1</code> if the end of the
|
||||
* stream is reached.
|
||||
*/
|
||||
public int read() throws IOException {
|
||||
long next = pointer + 1;
|
||||
long pos = readUntil(next);
|
||||
if (pos >= next) {
|
||||
byte[] buf =
|
||||
(byte[])data.get((int)(pointer >> SECTOR_SHIFT));
|
||||
return buf[(int)(pointer++ & SECTOR_MASK)] & 0xff;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads up to <code>len</code> bytes of data from the input stream into
|
||||
* an array of bytes. An attempt is made to read as many as
|
||||
* <code>len</code> bytes, but a smaller number may be read, possibly
|
||||
* zero. The number of bytes actually read is returned as an integer.
|
||||
*
|
||||
* <p> This method blocks until input data is available, end of file is
|
||||
* detected, or an exception is thrown.
|
||||
*
|
||||
* <p> If <code>b</code> is <code>null</code>, a
|
||||
* <code>NullPointerException</code> is thrown.
|
||||
*
|
||||
* <p> If <code>off</code> is negative, or <code>len</code> is negative, or
|
||||
* <code>off+len</code> is greater than the length of the array
|
||||
* <code>b</code>, then an <code>IndexOutOfBoundsException</code> is
|
||||
* thrown.
|
||||
*
|
||||
* <p> If <code>len</code> is zero, then no bytes are read and
|
||||
* <code>0</code> is returned; otherwise, there is an attempt to read at
|
||||
* least one byte. If no byte is available because the stream is at end of
|
||||
* file, the value <code>-1</code> is returned; otherwise, at least one
|
||||
* byte is read and stored into <code>b</code>.
|
||||
*
|
||||
* <p> The first byte read is stored into element <code>b[off]</code>, the
|
||||
* next one into <code>b[off+1]</code>, and so on. The number of bytes read
|
||||
* is, at most, equal to <code>len</code>. Let <i>k</i> be the number of
|
||||
* bytes actually read; these bytes will be stored in elements
|
||||
* <code>b[off]</code> through <code>b[off+</code><i>k</i><code>-1]</code>,
|
||||
* leaving elements <code>b[off+</code><i>k</i><code>]</code> through
|
||||
* <code>b[off+len-1]</code> unaffected.
|
||||
*
|
||||
* <p> In every case, elements <code>b[0]</code> through
|
||||
* <code>b[off]</code> and elements <code>b[off+len]</code> through
|
||||
* <code>b[b.length-1]</code> are unaffected.
|
||||
*
|
||||
* <p> If the first byte cannot be read for any reason other than end of
|
||||
* file, then an <code>IOException</code> is thrown. In particular, an
|
||||
* <code>IOException</code> is thrown if the input stream has been closed.
|
||||
*
|
||||
* @param b the buffer into which the data is read.
|
||||
* @param off the start offset in array <code>b</code>
|
||||
* at which the data is written.
|
||||
* @param len the maximum number of bytes to read.
|
||||
* @return the total number of bytes read into the buffer, or
|
||||
* <code>-1</code> if there is no more data because the end of
|
||||
* the stream has been reached.
|
||||
*/
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
if (b == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
if ((off < 0) || (len < 0) || (off + len > b.length)) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
long pos = readUntil(pointer + len);
|
||||
// End-of-stream
|
||||
if (pos <= pointer) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
byte[] buf = (byte[])data.get((int)(pointer >> SECTOR_SHIFT));
|
||||
int nbytes = Math.min(len, SECTOR_SIZE - (int)(pointer & SECTOR_MASK));
|
||||
System.arraycopy(buf, (int)(pointer & SECTOR_MASK),
|
||||
b, off, nbytes);
|
||||
pointer += nbytes;
|
||||
return nbytes;
|
||||
}
|
||||
}
|
@ -0,0 +1,943 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
package org.apache.xmlgraphics.image.codec.util;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* An abstract subclass of <code>java.io.InputStream</code> that allows seeking
|
||||
* within the input, similar to the <code>RandomAccessFile</code> class.
|
||||
* Additionally, the <code>DataInput</code> interface is supported and extended
|
||||
* to include support for little-endian representations of fundamental data
|
||||
* types.
|
||||
*
|
||||
* <p> In addition to the familiar methods from <code>InputStream</code>, the
|
||||
* methods <code>getFilePointer()</code>, <code>seek()</code>, are defined as in
|
||||
* the <code>RandomAccessFile</code> class. The <code>canSeekBackwards()</code>
|
||||
* method will return <code>true</code> if it is permissible to seek to a
|
||||
* position earlier in the stream than the current value of
|
||||
* <code>getFilePointer()</code>. Some subclasses of
|
||||
* <code>SeekableStream</code> guarantee the ability to seek backwards while
|
||||
* others may not offer this feature in the interest of providing greater
|
||||
* efficiency for those users who do not require it.
|
||||
*
|
||||
* <p> The <code>DataInput</code> interface is supported as well. This included
|
||||
* the <code>skipBytes()</code> and <code>readFully()</code> methods and a
|
||||
* variety of <code>read</code> methods for various data types.
|
||||
*
|
||||
* <p> Three classes are provided for the purpose of adapting a standard
|
||||
* <code>InputStream</code> to the <code>SeekableStream</code> interface.
|
||||
* <code>ForwardSeekableStream</code> does not allows seeking backwards, but is
|
||||
* inexpensive to use. <code>FileCacheSeekableStream</code> maintains a copy of
|
||||
* all of the data read from the input in a temporary file; this file will be
|
||||
* discarded automatically when the <code>FileSeekableStream</code> is
|
||||
* finalized, or when the JVM exits normally.
|
||||
* <code>FileCacheSeekableStream</code> is intended to be reasonably efficient
|
||||
* apart from the unavoidable use of disk space. In circumstances where the
|
||||
* creation of a temporary file is not possible,
|
||||
* <code>MemoryCacheSeekableStream</code> may be used.
|
||||
* <code>MemoryCacheSeekableStream</code> creates a potentially large in-memory
|
||||
* buffer to store the stream data and so should be avoided when possible.
|
||||
*
|
||||
* <p> The <code>FileSeekableStream</code> class wraps a <code>File</code> or
|
||||
* <code>RandomAccessFile</code>. It forwards requests to the real underlying
|
||||
* file. It performs a limited amount of caching in order to avoid excessive
|
||||
* I/O costs.
|
||||
*
|
||||
* <p> The <code>SegmentedSeekableStream</code> class performs a different sort
|
||||
* of function. It creates a <code>SeekableStream</code> from another
|
||||
* <code>SeekableStream</code> by selecting a series of portions or "segments".
|
||||
* Each segment starts at a specified location within the source
|
||||
* <code>SeekableStream</code> and extends for a specified number of bytes. The
|
||||
* <code>StreamSegmentMapper</code> interface and <code>StreamSegment</code>
|
||||
* class may be used to compute the segment positions dynamically.
|
||||
*
|
||||
* <p> A convenience methods, <code>wrapInputStream</code> is provided to
|
||||
* construct a suitable <code>SeekableStream</code> instance whose data is
|
||||
* supplied by a given <code>InputStream</code>. The caller, by means of the
|
||||
* <code>canSeekBackwards</code> parameter, determines whether support for
|
||||
* seeking backwards is required.
|
||||
*
|
||||
*/
|
||||
public abstract class SeekableStream extends InputStream implements DataInput {
|
||||
|
||||
// /**
|
||||
// * Returns a <code>SeekableStream</code> that will read from a
|
||||
// * given <code>InputStream</code>, optionally including support
|
||||
// * for seeking backwards. This is a convenience method that
|
||||
// * avoids the need to instantiate specific subclasses of
|
||||
// * <code>SeekableStream</code> depending on the current security
|
||||
// * model.
|
||||
// *
|
||||
// * @param is An <code>InputStream</code>.
|
||||
// * @param canSeekBackwards <code>true</code> if the ability to seek
|
||||
// * backwards in the output is required.
|
||||
// * @return An instance of <code>SeekableStream</code>.
|
||||
// */
|
||||
// public static SeekableStream wrapInputStream(InputStream is,
|
||||
// boolean canSeekBackwards) {
|
||||
// SeekableStream stream = null;
|
||||
//
|
||||
// if (canSeekBackwards) {
|
||||
// try {
|
||||
// stream = new FileCacheSeekableStream(is);
|
||||
// } catch (Exception e) {
|
||||
// stream = new MemoryCacheSeekableStream(is);
|
||||
// }
|
||||
// } else {
|
||||
// stream = new ForwardSeekableStream(is);
|
||||
// }
|
||||
// return stream;
|
||||
// }
|
||||
|
||||
// Methods from InputStream
|
||||
|
||||
/**
|
||||
* Reads the next byte of data from the input stream. The value byte is
|
||||
* returned as an <code>int</code> in the range <code>0</code> to
|
||||
* <code>255</code>. If no byte is available because the end of the stream
|
||||
* has been reached, the value <code>-1</code> is returned. This method
|
||||
* blocks until input data is available, the end of the stream is detected,
|
||||
* or an exception is thrown.
|
||||
*
|
||||
* <p> A subclass must provide an implementation of this method.
|
||||
*
|
||||
* @return the next byte of data, or <code>-1</code> if the end of the
|
||||
* stream is reached.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public abstract int read() throws IOException;
|
||||
|
||||
/**
|
||||
* Reads up to <code>len</code> bytes of data from the input stream into
|
||||
* an array of bytes. An attempt is made to read as many as
|
||||
* <code>len</code> bytes, but a smaller number may be read, possibly
|
||||
* zero. The number of bytes actually read is returned as an integer.
|
||||
*
|
||||
* <p> This method blocks until input data is available, end of stream is
|
||||
* detected, or an exception is thrown.
|
||||
*
|
||||
* <p> If <code>b</code> is <code>null</code>, a
|
||||
* <code>NullPointerException</code> is thrown.
|
||||
*
|
||||
* <p> If <code>off</code> is negative, or <code>len</code> is negative, or
|
||||
* <code>off+len</code> is greater than the length of the array
|
||||
* <code>b</code>, then an <code>IndexOutOfBoundsException</code> is
|
||||
* thrown.
|
||||
*
|
||||
* <p> If <code>len</code> is zero, then no bytes are read and
|
||||
* <code>0</code> is returned; otherwise, there is an attempt to read at
|
||||
* least one byte. If no byte is available because the stream is at end of
|
||||
* stream, the value <code>-1</code> is returned; otherwise, at least one
|
||||
* byte is read and stored into <code>b</code>.
|
||||
*
|
||||
* <p> The first byte read is stored into element <code>b[off]</code>, the
|
||||
* next one into <code>b[off+1]</code>, and so on. The number of bytes read
|
||||
* is, at most, equal to <code>len</code>. Let <i>k</i> be the number of
|
||||
* bytes actually read; these bytes will be stored in elements
|
||||
* <code>b[off]</code> through <code>b[off+</code><i>k</i><code>-1]</code>,
|
||||
* leaving elements <code>b[off+</code><i>k</i><code>]</code> through
|
||||
* <code>b[off+len-1]</code> unaffected.
|
||||
*
|
||||
* <p> In every case, elements <code>b[0]</code> through
|
||||
* <code>b[off]</code> and elements <code>b[off+len]</code> through
|
||||
* <code>b[b.length-1]</code> are unaffected.
|
||||
*
|
||||
* <p> If the first byte cannot be read for any reason other than end of
|
||||
* stream, then an <code>IOException</code> is thrown. In particular, an
|
||||
* <code>IOException</code> is thrown if the input stream has been closed.
|
||||
*
|
||||
* <p> A subclass must provide an implementation of this method.
|
||||
*
|
||||
* @param b the buffer into which the data is read.
|
||||
* @param off the start offset in array <code>b</code>
|
||||
* at which the data is written.
|
||||
* @param len the maximum number of bytes to read.
|
||||
* @return the total number of bytes read into the buffer, or
|
||||
* <code>-1</code> if there is no more data because the end of
|
||||
* the stream has been reached.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public abstract int read(byte[] b, int off, int len) throws IOException;
|
||||
|
||||
// Implemented in InputStream:
|
||||
//
|
||||
// public int read(byte[] b) throws IOException {
|
||||
// public long skip(long n) throws IOException
|
||||
// public int available) throws IOException
|
||||
// public void close() throws IOException;
|
||||
|
||||
/** Marked position, shared by {@link ForwardSeekableStream} */
|
||||
protected long markPos = -1L;
|
||||
|
||||
/**
|
||||
* Marks the current file position for later return using
|
||||
* the <code>reset()</code> method.
|
||||
*/
|
||||
public synchronized void mark(int readLimit) {
|
||||
try {
|
||||
markPos = getFilePointer();
|
||||
} catch (IOException e) {
|
||||
markPos = -1L;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the file position to its position at the time of
|
||||
* the immediately previous call to the <code>mark()</code>
|
||||
* method.
|
||||
*/
|
||||
public synchronized void reset() throws IOException {
|
||||
if (markPos != -1) {
|
||||
seek(markPos);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if marking is supported.
|
||||
* Marking is automatically supported for <code>SeekableStream</code>
|
||||
* subclasses that support seeking backeards. Subclasses that do
|
||||
* not support seeking backwards but do support marking must override
|
||||
* this method.
|
||||
*/
|
||||
public boolean markSupported() {
|
||||
return canSeekBackwards();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if this object supports calls to
|
||||
* <code>seek(pos)</code> with an offset <code>pos</code> smaller
|
||||
* than the current offset, as returned by <code>getFilePointer</code>.
|
||||
*/
|
||||
public boolean canSeekBackwards() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current offset in this stream.
|
||||
*
|
||||
* @return the offset from the beginning of the stream, in bytes,
|
||||
* at which the next read occurs.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public abstract long getFilePointer() throws IOException;
|
||||
|
||||
/**
|
||||
* Sets the offset, measured from the beginning of this
|
||||
* stream, at which the next read occurs.
|
||||
*
|
||||
* <p> If <code>canSeekBackwards()</code> returns <code>false</code>,
|
||||
* then setting <code>pos</code> to an offset smaller than
|
||||
* the current value of <code>getFilePointer()</code> will have
|
||||
* no effect.
|
||||
*
|
||||
* @param pos the offset position, measured in bytes from the
|
||||
* beginning of the stream, at which to set the stream
|
||||
* pointer.
|
||||
* @exception IOException if <code>pos</code> is less than
|
||||
* <code>0</code> or if an I/O error occurs.
|
||||
*/
|
||||
public abstract void seek(long pos) throws IOException;
|
||||
|
||||
// Methods from RandomAccessFile
|
||||
|
||||
/**
|
||||
* Reads <code>b.length</code> bytes from this stream into the byte
|
||||
* array, starting at the current stream pointer. This method reads
|
||||
* repeatedly from the stream until the requested number of bytes are
|
||||
* read. This method blocks until the requested number of bytes are
|
||||
* read, the end of the stream is detected, or an exception is thrown.
|
||||
*
|
||||
* @param b the buffer into which the data is read.
|
||||
* @exception EOFException if this stream reaches the end before reading
|
||||
* all the bytes.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public final void readFully(byte[] b) throws IOException {
|
||||
readFully(b, 0, b.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads exactly <code>len</code> bytes from this stream into the byte
|
||||
* array, starting at the current stream pointer. This method reads
|
||||
* repeatedly from the stream until the requested number of bytes are
|
||||
* read. This method blocks until the requested number of bytes are
|
||||
* read, the end of the stream is detected, or an exception is thrown.
|
||||
*
|
||||
* @param b the buffer into which the data is read.
|
||||
* @param off the start offset of the data.
|
||||
* @param len the number of bytes to read.
|
||||
* @exception EOFException if this stream reaches the end before reading
|
||||
* all the bytes.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public final void readFully(byte[] b, int off, int len)
|
||||
throws IOException {
|
||||
int n = 0;
|
||||
do {
|
||||
int count = this.read(b, off + n, len - n);
|
||||
if (count < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
n += count;
|
||||
} while (n < len);
|
||||
}
|
||||
|
||||
// Methods from DataInput, plus little-endian versions
|
||||
|
||||
/**
|
||||
* Attempts to skip over <code>n</code> bytes of input discarding the
|
||||
* skipped bytes.
|
||||
* <p>
|
||||
*
|
||||
* This method may skip over some smaller number of bytes, possibly zero.
|
||||
* This may result from any of a number of conditions; reaching end of
|
||||
* stream before <code>n</code> bytes have been skipped is only one
|
||||
* possibility. This method never throws an <code>EOFException</code>.
|
||||
* The actual number of bytes skipped is returned. If <code>n</code>
|
||||
* is negative, no bytes are skipped.
|
||||
*
|
||||
* @param n the number of bytes to be skipped.
|
||||
* @return the actual number of bytes skipped.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public int skipBytes(int n) throws IOException {
|
||||
if (n <= 0) {
|
||||
return 0;
|
||||
}
|
||||
return (int)skip(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a <code>boolean</code> from this stream. This method reads a
|
||||
* single byte from the stream, starting at the current stream pointer.
|
||||
* A value of <code>0</code> represents
|
||||
* <code>false</code>. Any other value represents <code>true</code>.
|
||||
* This method blocks until the byte is read, the end of the stream
|
||||
* is detected, or an exception is thrown.
|
||||
*
|
||||
* @return the <code>boolean</code> value read.
|
||||
* @exception EOFException if this stream has reached the end.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public final boolean readBoolean() throws IOException {
|
||||
int ch = this.read();
|
||||
if (ch < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
return (ch != 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a signed eight-bit value from this stream. This method reads a
|
||||
* byte from the stream, starting from the current stream pointer.
|
||||
* If the byte read is <code>b</code>, where
|
||||
* <code>0 <= b <= 255</code>,
|
||||
* then the result is:
|
||||
* <blockquote><pre>
|
||||
* (byte)(b)
|
||||
* </pre></blockquote>
|
||||
* <p>
|
||||
* This method blocks until the byte is read, the end of the stream
|
||||
* is detected, or an exception is thrown.
|
||||
*
|
||||
* @return the next byte of this stream as a signed eight-bit
|
||||
* <code>byte</code>.
|
||||
* @exception EOFException if this stream has reached the end.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public final byte readByte() throws IOException {
|
||||
int ch = this.read();
|
||||
if (ch < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
return (byte)(ch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an unsigned eight-bit number from this stream. This method reads
|
||||
* a byte from this stream, starting at the current stream pointer,
|
||||
* and returns that byte.
|
||||
* <p>
|
||||
* This method blocks until the byte is read, the end of the stream
|
||||
* is detected, or an exception is thrown.
|
||||
*
|
||||
* @return the next byte of this stream, interpreted as an unsigned
|
||||
* eight-bit number.
|
||||
* @exception EOFException if this stream has reached the end.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public final int readUnsignedByte() throws IOException {
|
||||
int ch = this.read();
|
||||
if (ch < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
return ch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a signed 16-bit number from this stream.
|
||||
* The method reads two
|
||||
* bytes from this stream, starting at the current stream pointer.
|
||||
* If the two bytes read, in order, are
|
||||
* <code>b1</code> and <code>b2</code>, where each of the two values is
|
||||
* between <code>0</code> and <code>255</code>, inclusive, then the
|
||||
* result is equal to:
|
||||
* <blockquote><pre>
|
||||
* (short)((b1 << 8) | b2)
|
||||
* </pre></blockquote>
|
||||
* <p>
|
||||
* This method blocks until the two bytes are read, the end of the
|
||||
* stream is detected, or an exception is thrown.
|
||||
*
|
||||
* @return the next two bytes of this stream, interpreted as a signed
|
||||
* 16-bit number.
|
||||
* @exception EOFException if this stream reaches the end before reading
|
||||
* two bytes.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public final short readShort() throws IOException {
|
||||
int ch1 = this.read();
|
||||
int ch2 = this.read();
|
||||
if ((ch1 | ch2) < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
return (short)((ch1 << 8) + (ch2 << 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a signed 16-bit number from this stream in little-endian order.
|
||||
* The method reads two
|
||||
* bytes from this stream, starting at the current stream pointer.
|
||||
* If the two bytes read, in order, are
|
||||
* <code>b1</code> and <code>b2</code>, where each of the two values is
|
||||
* between <code>0</code> and <code>255</code>, inclusive, then the
|
||||
* result is equal to:
|
||||
* <blockquote><pre>
|
||||
* (short)((b2 << 8) | b1)
|
||||
* </pre></blockquote>
|
||||
* <p>
|
||||
* This method blocks until the two bytes are read, the end of the
|
||||
* stream is detected, or an exception is thrown.
|
||||
*
|
||||
* @return the next two bytes of this stream, interpreted as a signed
|
||||
* 16-bit number.
|
||||
* @exception EOFException if this stream reaches the end before reading
|
||||
* two bytes.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public final short readShortLE() throws IOException {
|
||||
int ch1 = this.read();
|
||||
int ch2 = this.read();
|
||||
if ((ch1 | ch2) < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
return (short)((ch2 << 8) + (ch1 << 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an unsigned 16-bit number from this stream. This method reads
|
||||
* two bytes from the stream, starting at the current stream pointer.
|
||||
* If the bytes read, in order, are
|
||||
* <code>b1</code> and <code>b2</code>, where
|
||||
* <code>0 <= b1, b2 <= 255</code>,
|
||||
* then the result is equal to:
|
||||
* <blockquote><pre>
|
||||
* (b1 << 8) | b2
|
||||
* </pre></blockquote>
|
||||
* <p>
|
||||
* This method blocks until the two bytes are read, the end of the
|
||||
* stream is detected, or an exception is thrown.
|
||||
*
|
||||
* @return the next two bytes of this stream, interpreted as an
|
||||
* unsigned 16-bit integer.
|
||||
* @exception EOFException if this stream reaches the end before reading
|
||||
* two bytes.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public final int readUnsignedShort() throws IOException {
|
||||
int ch1 = this.read();
|
||||
int ch2 = this.read();
|
||||
if ((ch1 | ch2) < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
return (ch1 << 8) + (ch2 << 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an unsigned 16-bit number from this stream in little-endian order.
|
||||
* This method reads
|
||||
* two bytes from the stream, starting at the current stream pointer.
|
||||
* If the bytes read, in order, are
|
||||
* <code>b1</code> and <code>b2</code>, where
|
||||
* <code>0 <= b1, b2 <= 255</code>,
|
||||
* then the result is equal to:
|
||||
* <blockquote><pre>
|
||||
* (b2 << 8) | b1
|
||||
* </pre></blockquote>
|
||||
* <p>
|
||||
* This method blocks until the two bytes are read, the end of the
|
||||
* stream is detected, or an exception is thrown.
|
||||
*
|
||||
* @return the next two bytes of this stream, interpreted as an
|
||||
* unsigned 16-bit integer.
|
||||
* @exception EOFException if this stream reaches the end before reading
|
||||
* two bytes.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public final int readUnsignedShortLE() throws IOException {
|
||||
int ch1 = this.read();
|
||||
int ch2 = this.read();
|
||||
if ((ch1 | ch2) < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
return (ch2 << 8) + (ch1 << 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a Unicode character from this stream. This method reads two
|
||||
* bytes from the stream, starting at the current stream pointer.
|
||||
* If the bytes read, in order, are
|
||||
* <code>b1</code> and <code>b2</code>, where
|
||||
* <code>0 <= b1, b2 <= 255</code>,
|
||||
* then the result is equal to:
|
||||
* <blockquote><pre>
|
||||
* (char)((b1 << 8) | b2)
|
||||
* </pre></blockquote>
|
||||
* <p>
|
||||
* This method blocks until the two bytes are read, the end of the
|
||||
* stream is detected, or an exception is thrown.
|
||||
*
|
||||
* @return the next two bytes of this stream as a Unicode character.
|
||||
* @exception EOFException if this stream reaches the end before reading
|
||||
* two bytes.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public final char readChar() throws IOException {
|
||||
int ch1 = this.read();
|
||||
int ch2 = this.read();
|
||||
if ((ch1 | ch2) < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
return (char)((ch1 << 8) + (ch2 << 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a Unicode character from this stream in little-endian order.
|
||||
* This method reads two
|
||||
* bytes from the stream, starting at the current stream pointer.
|
||||
* If the bytes read, in order, are
|
||||
* <code>b1</code> and <code>b2</code>, where
|
||||
* <code>0 <= b1, b2 <= 255</code>,
|
||||
* then the result is equal to:
|
||||
* <blockquote><pre>
|
||||
* (char)((b2 << 8) | b1)
|
||||
* </pre></blockquote>
|
||||
* <p>
|
||||
* This method blocks until the two bytes are read, the end of the
|
||||
* stream is detected, or an exception is thrown.
|
||||
*
|
||||
* @return the next two bytes of this stream as a Unicode character.
|
||||
* @exception EOFException if this stream reaches the end before reading
|
||||
* two bytes.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public final char readCharLE() throws IOException {
|
||||
int ch1 = this.read();
|
||||
int ch2 = this.read();
|
||||
if ((ch1 | ch2) < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
return (char)((ch2 << 8) + (ch1 << 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a signed 32-bit integer from this stream. This method reads 4
|
||||
* bytes from the stream, starting at the current stream pointer.
|
||||
* If the bytes read, in order, are <code>b1</code>,
|
||||
* <code>b2</code>, <code>b3</code>, and <code>b4</code>, where
|
||||
* <code>0 <= b1, b2, b3, b4 <= 255</code>,
|
||||
* then the result is equal to:
|
||||
* <blockquote><pre>
|
||||
* (b1 << 24) | (b2 << 16) + (b3 << 8) + b4
|
||||
* </pre></blockquote>
|
||||
* <p>
|
||||
* This method blocks until the four bytes are read, the end of the
|
||||
* stream is detected, or an exception is thrown.
|
||||
*
|
||||
* @return the next four bytes of this stream, interpreted as an
|
||||
* <code>int</code>.
|
||||
* @exception EOFException if this stream reaches the end before reading
|
||||
* four bytes.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public final int readInt() throws IOException {
|
||||
int ch1 = this.read();
|
||||
int ch2 = this.read();
|
||||
int ch3 = this.read();
|
||||
int ch4 = this.read();
|
||||
if ((ch1 | ch2 | ch3 | ch4) < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a signed 32-bit integer from this stream in little-endian order.
|
||||
* This method reads 4
|
||||
* bytes from the stream, starting at the current stream pointer.
|
||||
* If the bytes read, in order, are <code>b1</code>,
|
||||
* <code>b2</code>, <code>b3</code>, and <code>b4</code>, where
|
||||
* <code>0 <= b1, b2, b3, b4 <= 255</code>,
|
||||
* then the result is equal to:
|
||||
* <blockquote><pre>
|
||||
* (b4 << 24) | (b3 << 16) + (b2 << 8) + b1
|
||||
* </pre></blockquote>
|
||||
* <p>
|
||||
* This method blocks until the four bytes are read, the end of the
|
||||
* stream is detected, or an exception is thrown.
|
||||
*
|
||||
* @return the next four bytes of this stream, interpreted as an
|
||||
* <code>int</code>.
|
||||
* @exception EOFException if this stream reaches the end before reading
|
||||
* four bytes.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public final int readIntLE() throws IOException {
|
||||
int ch1 = this.read();
|
||||
int ch2 = this.read();
|
||||
int ch3 = this.read();
|
||||
int ch4 = this.read();
|
||||
if ((ch1 | ch2 | ch3 | ch4) < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
return ((ch4 << 24) + (ch3 << 16) + (ch2 << 8) + (ch1 << 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an unsigned 32-bit integer from this stream. This method reads 4
|
||||
* bytes from the stream, starting at the current stream pointer.
|
||||
* If the bytes read, in order, are <code>b1</code>,
|
||||
* <code>b2</code>, <code>b3</code>, and <code>b4</code>, where
|
||||
* <code>0 <= b1, b2, b3, b4 <= 255</code>,
|
||||
* then the result is equal to:
|
||||
* <blockquote><pre>
|
||||
* (b1 << 24) | (b2 << 16) + (b3 << 8) + b4
|
||||
* </pre></blockquote>
|
||||
* <p>
|
||||
* This method blocks until the four bytes are read, the end of the
|
||||
* stream is detected, or an exception is thrown.
|
||||
*
|
||||
* @return the next four bytes of this stream, interpreted as a
|
||||
* <code>long</code>.
|
||||
* @exception EOFException if this stream reaches the end before reading
|
||||
* four bytes.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public final long readUnsignedInt() throws IOException {
|
||||
long ch1 = this.read();
|
||||
long ch2 = this.read();
|
||||
long ch3 = this.read();
|
||||
long ch4 = this.read();
|
||||
if ((ch1 | ch2 | ch3 | ch4) < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
|
||||
}
|
||||
|
||||
private byte[] ruileBuf = new byte[4];
|
||||
|
||||
/**
|
||||
* Reads an unsigned 32-bit integer from this stream in little-endian
|
||||
* order. This method reads 4
|
||||
* bytes from the stream, starting at the current stream pointer.
|
||||
* If the bytes read, in order, are <code>b1</code>,
|
||||
* <code>b2</code>, <code>b3</code>, and <code>b4</code>, where
|
||||
* <code>0 <= b1, b2, b3, b4 <= 255</code>,
|
||||
* then the result is equal to:
|
||||
* <blockquote><pre>
|
||||
* (b4 << 24) | (b3 << 16) + (b2 << 8) + b1
|
||||
* </pre></blockquote>
|
||||
* <p>
|
||||
* This method blocks until the four bytes are read, the end of the
|
||||
* stream is detected, or an exception is thrown.
|
||||
*
|
||||
* @return the next four bytes of this stream, interpreted as a
|
||||
* <code>long</code>.
|
||||
* @exception EOFException if this stream reaches the end before reading
|
||||
* four bytes.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public final long readUnsignedIntLE() throws IOException {
|
||||
this.readFully(ruileBuf);
|
||||
long ch1 = (ruileBuf[0] & 0xff);
|
||||
long ch2 = (ruileBuf[1] & 0xff);
|
||||
long ch3 = (ruileBuf[2] & 0xff);
|
||||
long ch4 = (ruileBuf[3] & 0xff);
|
||||
|
||||
return ((ch4 << 24) + (ch3 << 16) + (ch2 << 8) + (ch1 << 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a signed 64-bit integer from this stream. This method reads eight
|
||||
* bytes from the stream, starting at the current stream pointer.
|
||||
* If the bytes read, in order, are
|
||||
* <code>b1</code>, <code>b2</code>, <code>b3</code>,
|
||||
* <code>b4</code>, <code>b5</code>, <code>b6</code>,
|
||||
* <code>b7</code>, and <code>b8,</code> where:
|
||||
* <blockquote><pre>
|
||||
* 0 <= b1, b2, b3, b4, b5, b6, b7, b8 <=255,
|
||||
* </pre></blockquote>
|
||||
* <p>
|
||||
* then the result is equal to:
|
||||
* <p><blockquote><pre>
|
||||
* ((long)b1 << 56) + ((long)b2 << 48)
|
||||
* + ((long)b3 << 40) + ((long)b4 << 32)
|
||||
* + ((long)b5 << 24) + ((long)b6 << 16)
|
||||
* + ((long)b7 << 8) + b8
|
||||
* </pre></blockquote>
|
||||
* <p>
|
||||
* This method blocks until the eight bytes are read, the end of the
|
||||
* stream is detected, or an exception is thrown.
|
||||
*
|
||||
* @return the next eight bytes of this stream, interpreted as a
|
||||
* <code>long</code>.
|
||||
* @exception EOFException if this stream reaches the end before reading
|
||||
* eight bytes.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public final long readLong() throws IOException {
|
||||
return ((long)(readInt()) << 32) + (readInt() & 0xFFFFFFFFL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a signed 64-bit integer from this stream in little-endian
|
||||
* order. This method reads eight
|
||||
* bytes from the stream, starting at the current stream pointer.
|
||||
* If the bytes read, in order, are
|
||||
* <code>b1</code>, <code>b2</code>, <code>b3</code>,
|
||||
* <code>b4</code>, <code>b5</code>, <code>b6</code>,
|
||||
* <code>b7</code>, and <code>b8,</code> where:
|
||||
* <blockquote><pre>
|
||||
* 0 <= b1, b2, b3, b4, b5, b6, b7, b8 <=255,
|
||||
* </pre></blockquote>
|
||||
* <p>
|
||||
* then the result is equal to:
|
||||
* <p><blockquote><pre>
|
||||
* ((long)b1 << 56) + ((long)b2 << 48)
|
||||
* + ((long)b3 << 40) + ((long)b4 << 32)
|
||||
* + ((long)b5 << 24) + ((long)b6 << 16)
|
||||
* + ((long)b7 << 8) + b8
|
||||
* </pre></blockquote>
|
||||
* <p>
|
||||
* This method blocks until the eight bytes are read, the end of the
|
||||
* stream is detected, or an exception is thrown.
|
||||
*
|
||||
* @return the next eight bytes of this stream, interpreted as a
|
||||
* <code>long</code>.
|
||||
* @exception EOFException if this stream reaches the end before reading
|
||||
* eight bytes.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public final long readLongLE() throws IOException {
|
||||
int i1 = readIntLE();
|
||||
int i2 = readIntLE();
|
||||
return ((long)i2 << 32) + (i1 & 0xFFFFFFFFL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a <code>float</code> from this stream. This method reads an
|
||||
* <code>int</code> value, starting at the current stream pointer,
|
||||
* as if by the <code>readInt</code> method
|
||||
* and then converts that <code>int</code> to a <code>float</code>
|
||||
* using the <code>intBitsToFloat</code> method in class
|
||||
* <code>Float</code>.
|
||||
* <p>
|
||||
* This method blocks until the four bytes are read, the end of the
|
||||
* stream is detected, or an exception is thrown.
|
||||
*
|
||||
* @return the next four bytes of this stream, interpreted as a
|
||||
* <code>float</code>.
|
||||
* @exception EOFException if this stream reaches the end before reading
|
||||
* four bytes.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public final float readFloat() throws IOException {
|
||||
return Float.intBitsToFloat(readInt());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a <code>float</code> from this stream in little-endian order.
|
||||
* This method reads an
|
||||
* <code>int</code> value, starting at the current stream pointer,
|
||||
* as if by the <code>readInt</code> method
|
||||
* and then converts that <code>int</code> to a <code>float</code>
|
||||
* using the <code>intBitsToFloat</code> method in class
|
||||
* <code>Float</code>.
|
||||
* <p>
|
||||
* This method blocks until the four bytes are read, the end of the
|
||||
* stream is detected, or an exception is thrown.
|
||||
*
|
||||
* @return the next four bytes of this stream, interpreted as a
|
||||
* <code>float</code>.
|
||||
* @exception EOFException if this stream reaches the end before reading
|
||||
* four bytes.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public final float readFloatLE() throws IOException {
|
||||
return Float.intBitsToFloat(readIntLE());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a <code>double</code> from this stream. This method reads a
|
||||
* <code>long</code> value, starting at the current stream pointer,
|
||||
* as if by the <code>readLong</code> method
|
||||
* and then converts that <code>long</code> to a <code>double</code>
|
||||
* using the <code>longBitsToDouble</code> method in
|
||||
* class <code>Double</code>.
|
||||
* <p>
|
||||
* This method blocks until the eight bytes are read, the end of the
|
||||
* stream is detected, or an exception is thrown.
|
||||
*
|
||||
* @return the next eight bytes of this stream, interpreted as a
|
||||
* <code>double</code>.
|
||||
* @exception EOFException if this stream reaches the end before reading
|
||||
* eight bytes.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public final double readDouble() throws IOException {
|
||||
return Double.longBitsToDouble(readLong());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a <code>double</code> from this stream in little-endian order.
|
||||
* This method reads a
|
||||
* <code>long</code> value, starting at the current stream pointer,
|
||||
* as if by the <code>readLong</code> method
|
||||
* and then converts that <code>long</code> to a <code>double</code>
|
||||
* using the <code>longBitsToDouble</code> method in
|
||||
* class <code>Double</code>.
|
||||
* <p>
|
||||
* This method blocks until the eight bytes are read, the end of the
|
||||
* stream is detected, or an exception is thrown.
|
||||
*
|
||||
* @return the next eight bytes of this stream, interpreted as a
|
||||
* <code>double</code>.
|
||||
* @exception EOFException if this stream reaches the end before reading
|
||||
* eight bytes.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public final double readDoubleLE() throws IOException {
|
||||
return Double.longBitsToDouble(readLongLE());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the next line of text from this stream. This method successively
|
||||
* reads bytes from the stream, starting at the current stream pointer,
|
||||
* until it reaches a line terminator or the end
|
||||
* of the stream. Each byte is converted into a character by taking the
|
||||
* byte's value for the lower eight bits of the character and setting the
|
||||
* high eight bits of the character to zero. This method does not,
|
||||
* therefore, support the full Unicode character set.
|
||||
*
|
||||
* <p> A line of text is terminated by a carriage-return character
|
||||
* (<code>'\r'</code>), a newline character (<code>'\n'</code>), a
|
||||
* carriage-return character immediately followed by a newline character,
|
||||
* or the end of the stream. Line-terminating characters are discarded and
|
||||
* are not included as part of the string returned.
|
||||
*
|
||||
* <p> This method blocks until a newline character is read, a carriage
|
||||
* return and the byte following it are read (to see if it is a newline),
|
||||
* the end of the stream is reached, or an exception is thrown.
|
||||
*
|
||||
* @return the next line of text from this stream, or null if end
|
||||
* of stream is encountered before even one byte is read.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public final String readLine() throws IOException {
|
||||
StringBuffer input = new StringBuffer();
|
||||
int c = -1;
|
||||
boolean eol = false;
|
||||
|
||||
while (!eol) {
|
||||
c = read();
|
||||
switch (c) {
|
||||
case -1:
|
||||
case '\n':
|
||||
eol = true;
|
||||
break;
|
||||
case '\r':
|
||||
eol = true;
|
||||
long cur = getFilePointer();
|
||||
if ((read()) != '\n') {
|
||||
seek(cur);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
input.append((char)c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((c == -1) && (input.length() == 0)) {
|
||||
return null;
|
||||
}
|
||||
return input.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads in a string from this stream. The string has been encoded
|
||||
* using a modified UTF-8 format.
|
||||
* <p>
|
||||
* The first two bytes are read, starting from the current stream
|
||||
* pointer, as if by
|
||||
* <code>readUnsignedShort</code>. This value gives the number of
|
||||
* following bytes that are in the encoded string, not
|
||||
* the length of the resulting string. The following bytes are then
|
||||
* interpreted as bytes encoding characters in the UTF-8 format
|
||||
* and are converted into characters.
|
||||
* <p>
|
||||
* This method blocks until all the bytes are read, the end of the
|
||||
* stream is detected, or an exception is thrown.
|
||||
*
|
||||
* @return a Unicode string.
|
||||
* @exception EOFException if this stream reaches the end before
|
||||
* reading all the bytes.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
* @exception java.io.UTFDataFormatException if the bytes do not represent
|
||||
* valid UTF-8 encoding of a Unicode string.
|
||||
*/
|
||||
public final String readUTF() throws IOException {
|
||||
return DataInputStream.readUTF(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases any system resources associated with this stream
|
||||
* by calling the <code>close()</code> method.
|
||||
*/
|
||||
protected void finalize() throws Throwable {
|
||||
super.finalize();
|
||||
close();
|
||||
}
|
||||
}
|
@ -92,7 +92,7 @@ public abstract class Value implements Cloneable {
|
||||
INVOKE_INTERFACE, INVOKE_NEW, INVOKE_SPECIAL, INVOKE_STATIC, INVOKE_VIRTUAL, //
|
||||
LE("<="), LENGTH, LOCAL, LT("<"), MUL("*"), NE("!="), NEG, //
|
||||
NEW, NEW_ARRAY, NEW_MUTI_ARRAY, NOT, OR("|"), PARAMETER_REF, REM("%"), SHL("<<"), SHR(">>"), SUB("-"), THIS_REF, USHR(
|
||||
">>>"), XOR("^");
|
||||
">>>"), XOR("^"), FILLED_ARRAY;
|
||||
private String name;
|
||||
|
||||
VT() {
|
||||
|
@ -168,9 +168,9 @@ public final class Exprs {
|
||||
return new TypeExpr(VT.NEW_ARRAY, size, elementType);
|
||||
}
|
||||
|
||||
// public static NewExpr nNew(Type type) {
|
||||
// return new NewExpr(type);
|
||||
// }
|
||||
public static FilledArrayExpr nFilledArray(Type elementType, Value[] datas) {
|
||||
return new FilledArrayExpr(box(datas), elementType);
|
||||
}
|
||||
|
||||
public static NewMutiArrayExpr nNewMutiArray(Type base, int dim, Value[] sizes) {
|
||||
return new NewMutiArrayExpr(base, dim, box(sizes));
|
||||
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 Panxiaobo
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.googlecode.dex2jar.ir.expr;
|
||||
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
import com.googlecode.dex2jar.ir.Value;
|
||||
import com.googlecode.dex2jar.ir.Value.EnExpr;
|
||||
import com.googlecode.dex2jar.ir.Value.VT;
|
||||
import com.googlecode.dex2jar.ir.ValueBox;
|
||||
|
||||
/**
|
||||
* Represent a FILLED_ARRAY expression.
|
||||
*
|
||||
* @see VT#FILLED_ARRAY
|
||||
*/
|
||||
public class FilledArrayExpr extends EnExpr {
|
||||
|
||||
public Type type;
|
||||
|
||||
public FilledArrayExpr(ValueBox[] datas, Type type) {
|
||||
super(VT.FILLED_ARRAY, datas);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value clone() {
|
||||
ValueBox[] nOps = new ValueBox[ops.length];
|
||||
for (int i = 0; i < nOps.length; i++) {
|
||||
nOps[i] = new ValueBox(ops[i].value.clone());
|
||||
}
|
||||
return new FilledArrayExpr(nOps, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder().append('{');
|
||||
for (int i = 0; i < ops.length; i++) {
|
||||
sb.append(ops[i]).append(", ");
|
||||
}
|
||||
if (ops.length > 0) {
|
||||
sb.setLength(sb.length() - 2); // remove tail ", "
|
||||
}
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
@ -15,7 +15,6 @@
|
||||
*/
|
||||
package com.googlecode.dex2jar.ir.stmt;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
@ -124,7 +123,6 @@ public abstract class Stmt {
|
||||
public Object _ls_forward_frame;
|
||||
|
||||
public Stmt _ts_default_next;
|
||||
public List<Stmt> _ts_tos;
|
||||
|
||||
/**
|
||||
* The number of argument
|
||||
|
@ -0,0 +1,218 @@
|
||||
/*
|
||||
* dex2jar - Tools to work with android .dex and java .class files
|
||||
* Copyright (c) 2009-2012 Panxiaobo
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.googlecode.dex2jar.ir.ts;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
import com.googlecode.dex2jar.ir.Constant;
|
||||
import com.googlecode.dex2jar.ir.IrMethod;
|
||||
import com.googlecode.dex2jar.ir.Local;
|
||||
import com.googlecode.dex2jar.ir.Value;
|
||||
import com.googlecode.dex2jar.ir.Value.E1Expr;
|
||||
import com.googlecode.dex2jar.ir.Value.E2Expr;
|
||||
import com.googlecode.dex2jar.ir.Value.EnExpr;
|
||||
import com.googlecode.dex2jar.ir.Value.VT;
|
||||
import com.googlecode.dex2jar.ir.ValueBox;
|
||||
import com.googlecode.dex2jar.ir.expr.ArrayExpr;
|
||||
import com.googlecode.dex2jar.ir.expr.Exprs;
|
||||
import com.googlecode.dex2jar.ir.expr.FieldExpr;
|
||||
import com.googlecode.dex2jar.ir.stmt.Stmt;
|
||||
import com.googlecode.dex2jar.ir.stmt.Stmt.E1Stmt;
|
||||
import com.googlecode.dex2jar.ir.stmt.Stmt.E2Stmt;
|
||||
import com.googlecode.dex2jar.ir.stmt.Stmt.ST;
|
||||
import com.googlecode.dex2jar.ir.stmt.StmtList;
|
||||
import com.googlecode.dex2jar.ir.stmt.Stmts;
|
||||
|
||||
/**
|
||||
* run after {@link ZeroTransformer}, to deal with following code
|
||||
*
|
||||
* <pre>
|
||||
* int[] a = null;
|
||||
* int b = a[1];
|
||||
* </pre>
|
||||
*
|
||||
* replace {@code int b = a[1];} to {@code throw new NullPointException()}, and we get
|
||||
*
|
||||
* <pre>
|
||||
* int[] a = null;
|
||||
* throw new NullPointException();
|
||||
* </pre>
|
||||
*
|
||||
* @author Panxiaobo
|
||||
*
|
||||
*/
|
||||
public class ArrayNullPointerTransformer implements Transformer {
|
||||
|
||||
@Override
|
||||
public void transform(IrMethod irMethod) {
|
||||
for (Stmt p = irMethod.stmts.getFirst(); p != null;) {
|
||||
if (arrayNPE(p)) {
|
||||
Stmt q = p.getNext();
|
||||
replaceNPE(irMethod.stmts, irMethod.locals, p);
|
||||
p = q;
|
||||
continue;
|
||||
}
|
||||
p = p.getNext();
|
||||
}
|
||||
}
|
||||
|
||||
private void replaceNPE(StmtList stmts, List<Local> locals, Stmt p) {
|
||||
List<Value> values = new ArrayList<Value>();
|
||||
switch (p.et) {
|
||||
case E1:
|
||||
tryAdd(((E1Stmt) p).op.value, values);
|
||||
break;
|
||||
case E2:
|
||||
E2Stmt e2 = (E2Stmt) p;
|
||||
switch (e2.op1.value.vt) {
|
||||
case LOCAL:
|
||||
tryAdd(e2.op2.value, values);
|
||||
break;
|
||||
case ARRAY:
|
||||
ArrayExpr ae = (ArrayExpr) e2.op1.value;
|
||||
if (tryAdd(ae.op1.value, values)) {
|
||||
if (tryAdd(ae.op2.value, values)) {
|
||||
tryAdd(e2.op2.value, values);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FIELD:// putfield
|
||||
FieldExpr fe = (FieldExpr) e2.op1.value;
|
||||
if (fe.op == null || fe.op.value == null || tryAdd(fe.op.value, values)) {
|
||||
tryAdd(e2.op2.value, values);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (tryAdd(e2.op2.value, values)) {
|
||||
tryAdd(e2.op1.value, values);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (Value value : values) {
|
||||
switch (value.vt) {
|
||||
case CONSTANT:
|
||||
case LOCAL:
|
||||
break;
|
||||
default:
|
||||
Local n = Exprs.nLocal("xxx");
|
||||
locals.add(n);
|
||||
stmts.insertBefore(p, Stmts.nAssign(n, value));
|
||||
}
|
||||
}
|
||||
stmts.insertBefore(p,
|
||||
Stmts.nThrow(Exprs.nInvokeNew(new Value[0], new Type[0], Type.getType(NullPointerException.class))));
|
||||
stmts.remove(p);
|
||||
}
|
||||
|
||||
private boolean tryAdd(Value value, List<Value> values) {
|
||||
if (!arrayNPE(value)) {
|
||||
values.add(value);
|
||||
return true;
|
||||
} else {
|
||||
switch (value.et) {
|
||||
case E0:
|
||||
values.add(value);
|
||||
break;
|
||||
case E1:
|
||||
E1Expr e1 = (E1Expr) value;
|
||||
if (e1.op == null || e1.op.value == null) {
|
||||
return false;
|
||||
}
|
||||
tryAdd(e1.op.value, values);
|
||||
break;
|
||||
case E2:
|
||||
E2Expr e2 = (E2Expr) value;
|
||||
if (e2.vt == VT.ARRAY && e2.op1.value.vt == VT.CONSTANT) {
|
||||
Constant cst = (Constant) e2.op1.value;
|
||||
if (cst.value.equals(Integer.valueOf(0))) {
|
||||
tryAdd(e2.op2.value, values);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (tryAdd(e2.op1.value, values)) {
|
||||
tryAdd(e2.op2.value, values);
|
||||
}
|
||||
|
||||
case En:
|
||||
for (ValueBox vb : ((EnExpr) value).ops) {
|
||||
if (!tryAdd(vb.value, values)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean arrayNPE(Stmt p) {
|
||||
switch (p.et) {
|
||||
case E0:
|
||||
return false;
|
||||
case E1:
|
||||
if (p.st == ST.GOTO) {
|
||||
return false;
|
||||
}
|
||||
return arrayNPE(((E1Stmt) p).op.value);
|
||||
case E2:
|
||||
E2Stmt e2 = (E2Stmt) p;
|
||||
switch (e2.op1.value.vt) {
|
||||
case ARRAY:
|
||||
case FIELD:
|
||||
return arrayNPE(e2.op1.value) || arrayNPE(e2.op2.value);
|
||||
default:
|
||||
return arrayNPE(e2.op2.value) || arrayNPE(e2.op1.value);
|
||||
}
|
||||
case En:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean arrayNPE(Value value) {
|
||||
switch (value.et) {
|
||||
case E0:
|
||||
return false;
|
||||
case E1:
|
||||
E1Expr e1 = (E1Expr) value;
|
||||
if (e1.op == null || e1.op.value == null) {
|
||||
return false;
|
||||
}
|
||||
return arrayNPE(e1.op.value);
|
||||
case E2:
|
||||
E2Expr e2 = (E2Expr) value;
|
||||
if (e2.vt == VT.ARRAY && e2.op1.value.vt == VT.CONSTANT) {
|
||||
Constant cst = (Constant) e2.op1.value;
|
||||
if (cst.value.equals(Integer.valueOf(0))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return arrayNPE(e2.op1.value) || arrayNPE(e2.op2.value);
|
||||
|
||||
case En:
|
||||
for (ValueBox vb : ((EnExpr) value).ops) {
|
||||
if (arrayNPE(vb.value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package com.googlecode.dex2jar.ir.ts;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@ -14,6 +15,16 @@ import com.googlecode.dex2jar.ir.stmt.Stmt.ST;
|
||||
import com.googlecode.dex2jar.ir.stmt.StmtList;
|
||||
import com.googlecode.dex2jar.ir.stmt.Stmts;
|
||||
|
||||
/**
|
||||
* Try to clean following between a {@link Trap}
|
||||
* <ol>
|
||||
* <li>Move {@link Stmt}s outside a {@link Trap} if {@link Stmt}s are not throw</li>
|
||||
* <li>Remove {@link Trap} if all {@link Stmt}s are not throw</li>
|
||||
* </ol>
|
||||
*
|
||||
* @author bob
|
||||
*
|
||||
*/
|
||||
public class EndRemover implements Transformer {
|
||||
|
||||
static boolean isSimple(Stmt stmt) {
|
||||
@ -135,14 +146,15 @@ public class EndRemover implements Transformer {
|
||||
|
||||
@Override
|
||||
public void transform(IrMethod irMethod) {
|
||||
for (Trap trap : irMethod.traps) {
|
||||
for (Trap trap : new ArrayList<Trap>(irMethod.traps)) {// copy the list and we can remove one from original list
|
||||
LabelStmt start = null;
|
||||
|
||||
boolean removeTrap = true;
|
||||
for (Stmt p = trap.start.getNext(); p != null && p != trap.end;) {
|
||||
boolean notThrow = Cfg.notThrow(p);
|
||||
if (!notThrow) {
|
||||
start = null;
|
||||
p = p.getNext();
|
||||
removeTrap = false;
|
||||
continue;
|
||||
}
|
||||
switch (p.st) {
|
||||
@ -170,6 +182,9 @@ public class EndRemover implements Transformer {
|
||||
p = p.getNext();
|
||||
}
|
||||
}
|
||||
if (removeTrap) {
|
||||
irMethod.traps.remove(trap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,9 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
import com.googlecode.dex2jar.ir.Constant;
|
||||
import com.googlecode.dex2jar.ir.IrMethod;
|
||||
import com.googlecode.dex2jar.ir.Local;
|
||||
import com.googlecode.dex2jar.ir.Value;
|
||||
@ -28,13 +31,17 @@ import com.googlecode.dex2jar.ir.Value.E2Expr;
|
||||
import com.googlecode.dex2jar.ir.Value.EnExpr;
|
||||
import com.googlecode.dex2jar.ir.Value.VT;
|
||||
import com.googlecode.dex2jar.ir.ValueBox;
|
||||
import com.googlecode.dex2jar.ir.expr.ArrayExpr;
|
||||
import com.googlecode.dex2jar.ir.expr.Exprs;
|
||||
import com.googlecode.dex2jar.ir.expr.FilledArrayExpr;
|
||||
import com.googlecode.dex2jar.ir.expr.InvokeExpr;
|
||||
import com.googlecode.dex2jar.ir.expr.NewExpr;
|
||||
import com.googlecode.dex2jar.ir.expr.TypeExpr;
|
||||
import com.googlecode.dex2jar.ir.stmt.AssignStmt;
|
||||
import com.googlecode.dex2jar.ir.stmt.JumpStmt;
|
||||
import com.googlecode.dex2jar.ir.stmt.LookupSwitchStmt;
|
||||
import com.googlecode.dex2jar.ir.stmt.Stmt;
|
||||
import com.googlecode.dex2jar.ir.stmt.Stmt.ST;
|
||||
import com.googlecode.dex2jar.ir.stmt.StmtList;
|
||||
import com.googlecode.dex2jar.ir.stmt.Stmts;
|
||||
import com.googlecode.dex2jar.ir.stmt.TableSwitchStmt;
|
||||
@ -238,6 +245,113 @@ public class LocalRemove implements Transformer {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Merge some simple filled arrays
|
||||
* merge:
|
||||
* a[][]=new xx[2][];a[0]=new xx[2];a[0][0]=b;a[0][1]=c;a[1]=new xx[2];a[1][0]=d;a[1][1]=e; -> a[][]=new xx[][]{{b,c},{d,e}};
|
||||
* tmp[][]=new xx[][]{{b,c},{d,e}};a=tmp; -> a=new xx[][]{{b,c},{d,e}};
|
||||
* not merge:
|
||||
* a[]=new xx[3];a[0]=b;a[1]=c;(Not full)
|
||||
* a[]=new xx[2];a[0]=b;a[1]=c.gg();(Not simple)
|
||||
*/
|
||||
boolean changed = false;
|
||||
do {
|
||||
changed = false;
|
||||
for (int p = 0; p < orderList.size(); p++) {
|
||||
Stmt st = orderList.get(p);
|
||||
if (st == null || !list.contains(st)) {
|
||||
continue;
|
||||
}
|
||||
switch (st.st) {
|
||||
|
||||
case ASSIGN:
|
||||
AssignStmt as = (AssignStmt) st;
|
||||
if (as.op2.value.vt == VT.NEW_ARRAY) {
|
||||
TypeExpr te = (TypeExpr)as.op2.value;
|
||||
Value val = as.op1.value;
|
||||
if (te.op.value instanceof Constant) {
|
||||
int arraySize = (Integer)((Constant)te.op.value).value;
|
||||
Type type = te.type;
|
||||
int size = 0;
|
||||
int empty = 0;
|
||||
//Verify array data
|
||||
for (int j = 1; j < orderList.size() - p; j++) {
|
||||
Stmt st2 = orderList.get(p + j);
|
||||
if(st2 == null) {
|
||||
empty ++;
|
||||
continue;
|
||||
}
|
||||
if (st2.st == ST.ASSIGN) {
|
||||
AssignStmt as2 = (AssignStmt) st2;
|
||||
if(as2.op1.value.vt == VT.ARRAY) {
|
||||
ArrayExpr ae2 = (ArrayExpr)as2.op1.value;
|
||||
if ((ae2.op1.value == val) && (ae2.op2.value instanceof Constant)) {
|
||||
int idx = (Integer)((Constant)ae2.op2.value).value;
|
||||
if (idx == (j-empty-1)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
size = j -1;
|
||||
break;
|
||||
}
|
||||
int dataSize = size - empty;
|
||||
if (dataSize == arraySize) {//Not full array may cause some NullPoint problems
|
||||
Value[] vbs = new Value[arraySize];
|
||||
for (int j = 1; j <= size; j++) {
|
||||
Stmt st2 = orderList.get(p + j);
|
||||
if(st2 == null) {
|
||||
continue;
|
||||
}
|
||||
AssignStmt as2 = (AssignStmt) st2;
|
||||
ArrayExpr ae2 = (ArrayExpr)as2.op1.value;
|
||||
int idx = ((Integer)((Constant)ae2.op2.value).value);
|
||||
vbs[idx] = as2.op2.value;
|
||||
orderList.set((p + j), null);
|
||||
list.remove(st2);
|
||||
}
|
||||
Local loc = (Local)val;
|
||||
loc._ls_read_count -= dataSize;
|
||||
FilledArrayExpr fa = Exprs.nFilledArray(type, vbs);
|
||||
AssignStmt nas = Stmts.nAssign(loc, fa);
|
||||
list.replace(st, nas);
|
||||
orderList.set(p, nas);
|
||||
changed = true;
|
||||
//Merge tmp Locals
|
||||
if (loc._ls_write_count == 1 && loc._ls_read_count == 1) {
|
||||
Stmt st3 = null;
|
||||
for (int j = size + 1; j < orderList.size() - p; j++) {
|
||||
st3 = orderList.get(p + j);
|
||||
if (st3 != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (st3 != null && (st3.st == ST.ASSIGN || st3.st == ST.IDENTITY)) {
|
||||
AssignStmt as3 = (AssignStmt) st3;
|
||||
if (as3.op2.value == loc) {
|
||||
loc._ls_read_count = 0;
|
||||
loc._ls_write_count = 0;
|
||||
|
||||
list.remove(nas);
|
||||
orderList.set(p, null);
|
||||
|
||||
AssignStmt nas3 = Stmts.nAssign(as3.op1.value, fa);
|
||||
list.replace(st3, nas3);
|
||||
orderList.set(orderList.indexOf(st3), nas3);
|
||||
|
||||
je.locals.remove(loc);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (changed);
|
||||
|
||||
{
|
||||
List<ValueBox> vbs = new ArrayList<ValueBox>(20);
|
||||
|
||||
|
@ -24,6 +24,7 @@ import com.googlecode.dex2jar.ir.Value.E2Expr;
|
||||
import com.googlecode.dex2jar.ir.Value.VT;
|
||||
import com.googlecode.dex2jar.ir.expr.CastExpr;
|
||||
import com.googlecode.dex2jar.ir.expr.FieldExpr;
|
||||
import com.googlecode.dex2jar.ir.expr.FilledArrayExpr;
|
||||
import com.googlecode.dex2jar.ir.expr.InvokeExpr;
|
||||
import com.googlecode.dex2jar.ir.expr.NewExpr;
|
||||
import com.googlecode.dex2jar.ir.expr.RefExpr;
|
||||
@ -82,6 +83,14 @@ public class LocalType implements Transformer {
|
||||
return t2;
|
||||
}
|
||||
|
||||
public static boolean needMerge(Type t1, Type t2) {
|
||||
if ((t1.getSort() == Type.ARRAY && t2.getSort() == Type.OBJECT) ||
|
||||
(t2.getSort() == Type.ARRAY && t1.getSort() == Type.OBJECT)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static TypeBox trimTypeBox(TypeBox tb) {
|
||||
while (tb != tb.xtype.tb) {
|
||||
tb = tb.xtype.tb;
|
||||
@ -189,6 +198,7 @@ public class LocalType implements Transformer {
|
||||
switch (v.vt) {
|
||||
case ARRAY:
|
||||
// TODO associate array expr types
|
||||
type(tb1, Type.getType(Object.class));
|
||||
type(tb2, Type.INT_TYPE);
|
||||
break;
|
||||
case ADD:
|
||||
@ -235,8 +245,16 @@ public class LocalType implements Transformer {
|
||||
type(tb, Type.BOOLEAN_TYPE);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case En:
|
||||
switch (v.vt) {
|
||||
case FILLED_ARRAY:
|
||||
FilledArrayExpr fae = (FilledArrayExpr) v;
|
||||
type(fae, Type.getType("[" + fae.type.getDescriptor()));
|
||||
for (int i = 0; i < fae.ops.length; i++) {
|
||||
exec(fae.ops[i].value);
|
||||
}
|
||||
break;
|
||||
case INVOKE_NEW:
|
||||
case INVOKE_STATIC: {
|
||||
InvokeExpr ie = (InvokeExpr) v;
|
||||
@ -281,10 +299,13 @@ public class LocalType implements Transformer {
|
||||
tb2.xtype = tb1.xtype;
|
||||
return;
|
||||
}
|
||||
Type nt = merge(tb1.xtype.type, tb2.xtype.type);
|
||||
tb1.xtype.tb = tb2;
|
||||
tb1.xtype = tb2.xtype;
|
||||
tb2.xtype.type = nt;
|
||||
//Only merge for special case
|
||||
if (needMerge(tb1.xtype.type, tb2.xtype.type)) {
|
||||
Type nt = merge(tb1.xtype.type, tb2.xtype.type);
|
||||
tb1.xtype.tb = tb2;
|
||||
tb1.xtype = tb2.xtype;
|
||||
tb2.xtype.type = nt;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,8 +1,8 @@
|
||||
package com.googlecode.dex2jar.ir.ts;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.Stack;
|
||||
@ -23,7 +23,6 @@ import com.googlecode.dex2jar.ir.stmt.Stmts;
|
||||
import com.googlecode.dex2jar.ir.stmt.TableSwitchStmt;
|
||||
|
||||
public class TopologicalSort implements Transformer {
|
||||
|
||||
@Override
|
||||
public void transform(IrMethod irMethod) {
|
||||
if (irMethod.traps.size() > 0) {
|
||||
@ -34,7 +33,7 @@ public class TopologicalSort implements Transformer {
|
||||
// 1. generate graph
|
||||
init(stmts, irMethod.traps);
|
||||
|
||||
// 2.
|
||||
// 2.remove any loop in the graph
|
||||
removeLoop(stmts);
|
||||
|
||||
// 3. topological sorting algorithms
|
||||
@ -104,7 +103,7 @@ public class TopologicalSort implements Transformer {
|
||||
if (stmt._cfg_froms.size() == 0 || stack.size() == 0) {
|
||||
stmt._cfg_visited = visitedFlag;
|
||||
out.add(stmt);
|
||||
for (Stmt t : stmt._ts_tos) {
|
||||
for (Stmt t : stmt._cfg_tos) {
|
||||
t._cfg_froms.remove(stmt);
|
||||
if (t._cfg_visited != visitedFlag) {
|
||||
stack.push(t);
|
||||
@ -115,35 +114,106 @@ public class TopologicalSort implements Transformer {
|
||||
return out;
|
||||
}
|
||||
|
||||
private static class Item {
|
||||
public Stmt stmt;
|
||||
public Iterator<Stmt> it;
|
||||
public boolean visitedAdded = false;
|
||||
}
|
||||
|
||||
private Item buildItem(Stmt stmt) {
|
||||
Item item = new Item();
|
||||
item.stmt = stmt;
|
||||
item.it = new ArrayList<Stmt>(stmt._cfg_tos).iterator();
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* A graph has a cycle if and only if depth-first search produces a back edge
|
||||
* A graph has a cycle if and only if depth-first search produces a back edge if there are
|
||||
*
|
||||
* @param stmts
|
||||
*/
|
||||
private void removeLoop(StmtList stmts) {
|
||||
dfsRemove(stmts.getFirst(), new HashSet<Stmt>());
|
||||
if (stmts.getSize() < 50) {// use Recursive if no more than 50 stmts
|
||||
dfsRecursiveCallRemove(stmts.getFirst(), new HashSet<Stmt>());
|
||||
} else {
|
||||
dfsStackRemove(stmts.getFirst(), new HashSet<Stmt>());
|
||||
}
|
||||
}
|
||||
|
||||
private void dfsRemove(Stmt stmt, Set<Stmt> visited) {
|
||||
visited.add(stmt);
|
||||
for (Stmt to : new ArrayList<Stmt>(stmt._ts_tos)) {
|
||||
if (visited.contains(to)) {// a loop
|
||||
to._cfg_froms.remove(stmt);
|
||||
while (stmt._ts_tos.remove(to)) {
|
||||
/**
|
||||
* Remove loop by store value in stack
|
||||
*
|
||||
* @param stmt
|
||||
* @param visited
|
||||
*/
|
||||
private void dfsStackRemove(Stmt stmt, Set<Stmt> visited) {
|
||||
Stack<Item> stack = new Stack<Item>();
|
||||
stack.push(buildItem(stmt));
|
||||
while (!stack.empty()) {
|
||||
Item item = stack.peek();
|
||||
stmt = item.stmt;
|
||||
item.stmt._cfg_visited = true;
|
||||
if (!item.visitedAdded) {
|
||||
visited.add(item.stmt);
|
||||
item.visitedAdded = true;
|
||||
}
|
||||
boolean needPop = true;
|
||||
Iterator<Stmt> it = item.it;
|
||||
while (it.hasNext()) {
|
||||
Stmt to = it.next();
|
||||
if (visited.contains(to)) {// a loop here
|
||||
// cut the loop
|
||||
to._cfg_froms.remove(stmt);
|
||||
stmt._cfg_tos.remove(to);
|
||||
} else {
|
||||
if (!to._cfg_visited) {
|
||||
needPop = false;
|
||||
stack.push(buildItem(to));
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (!to._cfg_visited) {
|
||||
dfsRemove(to, visited);
|
||||
}
|
||||
if (needPop) {
|
||||
if (item.visitedAdded) {
|
||||
visited.remove(stmt);
|
||||
stack.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove Loop by Recursive Method Call, if there are 500+ stmt, we got a out-of-stack error
|
||||
*
|
||||
* @param stmt
|
||||
* @param visited
|
||||
*/
|
||||
private void dfsRecursiveCallRemove(Stmt stmt, Set<Stmt> visited) {
|
||||
if (stmt._cfg_visited) {// make sure every node in visited once
|
||||
return;
|
||||
} else {
|
||||
stmt._cfg_visited = true;
|
||||
}
|
||||
visited.add(stmt);
|
||||
// copy the _ts_tos, so we can modify the collection
|
||||
List<Stmt> tos = new ArrayList<Stmt>(stmt._cfg_tos);
|
||||
for (Stmt to : tos) {
|
||||
if (visited.contains(to)) {// a loop here
|
||||
// cut the loop
|
||||
to._cfg_froms.remove(stmt);
|
||||
stmt._cfg_tos.remove(to);
|
||||
} else {
|
||||
dfsRecursiveCallRemove(to, visited);
|
||||
}
|
||||
}
|
||||
visited.remove(stmt);
|
||||
stmt._cfg_visited = true;
|
||||
}
|
||||
|
||||
private static void link(Stmt from, Stmt to) {
|
||||
if (to == null) {// last stmt is a LabelStmt
|
||||
return;
|
||||
}
|
||||
from._ts_tos.add(to);
|
||||
from._cfg_tos.add(to);
|
||||
to._cfg_froms.add(from);
|
||||
}
|
||||
|
||||
@ -179,10 +249,10 @@ public class TopologicalSort implements Transformer {
|
||||
} else {
|
||||
stmt._cfg_froms.clear();
|
||||
}
|
||||
if (stmt._ts_tos == null) {
|
||||
stmt._ts_tos = new ArrayList<Stmt>(2);
|
||||
if (stmt._cfg_tos == null) {
|
||||
stmt._cfg_tos = new TreeSet<Stmt>(stmts);
|
||||
} else {
|
||||
stmt._ts_tos.clear();
|
||||
stmt._cfg_tos.clear();
|
||||
}
|
||||
}
|
||||
// 2.1 link exception handler
|
||||
@ -231,18 +301,17 @@ public class TopologicalSort implements Transformer {
|
||||
case GOTO:
|
||||
JumpStmt js = (JumpStmt) stmt;
|
||||
for (Stmt f : stmt._cfg_froms) {
|
||||
f._ts_tos.remove(stmt);
|
||||
f._ts_tos.addAll(stmt._ts_tos);
|
||||
f._cfg_tos.remove(stmt);
|
||||
f._cfg_tos.addAll(stmt._cfg_tos);
|
||||
f._ts_default_next = js.target;
|
||||
}
|
||||
|
||||
for (Stmt t : stmt._ts_tos) {
|
||||
for (Stmt t : stmt._cfg_tos) {
|
||||
t._cfg_froms.remove(stmt);
|
||||
t._cfg_froms.addAll(stmt._cfg_froms);
|
||||
}
|
||||
break;
|
||||
}
|
||||
Collections.reverse(stmt._ts_tos);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,7 @@ import com.googlecode.dex2jar.ir.ts.Cfg.FrameVisitor;
|
||||
*/
|
||||
public class ZeroTransformer implements Transformer {
|
||||
|
||||
static class Phi {
|
||||
public static class Phi {
|
||||
public Boolean isZero = null;
|
||||
// public Set<Phi> parents = new HashSet<Phi>();
|
||||
public Set<Phi> children = new HashSet<Phi>();
|
||||
@ -249,7 +249,7 @@ public class ZeroTransformer implements Transformer {
|
||||
break;
|
||||
case E2:
|
||||
E2Stmt e2 = (E2Stmt) p;
|
||||
if (e2.st != ST.ASSIGN && e2.st != ST.IDENTITY) {
|
||||
if (e2.op1.value.vt != VT.LOCAL) {
|
||||
replace(e2.op1, frame);
|
||||
}
|
||||
replace(e2.op2, frame);
|
||||
@ -262,7 +262,6 @@ public class ZeroTransformer implements Transformer {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void replace(ValueBox op, Phi[] frame) {
|
||||
|
@ -111,4 +111,17 @@ public class TopologicalSortTest {
|
||||
// System.out.println("after======");
|
||||
// System.out.println(jm);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHugeStmt() {
|
||||
IrMethod jm = new IrMethod();
|
||||
StmtList list = jm.stmts;
|
||||
Local b = nLocal("a");
|
||||
jm.locals.add(b);
|
||||
for (int i = 0; i < 100000; i++) {
|
||||
list.add(Stmts.nAssign(b, Constant.nInt(i)));
|
||||
}
|
||||
new TopologicalSort().transform(jm);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -72,7 +72,9 @@ import com.googlecode.dex2jar.visitors.DexCodeVisitor;
|
||||
}
|
||||
|
||||
private void findLabels(DataIn in, int instruction_size) {
|
||||
for (int baseOffset = in.getCurrentPosition(), currentOffset = 0; currentOffset < instruction_size; currentOffset = (in
|
||||
int baseOffset = in.getCurrentPosition();
|
||||
fixIssue130(in, instruction_size);
|
||||
for (int currentOffset = (in.getCurrentPosition() - baseOffset) / 2; currentOffset < instruction_size; currentOffset = (in
|
||||
.getCurrentPosition() - baseOffset) / 2) {
|
||||
int opcode = in.readUShortx();
|
||||
int uOpcodeH = opcode >> 8;
|
||||
@ -82,6 +84,7 @@ import com.googlecode.dex2jar.visitors.DexCodeVisitor;
|
||||
opcode = uOpcodeL;
|
||||
}
|
||||
}
|
||||
|
||||
OpcodeFormat format = OpcodeFormat.get(opcode, dex.apiLevel);
|
||||
try {
|
||||
switch (opcode) {
|
||||
@ -180,6 +183,41 @@ import com.googlecode.dex2jar.visitors.DexCodeVisitor;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a trick to fix issue 130 http://code.google.com/p/dex2jar/issues/detail?id=130
|
||||
*
|
||||
* <pre>
|
||||
* 036280: 3200 0900 |0000: if-eq v0, v0, 0009 // +0009
|
||||
* 036284: 2600 0300 0000 |0002: fill-array-data v0, 00000005 // +00000003
|
||||
* 03628a: 0003 0100 0800 0000 7010 ce0b 0000 ... |0005: array-data (8 units)
|
||||
* </pre>
|
||||
*
|
||||
* skip the if-eq, and direct read from 0009
|
||||
*
|
||||
* @param in
|
||||
* @param instruction_size
|
||||
*/
|
||||
private void fixIssue130(DataIn in, int instruction_size) {
|
||||
if (instruction_size < 4) {
|
||||
return;
|
||||
}
|
||||
int base = in.getCurrentPosition();
|
||||
int opcode = in.readUShortx();
|
||||
int uOpcodeH = opcode >> 8;
|
||||
{
|
||||
int uOpcodeL = opcode & 0xFF;
|
||||
if (uOpcodeL != 0xFF) {
|
||||
opcode = uOpcodeL;
|
||||
}
|
||||
}
|
||||
if ((opcode == OP_IF_EQ) && ((uOpcodeH & 0xF) == (uOpcodeH >> 4))) {
|
||||
int offset = in.readShortx();
|
||||
in.skip(offset * 2 - 4);
|
||||
} else {
|
||||
in.move(base);
|
||||
}
|
||||
}
|
||||
|
||||
private void findTryCatch(DataIn in, DexCodeVisitor dcv, int tries_size) {
|
||||
int encoded_catch_handler_list = in.getCurrentPosition() + tries_size * 8;
|
||||
for (int i = 0; i < tries_size; i++) {
|
||||
@ -293,12 +331,16 @@ import com.googlecode.dex2jar.visitors.DexCodeVisitor;
|
||||
dcv.visitEnd();
|
||||
}
|
||||
|
||||
// 处理指令
|
||||
private void acceptInsn(DataIn in, int instruction_size, DexOpcodeAdapter n) {
|
||||
// 处理指令
|
||||
|
||||
Iterator<Integer> labelOffsetIterator = this.labels.keySet().iterator();
|
||||
Integer nextLabelOffset = labelOffsetIterator.hasNext() ? labelOffsetIterator.next() : null;
|
||||
for (int baseOffset = in.getCurrentPosition(), currentOffset = 0; currentOffset < instruction_size; currentOffset = (in
|
||||
int baseOffset = in.getCurrentPosition();
|
||||
fixIssue130(in, instruction_size);
|
||||
for (int currentOffset = (in.getCurrentPosition() - baseOffset) / 2; currentOffset < instruction_size; currentOffset = (in
|
||||
.getCurrentPosition() - baseOffset) / 2) {
|
||||
|
||||
boolean currentOffsetVisited = false;
|
||||
while (nextLabelOffset != null) {// issue 65, a label may `inside` an instruction
|
||||
int _intNextLabelOffset = nextLabelOffset;// autobox
|
||||
@ -325,6 +367,7 @@ import com.googlecode.dex2jar.visitors.DexCodeVisitor;
|
||||
opcode = uOpcodeL;
|
||||
}
|
||||
}
|
||||
|
||||
OpcodeFormat format = OpcodeFormat.get(opcode, dex.apiLevel);
|
||||
|
||||
switch (format) {
|
||||
|
@ -16,10 +16,10 @@
|
||||
package com.googlecode.dex2jar.reader;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UTFDataFormatException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
@ -35,9 +35,8 @@ import com.googlecode.dex2jar.DexException;
|
||||
import com.googlecode.dex2jar.DexOpcodes;
|
||||
import com.googlecode.dex2jar.Field;
|
||||
import com.googlecode.dex2jar.Method;
|
||||
import com.googlecode.dex2jar.reader.io.BeArrayDataIn;
|
||||
import com.googlecode.dex2jar.reader.io.ArrayDataIn;
|
||||
import com.googlecode.dex2jar.reader.io.DataIn;
|
||||
import com.googlecode.dex2jar.reader.io.LeArrayDataIn;
|
||||
import com.googlecode.dex2jar.reader.io.OffsetedDataIn;
|
||||
import com.googlecode.dex2jar.visitors.DexAnnotationAble;
|
||||
import com.googlecode.dex2jar.visitors.DexClassVisitor;
|
||||
@ -216,11 +215,7 @@ public class DexFileReader {
|
||||
|
||||
static private DataIn opDataIn(byte[] data) {
|
||||
try {
|
||||
if (isLittleEndian) {
|
||||
return new LeArrayDataIn(readDex(data));
|
||||
} else {
|
||||
return new BeArrayDataIn(readDex(data));
|
||||
}
|
||||
return new ArrayDataIn(readDex(data), isLittleEndian);
|
||||
} catch (RuntimeException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
@ -544,11 +539,10 @@ public class DexFileReader {
|
||||
in.pushMove(offset);
|
||||
try {
|
||||
int length = (int) in.readULeb128();
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(length);
|
||||
for (int b = in.readByte(); b != 0; b = in.readByte()) {
|
||||
baos.write(b);
|
||||
}
|
||||
return new String(baos.toByteArray(), UTF8);
|
||||
StringBuilder buff = new StringBuilder((int) (length * 1.5));
|
||||
return Mutf8.decode(in, buff);
|
||||
} catch (UTFDataFormatException e) {
|
||||
throw new DexException(e, "fail to load string %d@%08x", id, offset);
|
||||
} finally {
|
||||
in.pop();
|
||||
}
|
||||
|
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.googlecode.dex2jar.reader;
|
||||
|
||||
import java.io.UTFDataFormatException;
|
||||
|
||||
import com.googlecode.dex2jar.reader.io.DataIn;
|
||||
|
||||
/**
|
||||
* Modified UTF-8 as described in the dex file format spec.
|
||||
*
|
||||
* <p>
|
||||
* Derived from libcore's MUTF-8 encoder at java.nio.charset.ModifiedUtf8.
|
||||
* <p>
|
||||
* Derived from com.android.dx.util.Mutf8
|
||||
*/
|
||||
public final class Mutf8 {
|
||||
private Mutf8() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes bytes from {@code in} into {@code sb} until a delimiter 0x00 is encountered. Returns a new string
|
||||
* containing the decoded characters.
|
||||
*/
|
||||
public static String decode(DataIn in, StringBuilder sb) throws UTFDataFormatException {
|
||||
sb.setLength(0);
|
||||
while (true) {
|
||||
char a = (char) (in.readUByte());
|
||||
if (a == 0) {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
if (a < '\u0080') {
|
||||
sb.append(a);
|
||||
} else if ((a & 0xe0) == 0xc0) {
|
||||
int b = in.readUByte();
|
||||
if ((b & 0xC0) != 0x80) {
|
||||
throw new UTFDataFormatException("bad second byte");
|
||||
}
|
||||
sb.append((char) (((a & 0x1F) << 6) | (b & 0x3F)));
|
||||
} else if ((a & 0xf0) == 0xe0) {
|
||||
int b = in.readUByte();
|
||||
int c = in.readUByte();
|
||||
if (((b & 0xC0) != 0x80) || ((c & 0xC0) != 0x80)) {
|
||||
throw new UTFDataFormatException("bad second or third byte");
|
||||
}
|
||||
sb.append((char) (((a & 0x0F) << 12) | ((b & 0x3F) << 6) | (c & 0x3F)));
|
||||
} else {
|
||||
throw new UTFDataFormatException("bad byte");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of bytes the modified UTF8 representation of 's' would take.
|
||||
*/
|
||||
private static long countBytes(String s, boolean shortLength) throws UTFDataFormatException {
|
||||
long result = 0;
|
||||
final int length = s.length();
|
||||
for (int i = 0; i < length; ++i) {
|
||||
char ch = s.charAt(i);
|
||||
if (ch != 0 && ch <= 127) { // U+0000 uses two bytes.
|
||||
++result;
|
||||
} else if (ch <= 2047) {
|
||||
result += 2;
|
||||
} else {
|
||||
result += 3;
|
||||
}
|
||||
if (shortLength && result > 65535) {
|
||||
throw new UTFDataFormatException("String more than 65535 UTF bytes long");
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the modified UTF-8 bytes corresponding to {@code s} into {@code dst}, starting at {@code offset}.
|
||||
*/
|
||||
public static void encode(byte[] dst, int offset, String s) {
|
||||
final int length = s.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
char ch = s.charAt(i);
|
||||
if (ch != 0 && ch <= 127) { // U+0000 uses two bytes.
|
||||
dst[offset++] = (byte) ch;
|
||||
} else if (ch <= 2047) {
|
||||
dst[offset++] = (byte) (0xc0 | (0x1f & (ch >> 6)));
|
||||
dst[offset++] = (byte) (0x80 | (0x3f & ch));
|
||||
} else {
|
||||
dst[offset++] = (byte) (0xe0 | (0x0f & (ch >> 12)));
|
||||
dst[offset++] = (byte) (0x80 | (0x3f & (ch >> 6)));
|
||||
dst[offset++] = (byte) (0x80 | (0x3f & ch));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array containing the <i>modified UTF-8</i> form of {@code s}.
|
||||
*/
|
||||
public static byte[] encode(String s) throws UTFDataFormatException {
|
||||
int utfCount = (int) countBytes(s, true);
|
||||
byte[] result = new byte[utfCount];
|
||||
encode(result, 0, s);
|
||||
return result;
|
||||
}
|
||||
}
|
@ -24,32 +24,45 @@ import java.util.Stack;
|
||||
* @author <a href="mailto:pxb1988@gmail.com">Panxiaobo</a>
|
||||
* @version $Rev$
|
||||
*/
|
||||
public abstract class ArrayDataIn extends ByteArrayInputStream implements DataIn {
|
||||
public class ArrayDataIn extends ByteArrayInputStream implements DataIn {
|
||||
public static ArrayDataIn be(byte[] data) {
|
||||
return new ArrayDataIn(data, false);
|
||||
}
|
||||
|
||||
public static ArrayDataIn be(byte[] data, int offset, int length) {
|
||||
return new ArrayDataIn(data, offset, length, false);
|
||||
}
|
||||
|
||||
public static ArrayDataIn le(byte[] data) {
|
||||
return new ArrayDataIn(data, true);
|
||||
}
|
||||
|
||||
public static ArrayDataIn le(byte[] data, int offset, int length) {
|
||||
return new ArrayDataIn(data, offset, length, true);
|
||||
}
|
||||
|
||||
private boolean isLE;
|
||||
|
||||
private Stack<Integer> stack = new Stack<Integer>();
|
||||
|
||||
public ArrayDataIn(byte[] data) {
|
||||
public ArrayDataIn(byte[] data, boolean isLE) {
|
||||
super(data);
|
||||
this.isLE = isLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readShortx() {
|
||||
return (short) readUShortx();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readIntx() {
|
||||
return readUIntx();
|
||||
public ArrayDataIn(byte[] buf, int offset, int length, boolean isLE) {
|
||||
super(buf, offset, length);
|
||||
this.isLE = isLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCurrentPosition() {
|
||||
return super.pos;
|
||||
return super.pos - super.mark;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void move(int absOffset) {
|
||||
super.pos = absOffset;
|
||||
super.pos = absOffset + super.mark;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -68,6 +81,11 @@ public abstract class ArrayDataIn extends ByteArrayInputStream implements DataIn
|
||||
this.move(absOffset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readByte() {
|
||||
return (byte) readUByte();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] readBytes(int size) {
|
||||
byte[] data = new byte[size];
|
||||
@ -79,6 +97,11 @@ public abstract class ArrayDataIn extends ByteArrayInputStream implements DataIn
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readIntx() {
|
||||
return readUIntx();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long readLeb128() {
|
||||
int bitpos = 0;
|
||||
@ -97,6 +120,28 @@ public abstract class ArrayDataIn extends ByteArrayInputStream implements DataIn
|
||||
return vln;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readShortx() {
|
||||
return (short) readUShortx();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readUByte() {
|
||||
if (super.pos >= super.count) {
|
||||
throw new RuntimeException("EOF");
|
||||
}
|
||||
return super.read();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readUIntx() {
|
||||
if (isLE) {
|
||||
return readUByte() | (readUByte() << 8) | (readUByte() << 16) | (readUByte() << 24);
|
||||
} else {
|
||||
return (readUByte() << 24) | (readUByte() << 16) | (readUByte() << 8) | readUByte();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long readULeb128() {
|
||||
long value = 0;
|
||||
@ -111,21 +156,17 @@ public abstract class ArrayDataIn extends ByteArrayInputStream implements DataIn
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readUShortx() {
|
||||
if (isLE) {
|
||||
return readUByte() | (readUByte() << 8);
|
||||
} else {
|
||||
return (readUByte() << 8) | readUByte();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void skip(int bytes) {
|
||||
super.skip(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readByte() {
|
||||
return (byte) readUByte();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readUByte() {
|
||||
if (super.pos >= super.count) {
|
||||
throw new RuntimeException("EOF");
|
||||
}
|
||||
return super.read();
|
||||
}
|
||||
}
|
||||
|
@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 Panxiaobo
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.googlecode.dex2jar.reader.io;
|
||||
|
||||
/**
|
||||
* @see DexFileReader#REVERSE_ENDIAN_CONSTANT
|
||||
* @author Panxiaobo
|
||||
*
|
||||
*/
|
||||
public class BeArrayDataIn extends ArrayDataIn implements DataIn {
|
||||
|
||||
public BeArrayDataIn(byte[] data) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readUShortx() {
|
||||
return (readUByte() << 8) | readUByte();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readUIntx() {
|
||||
return (readUByte() << 24) | (readUByte() << 16) | (readUByte() << 8) | readUByte();
|
||||
}
|
||||
}
|
@ -15,44 +15,21 @@
|
||||
*/
|
||||
package com.googlecode.dex2jar.reader.io;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.DataInput;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.util.Stack;
|
||||
|
||||
import com.googlecode.dex2jar.DexException;
|
||||
|
||||
public class BeRandomAccessFileInput implements DataIn, Closeable {
|
||||
protected RandomAccessFile r;
|
||||
|
||||
public BeRandomAccessFileInput(File file) {
|
||||
try {
|
||||
r = new RandomAccessFile(file, "r");
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new DexException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCurrentPosition() {
|
||||
try {
|
||||
return (int) r.getFilePointer();
|
||||
} catch (IOException e) {
|
||||
throw new DexException(e);
|
||||
}
|
||||
}
|
||||
public abstract class DataInputDataIn implements DataIn {
|
||||
protected DataInput in;
|
||||
private boolean isLE;
|
||||
|
||||
private Stack<Integer> stack = new Stack<Integer>();
|
||||
|
||||
@Override
|
||||
public void move(int absOffset) {
|
||||
try {
|
||||
r.seek(absOffset);
|
||||
} catch (IOException e) {
|
||||
throw new DexException(e);
|
||||
}
|
||||
public DataInputDataIn(DataInput in, boolean isLE) {
|
||||
this.in = in;
|
||||
this.isLE = isLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -74,7 +51,7 @@ public class BeRandomAccessFileInput implements DataIn, Closeable {
|
||||
@Override
|
||||
public int readByte() {
|
||||
try {
|
||||
return r.read();
|
||||
return in.readByte();
|
||||
} catch (IOException e) {
|
||||
throw new DexException(e);
|
||||
}
|
||||
@ -84,7 +61,7 @@ public class BeRandomAccessFileInput implements DataIn, Closeable {
|
||||
public byte[] readBytes(int size) {
|
||||
byte[] data = new byte[size];
|
||||
try {
|
||||
r.read(data);
|
||||
in.readFully(data);
|
||||
} catch (IOException e) {
|
||||
throw new DexException(e);
|
||||
}
|
||||
@ -96,33 +73,6 @@ public class BeRandomAccessFileInput implements DataIn, Closeable {
|
||||
return readUIntx();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readUIntx() {
|
||||
try {
|
||||
return r.readInt();
|
||||
} catch (IOException e) {
|
||||
throw new DexException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readShortx() {
|
||||
try {
|
||||
return r.readShort();
|
||||
} catch (IOException e) {
|
||||
throw new DexException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readUShortx() {
|
||||
try {
|
||||
return r.readUnsignedShort();
|
||||
} catch (IOException e) {
|
||||
throw new DexException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long readLeb128() {
|
||||
int bitpos = 0;
|
||||
@ -141,6 +91,33 @@ public class BeRandomAccessFileInput implements DataIn, Closeable {
|
||||
return vln;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readShortx() {
|
||||
try {
|
||||
return in.readShort();
|
||||
} catch (IOException e) {
|
||||
throw new DexException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readUByte() {
|
||||
try {
|
||||
return in.readUnsignedByte();
|
||||
} catch (IOException e) {
|
||||
throw new DexException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readUIntx() {
|
||||
if (isLE) {
|
||||
return readUByte() | (readUByte() << 8) | (readUByte() << 16) | (readUByte() << 24);
|
||||
} else {
|
||||
return (readUByte() << 24) | (readUByte() << 16) | (readUByte() << 8) | readUByte();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long readULeb128() {
|
||||
long value = 0;
|
||||
@ -156,26 +133,20 @@ public class BeRandomAccessFileInput implements DataIn, Closeable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readUByte() {
|
||||
try {
|
||||
return r.readUnsignedByte();
|
||||
} catch (IOException e) {
|
||||
throw new DexException(e);
|
||||
public int readUShortx() {
|
||||
if (isLE) {
|
||||
return readUByte() | (readUByte() << 8);
|
||||
} else {
|
||||
return (readUByte() << 8) | readUByte();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void skip(int bytes) {
|
||||
try {
|
||||
r.skipBytes(bytes);
|
||||
in.skipBytes(bytes);
|
||||
} catch (IOException e) {
|
||||
throw new DexException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
r.close();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
package com.googlecode.dex2jar.reader.io;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
import org.apache.xmlgraphics.image.codec.util.MemoryCacheSeekableStream;
|
||||
|
||||
public class InputStreamDataIn extends DataInputDataIn implements Closeable {
|
||||
public static InputStreamDataIn open(InputStream in) {
|
||||
return new InputStreamDataIn(in, true);
|
||||
}
|
||||
|
||||
public static InputStreamDataIn openApk(File file) {
|
||||
InputStream in;
|
||||
try {
|
||||
in = new FileInputStream(file);
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new RuntimeException("No valid apk", e);
|
||||
}
|
||||
return openApk(in);
|
||||
}
|
||||
|
||||
public static InputStreamDataIn openApk(InputStream in) {
|
||||
try {
|
||||
ZipInputStream zis = new ZipInputStream(in);
|
||||
for (ZipEntry entry = zis.getNextEntry(); entry != null; entry = zis.getNextEntry()) {
|
||||
if (entry.getName().equals("classes.dex")) {
|
||||
return new InputStreamDataIn(zis, true);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Not valid apk", e);
|
||||
}
|
||||
throw new RuntimeException("No valid apk");
|
||||
}
|
||||
|
||||
public InputStreamDataIn(InputStream stream, boolean isLE) {
|
||||
super(new MemoryCacheSeekableStream(stream), isLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
((MemoryCacheSeekableStream) in).close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCurrentPosition() {
|
||||
return (int) ((MemoryCacheSeekableStream) in).getFilePointer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void move(int absOffset) {
|
||||
try {
|
||||
((MemoryCacheSeekableStream) in).seek(absOffset);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 Panxiaobo
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.googlecode.dex2jar.reader.io;
|
||||
|
||||
/**
|
||||
* @see DexFileReader#ENDIAN_CONSTANT
|
||||
* @author Panxiaobo
|
||||
*
|
||||
*/
|
||||
public class LeArrayDataIn extends ArrayDataIn implements DataIn {
|
||||
|
||||
public LeArrayDataIn(byte[] data) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readUShortx() {
|
||||
return readUByte() | (readUByte() << 8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readUIntx() {
|
||||
return readUByte() | (readUByte() << 8) | (readUByte() << 16) | (readUByte() << 24);
|
||||
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 Panxiaobo
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.googlecode.dex2jar.reader.io;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class LeRandomAccessFileInput extends BeRandomAccessFileInput {
|
||||
|
||||
public LeRandomAccessFileInput(File file) {
|
||||
super(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readUShortx() {
|
||||
return readUByte() | (readUByte() << 8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readUIntx() {
|
||||
return readUByte() | (readUByte() << 8) | (readUByte() << 16) | (readUByte() << 24);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readIntx() {
|
||||
return readUIntx();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readShortx() {
|
||||
return (short) readUShortx();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 Panxiaobo
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.googlecode.dex2jar.reader.io;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
|
||||
import com.googlecode.dex2jar.DexException;
|
||||
|
||||
public class RandomAccessFileInput extends DataInputDataIn implements DataIn, Closeable {
|
||||
public RandomAccessFileInput(File file, boolean isLE) throws FileNotFoundException {
|
||||
super(new RandomAccessFile(file, "r"), isLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
((RandomAccessFile) in).close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCurrentPosition() {
|
||||
try {
|
||||
return (int) ((RandomAccessFile) in).getFilePointer();
|
||||
} catch (IOException e) {
|
||||
throw new DexException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void move(int absOffset) {
|
||||
try {
|
||||
((RandomAccessFile) in).seek(absOffset);
|
||||
} catch (IOException e) {
|
||||
throw new DexException(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -32,9 +32,9 @@ import com.googlecode.dex2jar.visitors.DexMethodVisitor;
|
||||
* @version $Rev$
|
||||
*/
|
||||
public class ASMifierClassV implements DexClassVisitor {
|
||||
ArrayOut out = new ArrayOut();
|
||||
List<ArrayOut> methodOuts = new ArrayList<ArrayOut>();
|
||||
List<ArrayOut> fieldOuts = new ArrayList<ArrayOut>();
|
||||
protected ArrayOut out = new ArrayOut();
|
||||
private List<ArrayOut> methodOuts = new ArrayList<ArrayOut>();
|
||||
private List<ArrayOut> fieldOuts = new ArrayList<ArrayOut>();
|
||||
|
||||
int fCount = 0;
|
||||
int mCount = 0;
|
||||
@ -45,8 +45,7 @@ public class ASMifierClassV implements DexClassVisitor {
|
||||
out.s("package %s;", pkgName);
|
||||
out.s("import com.googlecode.dex2jar.*;");
|
||||
out.s("import com.googlecode.dex2jar.visitors.*;");
|
||||
out.s("import static com.googlecode.dex2jar.DexOpcodes.*;");
|
||||
out.s("import static com.googlecode.dex2jar.util.Hex.*;");
|
||||
out.s("import static com.googlecode.dex2jar.OdexOpcodes.*;");
|
||||
out.s("public class %s {", javaClassName);
|
||||
out.push();
|
||||
out.s("public static void accept(DexFileVisitor v) {");
|
||||
|
@ -34,7 +34,7 @@ public class ASMifierCodeV implements OdexCodeVisitor, DexOpcodes {
|
||||
|
||||
@Override
|
||||
public void visitArrayStmt(int opcode, int formOrToReg, int arrayReg, int indexReg, int xt) {
|
||||
m.s("code.visitArrayStmt(%s,%s,%s,%s,%s);", op(opcode), formOrToReg, arrayReg, indexReg, xt);
|
||||
m.s("code.visitArrayStmt(%s,%s,%s,%s,%s);", op(opcode), formOrToReg, arrayReg, indexReg, xt(xt));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -44,7 +44,7 @@ public class ASMifierCodeV implements OdexCodeVisitor, DexOpcodes {
|
||||
|
||||
@Override
|
||||
public void visitBinopStmt(int opcode, int toReg, int r1, int r2, int xt) {
|
||||
m.s("code.visitBinopStmt(%s,%s,%s,%s,%s);", op(opcode), toReg, r1, r2, xt);
|
||||
m.s("code.visitBinopStmt(%s,%s,%s,%s,%s);", op(opcode), toReg, r1, r2, xt(xt));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -59,31 +59,31 @@ public class ASMifierCodeV implements OdexCodeVisitor, DexOpcodes {
|
||||
|
||||
@Override
|
||||
public void visitCmpStmt(int opcode, int distReg, int bB, int cC, int xt) {
|
||||
m.s("code.visitCmpStmt(%s,%s,%s,%s,%s);", op(opcode), distReg, bB, cC, xt);
|
||||
m.s("code.visitCmpStmt(%s,%s,%s,%s,%s);", op(opcode), distReg, bB, cC, xt(xt));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitConstStmt(int opcode, int toReg, Object value, int xt) {
|
||||
if (value instanceof Integer) {
|
||||
m.s("code.visitConstStmt(%s,%s,%s,%s); // int: 0x%08x float:%f", op(opcode), toReg, Escape.v(value), xt,
|
||||
value, Float.intBitsToFloat((Integer) value));
|
||||
m.s("code.visitConstStmt(%s,%s,%s,%s); // int: 0x%08x float:%f", op(opcode), toReg, Escape.v(value),
|
||||
xt(xt), value, Float.intBitsToFloat((Integer) value));
|
||||
} else if (value instanceof Long) {
|
||||
m.s("code.visitConstStmt(%s,%s,%s,%s); // long: 0x%016x double:%f", op(opcode), toReg, Escape.v(value),
|
||||
xt, value, Double.longBitsToDouble((Long) value));
|
||||
xt(xt), value, Double.longBitsToDouble((Long) value));
|
||||
} else {
|
||||
m.s("code.visitConstStmt(%s,%s,%s,%s);", op(opcode), toReg, Escape.v(value), xt);
|
||||
m.s("code.visitConstStmt(%s,%s,%s,%s);", op(opcode), toReg, Escape.v(value), xt(xt));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFieldStmt(int opcode, int fromOrToReg, Field field, int xt) {
|
||||
m.s("code.visitFieldStmt(%s,%s,%s,%s);", op(opcode), fromOrToReg, Escape.v(field), xt);
|
||||
m.s("code.visitFieldStmt(%s,%s,%s,%s);", op(opcode), fromOrToReg, Escape.v(field), xt(xt));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFieldStmt(int opcode, int fromOrToReg, int objReg, Field field, int xt) {
|
||||
m.s("code.visitFieldStmt(%s,%s,%s,%s,%s);", op(opcode), fromOrToReg, objReg, Escape.v(field), xt);
|
||||
m.s("code.visitFieldStmt(%s,%s,%s,%s,%s);", op(opcode), fromOrToReg, objReg, Escape.v(field), xt(xt));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -111,6 +111,35 @@ public class ASMifierCodeV implements OdexCodeVisitor, DexOpcodes {
|
||||
return "OP_" + DexOpcodeDump.dump(op);
|
||||
}
|
||||
|
||||
String xt(int vt) {
|
||||
switch (vt) {
|
||||
case 0:
|
||||
return "TYPE_SINGLE";
|
||||
case 1:
|
||||
return "TYPE_WIDE";
|
||||
case 2:
|
||||
return "TYPE_OBJECT";
|
||||
case 3:
|
||||
return "TYPE_BOOLEAN";
|
||||
case 4:
|
||||
return "TYPE_BYTE";
|
||||
case 5:
|
||||
return "TYPE_CHAR";
|
||||
case 6:
|
||||
return "TYPE_SHORT";
|
||||
case 7:
|
||||
return "TYPE_INT";
|
||||
case 8:
|
||||
return "TYPE_FLOAT";
|
||||
case 9:
|
||||
return "TYPE_LONG";
|
||||
case 10:
|
||||
return "TYPE_DOUBLE";
|
||||
default:
|
||||
return Integer.toString(vt);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitJumpStmt(int opcode, int a, int b, DexLabel label) {
|
||||
m.s("code.visitJumpStmt(%s,%s,%s,%s);", op(opcode), a, b, v(label));
|
||||
@ -155,12 +184,12 @@ public class ASMifierCodeV implements OdexCodeVisitor, DexOpcodes {
|
||||
|
||||
@Override
|
||||
public void visitMoveStmt(int opcode, int toReg, int xt) {
|
||||
m.s("code.visitMoveStmt(%s,%s,%s);", op(opcode), toReg, xt);
|
||||
m.s("code.visitMoveStmt(%s,%s,%s);", op(opcode), toReg, xt(xt));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMoveStmt(int opcode, int toReg, int fromReg, int xt) {
|
||||
m.s("code.visitMoveStmt(%s,%s,%s,%s);", op(opcode), toReg, fromReg, xt);
|
||||
m.s("code.visitMoveStmt(%s,%s,%s,%s);", op(opcode), toReg, fromReg, xt(xt));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -170,7 +199,7 @@ public class ASMifierCodeV implements OdexCodeVisitor, DexOpcodes {
|
||||
|
||||
@Override
|
||||
public void visitReturnStmt(int opcode, int reg, int xt) {
|
||||
m.s("code.visitReturnStmt(%s,%s,%s);", op(opcode), reg, xt);
|
||||
m.s("code.visitReturnStmt(%s,%s,%s);", op(opcode), reg, xt(xt));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -192,12 +221,12 @@ public class ASMifierCodeV implements OdexCodeVisitor, DexOpcodes {
|
||||
|
||||
@Override
|
||||
public void visitUnopStmt(int opcode, int toReg, int fromReg, int xt) {
|
||||
m.s("code.visitUnopStmt(%s,%s,%s,%s);", op(opcode), toReg, fromReg, xt);
|
||||
m.s("code.visitUnopStmt(%s,%s,%s,%s);", op(opcode), toReg, fromReg, xt(xt));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitUnopStmt(int opcode, int toReg, int fromReg, int xta, int xtb) {
|
||||
m.s("code.visitUnopStmt(%s,%s,%s,%s,%s);", op(opcode), toReg, fromReg, xta, xtb);
|
||||
m.s("code.visitUnopStmt(%s,%s,%s,%s,%s);", op(opcode), toReg, fromReg, xt(xta), xt(xtb));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -243,6 +272,7 @@ public class ASMifierCodeV implements OdexCodeVisitor, DexOpcodes {
|
||||
|
||||
@Override
|
||||
public void visitFieldStmt(int opcode, int fromOrToReg, int objReg, int fieldoff, int xt) {
|
||||
m.s("((OdexCodeVisitor)code).visitFieldStmt(%s,%s,%s,%s,%s);", op(opcode), fromOrToReg, objReg, fieldoff, xt);
|
||||
m.s("((OdexCodeVisitor)code).visitFieldStmt(%s,%s,%s,%s,%s);", op(opcode), fromOrToReg, objReg, fieldoff,
|
||||
xt(xt));
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,7 @@
|
||||
<dependency>
|
||||
<groupId>pxb.bc</groupId>
|
||||
<artifactId>p-rename</artifactId>
|
||||
<version>1.1</version>
|
||||
<version>1.3</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
|
@ -328,19 +328,21 @@ public class JasminifierClassAdapter extends ClassAdapter {
|
||||
if ((mn.access & Opcodes.ACC_DEPRECATED) != 0) {
|
||||
pw.println(".deprecated");
|
||||
}
|
||||
if (mn.instructions.size() > 0) {
|
||||
if (mn.instructions != null && mn.instructions.size() > 0) {
|
||||
labelNames.clear();
|
||||
for (int j = 0; j < mn.tryCatchBlocks.size(); ++j) {
|
||||
TryCatchBlockNode tcb = (TryCatchBlockNode) mn.tryCatchBlocks.get(j);
|
||||
pw.print(".catch ");
|
||||
pw.print(tcb.type == null ? "all" : tcb.type);
|
||||
pw.print(" from ");
|
||||
print(tcb.start);
|
||||
pw.print(" to ");
|
||||
print(tcb.end);
|
||||
pw.print(" using ");
|
||||
print(tcb.handler);
|
||||
pw.println();
|
||||
if (mn.tryCatchBlocks != null) {
|
||||
for (int j = 0; j < mn.tryCatchBlocks.size(); ++j) {
|
||||
TryCatchBlockNode tcb = (TryCatchBlockNode) mn.tryCatchBlocks.get(j);
|
||||
pw.print(".catch ");
|
||||
pw.print(tcb.type == null ? "all" : tcb.type);
|
||||
pw.print(" from ");
|
||||
print(tcb.start);
|
||||
pw.print(" to ");
|
||||
print(tcb.end);
|
||||
pw.print(" using ");
|
||||
print(tcb.handler);
|
||||
pw.println();
|
||||
}
|
||||
}
|
||||
for (int j = 0; j < mn.instructions.size(); ++j) {
|
||||
AbstractInsnNode in = mn.instructions.get(j);
|
||||
@ -533,24 +535,26 @@ public class JasminifierClassAdapter extends ClassAdapter {
|
||||
}
|
||||
});
|
||||
}
|
||||
for (int j = 0; j < mn.localVariables.size(); ++j) {
|
||||
LocalVariableNode lv = (LocalVariableNode) mn.localVariables.get(j);
|
||||
pw.print(".var ");
|
||||
pw.print(lv.index);
|
||||
pw.print(" is '");
|
||||
pw.print(lv.name);
|
||||
pw.print("' ");
|
||||
pw.print(lv.desc);
|
||||
if (lv.signature != null) {
|
||||
pw.print(" signature \"");
|
||||
pw.print(lv.signature);
|
||||
pw.print("\"");
|
||||
if (mn.localVariables != null) {
|
||||
for (int j = 0; j < mn.localVariables.size(); ++j) {
|
||||
LocalVariableNode lv = (LocalVariableNode) mn.localVariables.get(j);
|
||||
pw.print(".var ");
|
||||
pw.print(lv.index);
|
||||
pw.print(" is '");
|
||||
pw.print(lv.name);
|
||||
pw.print("' ");
|
||||
pw.print(lv.desc);
|
||||
if (lv.signature != null) {
|
||||
pw.print(" signature \"");
|
||||
pw.print(lv.signature);
|
||||
pw.print("\"");
|
||||
}
|
||||
pw.print(" from ");
|
||||
print(lv.start);
|
||||
pw.print(" to ");
|
||||
print(lv.end);
|
||||
pw.println();
|
||||
}
|
||||
pw.print(" from ");
|
||||
print(lv.start);
|
||||
pw.print(" to ");
|
||||
print(lv.end);
|
||||
pw.println();
|
||||
}
|
||||
println(".limit locals ", Integer.toString(mn.maxLocals));
|
||||
println(".limit stack ", Integer.toString(mn.maxStack));
|
||||
|
@ -43,7 +43,7 @@ public class LdcOptimizeAdapter extends MethodAdapter implements Opcodes {
|
||||
this.visitInsn(ACONST_NULL);
|
||||
} else if (cst instanceof Integer) {
|
||||
int value = (Integer) cst;
|
||||
if (value >= 0 && value <= 5) {
|
||||
if (value >= -1 && value <= 5) {
|
||||
super.visitInsn(ICONST_0 + value);
|
||||
} else if (value <= Byte.MAX_VALUE && value >= Byte.MIN_VALUE) {
|
||||
super.visitIntInsn(BIPUSH, value);
|
||||
|
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* dex2jar - Tools to work with android .dex and java .class files
|
||||
* Copyright (c) 2009-2012 Panxiaobo
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.googlecode.dex2jar.asm;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import org.objectweb.asm.ClassAdapter;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.tree.InnerClassNode;
|
||||
|
||||
/**
|
||||
*
|
||||
* sort visit to {@link #visitInnerClass(String, String, String, int)}
|
||||
*
|
||||
* @author Panxiaobo
|
||||
*
|
||||
*/
|
||||
public class OrderInnerOutterInsnNodeClassAdapter extends ClassAdapter implements Comparator<InnerClassNode> {
|
||||
public OrderInnerOutterInsnNodeClassAdapter(ClassVisitor cv) {
|
||||
super(cv);
|
||||
}
|
||||
|
||||
private final List<InnerClassNode> innerClassNodes = new ArrayList<InnerClassNode>(5);
|
||||
|
||||
public void visitInnerClass(final String name, final String outerName, final String innerName, final int access) {
|
||||
InnerClassNode icn = new InnerClassNode(name, outerName, innerName, access);
|
||||
innerClassNodes.add(icn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
Collections.sort(this.innerClassNodes, this);
|
||||
if (innerClassNodes.size() > 0) {
|
||||
for (InnerClassNode icn : innerClassNodes) {
|
||||
icn.accept(cv);
|
||||
}
|
||||
}
|
||||
super.visitEnd();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(InnerClassNode o1, InnerClassNode o2) {
|
||||
return o1.name.compareTo(o2.name);
|
||||
}
|
||||
}
|
@ -76,27 +76,26 @@ public class Dex2jar {
|
||||
+ " please refere smali http://code.google.com/p/smali/ to convert odex to dex");
|
||||
}
|
||||
|
||||
V3AccessFlagsAdapter afa = new V3AccessFlagsAdapter();
|
||||
V3InnerClzGather afa = new V3InnerClzGather();
|
||||
reader.accept(afa, DexFileReader.SKIP_CODE | DexFileReader.SKIP_DEBUG);
|
||||
try {
|
||||
reader.accept(new V3(afa.getAccessFlagsMap(), afa.getInnerNameMap(), afa.getExtraMember(),
|
||||
exceptionHandler, new ClassVisitorFactory() {
|
||||
reader.accept(new V3(afa.getClasses(), exceptionHandler, new ClassVisitorFactory() {
|
||||
@Override
|
||||
public ClassVisitor create(final String name) {
|
||||
return new ClassWriter(ClassWriter.COMPUTE_MAXS) {
|
||||
@Override
|
||||
public ClassVisitor create(final String name) {
|
||||
return new ClassWriter(ClassWriter.COMPUTE_MAXS) {
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
super.visitEnd();
|
||||
try {
|
||||
byte[] data = this.toByteArray();
|
||||
saveTo(data, name, dist);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
};
|
||||
public void visitEnd() {
|
||||
super.visitEnd();
|
||||
try {
|
||||
byte[] data = this.toByteArray();
|
||||
saveTo(data, name, dist);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
}, v3Config), readerConfig);
|
||||
};
|
||||
}
|
||||
}, v3Config), readerConfig);
|
||||
} catch (Exception e) {
|
||||
if (exceptionHandler == null) {
|
||||
throw e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e);
|
||||
|
@ -27,7 +27,9 @@ import com.googlecode.dex2jar.ir.Value.VT;
|
||||
import com.googlecode.dex2jar.ir.ValueBox;
|
||||
import com.googlecode.dex2jar.ir.expr.ArrayExpr;
|
||||
import com.googlecode.dex2jar.ir.expr.CastExpr;
|
||||
import com.googlecode.dex2jar.ir.expr.Exprs;
|
||||
import com.googlecode.dex2jar.ir.expr.FieldExpr;
|
||||
import com.googlecode.dex2jar.ir.expr.FilledArrayExpr;
|
||||
import com.googlecode.dex2jar.ir.expr.InvokeExpr;
|
||||
import com.googlecode.dex2jar.ir.expr.NewExpr;
|
||||
import com.googlecode.dex2jar.ir.expr.NewMutiArrayExpr;
|
||||
@ -356,10 +358,26 @@ public class IrMethod2AsmMethod implements Opcodes {
|
||||
reBuildLocalVar(ir, asm);
|
||||
}
|
||||
|
||||
/**
|
||||
* an empty try-catch block will cause other crash, we check this by finding non-label stmts between
|
||||
* {@link Trap#start} and {@link Trap#end}. if find we add the try-catch or we drop the try-catch.
|
||||
*
|
||||
* @param ir
|
||||
* @param asm
|
||||
*/
|
||||
private void reBuildTryCatchBlocks(IrMethod ir, MethodVisitor asm) {
|
||||
for (Trap trap : ir.traps) {
|
||||
asm.visitTryCatchBlock(trap.start.label, trap.end.label, trap.handler.label, trap.type == null ? null
|
||||
: trap.type.getInternalName());
|
||||
boolean needAdd = false;
|
||||
for (Stmt p = trap.start.getNext(); p != null && p != trap.end; p = p.getNext()) {
|
||||
if (p.st != ST.LABEL) {
|
||||
needAdd = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (needAdd) {
|
||||
asm.visitTryCatchBlock(trap.start.label, trap.end.label, trap.handler.label, trap.type == null ? null
|
||||
: trap.type.getInternalName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -508,6 +526,8 @@ public class IrMethod2AsmMethod implements Opcodes {
|
||||
if (optimizeSynchronized) {
|
||||
switch (v.vt) {
|
||||
case LOCAL:
|
||||
// FIXME do we have to disable local due to OptSyncTest ?
|
||||
// break;
|
||||
case CONSTANT: {
|
||||
String key;
|
||||
if (v.vt == VT.LOCAL) {
|
||||
@ -522,7 +542,8 @@ public class IrMethod2AsmMethod implements Opcodes {
|
||||
lockMap.put(key, nIndex);
|
||||
}
|
||||
break;
|
||||
// TODO other
|
||||
default:
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
asm.visitInsn(MONITORENTER);
|
||||
@ -740,6 +761,26 @@ public class IrMethod2AsmMethod implements Opcodes {
|
||||
}
|
||||
|
||||
private static void reBuildEnExpression(EnExpr value, MethodVisitor asm) {
|
||||
if (value.vt == VT.FILLED_ARRAY) {
|
||||
FilledArrayExpr fae = (FilledArrayExpr) value;
|
||||
TypeExpr te = Exprs.nNewArray(fae.type, Constant.nInt(fae.ops.length));
|
||||
reBuildE1Expression(te, asm);
|
||||
Type tp1 = LocalType.typeOf(fae);
|
||||
for (int i = 0; i < fae.ops.length; i++) {
|
||||
if (fae.ops[i].value == null)
|
||||
continue;
|
||||
asm.visitInsn(DUP);
|
||||
asm.visitLdcInsn(i);
|
||||
accept(fae.ops[i].value, asm);
|
||||
Type tp2 = LocalType.typeOf(fae.ops[i].value);
|
||||
if (tp1.getSort() == Type.ARRAY) {
|
||||
asm.visitInsn(Type.getType(tp1.getDescriptor().substring(1)).getOpcode(IASTORE));
|
||||
} else {
|
||||
asm.visitInsn(tp2.getOpcode(IASTORE));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (value.vt == VT.INVOKE_NEW) {
|
||||
asm.visitTypeInsn(NEW, ((InvokeExpr) value).methodOwnerType.getInternalName());
|
||||
asm.visitInsn(DUP);
|
||||
|
@ -25,6 +25,7 @@ import com.googlecode.dex2jar.ir.Value.E2Expr;
|
||||
import com.googlecode.dex2jar.ir.Value.EnExpr;
|
||||
import com.googlecode.dex2jar.ir.Value.VT;
|
||||
import com.googlecode.dex2jar.ir.ValueBox;
|
||||
import com.googlecode.dex2jar.ir.expr.FilledArrayExpr;
|
||||
import com.googlecode.dex2jar.ir.stmt.Stmt;
|
||||
import com.googlecode.dex2jar.ir.stmt.Stmt.E1Stmt;
|
||||
import com.googlecode.dex2jar.ir.stmt.Stmt.E2Stmt;
|
||||
@ -101,14 +102,29 @@ public class LocalCurrect implements Transformer {
|
||||
currectArrayInExpr(e2.op2);
|
||||
break;
|
||||
case En:
|
||||
EnExpr en = (EnExpr) value;
|
||||
for (ValueBox op : en.ops) {
|
||||
currectArrayInExpr(op);
|
||||
if (value.vt ==VT.FILLED_ARRAY) {
|
||||
detectFilledArray((FilledArrayExpr)value);
|
||||
} else {
|
||||
EnExpr en = (EnExpr) value;
|
||||
for (ValueBox op : en.ops) {
|
||||
currectArrayInExpr(op);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void detectFilledArray(FilledArrayExpr fae) {
|
||||
for (int i=0; i<fae.ops.length; i++) {
|
||||
Value val = fae.ops[i].value;
|
||||
if (val.vt == VT.FILLED_ARRAY) {
|
||||
detectFilledArray((FilledArrayExpr)val);
|
||||
} else {
|
||||
LocalType.type(val, Type.getType(fae.type.getDescriptor()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Type detectArray(E2Expr e2) {
|
||||
Type t1 = LocalType.typeOf(e2);
|
||||
Type t2 = LocalType.typeOf(e2.op1.value);
|
||||
|
@ -16,11 +16,11 @@
|
||||
package com.googlecode.dex2jar.v3;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
import com.googlecode.dex2jar.v3.V3InnerClzGather.Clz;
|
||||
import com.googlecode.dex2jar.visitors.DexClassVisitor;
|
||||
import com.googlecode.dex2jar.visitors.DexFileVisitor;
|
||||
|
||||
@ -35,9 +35,7 @@ public class V3 implements DexFileVisitor {
|
||||
public static final int PRINT_IR = 1 << 2;
|
||||
public static final int OPTIMIZE_SYNCHRONIZED = 1 << 3;
|
||||
protected ClassVisitorFactory cvf;
|
||||
protected Map<String, Integer> accessFlagsMap;
|
||||
protected Map<String, String> innerNameMap;
|
||||
protected Map<String, Set<String>> extraMemberClass;
|
||||
protected Map<String, Clz> innerClz;
|
||||
protected DexExceptionHandler exceptionHandler;
|
||||
protected int config;
|
||||
|
||||
@ -47,18 +45,13 @@ public class V3 implements DexFileVisitor {
|
||||
/* package */
|
||||
static final boolean DEBUG = false;
|
||||
|
||||
public V3(Map<String, Integer> innerAccessFlagsMap, Map<String, String> innerNameMap,
|
||||
Map<String, Set<String>> extraMemberClass, DexExceptionHandler exceptionHandler,
|
||||
ClassVisitorFactory classVisitorFactory) {
|
||||
this(innerAccessFlagsMap, innerNameMap, extraMemberClass, exceptionHandler, classVisitorFactory, 0);
|
||||
public V3(Map<String, Clz> innerClz, DexExceptionHandler exceptionHandler, ClassVisitorFactory classVisitorFactory) {
|
||||
this(innerClz, exceptionHandler, classVisitorFactory, 0);
|
||||
}
|
||||
|
||||
public V3(Map<String, Integer> innerAccessFlagsMap, Map<String, String> innerNameMap,
|
||||
Map<String, Set<String>> extraMemberClass, DexExceptionHandler exceptionHandler,
|
||||
ClassVisitorFactory classVisitorFactory, int config) {
|
||||
this.accessFlagsMap = innerAccessFlagsMap;
|
||||
this.innerNameMap = innerNameMap;
|
||||
this.extraMemberClass = extraMemberClass;
|
||||
public V3(Map<String, Clz> innerClz, DexExceptionHandler exceptionHandler, ClassVisitorFactory classVisitorFactory,
|
||||
int config) {
|
||||
this.innerClz = innerClz;
|
||||
this.exceptionHandler = exceptionHandler;
|
||||
this.cvf = classVisitorFactory;
|
||||
this.config = config;
|
||||
@ -70,8 +63,8 @@ public class V3 implements DexFileVisitor {
|
||||
if (cv == null) {
|
||||
return null;
|
||||
}
|
||||
return new V3ClassAdapter(accessFlagsMap, innerNameMap, this.extraMemberClass, this.exceptionHandler, cv,
|
||||
access_flags, className, superClass, interfaceNames, config);
|
||||
return new V3ClassAdapter(innerClz.get(className), this.exceptionHandler, cv, access_flags, className,
|
||||
superClass, interfaceNames, config);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,132 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 Panxiaobo
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.googlecode.dex2jar.v3;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import com.googlecode.dex2jar.Annotation;
|
||||
import com.googlecode.dex2jar.Annotation.Item;
|
||||
import com.googlecode.dex2jar.Method;
|
||||
import com.googlecode.dex2jar.visitors.DexAnnotationVisitor;
|
||||
import com.googlecode.dex2jar.visitors.DexClassVisitor;
|
||||
import com.googlecode.dex2jar.visitors.DexFileVisitor;
|
||||
import com.googlecode.dex2jar.visitors.EmptyVisitor;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:pxb1988@gmail.com">Panxiaobo</a>
|
||||
* @version $Rev$
|
||||
*/
|
||||
public class V3AccessFlagsAdapter implements DexFileVisitor {
|
||||
private Map<String, Integer> map = new HashMap<String, Integer>();
|
||||
private Map<String, String> innerNameMap = new HashMap<String, String>();
|
||||
|
||||
private Map<String, Set<String>> extraMember = new HashMap<String, Set<String>>();
|
||||
|
||||
/**
|
||||
* @return the innerNameMap
|
||||
*/
|
||||
public Map<String, String> getInnerNameMap() {
|
||||
return innerNameMap;
|
||||
}
|
||||
|
||||
public Map<String, Integer> getAccessFlagsMap() {
|
||||
return map;
|
||||
}
|
||||
|
||||
public Map<String, Set<String>> getExtraMember() {
|
||||
return extraMember;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see com.googlecode.dex2jar.visitors.DexFileVisitor#visit(int, java.lang.String, java.lang.String,
|
||||
* java.lang.String[])
|
||||
*/
|
||||
@Override
|
||||
public DexClassVisitor visit(int access_flags, final String className, String superClass, String[] interfaceNames) {
|
||||
|
||||
return new EmptyVisitor() {
|
||||
protected List<Annotation> anns = new ArrayList<Annotation>();
|
||||
|
||||
@Override
|
||||
public DexAnnotationVisitor visitAnnotation(String name, boolean visible) {
|
||||
Annotation ann = new Annotation(name, visible);
|
||||
anns.add(ann);
|
||||
return new V3AnnAdapter(ann);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
String enclosingClass = null;
|
||||
for (Annotation ann : anns) {
|
||||
if (ann.type.equals("Ldalvik/annotation/EnclosingClass;")) {
|
||||
for (Item i : ann.items) {
|
||||
if (i.name.equals("value")) {
|
||||
enclosingClass = i.value.toString();
|
||||
}
|
||||
}
|
||||
} else if (ann.type.equals("Ldalvik/annotation/EnclosingMethod;")) {
|
||||
for (Item i : ann.items) {
|
||||
if ("value".equals(i.name)) {
|
||||
Method m = (Method) i.value;
|
||||
enclosingClass = m.getOwner();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (Annotation ann : anns) {
|
||||
if ("Ldalvik/annotation/InnerClass;".equals(ann.type)) {
|
||||
Integer acc = null;
|
||||
String name = null;
|
||||
for (Item it : ann.items) {
|
||||
if ("accessFlags".equals(it.name)) {
|
||||
acc = (Integer) it.value;
|
||||
} else if ("name".equals(it.name)) {
|
||||
name = (String) it.value;
|
||||
}
|
||||
}
|
||||
map.put(className, acc);
|
||||
innerNameMap.put(className, name);
|
||||
if (name == null) {
|
||||
Set<String> set = extraMember.get(enclosingClass);
|
||||
if (set == null) {
|
||||
set = new TreeSet<String>();
|
||||
extraMember.put(enclosingClass, set);
|
||||
}
|
||||
set.add(className);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see com.googlecode.dex2jar.visitors.DexFileVisitor#visitEnd()
|
||||
*/
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
}
|
||||
|
||||
}
|
@ -22,7 +22,7 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.Stack;
|
||||
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
@ -34,6 +34,8 @@ import com.googlecode.dex2jar.Annotation.Item;
|
||||
import com.googlecode.dex2jar.DexType;
|
||||
import com.googlecode.dex2jar.Field;
|
||||
import com.googlecode.dex2jar.Method;
|
||||
import com.googlecode.dex2jar.asm.OrderInnerOutterInsnNodeClassAdapter;
|
||||
import com.googlecode.dex2jar.v3.V3InnerClzGather.Clz;
|
||||
import com.googlecode.dex2jar.visitors.DexAnnotationVisitor;
|
||||
import com.googlecode.dex2jar.visitors.DexClassVisitor;
|
||||
import com.googlecode.dex2jar.visitors.DexFieldVisitor;
|
||||
@ -51,32 +53,24 @@ public class V3ClassAdapter implements DexClassVisitor {
|
||||
protected List<Annotation> anns = new ArrayList<Annotation>();
|
||||
protected boolean build = false;
|
||||
protected String className;
|
||||
protected Clz clz;
|
||||
protected int config;
|
||||
protected ClassVisitor cv;
|
||||
protected DexExceptionHandler exceptionHandler;
|
||||
protected Map<String, Set<String>> extraMemberClass;
|
||||
protected String file;
|
||||
protected Map<String, Integer> innerAccessFlagsMap;
|
||||
protected Map<String, String> innerNameMap;
|
||||
protected String[] interfaceNames;
|
||||
protected boolean isInnerClass = false;
|
||||
protected String superClass;
|
||||
protected int config;
|
||||
|
||||
public V3ClassAdapter(Map<String, Integer> accessFlagsMap, Map<String, String> innerNameMap,
|
||||
Map<String, Set<String>> extraMemberClass, DexExceptionHandler exceptionHandler, ClassVisitor cv,
|
||||
int access_flags, String className, String superClass, String[] interfaceNames) {
|
||||
this(accessFlagsMap, innerNameMap, extraMemberClass, exceptionHandler, cv, access_flags, className, superClass,
|
||||
interfaceNames, 0);
|
||||
public V3ClassAdapter(Clz clz, DexExceptionHandler exceptionHandler, ClassVisitor cv, int access_flags,
|
||||
String className, String superClass, String[] interfaceNames) {
|
||||
this(clz, exceptionHandler, cv, access_flags, className, superClass, interfaceNames, 0);
|
||||
}
|
||||
|
||||
public V3ClassAdapter(Map<String, Integer> accessFlagsMap, Map<String, String> innerNameMap,
|
||||
Map<String, Set<String>> extraMemberClass, DexExceptionHandler exceptionHandler, ClassVisitor cv,
|
||||
int access_flags, String className, String superClass, String[] interfaceNames, int config) {
|
||||
public V3ClassAdapter(Clz clz, DexExceptionHandler exceptionHandler, ClassVisitor cv, int access_flags,
|
||||
String className, String superClass, String[] interfaceNames, int config) {
|
||||
super();
|
||||
this.innerAccessFlagsMap = accessFlagsMap;
|
||||
this.innerNameMap = innerNameMap;
|
||||
this.extraMemberClass = extraMemberClass;
|
||||
this.cv = cv;
|
||||
this.clz = clz;
|
||||
this.cv = new OrderInnerOutterInsnNodeClassAdapter(cv);
|
||||
this.access_flags = access_flags;
|
||||
this.className = className;
|
||||
this.superClass = superClass;
|
||||
@ -88,8 +82,6 @@ public class V3ClassAdapter implements DexClassVisitor {
|
||||
protected void build() {
|
||||
if (!build) {
|
||||
String signature = null;
|
||||
String enclosingClass = null;
|
||||
Method enclosingMethod = null;
|
||||
for (Iterator<Annotation> it = anns.iterator(); it.hasNext();) {
|
||||
Annotation ann = it.next();
|
||||
if ("Ldalvik/annotation/Signature;".equals(ann.type)) {
|
||||
@ -104,42 +96,12 @@ public class V3ClassAdapter implements DexClassVisitor {
|
||||
signature = sb.toString();
|
||||
}
|
||||
}
|
||||
} else if (ann.type.equals("Ldalvik/annotation/EnclosingClass;")) {
|
||||
it.remove();
|
||||
for (Item i : ann.items) {
|
||||
if (i.name.equals("value")) {
|
||||
enclosingClass = i.value.toString();
|
||||
}
|
||||
}
|
||||
} else if (ann.type.equals("Ldalvik/annotation/EnclosingMethod;")) {
|
||||
it.remove();
|
||||
for (Item i : ann.items) {
|
||||
if ("value".equals(i.name)) {
|
||||
enclosingMethod = (Method) i.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isInnerClass) {
|
||||
Integer i = innerAccessFlagsMap.get(className);
|
||||
if (i != null) {
|
||||
access_flags = i;
|
||||
}
|
||||
}
|
||||
int accessInClass = access_flags;
|
||||
if ((access_flags & Opcodes.ACC_INTERFACE) == 0) { // issue 55
|
||||
accessInClass |= Opcodes.ACC_SUPER;// 解决生成的class文件使用dx重新转换时使用的指令与原始指令不同的问题
|
||||
}
|
||||
|
||||
// access in class has no acc_static or acc_private
|
||||
accessInClass &= ~(Opcodes.ACC_STATIC | Opcodes.ACC_PRIVATE);
|
||||
|
||||
if (isInnerClass && (access_flags & Opcodes.ACC_PROTECTED) != 0) {
|
||||
accessInClass &= ~Opcodes.ACC_PROTECTED;
|
||||
accessInClass |= Opcodes.ACC_PUBLIC;
|
||||
}
|
||||
|
||||
Clz clz = this.clz;
|
||||
int access = clz.access;
|
||||
boolean isInnerClass = clz.enclosingClass != null || clz.enclosingMethod != null;
|
||||
int accessInClass = clearClassAccess(isInnerClass, access);
|
||||
String[] nInterfaceNames = null;
|
||||
if (interfaceNames != null) {
|
||||
nInterfaceNames = new String[interfaceNames.length];
|
||||
@ -150,51 +112,22 @@ public class V3ClassAdapter implements DexClassVisitor {
|
||||
cv.visit(Opcodes.V1_6, accessInClass, Type.getType(className).getInternalName(), signature,
|
||||
superClass == null ? null : Type.getType(superClass).getInternalName(), nInterfaceNames);
|
||||
|
||||
for (Annotation ann : anns) {
|
||||
if (ann.type.equals("Ldalvik/annotation/MemberClasses;")) {
|
||||
Set<String> extraMember = extraMemberClass.get(className);
|
||||
extraMember = extraMember == null ? new TreeSet<String>() : new TreeSet<String>(extraMember);
|
||||
for (Item i : ann.items) {
|
||||
if (i.name.equals("value")) {
|
||||
for (Item j : ((Annotation) i.value).items) {
|
||||
String name = j.value.toString();
|
||||
extraMember.add(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (String name : extraMember) {
|
||||
Integer access = innerAccessFlagsMap.get(name);
|
||||
String innerName = innerNameMap.get(name);
|
||||
cv.visitInnerClass(Type.getType(name).getInternalName(), Type.getType(className)
|
||||
.getInternalName(), innerName, access == null ? 0 : access);
|
||||
}
|
||||
continue;
|
||||
} else if (ann.type.equals("Ldalvik/annotation/InnerClass;")) {
|
||||
String name = null;
|
||||
for (Item i : ann.items) {
|
||||
if (i.name.equals("name")) {
|
||||
name = (String) i.value;
|
||||
}
|
||||
}
|
||||
int accessInInnerClassAttr = access_flags & (~Opcodes.ACC_SUPER);// inner class attr has no
|
||||
// acc_super
|
||||
searchInnerClass(clz);
|
||||
if (isInnerClass) {
|
||||
// build Outer Clz
|
||||
if (clz.innerName == null) {// anonymous Innerclass
|
||||
Method enclosingMethod = clz.enclosingMethod;
|
||||
if (enclosingMethod != null) {
|
||||
cv.visitOuterClass(Type.getType(enclosingMethod.getOwner()).getInternalName(),
|
||||
enclosingMethod.getName(), enclosingMethod.getDesc());
|
||||
}
|
||||
|
||||
if (name == null) {
|
||||
if (enclosingMethod == null) {
|
||||
cv.visitOuterClass(Type.getType(enclosingClass).getInternalName(), null, null);
|
||||
}
|
||||
cv.visitInnerClass(Type.getType(className).getInternalName(), null, null,
|
||||
accessInInnerClassAttr);
|
||||
} else {
|
||||
cv.visitInnerClass(Type.getType(className).getInternalName(), Type.getType(enclosingClass)
|
||||
.getInternalName(), name, accessInInnerClassAttr);
|
||||
Clz enclosingClass = clz.enclosingClass;
|
||||
cv.visitOuterClass(Type.getType(enclosingClass.name).getInternalName(), null, null);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
searchEnclosing(clz);
|
||||
}
|
||||
for (Annotation ann : anns) {
|
||||
AnnotationVisitor av = cv.visitAnnotation(ann.type, ann.visible);
|
||||
V3AnnAdapter.accept(ann.items, av);
|
||||
av.visitEnd();
|
||||
@ -206,10 +139,125 @@ public class V3ClassAdapter implements DexClassVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For structure
|
||||
*
|
||||
* <pre>
|
||||
* class A {
|
||||
* class B {
|
||||
* class WeAreHere {
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* this method will add
|
||||
*
|
||||
* <pre>
|
||||
* InnerClass Outter
|
||||
* A$B$WeAreHere A$B
|
||||
* A$B A
|
||||
* </pre>
|
||||
*
|
||||
* to WeAreHere.class
|
||||
*
|
||||
* @param clz
|
||||
*/
|
||||
private void searchEnclosing(Clz clz) {
|
||||
for (Clz p = clz; p != null; p = p.enclosingClass) {
|
||||
Clz enclosingClass = p.enclosingClass;
|
||||
if (enclosingClass == null) {
|
||||
break;
|
||||
}
|
||||
int accessInInner = clearInnerAccess(p.access);
|
||||
if (p.innerName != null) {// non-anonymous Innerclass
|
||||
cv.visitInnerClass(Type.getType(p.name).getInternalName(), Type.getType(enclosingClass.name)
|
||||
.getInternalName(), p.innerName, accessInInner);
|
||||
} else {// anonymous Innerclass
|
||||
cv.visitInnerClass(Type.getType(p.name).getInternalName(), null, null, accessInInner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For structure
|
||||
*
|
||||
* <pre>
|
||||
* class WeAreHere {
|
||||
* class A {
|
||||
* class B {
|
||||
*
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* this method will add
|
||||
*
|
||||
* <pre>
|
||||
* InnerClass Outter
|
||||
* WeAreHere$A$B WeAreHere$A
|
||||
* WeAreHere$A WeAreHere
|
||||
* </pre>
|
||||
*
|
||||
* to WeAreHere.class
|
||||
*
|
||||
* @param clz
|
||||
*/
|
||||
private void searchInnerClass(Clz clz) {
|
||||
Set<Clz> visited = new HashSet<Clz>();
|
||||
Stack<Clz> stack = new Stack<Clz>();
|
||||
stack.push(clz);
|
||||
while (!stack.empty()) {
|
||||
clz = stack.pop();
|
||||
if (visited.contains(clz)) {
|
||||
continue;
|
||||
} else {
|
||||
visited.add(clz);
|
||||
}
|
||||
if (clz.inners != null) {
|
||||
for (Clz inner : clz.inners) {
|
||||
if (inner.innerName == null) {// anonymous Innerclass
|
||||
cv.visitInnerClass(Type.getType(inner.name).getInternalName(), null, null,
|
||||
clearInnerAccess(inner.access));
|
||||
} else {// non-anonymous Innerclass
|
||||
cv.visitInnerClass(Type.getType(inner.name).getInternalName(), Type.getType(className)
|
||||
.getInternalName(), inner.innerName, clearInnerAccess(inner.access));
|
||||
}
|
||||
stack.push(inner);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int clearClassAccess(boolean isInner, int access) {
|
||||
if ((access & Opcodes.ACC_INTERFACE) == 0) { // issue 55
|
||||
access |= Opcodes.ACC_SUPER;// 解决生成的class文件使用dx重新转换时使用的指令与原始指令不同的问题
|
||||
}
|
||||
// access in class has no acc_static or acc_private
|
||||
access &= ~(Opcodes.ACC_STATIC | Opcodes.ACC_PRIVATE);
|
||||
if (isInner && (access & Opcodes.ACC_PROTECTED) != 0) {// protected inner class are public
|
||||
access &= ~Opcodes.ACC_PROTECTED;
|
||||
access |= Opcodes.ACC_PUBLIC;
|
||||
}
|
||||
return access;
|
||||
}
|
||||
|
||||
private int clearInnerAccess(int access) {
|
||||
access &= (~Opcodes.ACC_SUPER);// inner class attr has no acc_super
|
||||
if (0 != (access & Opcodes.ACC_PRIVATE)) {// clear public/protected if it is private
|
||||
access &= ~(Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED);
|
||||
} else if (0 != (access & Opcodes.ACC_PROTECTED)) {// clear public if it is protected
|
||||
access &= ~(Opcodes.ACC_PUBLIC);
|
||||
}
|
||||
return access;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DexAnnotationVisitor visitAnnotation(String name, boolean visible) {
|
||||
if (!isInnerClass) {
|
||||
isInnerClass = "Ldalvik/annotation/InnerClass;".equals(name);
|
||||
if (name.equals("Ldalvik/annotation/EnclosingClass;") || name.equals("Ldalvik/annotation/EnclosingMethod;")
|
||||
|| "Ldalvik/annotation/InnerClass;".equals(name) || "Ldalvik/annotation/MemberClasses;".equals(name)) {
|
||||
return null;
|
||||
}
|
||||
if (name.equals("Ldalvik/annotation/AnnotationDefault;")) {
|
||||
return new EmptyVisitor() {
|
||||
|
@ -0,0 +1,185 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 Panxiaobo
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.googlecode.dex2jar.v3;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
import com.googlecode.dex2jar.Annotation;
|
||||
import com.googlecode.dex2jar.Annotation.Item;
|
||||
import com.googlecode.dex2jar.Method;
|
||||
import com.googlecode.dex2jar.visitors.DexAnnotationVisitor;
|
||||
import com.googlecode.dex2jar.visitors.DexClassVisitor;
|
||||
import com.googlecode.dex2jar.visitors.DexFileVisitor;
|
||||
import com.googlecode.dex2jar.visitors.EmptyVisitor;
|
||||
|
||||
/**
|
||||
* gathering inner
|
||||
*
|
||||
* @author <a href="mailto:pxb1988@gmail.com">Panxiaobo</a>
|
||||
* @version $Rev$
|
||||
*/
|
||||
public class V3InnerClzGather implements DexFileVisitor {
|
||||
public static class Clz {
|
||||
public int access;
|
||||
public Clz enclosingClass;
|
||||
public Method enclosingMethod;
|
||||
public String innerName;
|
||||
public Set<Clz> inners = null;
|
||||
public final String name;
|
||||
|
||||
public Clz(String name) {
|
||||
super();
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
void addInner(Clz clz) {
|
||||
if (inners == null) {
|
||||
inners = new HashSet<Clz>();
|
||||
}
|
||||
inners.add(clz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
Clz other = (Clz) obj;
|
||||
if (name == null) {
|
||||
if (other.name != null)
|
||||
return false;
|
||||
} else if (!name.equals(other.name))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((name == null) ? 0 : name.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "" + name;
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Clz> classes = new HashMap<String, Clz>();
|
||||
|
||||
private Clz get(String name) {
|
||||
Clz clz = classes.get(name);
|
||||
if (clz == null) {
|
||||
clz = new Clz(name);
|
||||
classes.put(name, clz);
|
||||
}
|
||||
return clz;
|
||||
}
|
||||
|
||||
public Map<String, Clz> getClasses() {
|
||||
return classes;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see com.googlecode.dex2jar.visitors.DexFileVisitor#visit(int, java.lang.String, java.lang.String,
|
||||
* java.lang.String[])
|
||||
*/
|
||||
@Override
|
||||
public DexClassVisitor visit(int access_flags, final String className, String superClass, String[] interfaceNames) {
|
||||
|
||||
final Clz clz = get(className);
|
||||
clz.access |= access_flags;
|
||||
|
||||
return new EmptyVisitor() {
|
||||
protected List<Annotation> anns = new ArrayList<Annotation>();
|
||||
|
||||
@Override
|
||||
public DexAnnotationVisitor visitAnnotation(String name, boolean visible) {
|
||||
Annotation ann = new Annotation(name, visible);
|
||||
anns.add(ann);
|
||||
return new V3AnnAdapter(ann);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
for (Annotation ann : anns) {
|
||||
if (ann.type.equals("Ldalvik/annotation/EnclosingClass;")) {
|
||||
for (Item i : ann.items) {
|
||||
if (i.name.equals("value")) {
|
||||
clz.enclosingClass = get(i.value.toString());
|
||||
clz.enclosingClass.addInner(clz);
|
||||
}
|
||||
}
|
||||
} else if (ann.type.equals("Ldalvik/annotation/EnclosingMethod;")) {
|
||||
for (Item i : ann.items) {
|
||||
if ("value".equals(i.name)) {
|
||||
Method m = (Method) i.value;
|
||||
if (m != null) {
|
||||
clz.enclosingMethod = m;
|
||||
clz.enclosingClass = get(clz.enclosingMethod.getOwner());
|
||||
clz.enclosingClass.addInner(clz);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if ("Ldalvik/annotation/InnerClass;".equals(ann.type)) {
|
||||
for (Item it : ann.items) {
|
||||
if ("accessFlags".equals(it.name)) {
|
||||
clz.access |= (Integer) it.value;
|
||||
} else if ("name".equals(it.name)) {
|
||||
clz.innerName = (String) it.value;
|
||||
}
|
||||
}
|
||||
} else if ("Ldalvik/annotation/MemberClasses;".equals(ann.type)) {
|
||||
for (Item it : ann.items) {
|
||||
if ("value".equals(it.name)) {
|
||||
Annotation v = (Annotation) it.value;
|
||||
for (Item item : v.items) {
|
||||
Type type = (Type) item.value;
|
||||
Clz inner = get(type.getDescriptor());
|
||||
clz.addInner(inner);
|
||||
inner.enclosingClass = clz;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see com.googlecode.dex2jar.visitors.DexFileVisitor#visitEnd()
|
||||
*/
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
}
|
||||
|
||||
}
|
@ -37,6 +37,7 @@ import com.googlecode.dex2jar.ir.stmt.LabelStmt;
|
||||
import com.googlecode.dex2jar.ir.stmt.Stmt;
|
||||
import com.googlecode.dex2jar.ir.stmt.Stmt.ST;
|
||||
import com.googlecode.dex2jar.ir.stmt.StmtList;
|
||||
import com.googlecode.dex2jar.ir.ts.ArrayNullPointerTransformer;
|
||||
import com.googlecode.dex2jar.ir.ts.EndRemover;
|
||||
import com.googlecode.dex2jar.ir.ts.ExceptionHandlerCurrectTransformer;
|
||||
import com.googlecode.dex2jar.ir.ts.FixVar;
|
||||
@ -60,8 +61,8 @@ public class V3MethodAdapter implements DexMethodVisitor, Opcodes {
|
||||
protected static Transformer topologicalSort = new TopologicalSort();
|
||||
private static final Logger log = Logger.getLogger(V3MethodAdapter.class.getName());
|
||||
protected static Transformer[] tses = new Transformer[] { new ExceptionHandlerCurrectTransformer(),
|
||||
new ZeroTransformer(), new FixVar(), new LocalSplit(), new LocalRemove(), new LocalType(),
|
||||
new LocalCurrect() };
|
||||
new ZeroTransformer(), new ArrayNullPointerTransformer(), new FixVar(), new LocalSplit(),
|
||||
new LocalRemove(), new LocalType(), new LocalCurrect() };
|
||||
static {
|
||||
log.log(Level.CONFIG, "InsnList.check=false");
|
||||
// Optimize Tree Analyzer
|
||||
|
@ -6,8 +6,11 @@ import static com.googlecode.dex2jar.DexOpcodes.OP_AGET;
|
||||
import static com.googlecode.dex2jar.DexOpcodes.OP_APUT;
|
||||
import static com.googlecode.dex2jar.DexOpcodes.OP_ARRAY_LENGTH;
|
||||
import static com.googlecode.dex2jar.DexOpcodes.OP_CONST;
|
||||
import static com.googlecode.dex2jar.DexOpcodes.OP_GOTO;
|
||||
import static com.googlecode.dex2jar.DexOpcodes.OP_INVOKE_VIRTUAL;
|
||||
import static com.googlecode.dex2jar.DexOpcodes.OP_NEW_ARRAY;
|
||||
import static com.googlecode.dex2jar.DexOpcodes.OP_RETURN_VOID;
|
||||
import static com.googlecode.dex2jar.DexOpcodes.TYPE_OBJECT;
|
||||
import static com.googlecode.dex2jar.DexOpcodes.TYPE_INT;
|
||||
import static com.googlecode.dex2jar.DexOpcodes.TYPE_SINGLE;
|
||||
|
||||
@ -15,6 +18,7 @@ import org.junit.Test;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.tree.analysis.AnalyzerException;
|
||||
|
||||
import com.googlecode.dex2jar.DexLabel;
|
||||
import com.googlecode.dex2jar.Method;
|
||||
import com.googlecode.dex2jar.v3.V3;
|
||||
import com.googlecode.dex2jar.visitors.DexClassVisitor;
|
||||
@ -62,6 +66,27 @@ public class ArrayTypeTest {
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
public static void merge1(DexClassVisitor cv) {// obj = array
|
||||
DexMethodVisitor mv = cv.visitMethod(ACC_PUBLIC | ACC_STATIC, new Method("La;", "b", new String[] {}, "V"));
|
||||
DexCodeVisitor code = mv.visitCode();
|
||||
DexLabel L0 = new DexLabel();
|
||||
DexLabel L1 = new DexLabel();
|
||||
code.visitArguments(3, new int[] {});
|
||||
code.visitConstStmt(OP_CONST, 0, 0, TYPE_SINGLE);
|
||||
code.visitJumpStmt(OP_GOTO, L1);
|
||||
code.visitLabel(L0);
|
||||
code.visitUnopStmt(OP_ARRAY_LENGTH, 1, 0, TYPE_INT);
|
||||
code.visitConstStmt(OP_CONST, 1, 0, TYPE_SINGLE);
|
||||
code.visitArrayStmt(OP_AGET, 2, 0, 1, TYPE_OBJECT);
|
||||
code.visitReturnStmt(OP_RETURN_VOID);
|
||||
code.visitLabel(L1);
|
||||
code.visitConstStmt(OP_CONST, 1, 1, TYPE_SINGLE);
|
||||
code.visitClassStmt(OP_NEW_ARRAY, 0, 1, "[Ljava/security/cert/X509Certificate;");
|
||||
code.visitJumpStmt(OP_GOTO, L0);
|
||||
code.visitEnd();
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test120() throws IllegalArgumentException, IllegalAccessException, AnalyzerException {
|
||||
TestDexClassV cv = new TestDexClassV("Lt", V3.OPTIMIZE_SYNCHRONIZED | V3.TOPOLOGICAL_SORT);
|
||||
@ -70,20 +95,28 @@ public class ArrayTypeTest {
|
||||
TestUtils.verify(cr);
|
||||
}
|
||||
|
||||
// FIXME issue 122
|
||||
// @Test
|
||||
// issue 122
|
||||
@Test
|
||||
public void test122() throws IllegalArgumentException, IllegalAccessException, AnalyzerException {
|
||||
TestDexClassV cv = new TestDexClassV("Lt", V3.OPTIMIZE_SYNCHRONIZED | V3.TOPOLOGICAL_SORT);
|
||||
a122(cv);
|
||||
ClassReader cr = new ClassReader(cv.toByteArray());
|
||||
TestUtils.verify(cr);
|
||||
}
|
||||
|
||||
// issue 123
|
||||
@Test
|
||||
public void test123() throws IllegalArgumentException, IllegalAccessException, AnalyzerException {
|
||||
TestDexClassV cv = new TestDexClassV("Lt", V3.OPTIMIZE_SYNCHRONIZED | V3.TOPOLOGICAL_SORT);
|
||||
a123(cv);
|
||||
ClassReader cr = new ClassReader(cv.toByteArray());
|
||||
TestUtils.verify(cr);
|
||||
}
|
||||
|
||||
// FIXME issue 123
|
||||
// @Test
|
||||
public void test123() throws IllegalArgumentException, IllegalAccessException, AnalyzerException {
|
||||
@Test
|
||||
public void testMerge1() throws IllegalArgumentException, IllegalAccessException, AnalyzerException {
|
||||
TestDexClassV cv = new TestDexClassV("Lt", V3.OPTIMIZE_SYNCHRONIZED | V3.TOPOLOGICAL_SORT);
|
||||
a122(cv);
|
||||
merge1(cv);
|
||||
ClassReader cr = new ClassReader(cv.toByteArray());
|
||||
TestUtils.verify(cr);
|
||||
}
|
||||
|
@ -0,0 +1,195 @@
|
||||
package com.googlecode.dex2jar.test;
|
||||
|
||||
import static com.googlecode.dex2jar.DexOpcodes.OP_CONST_STRING;
|
||||
import static com.googlecode.dex2jar.DexOpcodes.OP_GOTO;
|
||||
import static com.googlecode.dex2jar.DexOpcodes.OP_IF_EQZ;
|
||||
import static com.googlecode.dex2jar.DexOpcodes.OP_IF_NEZ;
|
||||
import static com.googlecode.dex2jar.DexOpcodes.OP_IGET;
|
||||
import static com.googlecode.dex2jar.DexOpcodes.OP_INVOKE_DIRECT;
|
||||
import static com.googlecode.dex2jar.DexOpcodes.OP_INVOKE_STATIC;
|
||||
import static com.googlecode.dex2jar.DexOpcodes.OP_INVOKE_VIRTUAL;
|
||||
import static com.googlecode.dex2jar.DexOpcodes.OP_MOVE;
|
||||
import static com.googlecode.dex2jar.DexOpcodes.OP_MOVE_EXCEPTION;
|
||||
import static com.googlecode.dex2jar.DexOpcodes.OP_MOVE_RESULT;
|
||||
import static com.googlecode.dex2jar.DexOpcodes.OP_NEW_INSTANCE;
|
||||
import static com.googlecode.dex2jar.DexOpcodes.OP_RETURN;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
import com.googlecode.dex2jar.DexLabel;
|
||||
import com.googlecode.dex2jar.Field;
|
||||
import com.googlecode.dex2jar.Method;
|
||||
import com.googlecode.dex2jar.v3.V3;
|
||||
import com.googlecode.dex2jar.v3.V3MethodAdapter;
|
||||
import com.googlecode.dex2jar.visitors.DexClassVisitor;
|
||||
import com.googlecode.dex2jar.visitors.DexCodeVisitor;
|
||||
import com.googlecode.dex2jar.visitors.DexMethodVisitor;
|
||||
import com.googlecode.dex2jar.visitors.EmptyVisitor;
|
||||
|
||||
public class EmptyTrapTest implements Opcodes {
|
||||
|
||||
@Test
|
||||
public void test1() throws Exception {
|
||||
try {
|
||||
final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
|
||||
cw.visit(V1_6, ACC_PUBLIC, "JSResponseTest", null, "java/lang/Object", null);
|
||||
m005_toJSONString(new EmptyVisitor() {
|
||||
@Override
|
||||
public DexMethodVisitor visitMethod(int accessFlags, Method method) {
|
||||
return new V3MethodAdapter(accessFlags, method, null, V3.OPTIMIZE_SYNCHRONIZED
|
||||
| V3.TOPOLOGICAL_SORT) {
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
super.visitEnd();
|
||||
methodNode.accept(cw);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
ClassReader cr = new ClassReader(cw.toByteArray());
|
||||
TestUtils.verify(cr);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public static void m005_toJSONString(DexClassVisitor cv) {
|
||||
DexMethodVisitor mv = cv.visitMethod(0, new Method("LJSResponseTest;", "toJSONString", new String[] {},
|
||||
"Ljava/lang/String;"));
|
||||
if (mv != null) {
|
||||
DexCodeVisitor code = mv.visitCode();
|
||||
if (code != null) {
|
||||
code.visitArguments(6, new int[] { 5 });
|
||||
DexLabel L0 = new DexLabel();
|
||||
DexLabel L1 = new DexLabel();
|
||||
DexLabel L2 = new DexLabel();
|
||||
code.visitTryCatch(L0, L1, L2, "Lorg/json/JSONException;");
|
||||
DexLabel L3 = new DexLabel();
|
||||
DexLabel L4 = new DexLabel();
|
||||
code.visitTryCatch(L3, L4, L2, "Lorg/json/JSONException;");
|
||||
DexLabel L5 = new DexLabel();
|
||||
DexLabel L6 = new DexLabel();
|
||||
code.visitTryCatch(L5, L6, L2, "Lorg/json/JSONException;");
|
||||
DexLabel L7 = new DexLabel();
|
||||
code.visitLineNumber(50, L7);
|
||||
code.visitLineNumber(50, L0);
|
||||
DexLabel L8 = new DexLabel();
|
||||
code.visitLineNumber(51, L8);
|
||||
DexLabel L9 = new DexLabel();
|
||||
code.visitLineNumber(67, L9);
|
||||
DexLabel L10 = new DexLabel();
|
||||
code.visitLineNumber(52, L10);
|
||||
DexLabel L11 = new DexLabel();
|
||||
code.visitLineNumber(53, L11);
|
||||
DexLabel L12 = new DexLabel();
|
||||
code.visitLineNumber(54, L12);
|
||||
DexLabel L13 = new DexLabel();
|
||||
code.visitLineNumber(55, L13);
|
||||
DexLabel L14 = new DexLabel();
|
||||
code.visitLineNumber(56, L14);
|
||||
DexLabel L15 = new DexLabel();
|
||||
code.visitLineNumber(57, L15);
|
||||
code.visitLineNumber(67, L1);
|
||||
code.visitLineNumber(58, L3);
|
||||
DexLabel L16 = new DexLabel();
|
||||
code.visitLineNumber(59, L16);
|
||||
code.visitLineNumber(62, L2);
|
||||
code.visitLocalVariable("object", "Lorg/json/JSONObject;", null, L11, L2, 1);
|
||||
DexLabel L17 = new DexLabel();
|
||||
code.visitLineNumber(64, L17);
|
||||
DexLabel L18 = new DexLabel();
|
||||
code.visitLineNumber(65, L18);
|
||||
code.visitLineNumber(61, L5);
|
||||
code.visitLocalVariable("jsonException", "Lorg/json/JSONException;", null, L17, L5, 0);
|
||||
DexLabel L19 = new DexLabel();
|
||||
code.visitLocalVariable("object", "Lorg/json/JSONObject;", null, L5, L19, 1);
|
||||
code.visitLocalVariable("this", "LJSResponseTest;", null, L7, L19, 5);
|
||||
code.visitLabel(L7);
|
||||
code.visitConstStmt(OP_CONST_STRING, 2, "response", 2);
|
||||
code.visitConstStmt(OP_CONST_STRING, 4, "", 2);
|
||||
code.visitLabel(L0);
|
||||
code.visitFieldStmt(OP_IGET, 2, 5, new Field("LJSResponseTest;", "className", "Ljava/lang/String;"), 2);
|
||||
code.visitJumpStmt(OP_IF_EQZ, 2, L8);
|
||||
code.visitFieldStmt(OP_IGET, 2, 5, new Field("LJSResponseTest;", "methodName", "Ljava/lang/String;"), 2);
|
||||
code.visitJumpStmt(OP_IF_NEZ, 2, L10);
|
||||
code.visitLabel(L8);
|
||||
code.visitConstStmt(OP_CONST_STRING, 2, "", 2);
|
||||
code.visitMoveStmt(OP_MOVE, 2, 4, 2);
|
||||
code.visitLabel(L9);
|
||||
code.visitReturnStmt(OP_RETURN, 2, 2);
|
||||
code.visitLabel(L10);
|
||||
code.visitClassStmt(OP_NEW_INSTANCE, 1, "Lorg/json/JSONObject;");
|
||||
code.visitMethodStmt(OP_INVOKE_DIRECT, new int[] { 1 }, new Method("Lorg/json/JSONObject;", "<init>",
|
||||
new String[] {}, "V"));
|
||||
code.visitLabel(L11);
|
||||
code.visitConstStmt(OP_CONST_STRING, 2, "class", 2);
|
||||
code.visitFieldStmt(OP_IGET, 3, 5, new Field("LJSResponseTest;", "className", "Ljava/lang/String;"), 2);
|
||||
code.visitMethodStmt(OP_INVOKE_VIRTUAL, new int[] { 1, 2, 3 }, new Method("Lorg/json/JSONObject;",
|
||||
"put", new String[] { "Ljava/lang/String;", "Ljava/lang/Object;" }, "Lorg/json/JSONObject;"));
|
||||
code.visitLabel(L12);
|
||||
code.visitConstStmt(OP_CONST_STRING, 2, "call", 2);
|
||||
code.visitFieldStmt(OP_IGET, 3, 5, new Field("LJSResponseTest;", "methodName", "Ljava/lang/String;"), 2);
|
||||
code.visitMethodStmt(OP_INVOKE_VIRTUAL, new int[] { 1, 2, 3 }, new Method("Lorg/json/JSONObject;",
|
||||
"put", new String[] { "Ljava/lang/String;", "Ljava/lang/Object;" }, "Lorg/json/JSONObject;"));
|
||||
code.visitLabel(L13);
|
||||
code.visitConstStmt(OP_CONST_STRING, 2, "result", 2);
|
||||
code.visitFieldStmt(OP_IGET, 3, 5, new Field("LJSResponseTest;", "result", "I"), 0);
|
||||
code.visitMethodStmt(OP_INVOKE_VIRTUAL, new int[] { 1, 2, 3 }, new Method("Lorg/json/JSONObject;",
|
||||
"put", new String[] { "Ljava/lang/String;", "I" }, "Lorg/json/JSONObject;"));
|
||||
code.visitLabel(L14);
|
||||
code.visitFieldStmt(OP_IGET, 2, 5, new Field("LJSResponseTest;", "response", "Ljava/lang/Object;"), 2);
|
||||
code.visitJumpStmt(OP_IF_EQZ, 2, L3);
|
||||
code.visitLabel(L15);
|
||||
code.visitConstStmt(OP_CONST_STRING, 2, "response", 2);
|
||||
code.visitFieldStmt(OP_IGET, 3, 5, new Field("LJSResponseTest;", "response", "Ljava/lang/Object;"), 2);
|
||||
code.visitMethodStmt(OP_INVOKE_VIRTUAL, new int[] { 1, 2, 3 }, new Method("Lorg/json/JSONObject;",
|
||||
"put", new String[] { "Ljava/lang/String;", "Ljava/lang/Object;" }, "Lorg/json/JSONObject;"));
|
||||
code.visitLabel(L1);
|
||||
code.visitMethodStmt(OP_INVOKE_VIRTUAL, new int[] { 1 }, new Method("Lorg/json/JSONObject;",
|
||||
"toString", new String[] {}, "Ljava/lang/String;"));
|
||||
code.visitMoveStmt(OP_MOVE_RESULT, 2, 2);
|
||||
code.visitJumpStmt(OP_GOTO, L9);
|
||||
code.visitLabel(L3);
|
||||
code.visitFieldStmt(OP_IGET, 2, 5, new Field("LJSResponseTest;", "dataResponse", "[B"), 2);
|
||||
code.visitJumpStmt(OP_IF_EQZ, 2, L5);
|
||||
code.visitLabel(L16);
|
||||
code.visitConstStmt(OP_CONST_STRING, 2, "response", 2);
|
||||
code.visitFieldStmt(OP_IGET, 3, 5, new Field("LJSResponseTest;", "dataResponse", "[B"), 2);
|
||||
code.visitMethodStmt(OP_INVOKE_STATIC, new int[] { 3 }, new Method("LBase64;", "encode",
|
||||
new String[] { "[B" }, "Ljava/lang/String;"));
|
||||
code.visitMoveStmt(OP_MOVE_RESULT, 3, 2);
|
||||
code.visitMethodStmt(OP_INVOKE_VIRTUAL, new int[] { 1, 2, 3 }, new Method("Lorg/json/JSONObject;",
|
||||
"put", new String[] { "Ljava/lang/String;", "Ljava/lang/Object;" }, "Lorg/json/JSONObject;"));
|
||||
code.visitLabel(L4);
|
||||
code.visitJumpStmt(OP_GOTO, L1);
|
||||
code.visitLabel(L2);
|
||||
code.visitMoveStmt(OP_MOVE_EXCEPTION, 2, 2);
|
||||
code.visitMoveStmt(OP_MOVE, 0, 2, 2);
|
||||
code.visitLabel(L17);
|
||||
code.visitConstStmt(OP_CONST_STRING, 2, "MillennialMediaSDK", 2);
|
||||
code.visitMethodStmt(OP_INVOKE_VIRTUAL, new int[] { 0 }, new Method("Lorg/json/JSONException;",
|
||||
"getMessage", new String[] {}, "Ljava/lang/String;"));
|
||||
code.visitMoveStmt(OP_MOVE_RESULT, 3, 2);
|
||||
code.visitMethodStmt(OP_INVOKE_STATIC, new int[] { 2, 3 }, new Method("Landroid/util/Log;", "e",
|
||||
new String[] { "Ljava/lang/String;", "Ljava/lang/String;" }, "I"));
|
||||
code.visitLabel(L18);
|
||||
code.visitConstStmt(OP_CONST_STRING, 2, "", 2);
|
||||
code.visitMoveStmt(OP_MOVE, 2, 4, 2);
|
||||
code.visitJumpStmt(OP_GOTO, L9);
|
||||
code.visitLabel(L5);
|
||||
code.visitConstStmt(OP_CONST_STRING, 2, "", 2);
|
||||
code.visitLabel(L6);
|
||||
code.visitMoveStmt(OP_MOVE, 2, 4, 2);
|
||||
code.visitJumpStmt(OP_GOTO, L9);
|
||||
code.visitLabel(L19);
|
||||
code.visitEnd();
|
||||
}
|
||||
mv.visitEnd();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
package com.googlecode.dex2jar.test;
|
||||
|
||||
import static com.googlecode.dex2jar.DexOpcodes.ACC_PUBLIC;
|
||||
import static com.googlecode.dex2jar.DexOpcodes.ACC_STATIC;
|
||||
import static com.googlecode.dex2jar.DexOpcodes.OP_CONST_STRING;
|
||||
import static com.googlecode.dex2jar.DexOpcodes.OP_INVOKE_VIRTUAL;
|
||||
import static com.googlecode.dex2jar.DexOpcodes.OP_MONITOR_EXIT;
|
||||
import static com.googlecode.dex2jar.DexOpcodes.OP_MOVE_EXCEPTION;
|
||||
import static com.googlecode.dex2jar.DexOpcodes.OP_RETURN_VOID;
|
||||
import static com.googlecode.dex2jar.DexOpcodes.OP_THROW;
|
||||
import static com.googlecode.dex2jar.DexOpcodes.TYPE_OBJECT;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.tree.analysis.AnalyzerException;
|
||||
|
||||
import com.googlecode.dex2jar.DexLabel;
|
||||
import com.googlecode.dex2jar.DexOpcodes;
|
||||
import com.googlecode.dex2jar.Field;
|
||||
import com.googlecode.dex2jar.Method;
|
||||
import com.googlecode.dex2jar.v3.V3;
|
||||
import com.googlecode.dex2jar.visitors.DexClassVisitor;
|
||||
import com.googlecode.dex2jar.visitors.DexCodeVisitor;
|
||||
import com.googlecode.dex2jar.visitors.DexMethodVisitor;
|
||||
|
||||
public class OptSyncTest {
|
||||
/**
|
||||
* Generate the following code
|
||||
*
|
||||
* <pre>
|
||||
* public static void a() {
|
||||
* a0 = System.out
|
||||
* L0:
|
||||
* lock a0 <= a0 is inside a try-catch
|
||||
* a1="haha"
|
||||
* a0.println(a1)
|
||||
* L1:
|
||||
* unlock a0
|
||||
* return
|
||||
* L2:
|
||||
* a1 := @Exception
|
||||
* unlock a0
|
||||
* throw a1
|
||||
* =============
|
||||
* .catch L0 - L1 > L2 // all
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @param cv
|
||||
*/
|
||||
public static void a(DexClassVisitor cv) {
|
||||
DexMethodVisitor mv = cv.visitMethod(ACC_PUBLIC | ACC_STATIC, new Method("La;", "a", new String[] {}, "V"));
|
||||
DexCodeVisitor code = mv.visitCode();
|
||||
int v0 = 0;
|
||||
int v1 = 1;
|
||||
DexLabel try_start = new DexLabel();
|
||||
DexLabel try_end = new DexLabel();
|
||||
DexLabel catch_a = new DexLabel();
|
||||
|
||||
code.visitTryCatch(try_start, try_end, catch_a, null);
|
||||
code.visitArguments(2, new int[] {});
|
||||
code.visitFieldStmt(DexOpcodes.OP_SGET, v0, new Field("Ljava/lang/System;", "out", "Ljava/io/PrintStream;"),
|
||||
DexOpcodes.TYPE_OBJECT);
|
||||
code.visitLabel(try_start);
|
||||
code.visitMonitorStmt(DexOpcodes.OP_MONITOR_ENTER, v0);
|
||||
code.visitConstStmt(OP_CONST_STRING, v1, "haha", TYPE_OBJECT);
|
||||
code.visitMethodStmt(OP_INVOKE_VIRTUAL, new int[] { v0, v1 }, new Method("Ljava/io/PrintString;", "println",
|
||||
new String[] { "Ljava/lang/String;" }, "V"));
|
||||
code.visitLabel(try_end);
|
||||
code.visitMonitorStmt(OP_MONITOR_EXIT, v0);
|
||||
code.visitReturnStmt(OP_RETURN_VOID);
|
||||
code.visitLabel(catch_a);
|
||||
code.visitMoveStmt(OP_MOVE_EXCEPTION, v1, TYPE_OBJECT);
|
||||
code.visitMonitorStmt(OP_MONITOR_EXIT, v0);
|
||||
code.visitReturnStmt(OP_THROW, v1, TYPE_OBJECT);
|
||||
code.visitEnd();
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
// FIXME this test case shows a special scenario which case the class verify fail
|
||||
// @Test
|
||||
public void test() throws IllegalArgumentException, IllegalAccessException, AnalyzerException {
|
||||
TestDexClassV cv = new TestDexClassV("Lt", V3.OPTIMIZE_SYNCHRONIZED | V3.TOPOLOGICAL_SORT);
|
||||
a(cv);
|
||||
ClassReader cr = new ClassReader(cv.toByteArray());
|
||||
TestUtils.verify(cr);
|
||||
}
|
||||
}
|
@ -222,6 +222,16 @@ public abstract class TestUtils {
|
||||
|
||||
for (int i = 0; i < methods.size(); ++i) {
|
||||
MethodNode method = (MethodNode) methods.get(i);
|
||||
|
||||
List tryCatchBlocks = method.tryCatchBlocks;
|
||||
for (int j = 0; j < tryCatchBlocks.size(); j++) {
|
||||
TryCatchBlockNode tcn = (TryCatchBlockNode) tryCatchBlocks.get(j);
|
||||
if (tcn.start.equals(tcn.end)) {
|
||||
throw new DexException("try/catch block %d in %s has same start(%s) and end(%s)", j, method.name,
|
||||
tcn.start.getLabel(), tcn.end.getLabel());
|
||||
}
|
||||
}
|
||||
|
||||
BasicVerifier verifier = new BasicVerifier();
|
||||
Analyzer a = new Analyzer(verifier);
|
||||
try {
|
||||
|
@ -33,7 +33,7 @@ import com.googlecode.dex2jar.reader.DexFileReader;
|
||||
import com.googlecode.dex2jar.v3.ClassVisitorFactory;
|
||||
import com.googlecode.dex2jar.v3.Main;
|
||||
import com.googlecode.dex2jar.v3.V3;
|
||||
import com.googlecode.dex2jar.v3.V3AccessFlagsAdapter;
|
||||
import com.googlecode.dex2jar.v3.V3InnerClzGather;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:pxb1988@gmail.com">Panxiaobo</a>
|
||||
@ -62,45 +62,44 @@ public class V3Test {
|
||||
final int lineCount = 50;
|
||||
|
||||
DexFileReader reader = new DexFileReader(data);
|
||||
V3AccessFlagsAdapter afa = new V3AccessFlagsAdapter();
|
||||
V3InnerClzGather afa = new V3InnerClzGather();
|
||||
final List<String> exes = new ArrayList<String>();
|
||||
reader.accept(afa, DexFileReader.SKIP_CODE | DexFileReader.SKIP_DEBUG);
|
||||
System.out.flush();
|
||||
String logFileName = "target/v3.log." + System.currentTimeMillis();
|
||||
final PrintWriter log = new PrintWriter(logFileName, "UTF-8");
|
||||
System.out.write(String.format("%05d ", 0).getBytes(UTF8));
|
||||
reader.accept(new V3(afa.getAccessFlagsMap(), afa.getInnerNameMap(), afa.getExtraMember(), null,
|
||||
new ClassVisitorFactory() {
|
||||
int count = 0;
|
||||
reader.accept(new V3(afa.getClasses(), null, new ClassVisitorFactory() {
|
||||
int count = 0;
|
||||
|
||||
@Override
|
||||
public ClassVisitor create(final String name) {
|
||||
return new ClassWriter(ClassWriter.COMPUTE_MAXS) {
|
||||
@Override
|
||||
public ClassVisitor create(final String name) {
|
||||
return new ClassWriter(ClassWriter.COMPUTE_MAXS) {
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
count++;
|
||||
try {
|
||||
super.visitEnd();
|
||||
byte[] data = this.toByteArray();
|
||||
FileUtils.writeByteArrayToFile(new File(destDir, name + ".class"), data);
|
||||
TestUtils.verify(new ClassReader(data), log);
|
||||
System.out.write('.');
|
||||
} catch (Throwable e) {
|
||||
System.out.write('X');
|
||||
exes.add(String.format("%05d %s - %s", count - 1, name, e.getMessage()));
|
||||
}
|
||||
if (count % lineCount == 0) {
|
||||
try {
|
||||
System.out.write(String.format("\n%05d ", count).getBytes(UTF8));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
System.out.flush();
|
||||
public void visitEnd() {
|
||||
count++;
|
||||
try {
|
||||
super.visitEnd();
|
||||
byte[] data = this.toByteArray();
|
||||
FileUtils.writeByteArrayToFile(new File(destDir, name + ".class"), data);
|
||||
TestUtils.verify(new ClassReader(data), log);
|
||||
System.out.write('.');
|
||||
} catch (Throwable e) {
|
||||
System.out.write('X');
|
||||
exes.add(String.format("%05d %s - %s", count - 1, name, e.getMessage()));
|
||||
}
|
||||
if (count % lineCount == 0) {
|
||||
try {
|
||||
System.out.write(String.format("\n%05d ", count).getBytes(UTF8));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
System.out.flush();
|
||||
}
|
||||
}, V3.REUSE_REGISTER | V3.TOPOLOGICAL_SORT | V3.OPTIMIZE_SYNCHRONIZED), DexFileReader.SKIP_DEBUG);
|
||||
};
|
||||
}
|
||||
}, V3.REUSE_REGISTER | V3.TOPOLOGICAL_SORT | V3.OPTIMIZE_SYNCHRONIZED), DexFileReader.SKIP_DEBUG);
|
||||
System.out.flush();
|
||||
System.out.println();
|
||||
log.close();
|
||||
|
@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 Panxiaobo
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package res;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:pxb1988@gmail.com">Panxiaobo</a>
|
||||
*
|
||||
*/
|
||||
public class I56 {
|
||||
static class AStaticInnerClass {
|
||||
}
|
||||
|
||||
class AInnerClass {
|
||||
}
|
||||
|
||||
interface AInterface {
|
||||
}
|
||||
|
||||
void aaa() {
|
||||
new ArrayList() {
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
List a = new ArrayList() {
|
||||
};
|
||||
|
||||
static interface AStaticInterface {
|
||||
}
|
||||
}
|
102
dex-translator/src/test/java/res/I56_AccessFlag.java
Normal file
102
dex-translator/src/test/java/res/I56_AccessFlag.java
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 Panxiaobo
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package res;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:pxb1988@gmail.com">Panxiaobo</a>
|
||||
*
|
||||
*/
|
||||
public class I56_AccessFlag {
|
||||
static class AStaticInnerClass {
|
||||
}
|
||||
|
||||
class AInnerClass {
|
||||
class AAA {
|
||||
}
|
||||
}
|
||||
|
||||
interface AInterface {
|
||||
}
|
||||
|
||||
public static class B1 {
|
||||
};
|
||||
|
||||
public static class B11 {
|
||||
public static class XXX1 {
|
||||
};
|
||||
};
|
||||
|
||||
private static class B2 {
|
||||
};
|
||||
|
||||
protected static class B3 {
|
||||
};
|
||||
|
||||
/* package */static class B0 {
|
||||
};
|
||||
|
||||
final static class B4 {
|
||||
}
|
||||
|
||||
/* package */class C0 {
|
||||
}
|
||||
|
||||
public class C1 {
|
||||
}
|
||||
|
||||
private class C2 {
|
||||
}
|
||||
|
||||
protected class C3 {
|
||||
}
|
||||
|
||||
final class C4 {
|
||||
}
|
||||
|
||||
synchronized void sync1() {
|
||||
}
|
||||
|
||||
/**
|
||||
* seams that dx translate this method to
|
||||
*
|
||||
* <pre>
|
||||
* void sync2() {
|
||||
* synchronized (this) {
|
||||
* new Object();
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
synchronized void sync2() {
|
||||
new Object();
|
||||
}
|
||||
|
||||
void a() {
|
||||
new Object() {
|
||||
Object o = new Object() {
|
||||
};
|
||||
|
||||
class AX {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Object o = new Object() {
|
||||
};
|
||||
|
||||
static interface AStaticInterface {
|
||||
}
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
package res;
|
||||
|
||||
import java.io.PrintStream;
|
||||
|
||||
public class OptimizeSynchronized {
|
||||
public void a() {
|
||||
synchronized (this) {
|
||||
@ -20,4 +22,11 @@ public class OptimizeSynchronized {
|
||||
System.out.println(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void d() {
|
||||
PrintStream out = System.out;
|
||||
synchronized (out) {
|
||||
out.print("aa");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
7
dex-translator/src/test/java/res/U0000String.java
Normal file
7
dex-translator/src/test/java/res/U0000String.java
Normal file
@ -0,0 +1,7 @@
|
||||
package res;
|
||||
|
||||
public class U0000String {
|
||||
void a() {
|
||||
System.out.println("AAA\u0000ZZZ");
|
||||
}
|
||||
}
|
8
pom.xml
8
pom.xml
@ -130,10 +130,10 @@ Rev = {node|short}
|
||||
</plugins>
|
||||
</build>
|
||||
<properties>
|
||||
<dex2jar.tools.version>0.0.0.5</dex2jar.tools.version>
|
||||
<dex2jar.ir.version>1.7</dex2jar.ir.version>
|
||||
<dex2jar.reader.version>1.10</dex2jar.reader.version>
|
||||
<dex2jar.translator.version>0.0.9.9</dex2jar.translator.version>
|
||||
<dex2jar.tools.version>0.0.0.6</dex2jar.tools.version>
|
||||
<dex2jar.ir.version>1.8</dex2jar.ir.version>
|
||||
<dex2jar.reader.version>1.11</dex2jar.reader.version>
|
||||
<dex2jar.translator.version>0.0.9.10</dex2jar.translator.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
</project>
|
||||
|
Loading…
Reference in New Issue
Block a user