com.googlecode.d2j.dex.writer.DexFileWriter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gradle Show documentation
Show all versions of gradle Show documentation
fakeradnroid gradle builder
/*
* dex2jar - Tools to work with android .dex and java .class files
* Copyright (c) 2009-2013 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.d2j.dex.writer;
import com.googlecode.d2j.dex.writer.io.ByteBufferOut;
import com.googlecode.d2j.dex.writer.io.DataOut;
import com.googlecode.d2j.dex.writer.item.*;
import com.googlecode.d2j.dex.writer.item.SectionItem.SectionType;
import com.googlecode.d2j.visitors.DexClassVisitor;
import com.googlecode.d2j.visitors.DexFileVisitor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.Adler32;
public class DexFileWriter extends DexFileVisitor {
private static final boolean DEBUG = false;
MapListItem mapItem;
HeadItem headItem;
public ConstPool cp = new ConstPool();
static private DataOut wrapDumpOut(final DataOut out0) {
return (DataOut) Proxy.newProxyInstance(
DexFileWriter.class.getClassLoader(),
new Class[]{DataOut.class}, new InvocationHandler() {
int indent = 0;
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
if (method.getParameterTypes().length > 0
&& method.getParameterTypes()[0]
.equals(String.class)) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < indent; i++) {
sb.append(" ");
}
sb.append(String.format("%05d ", out0.offset()));
sb.append(method.getName() + " [");
for (Object arg : args) {
if (arg instanceof byte[]) {
byte[] data = (byte[]) arg;
sb.append("0x[");
int start = 0;
int size = data.length;
if (args.length > 2) {
start = (Integer) args[2];
size = (Integer) args[3];
}
for (int i = 0; i < size; i++) {
sb.append(String.format("%02x",
data[start + i] & 0xff));
if (i != size - 1) {
sb.append(", ");
}
}
sb.append("], ");
} else {
sb.append(arg).append(", ");
}
}
sb.append("]");
System.out.println(sb);
}
if (method.getName().equals("begin")) {
indent++;
}
if (method.getName().equals("end")) {
indent--;
}
return method.invoke(out0, args);
}
});
}
void buildMapListItem() {
// begin ===========
// satisfy 'bool DexFileVerifier::CheckMap()' on art/runtime/dex_file_verifier.cc
// make sure the items are not empty
if (cp.classDefs.isEmpty()) {
System.err.println("WARN: no classdef on the dex");
}
if (cp.methods.isEmpty()) {
cp.uniqMethod("Ljava/lang/Object;", "", new String[0], "V");
}
if (cp.fields.isEmpty()) {
cp.uniqField("Ljava/lang/System;", "out", "Ljava/io/PrintStream;");
}
if (cp.protos.isEmpty()) {
cp.uniqProto(new String[0], "V");
}
if (cp.types.isEmpty()) {
cp.uniqType("V");
}
if (cp.strings.isEmpty()) {
cp.uniqString("V");
}
// end ===========
mapItem = new MapListItem();
headItem = new HeadItem();
SectionItem headSection = new SectionItem<>(SectionType.TYPE_HEADER_ITEM);
headSection.items.add(headItem);
SectionItem mapSection = new SectionItem(SectionType.TYPE_MAP_LIST);
mapSection.items.add(mapItem);
SectionItem stringIdSection = new SectionItem<>(
SectionType.TYPE_STRING_ID_ITEM, cp.strings.values());
SectionItem typeIdSection = new SectionItem<>(
SectionType.TYPE_TYPE_ID_ITEM, cp.types.values());
SectionItem protoIdSection = new SectionItem<>(
SectionType.TYPE_PROTO_ID_ITEM, cp.protos.values());
SectionItem fieldIdSection = new SectionItem<>(
SectionType.TYPE_FIELD_ID_ITEM, cp.fields.values());
SectionItem methodIdSection = new SectionItem<>(
SectionType.TYPE_METHOD_ID_ITEM, cp.methods.values());
SectionItem classDefSection = new SectionItem<>(
SectionType.TYPE_CLASS_DEF_ITEM, cp.buildSortedClassDefItems());
SectionItem typeListSection = new SectionItem<>(
SectionType.TYPE_TYPE_LIST, cp.typeLists.values());
SectionItem annotationSetRefListItemSection = new SectionItem<>(
SectionType.TYPE_ANNOTATION_SET_REF_LIST,
cp.annotationSetRefListItems.values());
SectionItem annotationSetSection = new SectionItem<>(
SectionType.TYPE_ANNOTATION_SET_ITEM,
cp.annotationSetItems.values());
SectionItem classDataItemSection = new SectionItem<>(
SectionType.TYPE_CLASS_DATA_ITEM, cp.classDataItems);
SectionItem codeItemSection = new SectionItem<>(
SectionType.TYPE_CODE_ITEM, cp.codeItems);
SectionItem stringDataItemSection = new SectionItem<>(
SectionType.TYPE_STRING_DATA_ITEM, cp.stringDatas);
SectionItem debugInfoSection = new SectionItem<>(
SectionType.TYPE_DEBUG_INFO_ITEM, cp.debugInfoItems);
SectionItem annotationItemSection = new SectionItem<>(
SectionType.TYPE_ANNOTATION_ITEM, cp.annotationItems.values());
SectionItem encodedArrayItemSection = new SectionItem<>(
SectionType.TYPE_ENCODED_ARRAY_ITEM, cp.encodedArrayItems);
SectionItem annotationsDirectoryItemSection = new SectionItem<>(
SectionType.TYPE_ANNOTATIONS_DIRECTORY_ITEM,
cp.annotationsDirectoryItems);
{
headItem.mapSection = mapSection;
headItem.stringIdSection = stringIdSection;
headItem.typeIdSection = typeIdSection;
headItem.protoIdSection = protoIdSection;
headItem.fieldIdSection = fieldIdSection;
headItem.methodIdSection = methodIdSection;
headItem.classDefSection = classDefSection;
}
List> dataSectionItems = new ArrayList<>();
{
dataSectionItems.add(mapSection); // data section
dataSectionItems.add(typeListSection);// data section
dataSectionItems.add(annotationSetRefListItemSection);// data
// section
dataSectionItems.add(annotationSetSection);// data section
// make codeItem Before classDataItem
dataSectionItems.add(codeItemSection);// data section
dataSectionItems.add(classDataItemSection);// data section
dataSectionItems.add(stringDataItemSection);// data section
dataSectionItems.add(debugInfoSection);// data section
dataSectionItems.add(annotationItemSection);// data section
dataSectionItems.add(encodedArrayItemSection);// data section
dataSectionItems.add(annotationsDirectoryItemSection);// data
// section
}
List> items = mapItem.items;
{
items.add(headSection);
items.add(stringIdSection);
items.add(typeIdSection);
items.add(protoIdSection);
items.add(fieldIdSection);
items.add(methodIdSection);
items.add(classDefSection);
items.addAll(dataSectionItems);
}
// cp is useless now since all value are copied now
cp.clean();
cp = null;
}
public byte[] toByteArray() {
// init structure for writing
buildMapListItem();
// place all item into file, we can know the size now
final int size = place();
ByteBuffer buffer = ByteBuffer.allocate(size);
DataOut out = new ByteBufferOut(buffer);
if (DEBUG) {
out = wrapDumpOut(out);
}
// write it
write(out);
if (size != buffer.position()) {
throw new RuntimeException("generated different file size, planned " + size + ", but is " + buffer.position());
}
// update the CRC/ sha1 checksum in dex header
updateChecksum(buffer, size);
return buffer.array();
}
public static void updateChecksum(ByteBuffer buffer, int size) {
byte[] data = buffer.array();
MessageDigest digest;
try {
digest = MessageDigest.getInstance("SHA-1");
} catch (NoSuchAlgorithmException e) {
throw new AssertionError();
}
digest.update(data, 32, size - 32);
byte[] sha1 = digest.digest();
System.arraycopy(sha1, 0, data, 12, sha1.length);
Adler32 adler32 = new Adler32();
adler32.update(data, 12, size - 12);
int v = (int) adler32.getValue();
buffer.position(8);
buffer.putInt(v);
}
private void write(DataOut out) {
List> list = new ArrayList<>(mapItem.items);
// mapItem is useless now
this.mapItem = null;
for (int i = 0; i < list.size(); i++) {
SectionItem> section = list.get(i);
list.set(i, null);
BaseItem.addPadding(out, out.offset(),
section.sectionType.alignment);
if (out.offset() != section.offset) {
throw new RuntimeException(section.sectionType
+ " start with different position, planned:"
+ section.offset + ", but is:" + out.offset());
}
section.write(out);
}
}
private int place() {
// 2. order
mapItem.cleanZeroSizeEntry();
// 3. place
int offset = 0;
// int index = 0;
for (SectionItem> section : mapItem.items) {
offset = BaseItem.padding(offset, section.sectionType.alignment);
section.offset = offset;
// section.index = index;
// index++;
offset = section.place(offset);
}
int size = offset;
{ // fix size
headItem.fileSize = size;
// headItem is useless now
this.headItem = null;
}
return size;
}
@Override
public DexClassVisitor visit(int accessFlag, String name,
String superClass, String[] itfClass) {
ClassDefItem defItem = cp.putClassDefItem(accessFlag, name, superClass,
itfClass);
return new ClassWriter(defItem, cp);
}
}