merge 0.0.9.10 to default

This commit is contained in:
Panxiaobo 2012-10-21 13:05:33 +08:00
commit e4a43f67d1
48 changed files with 3194 additions and 680 deletions

View File

@ -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) {

View File

@ -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;
}
}

View File

@ -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&nbsp;&lt;=&nbsp;b&nbsp;&lt;=&nbsp;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 &lt;&lt; 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 &lt;&lt; 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&nbsp;&lt;=&nbsp;b1, b2&nbsp;&lt;=&nbsp;255</code>,
* then the result is equal to:
* <blockquote><pre>
* (b1 &lt;&lt; 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&nbsp;&lt;=&nbsp;b1, b2&nbsp;&lt;=&nbsp;255</code>,
* then the result is equal to:
* <blockquote><pre>
* (b2 &lt;&lt; 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&nbsp;&lt;=&nbsp;b1,&nbsp;b2&nbsp;&lt;=&nbsp;255</code>,
* then the result is equal to:
* <blockquote><pre>
* (char)((b1 &lt;&lt; 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&nbsp;&lt;=&nbsp;b1,&nbsp;b2&nbsp;&lt;=&nbsp;255</code>,
* then the result is equal to:
* <blockquote><pre>
* (char)((b2 &lt;&lt; 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&nbsp;&lt;=&nbsp;b1, b2, b3, b4&nbsp;&lt;=&nbsp;255</code>,
* then the result is equal to:
* <blockquote><pre>
* (b1 &lt;&lt; 24) | (b2 &lt;&lt; 16) + (b3 &lt;&lt; 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&nbsp;&lt;=&nbsp;b1, b2, b3, b4&nbsp;&lt;=&nbsp;255</code>,
* then the result is equal to:
* <blockquote><pre>
* (b4 &lt;&lt; 24) | (b3 &lt;&lt; 16) + (b2 &lt;&lt; 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&nbsp;&lt;=&nbsp;b1, b2, b3, b4&nbsp;&lt;=&nbsp;255</code>,
* then the result is equal to:
* <blockquote><pre>
* (b1 &lt;&lt; 24) | (b2 &lt;&lt; 16) + (b3 &lt;&lt; 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&nbsp;&lt;=&nbsp;b1, b2, b3, b4&nbsp;&lt;=&nbsp;255</code>,
* then the result is equal to:
* <blockquote><pre>
* (b4 &lt;&lt; 24) | (b3 &lt;&lt; 16) + (b2 &lt;&lt; 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 &lt;= b1, b2, b3, b4, b5, b6, b7, b8 &lt;=255,
* </pre></blockquote>
* <p>
* then the result is equal to:
* <p><blockquote><pre>
* ((long)b1 &lt;&lt; 56) + ((long)b2 &lt;&lt; 48)
* + ((long)b3 &lt;&lt; 40) + ((long)b4 &lt;&lt; 32)
* + ((long)b5 &lt;&lt; 24) + ((long)b6 &lt;&lt; 16)
* + ((long)b7 &lt;&lt; 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 &lt;= b1, b2, b3, b4, b5, b6, b7, b8 &lt;=255,
* </pre></blockquote>
* <p>
* then the result is equal to:
* <p><blockquote><pre>
* ((long)b1 &lt;&lt; 56) + ((long)b2 &lt;&lt; 48)
* + ((long)b3 &lt;&lt; 40) + ((long)b4 &lt;&lt; 32)
* + ((long)b5 &lt;&lt; 24) + ((long)b6 &lt;&lt; 16)
* + ((long)b7 &lt;&lt; 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>'&#92;r'</code>), a newline character (<code>'&#92;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();
}
}

View File

@ -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() {

View File

@ -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));

View File

@ -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();
}
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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);

View File

@ -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

View File

@ -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);
}
}

View File

@ -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) {

View File

@ -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);
}
}

View File

@ -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) {

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}
}

View File

@ -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) {");

View File

@ -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));
}
}

View File

@ -34,7 +34,7 @@
<dependency>
<groupId>pxb.bc</groupId>
<artifactId>p-rename</artifactId>
<version>1.1</version>
<version>1.3</version>
</dependency>
</dependencies>
<build>

View File

@ -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));

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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() {
}
}

View File

@ -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() {

View File

@ -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() {
}
}

View File

@ -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

View File

@ -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);
}

View File

@ -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();
}
}
}

View File

@ -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);
}
}

View File

@ -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 {

View File

@ -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();

View File

@ -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 {
}
}

View 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 {
}
}

View File

@ -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");
}
}
}

View File

@ -0,0 +1,7 @@
package res;
public class U0000String {
void a() {
System.out.println("AAA\u0000ZZZ");
}
}

View File

@ -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>