Initial commit of writer functionality

It's not fully functional yet, but the structure is there, and it's
got most item types
This commit is contained in:
Ben Gruver 2012-11-18 14:33:32 -08:00
parent 22c3185bb7
commit 4ffbfa2e71
21 changed files with 2385 additions and 12 deletions

View File

@ -98,5 +98,9 @@ public final class DexFileFactory {
return new DexBackedDexFile(dexBuf);
}
public static void writeDexFile(String path, DexFile dexFile) throws IOException {
org.jf.dexlib2.writer.DexFile.writeTo(path, dexFile);
}
private DexFileFactory() {}
}

View File

@ -65,7 +65,7 @@ public abstract class BaseAnnotation implements Annotation {
return CollectionUtils.compareAsSet(getElements(), o.getElements());
}
public static final Comparator<Annotation> COMPARE_BY_TYPE = new Comparator<Annotation>() {
public static final Comparator<Annotation> BY_TYPE = new Comparator<Annotation>() {
@Override
public int compare(Annotation annotation1, Annotation annotation2) {
return annotation1.getType().compareTo(annotation2.getType());

View File

@ -60,7 +60,7 @@ public abstract class BaseAnnotationElement implements AnnotationElement {
return getValue().compareTo(o.getValue());
}
public static final Comparator<AnnotationElement> COMPARE_BY_NAME = new Comparator<AnnotationElement>() {
public static final Comparator<AnnotationElement> BY_NAME = new Comparator<AnnotationElement>() {
@Override
public int compare(@Nonnull AnnotationElement element1, @Nonnull AnnotationElement element2) {
return element1.getName().compareTo(element2.getName());

View File

@ -35,6 +35,7 @@ import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import org.jf.dexlib2.ValueType;
import org.jf.dexlib2.iface.value.*;
import org.jf.util.ExceptionWithContext;
import org.jf.util.ImmutableConverter;
import javax.annotation.Nonnull;
@ -82,6 +83,33 @@ public class ImmutableEncodedValueFactory {
}
}
@Nonnull
public static EncodedValue defaultValueForType(String type) {
switch (type.charAt(0)) {
case 'Z':
return new ImmutableBooleanEncodedValue(false);
case 'B':
return new ImmutableByteEncodedValue((byte)0);
case 'S':
return new ImmutableShortEncodedValue((short)0);
case 'C':
return new ImmutableCharEncodedValue((char)0);
case 'I':
return new ImmutableIntEncodedValue(0);
case 'J':
return new ImmutableLongEncodedValue(0);
case 'F':
return new ImmutableFloatEncodedValue(0);
case 'D':
return new ImmutableDoubleEncodedValue(0);
case 'L':
case '[':
return ImmutableNullEncodedValue.INSTANCE;
default:
throw new ExceptionWithContext("Unrecognized type: %s", type);
}
}
@Nullable
public static ImmutableEncodedValue ofNullable(@Nullable EncodedValue encodedValue) {
if (encodedValue == null) {

View File

@ -0,0 +1,45 @@
/*
* Copyright 2012, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jf.dexlib2.util;
import org.jf.dexlib2.AccessFlags;
import org.jf.dexlib2.iface.Field;
import javax.annotation.Nonnull;
public final class FieldUtil {
public static boolean isStatic(@Nonnull Field field) {
return AccessFlags.STATIC.isSet(field.getAccessFlags());
}
private FieldUtil() {}
}

View File

@ -0,0 +1,257 @@
/*
* Copyright 2012, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jf.dexlib2.writer;
import com.google.common.base.Predicate;
import com.google.common.collect.*;
import org.jf.dexlib2.iface.ClassDef;
import org.jf.dexlib2.iface.Field;
import org.jf.dexlib2.iface.Method;
import org.jf.dexlib2.iface.MethodParameter;
import org.jf.util.CollectionUtils;
import org.jf.util.ExceptionWithContext;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.util.*;
public class AnnotationDirectoryPool {
@Nonnull private final Map<Key, Integer> internedAnnotationDirectoryItems = Maps.newHashMap();
@Nonnull private final Map<String, Integer> nonInternedAnnotationDirectoryOffsetMap = Maps.newHashMap();
@Nonnull private final List<Key> nonInternedAnnotationDirectoryItems = Lists.newArrayList();
@Nonnull private final DexFile dexFile;
public AnnotationDirectoryPool(@Nonnull DexFile dexFile) {
this.dexFile = dexFile;
}
public void intern(@Nonnull ClassDef classDef) {
Key key = new Key(classDef);
if (key.hasNonClassAnnotations()) {
nonInternedAnnotationDirectoryItems.add(key);
} else if (key.hasClassAnnotations()) {
Integer prev = internedAnnotationDirectoryItems.put(key, 0);
if (prev != null) {
// we don't need to re-intern the contents
return;
}
} else {
// it's empty. nothing to do.
return;
}
dexFile.annotationSetPool.intern(classDef.getAnnotations());
for (Field field: key.getFieldsWithAnnotations()) {
dexFile.annotationSetPool.intern(field.getAnnotations());
}
for (Method method: key.getMethodsWithAnnotations()) {
dexFile.annotationSetPool.intern(method.getAnnotations());
}
for (Method method: key.getMethodsWithParameterAnnotations()) {
dexFile.annotationSetRefPool.intern(method);
}
}
public int getOffset(@Nonnull ClassDef classDef) {
Integer offset = nonInternedAnnotationDirectoryOffsetMap.get(classDef.getType());
if (offset == null) {
Key key = new Key(classDef);
if (!key.hasNonClassAnnotations()) {
offset = internedAnnotationDirectoryItems.get(key);
}
}
if (offset == null) {
throw new ExceptionWithContext("Annotation directory not found for class %s.", classDef.getType());
}
return offset;
}
public void write(@Nonnull DexWriter writer) throws IOException {
// we'll write out the interned items first
List<Key> directoryItems = Lists.newArrayList(internedAnnotationDirectoryItems.keySet());
Collections.sort(directoryItems);
for (Key key: directoryItems) {
writer.align();
internedAnnotationDirectoryItems.put(key, writer.getPosition());
writer.writeInt(dexFile.annotationSetPool.getOffset(key.classDef.getAnnotations()));
writer.writeInt(0);
writer.writeInt(0);
writer.writeInt(0);
}
// now, write out the non-internable items
directoryItems = nonInternedAnnotationDirectoryItems;
Collections.sort(directoryItems);
for (Key key: directoryItems) {
writer.align();
nonInternedAnnotationDirectoryOffsetMap.put(key.classDef.getType(), writer.getPosition());
writer.writeInt(dexFile.annotationSetPool.getOffset(key.classDef.getAnnotations()));
writer.writeInt(key.fieldAnnotationCount);
writer.writeInt(key.methodAnnotationCount);
writer.writeInt(key.parameterAnnotationCount);
Iterable<? extends Field> fieldsWithAnnotations = null;
if (CollectionUtils.isNaturalSortedSet(key.classDef.getFields())) {
fieldsWithAnnotations = key.getFieldsWithAnnotations();
} else {
fieldsWithAnnotations = Lists.newArrayList(key.getFieldsWithAnnotations());
Collections.sort((List<? extends Field>)fieldsWithAnnotations);
}
for (Field field: fieldsWithAnnotations) {
writer.writeInt(dexFile.fieldPool.getIndex(field));
writer.writeInt(dexFile.annotationSetPool.getOffset(field.getAnnotations()));
}
boolean sortMethods = CollectionUtils.isNaturalSortedSet(key.classDef.getMethods());
Iterable<? extends Method> methodsWithAnnotations = null;
if (sortMethods) {
methodsWithAnnotations = Lists.newArrayList(key.getMethodsWithAnnotations());
Collections.sort((List<? extends Method>)methodsWithAnnotations);
} else {
methodsWithAnnotations = key.getMethodsWithAnnotations();
}
for (Method method: methodsWithAnnotations) {
writer.writeInt(dexFile.methodPool.getIndex(method));
writer.writeInt(dexFile.annotationSetPool.getOffset(method.getAnnotations()));
}
Iterable<? extends Method> methodsWithParameterAnnotations = null;
if (sortMethods) {
methodsWithParameterAnnotations = Lists.newArrayList(key.getMethodsWithParameterAnnotations());
Collections.sort((List<? extends Method>)methodsWithParameterAnnotations);
} else {
methodsWithParameterAnnotations = key.getMethodsWithParameterAnnotations();
}
for (Method method: methodsWithParameterAnnotations) {
writer.writeInt(dexFile.methodPool.getIndex(method));
writer.writeInt(dexFile.annotationSetRefPool.getOffset(method));
}
}
}
private static final Predicate<Field> FIELD_HAS_ANNOTATION = new Predicate<Field>() {
@Override
public boolean apply(Field input) { return input.getAnnotations().size() > 0; }
};
private static final Predicate<Method> METHOD_HAS_ANNOTATION = new Predicate<Method>() {
@Override
public boolean apply(Method input) { return input.getAnnotations().size() > 0; }
};
private static final Predicate<Method> METHOD_HAS_PARAMETER_ANNOTATION = new Predicate<Method>() {
@Override
public boolean apply(Method input) {
for (MethodParameter parameter: input.getParameters()) {
if (parameter.getAnnotations().size() > 0) {
return true;
}
}
return false;
}
};
private static class Key implements Comparable<Key> {
@Nonnull private final ClassDef classDef;
private final int fieldAnnotationCount;
private final int methodAnnotationCount;
private final int parameterAnnotationCount;
public Key(@Nonnull ClassDef classDef) {
this.classDef = classDef;
this.fieldAnnotationCount = Iterables.size(getFieldsWithAnnotations());
this.methodAnnotationCount = Iterables.size(getMethodsWithAnnotations());
this.parameterAnnotationCount = Iterables.size(getMethodsWithParameterAnnotations());
}
public int getFieldAnnotationCount() { return fieldAnnotationCount; }
public int getMethodAnnotationCount() { return methodAnnotationCount; }
public int getParameterAnnotationCount() { return parameterAnnotationCount; }
@Nonnull
public Iterable<? extends Field> getFieldsWithAnnotations() {
return FluentIterable.from(classDef.getFields()).filter(FIELD_HAS_ANNOTATION);
}
@Nonnull
public Iterable<? extends Method> getMethodsWithAnnotations() {
return FluentIterable.from(classDef.getMethods()).filter(METHOD_HAS_ANNOTATION);
}
@Nonnull
public Iterable<? extends Method> getMethodsWithParameterAnnotations() {
return FluentIterable.from(classDef.getMethods()).filter(METHOD_HAS_PARAMETER_ANNOTATION);
}
public boolean hasClassAnnotations() {
return classDef.getAnnotations().size() > 0;
}
public boolean hasNonClassAnnotations() {
return fieldAnnotationCount > 0 ||
methodAnnotationCount > 0 ||
parameterAnnotationCount > 0;
}
@Override
public int hashCode() {
// hashCode is only used for internable items - those that only have class annotations.
return classDef.getAnnotations().hashCode();
}
@Override
public boolean equals(Object o) {
// equals is only used for internable items - those that only have class annotations
if (o instanceof Key) {
Key other = (Key)o;
if (classDef.getAnnotations().size() != other.classDef.getAnnotations().size()) {
return false;
}
return Iterables.elementsEqual(classDef.getAnnotations(), other.classDef.getAnnotations());
}
return false;
}
@Override
public int compareTo(Key o) {
// compareTo will only be called on keys of the same internability. An internable key will not be compared
// with a non-internable one.
if (hasClassAnnotations()) {
return classDef.getType().compareTo(o.classDef.getType());
}
return CollectionUtils.compareAsSet(classDef.getAnnotations(), o.classDef.getAnnotations());
}
}
}

View File

@ -0,0 +1,94 @@
/*
* Copyright 2012, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jf.dexlib2.writer;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.jf.dexlib2.base.BaseAnnotationElement;
import org.jf.dexlib2.iface.Annotation;
import org.jf.dexlib2.iface.AnnotationElement;
import org.jf.util.ExceptionWithContext;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
public class AnnotationPool {
@Nonnull private final Map<Annotation, Integer> internedAnnotations = Maps.newHashMap();
@Nonnull private final DexFile dexFile;
public AnnotationPool(@Nonnull DexFile dexFile) {
this.dexFile = dexFile;
}
public void intern(@Nonnull Annotation annotation) {
Integer prev = internedAnnotations.put(annotation, 0);
if (prev == null) {
dexFile.typePool.intern(annotation.getType());
for (AnnotationElement element: annotation.getElements()) {
dexFile.stringPool.intern(element.getName());
dexFile.internEncodedValue(element.getValue());
}
}
}
public int getOffset(@Nonnull Annotation annotation) {
Integer offset = internedAnnotations.get(annotation);
if (offset == null) {
throw new ExceptionWithContext("Annotation not found.");
}
return offset;
}
public void write(@Nonnull DexWriter writer) throws IOException {
List<Annotation> annotations = Lists.newArrayList(internedAnnotations.keySet());
Collections.sort(annotations);
for (Annotation annotation: annotations) {
internedAnnotations.put(annotation, writer.getPosition());
writer.writeUbyte(annotation.getVisibility());
writer.writeUleb128(dexFile.typePool.getIndex(annotation.getType()));
writer.writeUleb128(annotation.getElements().size());
SortedSet<? extends AnnotationElement> sortedElements =
ImmutableSortedSet.copyOf(BaseAnnotationElement.BY_NAME, annotation.getElements());
for (AnnotationElement element: sortedElements) {
writer.writeUleb128(dexFile.stringPool.getIndex(element.getName()));
dexFile.writeEncodedValue(writer, element.getValue());
}
}
}
}

View File

@ -0,0 +1,93 @@
/*
* Copyright 2012, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jf.dexlib2.writer;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Ordering;
import org.jf.dexlib2.base.BaseAnnotation;
import org.jf.dexlib2.iface.Annotation;
import org.jf.util.CollectionUtils;
import org.jf.util.ExceptionWithContext;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.util.*;
public class AnnotationSetPool {
@Nonnull private final Map<Set<? extends Annotation>, Integer> internedAnnotationSetItems = Maps.newHashMap();
@Nonnull private final DexFile dexFile;
public AnnotationSetPool(@Nonnull DexFile dexFile) {
this.dexFile = dexFile;
}
public void intern(@Nonnull Set<? extends Annotation> annotationSet) {
if (annotationSet.size() > 0) {
Integer prev = internedAnnotationSetItems.put(annotationSet, 0);
if (prev == null) {
for (Annotation annotation: annotationSet) {
dexFile.annotationPool.intern(annotation);
}
}
}
}
public int getOffset(@Nonnull Set<? extends Annotation> annotationSet) {
if (annotationSet.size() == 0) {
return 0;
}
Integer offset = internedAnnotationSetItems.get(annotationSet);
if (offset == null) {
throw new ExceptionWithContext("Annotation set not found.");
}
return offset;
}
public void write(@Nonnull DexWriter writer) throws IOException {
List<Set<? extends Annotation>> annotationSets =
Lists.newArrayList(internedAnnotationSetItems.keySet());
Collections.sort(annotationSets, CollectionUtils.listComparator(Ordering.natural()));
for (Set<? extends Annotation> annotationSet: annotationSets) {
SortedSet<? extends Annotation> sortedAnnotationSet = ImmutableSortedSet.copyOf(BaseAnnotation.BY_TYPE,
annotationSet);
writer.align();
internedAnnotationSetItems.put(annotationSet, writer.getPosition());
writer.writeInt(annotationSet.size());
for (Annotation annotation: sortedAnnotationSet) {
writer.writeInt(dexFile.annotationPool.getOffset(annotation));
}
}
}
}

View File

@ -0,0 +1,162 @@
/*
* Copyright 2012, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jf.dexlib2.writer;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Ordering;
import com.google.common.primitives.Ints;
import org.jf.dexlib2.iface.Annotation;
import org.jf.dexlib2.iface.Method;
import org.jf.dexlib2.iface.MethodParameter;
import org.jf.util.CollectionUtils;
import org.jf.util.ExceptionWithContext;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.util.*;
public class AnnotationSetRefPool {
@Nonnull private final Map<Key, Integer> internedAnnotationSetRefItems = Maps.newHashMap();
@Nonnull private final DexFile dexFile;
public AnnotationSetRefPool(@Nonnull DexFile dexFile) {
this.dexFile = dexFile;
}
public void intern(@Nonnull Method method) {
Key annotationSetRefKey = new Key(method);
Integer prev = internedAnnotationSetRefItems.put(annotationSetRefKey, 0);
if (prev == null) {
for (Set<? extends Annotation> annotationSet: annotationSetRefKey.getAnnotationSets()) {
dexFile.annotationSetPool.intern(annotationSet);
}
}
}
public int getOffset(@Nonnull Method method) {
Key annotationSetRefKey = new Key(method);
Integer offset = internedAnnotationSetRefItems.put(annotationSetRefKey, 0);
if (offset == null) {
throw new ExceptionWithContext("Annotation set ref not found.");
}
return offset;
}
public void write(@Nonnull DexWriter writer) throws IOException {
List<Key> annotationSetRefs =
Lists.newArrayList(internedAnnotationSetRefItems.keySet());
Collections.sort(annotationSetRefs);
for (Key key: annotationSetRefs) {
writer.align();
internedAnnotationSetRefItems.put(key, writer.getPosition());
writer.writeInt(key.getAnnotationSetCount());
for (Set<? extends Annotation> annotationSet: key.getAnnotationSets()) {
writer.writeInt(dexFile.annotationSetPool.getOffset(annotationSet));
}
}
}
private static class Key implements Comparable<Key> {
@Nonnull private final Method method;
private final int size;
public Key(@Nonnull Method method) {
this.method = method;
this.size = CollectionUtils.lastIndexOf(method.getParameters(), HAS_ANNOTATIONS) + 1;
}
public int getAnnotationSetCount() {
return size;
}
public Iterable<Set<? extends Annotation>> getAnnotationSets() {
return FluentIterable.from(method.getParameters())
.limit(size)
.transform(PARAMETER_ANNOTATIONS);
}
@Override
public int hashCode() {
int hashCode = 1;
for (Set<? extends Annotation> annotationSet: getAnnotationSets()) {
hashCode = hashCode*31 + annotationSet.hashCode();
}
return hashCode;
}
@Override
public boolean equals(Object o) {
if (o instanceof Key) {
Key other = (Key)o;
if (size != other.size) {
return false;
}
Iterator<Set<? extends Annotation>> otherAnnotationSets = getAnnotationSets().iterator();
for (Set<? extends Annotation> annotationSet: getAnnotationSets()) {
if (!annotationSet.equals(otherAnnotationSets)) {
return false;
}
}
return true;
}
return false;
}
private static final Predicate<MethodParameter> HAS_ANNOTATIONS = new Predicate<MethodParameter>() {
@Override
public boolean apply(MethodParameter input) {
return input.getAnnotations().size() > 0;
}
};
private static final Function<MethodParameter, Set<? extends Annotation>> PARAMETER_ANNOTATIONS =
new Function<MethodParameter, Set<? extends Annotation>>() {
@Override
public Set<? extends Annotation> apply(MethodParameter input) {
return input.getAnnotations();
}
};
@Override
public int compareTo(Key o) {
int res = Ints.compare(size, o.size);
if (res != 0) return res;
return CollectionUtils.compareAsIterable(CollectionUtils.setComparator(Ordering.natural()),
getAnnotationSets(), o.getAnnotationSets());
}
}
}

View File

@ -0,0 +1,314 @@
/*
* Copyright 2012, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jf.dexlib2.writer;
import com.google.common.collect.ImmutableSortedSet;
import org.jf.dexlib2.DebugItemType;
import org.jf.dexlib2.ReferenceType;
import org.jf.dexlib2.ValueType;
import org.jf.dexlib2.iface.*;
import org.jf.dexlib2.iface.debug.DebugItem;
import org.jf.dexlib2.iface.debug.SetSourceFile;
import org.jf.dexlib2.iface.debug.StartLocal;
import org.jf.dexlib2.iface.instruction.Instruction;
import org.jf.dexlib2.iface.instruction.ReferenceInstruction;
import org.jf.dexlib2.iface.reference.*;
import org.jf.dexlib2.iface.value.*;
import org.jf.util.ExceptionWithContext;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Collection;
import java.util.List;
import java.util.Set;
public class DexFile {
// package-private access for these
@Nonnull final StringPool stringPool = new StringPool();
@Nonnull final TypePool typePool = new TypePool(this);
@Nonnull final FieldPool fieldPool = new FieldPool(this);
@Nonnull final ProtoPool protoPool = new ProtoPool(this);
@Nonnull final MethodPool methodPool = new MethodPool(this);
@Nonnull final TypeListPool typeListPool = new TypeListPool(this);
@Nonnull final EncodedArrayPool encodedArrayPool = new EncodedArrayPool(this);
@Nonnull final AnnotationPool annotationPool = new AnnotationPool(this);
@Nonnull final AnnotationSetPool annotationSetPool = new AnnotationSetPool(this);
@Nonnull final AnnotationSetRefPool annotationSetRefPool = new AnnotationSetRefPool(this);
@Nonnull final AnnotationDirectoryPool annotationDirectoryPool = new AnnotationDirectoryPool(this);
@Nonnull private final Set<? extends ClassDef> classes;
private DexFile(Set<? extends ClassDef> classes) {
this.classes = classes;
for (ClassDef classDef: classes) {
internClass(classDef);
}
}
public void writeEncodedValue(@Nonnull DexWriter writer, @Nonnull EncodedValue encodedValue) throws IOException {
int valueType = encodedValue.getValueType();
switch (valueType) {
case ValueType.ANNOTATION:
AnnotationEncodedValue annotationEncodedValue = (AnnotationEncodedValue)encodedValue;
Collection<? extends AnnotationElement> annotationElements = annotationEncodedValue.getElements();
writer.writeUleb128(typePool.getIndex(annotationEncodedValue.getType()));
writer.writeUleb128(annotationElements.size());
for (AnnotationElement element: annotationElements) {
writer.writeUleb128(stringPool.getIndex(element.getName()));
writeEncodedValue(writer, element.getValue());
}
break;
case ValueType.ARRAY:
ArrayEncodedValue arrayEncodedValue = (ArrayEncodedValue)encodedValue;
Collection<? extends EncodedValue> elements = arrayEncodedValue.getValue();
writer.writeUleb128(elements.size());
for (EncodedValue element: elements) {
writeEncodedValue(writer, element);
}
break;
case ValueType.BOOLEAN:
writer.writeEncodedValueHeader(valueType, (((BooleanEncodedValue)encodedValue).getValue()?1:0));
break;
case ValueType.BYTE:
writer.writeEncodedInt(valueType, ((ByteEncodedValue)encodedValue).getValue());
break;
case ValueType.CHAR:
writer.writeEncodedInt(valueType, ((CharEncodedValue)encodedValue).getValue());
break;
case ValueType.DOUBLE:
writer.writeEncodedDouble(valueType, ((DoubleEncodedValue)encodedValue).getValue());
break;
case ValueType.ENUM:
writer.writeEncodedUint(valueType, fieldPool.getIndex(((EnumEncodedValue)encodedValue).getValue()));
break;
case ValueType.FIELD:
writer.writeEncodedUint(valueType, fieldPool.getIndex(((FieldEncodedValue)encodedValue).getValue()));
break;
case ValueType.FLOAT:
writer.writeEncodedFloat(valueType, ((FloatEncodedValue)encodedValue).getValue());
break;
case ValueType.INT:
writer.writeEncodedInt(valueType, ((IntEncodedValue)encodedValue).getValue());
break;
case ValueType.LONG:
writer.writeEncodedLong(valueType, ((LongEncodedValue)encodedValue).getValue());
break;
case ValueType.METHOD:
writer.writeEncodedUint(valueType, methodPool.getIndex(((MethodEncodedValue)encodedValue).getValue()));
break;
case ValueType.NULL:
writer.write(valueType);
break;
case ValueType.SHORT:
writer.writeEncodedInt(valueType, ((ShortEncodedValue)encodedValue).getValue());
break;
case ValueType.STRING:
writer.writeEncodedUint(valueType, stringPool.getIndex(((StringEncodedValue)encodedValue).getValue()));
break;
case ValueType.TYPE:
writer.writeEncodedUint(valueType, typePool.getIndex(((TypeEncodedValue)encodedValue).getValue()));
break;
default:
throw new ExceptionWithContext("Unrecognized value type: %d", encodedValue.getValueType());
}
}
public void internEncodedValue(@Nonnull EncodedValue encodedValue) {
switch (encodedValue.getValueType()) {
case ValueType.ARRAY:
ArrayEncodedValue arrayEncodedValue = (ArrayEncodedValue)encodedValue;
for (EncodedValue value: arrayEncodedValue.getValue()) {
internEncodedValue(value);
}
return;
case ValueType.ANNOTATION:
AnnotationEncodedValue annotationEncodedValue = (AnnotationEncodedValue)encodedValue;
typePool.intern(annotationEncodedValue.getType());
for(AnnotationElement annotationElement: annotationEncodedValue.getElements()) {
stringPool.intern(annotationElement.getName());
internEncodedValue(annotationElement.getValue());
}
return;
case ValueType.STRING:
StringEncodedValue stringEncodedValue = (StringEncodedValue)encodedValue;
stringPool.intern(stringEncodedValue.getValue());
return;
case ValueType.TYPE:
TypeEncodedValue typeEncodedValue = (TypeEncodedValue)encodedValue;
typePool.intern(typeEncodedValue.getValue());
return;
case ValueType.ENUM:
EnumEncodedValue enumEncodedValue = (EnumEncodedValue)encodedValue;
fieldPool.intern(enumEncodedValue.getValue());
return;
case ValueType.FIELD:
FieldEncodedValue fieldEncodedValue = (FieldEncodedValue)encodedValue;
fieldPool.intern(fieldEncodedValue.getValue());
return;
case ValueType.METHOD:
MethodEncodedValue methodEncodedValue = (MethodEncodedValue)encodedValue;
methodPool.intern(methodEncodedValue.getValue());
return;
default:
// nothing to do
break;
}
}
public void internFields(@Nonnull ClassDef classDef) {
for (Field field: classDef.getFields()) {
fieldPool.intern(field);
}
}
public void internMethods(@Nonnull ClassDef classDef) {
for (Method method: classDef.getMethods()) {
methodPool.intern(method);
for (MethodParameter param: method.getParameters()) {
stringPool.internNullable(param.getName());
}
internMethodImplementation(method.getImplementation());
}
}
public void internMethodImplementation(@Nullable MethodImplementation methodImplementation) {
if (methodImplementation != null) {
internDebugItems(methodImplementation.getDebugItems());
internTryBlocks(methodImplementation.getTryBlocks());
internInstructions(methodImplementation.getInstructions());
}
}
public void internDebugItems(Iterable<? extends DebugItem> debugItems) {
// TODO: debug_info_items should technically be internable...
for (DebugItem debugItem: debugItems) {
switch (debugItem.getDebugItemType()) {
case DebugItemType.START_LOCAL:
StartLocal startLocal = (StartLocal)debugItem;
stringPool.internNullable(startLocal.getName());
typePool.internNullable(startLocal.getType());
stringPool.internNullable(startLocal.getSignature());
break;
case DebugItemType.SET_SOURCE_FILE:
stringPool.internNullable(((SetSourceFile) debugItem).getSourceFile());
break;
}
}
}
public void internTryBlocks(List<? extends TryBlock> tryBlocks) {
for (TryBlock tryBlock: tryBlocks) {
for (ExceptionHandler handler: tryBlock.getExceptionHandlers()) {
typePool.internNullable(handler.getExceptionType());
}
}
}
public void internInstructions(Iterable<? extends Instruction> instructions) {
for (Instruction instruction: instructions) {
if (instruction instanceof ReferenceInstruction) {
Reference reference = ((ReferenceInstruction)instruction).getReference();
switch (instruction.getOpcode().referenceType) {
case ReferenceType.STRING:
stringPool.intern((StringReference) reference);
break;
case ReferenceType.TYPE:
typePool.intern((TypeReference)reference);
break;
case ReferenceType.FIELD:
fieldPool.intern((FieldReference) reference);
break;
case ReferenceType.METHOD:
methodPool.intern((MethodReference)reference);
break;
default:
throw new ExceptionWithContext("Unrecognized reference type: %d",
instruction.getOpcode().referenceType);
}
}
}
}
public void internClass(@Nonnull ClassDef classDef) {
typePool.intern(classDef.getType());
typePool.internNullable(classDef.getSuperclass());
typeListPool.intern(ImmutableSortedSet.copyOf(classDef.getInterfaces()));
stringPool.internNullable(classDef.getSourceFile());
encodedArrayPool.intern(classDef);
annotationDirectoryPool.intern(classDef);
internFields(classDef);
internMethods(classDef);
}
private void writeTo(@Nonnull String path) throws IOException {
RandomAccessFile raf = new RandomAccessFile(path, "rw");
try {
int dataOffset = stringPool.getIndexedSectionSize() + typePool.getIndexedSectionSize() +
fieldPool.getIndexedSectionSize() + protoPool.getIndexedSectionSize() +
methodPool.getIndexedSectionSize();
DexWriter indexWriter = outputAt(raf, 0);
DexWriter offsetWriter = outputAt(raf, dataOffset);
try {
stringPool.write(indexWriter, offsetWriter);
typePool.write(indexWriter);
fieldPool.write(indexWriter);
typeListPool.write(offsetWriter);
protoPool.write(indexWriter);
methodPool.write(indexWriter);
encodedArrayPool.write(offsetWriter);
annotationPool.write(offsetWriter);
annotationSetPool.write(offsetWriter);
annotationSetRefPool.write(offsetWriter);
annotationDirectoryPool.write(offsetWriter);
} finally {
indexWriter.close();
offsetWriter.close();
}
} finally {
raf.close();
}
}
private static DexWriter outputAt(RandomAccessFile raf, int filePosition) throws IOException {
return new DexWriter(raf.getChannel(), filePosition);
}
public static void writeTo(@Nonnull String path, @Nonnull org.jf.dexlib2.iface.DexFile input) throws IOException {
DexFile dexFile = new DexFile(input.getClasses());
dexFile.writeTo(path);
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright 2012, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jf.dexlib2.writer;
public class DexItemType {
public static final int HEADER_ITEM = 0x0000;
public static final int STRING_ID_ITEM = 0x0001;
public static final int TYPE_ID_ITEM = 0x0002;
public static final int PROTO_ID_ITEM = 0x0003;
public static final int FIELD_ID_ITEM = 0x0004;
public static final int METHOD_ID_ITEM = 0x0005;
public static final int CLASS_DEF_ITEM = 0x0006;
public static final int MAP_LIST = 0x1000;
public static final int TYPE_LIST = 0x1001;
public static final int ANNOTATION_SET_REF_LIST = 0x1002;
public static final int ANNOTATION_SET_ITEM = 0x1003;
public static final int CLASS_DATA_ITEM = 0x2000;
public static final int CODE_ITEM = 0x2001;
public static final int STRING_DATA_ITEM = 0x2002;
public static final int DEBUG_INFO_ITEM = 0x2003;
public static final int ANNOTATION_ITEM = 0x2004;
public static final int ENCODED_ARRAY_ITEM = 0x2005;
public static final int ANNOTATION_DIRECTORY_ITEM = 0x2006;
}

View File

@ -0,0 +1,342 @@
/*
* Copyright 2012, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jf.dexlib2.writer;
import com.google.common.base.Preconditions;
import org.jf.util.ExceptionWithContext;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
public class DexWriter extends OutputStream {
private static final int MAP_SIZE = 1024*1024;
private static final int BUF_SIZE = 256*1024;
@Nonnull private final FileChannel channel;
private MappedByteBuffer byteBuffer;
/** The position in the file at which byteBuffer starts. */
private int mappedFilePosition;
private byte[] buf = new byte[BUF_SIZE];
/** The index within buf to write to */
private int bufPosition;
/**
* A temporary buffer that can be used for larger writes. Can be replaced with a larger buffer if needed.
* Must be at least 8 bytes
*/
private byte[] tempBuf = new byte[8];
/** A buffer of 0s we used for writing alignment values */
private byte[] zeroBuf = new byte[3];
public DexWriter(FileChannel channel, int position) throws IOException {
this.channel = channel;
this.mappedFilePosition = position;
byteBuffer = channel.map(FileChannel.MapMode.READ_WRITE, position, MAP_SIZE);
}
@Override
public void write(int b) throws IOException {
if (bufPosition >= BUF_SIZE) {
flushBuffer();
}
buf[bufPosition++] = (byte)b;
}
@Override
public void write(byte[] b) throws IOException {
write(b, 0, b.length);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
int toWrite = len;
if (bufPosition == BUF_SIZE) {
flushBuffer();
}
int remainingBuffer = BUF_SIZE - bufPosition;
if (toWrite >= remainingBuffer) {
// fill up and write out the current buffer
System.arraycopy(b, 0, buf, bufPosition, remainingBuffer);
bufPosition += remainingBuffer;
toWrite -= remainingBuffer;
flushBuffer();
// skip the intermediate buffer while we have a full buffer's worth
while (toWrite >= BUF_SIZE) {
writeBufferToMap(b, len - toWrite, BUF_SIZE);
toWrite -= BUF_SIZE;
}
}
// write out the final chunk, if any
if (toWrite > 0) {
System.arraycopy(b, len-toWrite, buf, bufPosition, len);
bufPosition += len;
}
}
public void writeInt(int value) throws IOException {
write(value);
write(value >> 8);
write(value >> 16);
write(value >> 24);
}
public void writeShort(int value) throws IOException {
if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) {
throw new ExceptionWithContext("Short value out of range: %d", value);
}
write(value);
write(value >> 8);
}
public void writeUshort(int value) throws IOException {
if (value < 0 || value > 0xFFFF) {
throw new ExceptionWithContext("Unsigned short value out of range: %d", value);
}
write(value);
write(value >> 8);
}
public void writeUbyte(int value) throws IOException {
if (value < 0 || value > 0xFF) {
throw new ExceptionWithContext("Unsigned byte value out of range: %d", value);
}
write(value);
}
public void writeUleb128(int value) throws IOException {
while (value > 0x7f) {
write((value & 0x7f) | 0x80);
value >>>= 7;
}
write(value);
}
/* public static byte[] encodeSignedIntegralValue(long value) {
int requiredBytes = getRequiredBytesForSignedIntegralValue(value);
byte[] bytes = new byte[requiredBytes];
for (int i = 0; i < requiredBytes; i++) {
bytes[i] = (byte) value;
value >>= 8;
}
return bytes;
}*/
/*
public static long decodeUnsignedIntegralValue(byte[] bytes) {
long value = 0;
for (int i = 0; i < bytes.length; i++) {
value |= (((long)(bytes[i] & 0xFF)) << i * 8);
}
return value;
}
*/
/*
public static byte[] encodeUnsignedIntegralValue(long value) {
int requiredBytes = getRequiredBytesForUnsignedIntegralValue(value);
byte[] bytes = new byte[requiredBytes];
for (int i = 0; i < requiredBytes; i++) {
bytes[i] = (byte) value;
value >>= 8;
}
return bytes;
}
*/
public void writeEncodedValueHeader(int valueType, int valueArg) throws IOException {
write(valueType | (valueArg << 5));
}
public void writeEncodedInt(int valueType, int value) throws IOException {
int index = 0;
if (value >= 0) {
while (value > 0x7f) {
tempBuf[index++] = (byte)value;
value >>= 8;
}
} else {
while (value < -0x80) {
tempBuf[index++] = (byte)value;
value >>= 8;
}
}
tempBuf[index++] = (byte)value;
writeEncodedValueHeader(valueType, index);
write(tempBuf, 0, index);
}
public void writeEncodedLong(int valueType, long value) throws IOException {
int index = 0;
if (value >= 0) {
while (value > 0x7f) {
tempBuf[index++] = (byte)value;
value >>= 8;
}
} else {
while (value < -0x80) {
tempBuf[index++] = (byte)value;
value >>= 8;
}
}
tempBuf[index++] = (byte)value;
writeEncodedValueHeader(valueType, index);
write(tempBuf, 0, index);
}
public void writeEncodedUint(int valueType, int value) throws IOException {
int index = 0;
do {
tempBuf[index++] = (byte)value;
value >>= 8;
} while (value != 0);
writeEncodedValueHeader(valueType, index);
write(tempBuf, 0, index);
}
public void writeEncodedFloat(int valueType, float value) throws IOException {
int intValue = Float.floatToRawIntBits(value);
int index = 3;
do {
buf[index--] = (byte)((intValue & 0xFF000000) >>> 24);
intValue <<= 8;
} while (intValue != 0);
writeEncodedValueHeader(valueType, 4-index);
write(buf, index+1, 4-index);
}
public void writeEncodedDouble(int valueType, double value) throws IOException {
long longValue = Double.doubleToRawLongBits(value);
int index = 7;
do {
buf[index--] = (byte)((longValue & 0xFF00000000000000L) >>> 56);
longValue <<= 8;
} while (longValue != 0);
writeEncodedValueHeader(valueType, 7-index);
write(buf, index+1, 7-index);
}
public void writeString(String string) throws IOException {
int len = string.length();
// make sure we have enough room in the temporary buffer
if (tempBuf.length <= string.length()*3) {
tempBuf = new byte[string.length()*3];
}
final byte[] buf = tempBuf;
int bufPos = 0;
for (int i = 0; i < len; i++) {
char c = string.charAt(i);
if ((c != 0) && (c < 0x80)) {
buf[bufPos++] = (byte)c;
} else if (c < 0x800) {
buf[bufPos++] = (byte)(((c >> 6) & 0x1f) | 0xc0);
buf[bufPos++] = (byte)((c & 0x3f) | 0x80);
} else {
buf[bufPos++] = (byte)(((c >> 12) & 0x0f) | 0xe0);
buf[bufPos++] = (byte)(((c >> 6) & 0x3f) | 0x80);
buf[bufPos++] = (byte)((c & 0x3f) | 0x80);
}
}
write(buf, 0, bufPos);
}
public void align() throws IOException {
int zeros = (-getPosition()) & 3;
if (zeros > 0) {
write(zeroBuf, 0, zeros);
}
}
@Override
public void flush() throws IOException {
if (bufPosition > 0) {
writeBufferToMap(buf, 0, bufPosition);
bufPosition = 0;
}
byteBuffer.force();
mappedFilePosition += byteBuffer.position();
channel.position(mappedFilePosition + MAP_SIZE);
channel.write(ByteBuffer.wrap(new byte[]{0}));
byteBuffer = channel.map(FileChannel.MapMode.READ_WRITE, mappedFilePosition, MAP_SIZE);
}
@Override
public void close() throws IOException {
if (bufPosition > 0) {
writeBufferToMap(buf, 0, bufPosition);
bufPosition = 0;
}
byteBuffer.force();
byteBuffer = null;
buf = null;
tempBuf = null;
}
private void flushBuffer() throws IOException {
Preconditions.checkState(bufPosition == BUF_SIZE);
writeBufferToMap(buf, 0, BUF_SIZE);
bufPosition = 0;
}
private void writeBufferToMap(byte[] buf, int bufOffset, int len) throws IOException {
// we always write BUF_SIZE, which evenly divides our mapped size, so we only care if remaining is 0 yet
if (!byteBuffer.hasRemaining()) {
byteBuffer.force();
mappedFilePosition += MAP_SIZE;
byteBuffer = channel.map(FileChannel.MapMode.READ_WRITE, mappedFilePosition, MAP_SIZE);
}
byteBuffer.put(buf, bufOffset, len);
}
public int getPosition() {
return mappedFilePosition + byteBuffer.position() + bufPosition;
}
}

View File

@ -0,0 +1,194 @@
/*
* Copyright 2012, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jf.dexlib2.writer;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.primitives.Ints;
import org.jf.dexlib2.iface.ClassDef;
import org.jf.dexlib2.iface.Field;
import org.jf.dexlib2.iface.value.EncodedValue;
import org.jf.dexlib2.immutable.value.ImmutableEncodedValueFactory;
import org.jf.dexlib2.util.EncodedValueUtils;
import org.jf.dexlib2.util.FieldUtil;
import org.jf.util.CollectionUtils;
import org.jf.util.ExceptionWithContext;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.util.*;
public class EncodedArrayPool {
@Nonnull private final Map<Key, Integer> internedEncodedArrayItems = Maps.newHashMap();
@Nonnull private final DexFile dexFile;
public EncodedArrayPool(DexFile dexFile) {
this.dexFile = dexFile;
}
public void intern(@Nonnull ClassDef classDef) {
Key key = Key.of(classDef);
if (key != null) {
Integer prev = internedEncodedArrayItems.put(key, 0);
if (prev == null) {
for (EncodedValue encodedValue: key.getElements()) {
dexFile.internEncodedValue(encodedValue);
}
}
}
}
public int getOffset(@Nonnull ClassDef classDef) {
Key key = Key.of(classDef);
if (key != null) {
Integer offset = internedEncodedArrayItems.get(key);
if (offset == null) {
throw new ExceptionWithContext("Encoded array not found.");
}
return offset;
}
return 0;
}
public void write(@Nonnull DexWriter writer) throws IOException {
List<Key> encodedArrays = Lists.newArrayList(internedEncodedArrayItems.keySet());
Collections.sort(encodedArrays);
for (Key encodedArray: encodedArrays) {
internedEncodedArrayItems.put(encodedArray, writer.getPosition());
writer.writeUleb128(encodedArray.getElementCount());
for (EncodedValue value: encodedArray.getElements()) {
dexFile.writeEncodedValue(writer, value);
}
}
}
public static class Key implements Comparable<Key> {
private final Set<? extends Field> fields;
private final int size;
private static final Function<Field, EncodedValue> GET_INITIAL_VALUE =
new Function<Field, EncodedValue>() {
@Override
public EncodedValue apply(Field input) {
EncodedValue initialValue = input.getInitialValue();
if (initialValue == null) {
return ImmutableEncodedValueFactory.defaultValueForType(input.getType());
}
return initialValue;
}
};
private Key(@Nonnull Set<? extends Field> fields, int size) {
this.fields = fields;
this.size = size;
}
@Nullable
public static Key of(@Nonnull ClassDef classDef) {
Set<? extends Field> fields = classDef.getFields();
Iterable<? extends Field> staticFields = FluentIterable.from(classDef.getFields()).filter(IS_STATIC_FIELD);
int lastIndex = CollectionUtils.lastIndexOf(staticFields, HAS_INITIALIZER);
if (lastIndex > -1) {
return new Key(fields, lastIndex+1);
}
return null;
}
public int getElementCount() {
return size;
}
@Nonnull
public Iterable<EncodedValue> getElements() {
return FluentIterable.from(fields)
.filter(IS_STATIC_FIELD)
.limit(size)
.transform(GET_INITIAL_VALUE);
}
@Override
public int hashCode() {
return CollectionUtils.listHashCode(getElements());
}
@Override
public boolean equals(Object o) {
if (o instanceof Key) {
Key other = (Key)o;
if (size != other.size) {
return false;
}
return Iterables.elementsEqual(getElements(), other.getElements());
}
return false;
}
private static final Predicate<Field> HAS_INITIALIZER = new Predicate<Field>() {
@Override
public boolean apply(Field input) {
EncodedValue encodedValue = input.getInitialValue();
return encodedValue != null && !EncodedValueUtils.isDefaultValue(encodedValue);
}
};
private static final Predicate<Field> IS_STATIC_FIELD = new Predicate<Field>() {
@Override
public boolean apply(Field input) {
return FieldUtil.isStatic(input);
}
};
@Override
public int compareTo(Key o) {
int res = Ints.compare(size, o.size);
if (res != 0) {
return res;
}
Iterator<EncodedValue> otherElements = o.getElements().iterator();
for (EncodedValue element: getElements()) {
res = element.compareTo(otherElements.next());
if (res != 0) {
return res;
}
}
return 0;
}
}
}

View File

@ -0,0 +1,88 @@
/*
* Copyright 2012, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jf.dexlib2.writer;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.jf.dexlib2.iface.reference.FieldReference;
import org.jf.dexlib2.util.ReferenceUtil;
import org.jf.util.ExceptionWithContext;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public class FieldPool {
private final static int FIELD_ID_ITEM_SIZE = 8;
@Nonnull private final Map<FieldReference, Integer> internedFieldIdItems = Maps.newHashMap();
@Nonnull private final DexFile dexFile;
public FieldPool(@Nonnull DexFile dexFile) {
this.dexFile = dexFile;
}
public void intern(@Nonnull FieldReference field) {
Integer prev = internedFieldIdItems.put(field, 0);
if (prev == null) {
dexFile.typePool.intern(field.getDefiningClass());
dexFile.stringPool.intern(field.getName());
dexFile.typePool.intern(field.getType());
}
}
public int getIndex(@Nonnull FieldReference fieldReference) {
Integer index = internedFieldIdItems.get(fieldReference);
if (index == null) {
throw new ExceptionWithContext("Field not found.: %s", ReferenceUtil.getFieldDescriptor(fieldReference));
}
return index;
}
public int getIndexedSectionSize() {
return internedFieldIdItems.size() * FIELD_ID_ITEM_SIZE;
}
public void write(@Nonnull DexWriter writer) throws IOException {
List<FieldReference> fields = Lists.newArrayList(internedFieldIdItems.keySet());
Collections.sort(fields);
int index = 0;
for (FieldReference field: fields) {
internedFieldIdItems.put(field, index++);
writer.writeUshort(dexFile.typePool.getIndex(field.getDefiningClass()));
writer.writeUshort(dexFile.typePool.getIndex(field.getType()));
writer.writeInt(dexFile.stringPool.getIndex(field.getName()));
}
}
}

View File

@ -0,0 +1,86 @@
/*
* Copyright 2012, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jf.dexlib2.writer;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.jf.dexlib2.iface.reference.MethodReference;
import org.jf.dexlib2.util.ReferenceUtil;
import org.jf.util.ExceptionWithContext;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.util.List;
import java.util.Map;
public class MethodPool {
private final static int METHOD_ID_ITEM_SIZE = 8;
@Nonnull private final Map<MethodReference, Integer> internedMethodIdItems = Maps.newHashMap();
@Nonnull private final DexFile dexFile;
public MethodPool(@Nonnull DexFile dexFile) {
this.dexFile = dexFile;
}
public void intern(@Nonnull MethodReference method) {
Integer prev = internedMethodIdItems.put(method, 0);
if (prev == null) {
dexFile.typePool.intern(method.getDefiningClass());
dexFile.protoPool.intern(method);
dexFile.stringPool.intern(method.getName());
}
}
public int getIndex(@Nonnull MethodReference methodReference) {
Integer index = internedMethodIdItems.get(methodReference);
if (index == null) {
throw new ExceptionWithContext("Method not found.: %s", ReferenceUtil.getMethodDescriptor(methodReference));
}
return index;
}
public int getIndexedSectionSize() {
return internedMethodIdItems.size() * METHOD_ID_ITEM_SIZE;
}
public void write(@Nonnull DexWriter writer) throws IOException {
List<MethodReference> methods = Lists.newArrayList(internedMethodIdItems.keySet());
int index = 0;
for (MethodReference method: methods) {
internedMethodIdItems.put(method, index++);
writer.writeUshort(dexFile.typePool.getIndex(method.getDefiningClass()));
writer.writeUshort(dexFile.protoPool.getIndex(method));
writer.writeInt(dexFile.stringPool.getIndex(method.getName()));
}
}
}

View File

@ -0,0 +1,160 @@
/*
* Copyright 2012, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jf.dexlib2.writer;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.jf.dexlib2.iface.reference.MethodReference;
import org.jf.dexlib2.iface.reference.TypeReference;
import org.jf.util.CollectionUtils;
import org.jf.util.ExceptionWithContext;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public class ProtoPool {
private final static int PROTO_ID_ITEM_SIZE = 12;
@Nonnull private final Map<Key, Integer> internedProtoIdItems = Maps.newHashMap();
@Nonnull private final DexFile dexFile;
public ProtoPool(@Nonnull DexFile dexFile) {
this.dexFile = dexFile;
}
public void intern(@Nonnull MethodReference method) {
// We can't use method directly, because it is likely a full MethodReference. We use a wrapper that computes
// hashCode and equals based only on the prototype fields
Key key = new Key(method);
Integer prev = internedProtoIdItems.put(key, 0);
if (prev == null) {
dexFile.stringPool.intern(key.getShorty());
dexFile.typePool.intern(method.getReturnType());
dexFile.typeListPool.intern(method.getParameters());
}
}
public int getIndex(@Nonnull MethodReference method) {
Key key = new Key(method);
Integer index = internedProtoIdItems.get(key);
if (index == null) {
throw new ExceptionWithContext("Prototype not found.: %s", key);
}
return index;
}
public int getIndexedSectionSize() {
return internedProtoIdItems.size() * PROTO_ID_ITEM_SIZE;
}
public void write(@Nonnull DexWriter writer) throws IOException {
List<Key> prototypes = Lists.newArrayList(internedProtoIdItems.keySet());
Collections.sort(prototypes);
int index = 0;
for (Key proto: prototypes) {
internedProtoIdItems.put(proto, index++);
writer.writeInt(dexFile.stringPool.getIndex(proto.getShorty()));
writer.writeInt(dexFile.typePool.getIndex(proto.getReturnType()));
writer.writeInt(dexFile.typeListPool.getOffset(proto.getParameters()));
}
}
private static class Key implements Comparable<Key> {
@Nonnull private final MethodReference method;
public Key(@Nonnull MethodReference method) {
this.method = method;
}
@Nonnull public String getReturnType() { return method.getReturnType(); }
@Nonnull public Collection<? extends TypeReference> getParameters() {
return method.getParameters();
}
public String getShorty() {
Collection<? extends TypeReference> params = method.getParameters();
StringBuilder sb = new StringBuilder(params.size() + 1);
sb.append(getShortyType(method.getReturnType()));
for (TypeReference typeRef: params) {
sb.append(getShortyType(typeRef));
}
return sb.toString();
}
private static char getShortyType(CharSequence type) {
if (type.length() > 1) {
return 'L';
}
return type.charAt(0);
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append('(');
for (TypeReference type: getParameters()) {
sb.append(type);
}
sb.append(')');
sb.append(getReturnType());
return sb.toString();
}
@Override
public int hashCode() {
int hashCode = getReturnType().hashCode();
return hashCode*31 + getParameters().hashCode();
}
@Override
public boolean equals(@Nullable Object o) {
if (o instanceof Key) {
Key other = (Key)o;
return getReturnType().equals(other.getReturnType()) &&
getParameters().equals(other.getParameters());
}
return false;
}
@Override
public int compareTo(@Nonnull Key o) {
int res = getReturnType().compareTo(o.getReturnType());
if (res != 0) return res;
return CollectionUtils.compareAsList(getParameters(), o.getParameters());
}
}
}

View File

@ -0,0 +1,93 @@
/*
* Copyright 2012, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jf.dexlib2.writer;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.jf.util.ExceptionWithContext;
import org.jf.util.StringUtils;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public class StringPool {
private final static int STRING_ID_ITEM_SIZE = 4;
@Nonnull private final Map<String, Integer> internedStringIdItems = Maps.newHashMap();
public void intern(@Nonnull CharSequence string) {
internedStringIdItems.put(string.toString(), 0);
}
public void internNullable(@Nullable CharSequence string) {
if (string != null) {
intern(string);
}
}
public int getIndex(@Nonnull CharSequence string) {
Integer index = internedStringIdItems.get(string.toString());
if (index == null) {
throw new ExceptionWithContext("String not found.: %s",
StringUtils.escapeString(string.toString()));
}
return index;
}
public int getIndexNullable(@Nullable CharSequence string) {
if (string == null) {
return -1;
}
return getIndex(string);
}
public int getIndexedSectionSize() {
return internedStringIdItems.size() * STRING_ID_ITEM_SIZE;
}
public void write(@Nonnull DexWriter indexWriter, @Nonnull DexWriter offsetWriter) throws IOException {
List<String> strings = Lists.newArrayList(internedStringIdItems.keySet());
Collections.sort(strings);
int index = 0;
for (String string: strings) {
internedStringIdItems.put(string, index++);
indexWriter.writeInt(offsetWriter.getPosition());
offsetWriter.writeUleb128(string.length());
offsetWriter.writeString(string);
offsetWriter.write(0);
}
}
}

View File

@ -0,0 +1,150 @@
/*
* Copyright 2012, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jf.dexlib2.writer;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.jf.util.ExceptionWithContext;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.util.*;
public class TypeListPool {
@Nonnull private final Map<Key, Integer> internedTypeListItems = Maps.newHashMap();
@Nonnull private final DexFile dexFile;
public TypeListPool(@Nonnull DexFile dexFile) {
this.dexFile = dexFile;
}
public void intern(@Nonnull Collection<? extends CharSequence> types) {
Key key = new Key(types);
Integer prev = internedTypeListItems.put(key, 0);
if (prev == null) {
for (CharSequence type: types) {
dexFile.typePool.intern(type);
}
}
}
public int getOffset(@Nonnull Collection<? extends CharSequence> types) {
Key key = new Key(types);
Integer offset = internedTypeListItems.get(key);
if (offset == null) {
throw new ExceptionWithContext("Type list not found.: %s", key);
}
return offset;
}
public void write(@Nonnull DexWriter writer) throws IOException {
List<Key> typeLists = Lists.newArrayList(internedTypeListItems.keySet());
Collections.sort(typeLists);
for (Key typeList: typeLists) {
writer.align();
internedTypeListItems.put(typeList, writer.getPosition());
Collection<? extends CharSequence> types = typeList.getTypes();
writer.writeInt(types.size());
for (CharSequence type: types) {
writer.writeUshort(dexFile.typePool.getIndex(type));
}
}
}
public static class Key implements Comparable<Key> {
@Nonnull private Collection<? extends CharSequence> types;
public Key(@Nonnull Collection<? extends CharSequence> types) {
this.types = types;
}
@Override
public int hashCode() {
int hashCode = 1;
for (CharSequence type: types) {
hashCode = hashCode*31 + type.toString().hashCode();
}
return hashCode;
}
@Override
public boolean equals(Object o) {
if (o instanceof Key) {
Key other = (Key)o;
if (types.size() != other.types.size()) {
return false;
}
Iterator<? extends CharSequence> otherTypes = other.types.iterator();
for (CharSequence type: types) {
if (!type.toString().equals(otherTypes.next().toString())) {
return false;
}
}
return true;
}
return false;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (CharSequence type: types) {
sb.append(type.toString());
}
return sb.toString();
}
@Nonnull
public Collection<? extends CharSequence> getTypes() {
return types;
}
@Override
public int compareTo(Key o) {
Iterator<? extends CharSequence> other = o.types.iterator();
for (CharSequence type: types) {
if (!other.hasNext()) {
return 1;
}
int comparison = type.toString().compareTo(other.next().toString());
if (comparison != 0) {
return comparison;
}
}
if (other.hasNext()) {
return -1;
}
return 0;
}
}
}

View File

@ -0,0 +1,97 @@
/*
* Copyright 2012, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jf.dexlib2.writer;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.jf.util.ExceptionWithContext;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public class TypePool {
private final static int TYPE_ID_ITEM_SIZE = 4;
@Nonnull private final Map<String, Integer> internedTypeIdItems = Maps.newHashMap();
@Nonnull private final DexFile dexFile;
public TypePool(@Nonnull DexFile dexFile) {
this.dexFile = dexFile;
}
public void intern(@Nonnull CharSequence type) {
Integer prev = internedTypeIdItems.put(type.toString(), 0);
if (prev == null) {
dexFile.stringPool.intern(type);
}
}
public void internNullable(@Nullable CharSequence type) {
if (type != null) {
intern(type);
}
}
public int getIndex(@Nonnull CharSequence type) {
Integer index = internedTypeIdItems.get(type.toString());
if (index == null) {
throw new ExceptionWithContext("Type not found.: %s", type);
}
return index;
}
public int getIndexNullable(@Nullable CharSequence type) {
if (type == null) {
return -1;
}
return getIndex(type);
}
public int getIndexedSectionSize() {
return internedTypeIdItems.size() * TYPE_ID_ITEM_SIZE;
}
public void write(@Nonnull DexWriter writer) throws IOException {
List<String> types = Lists.newArrayList(internedTypeIdItems.keySet());
Collections.sort(types);
int index = 0;
for (String type: types) {
internedTypeIdItems.put(type, index++);
int stringIndex = dexFile.stringPool.getIndex(type);
writer.writeInt(stringIndex);
}
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright 2012, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jf.dexlib2.writer.util;
import org.jf.dexlib2.iface.reference.MethodReference;
import org.jf.dexlib2.iface.reference.TypeReference;
import javax.annotation.Nonnull;
import java.util.Collection;
public final class PrototypeUtils {
private PrototypeUtils() {}
}

View File

@ -31,17 +31,35 @@
package org.jf.util;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Ordering;
import com.google.common.primitives.Ints;
import javax.annotation.Nonnull;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.SortedSet;
import java.util.*;
public class CollectionUtils {
public static <T> int listHashCode(@Nonnull Iterable<T> iterable) {
int hashCode = 1;
for (T item: iterable) {
hashCode = hashCode*31 + item.hashCode();
}
return hashCode;
}
public static <T> int lastIndexOf(@Nonnull Iterable<T> iterable, @Nonnull Predicate<? super T> predicate) {
int index = 0;
int lastMatchingIndex = -1;
for (T item: iterable) {
if (predicate.apply(item)) {
lastMatchingIndex = index;
}
index++;
}
return lastMatchingIndex;
}
public static <T extends Comparable<? super T>> int compareAsList(@Nonnull Collection<? extends T> list1,
@Nonnull Collection<? extends T> list2) {
int res = Ints.compare(list1.size(), list2.size());
@ -54,6 +72,27 @@ public class CollectionUtils {
return 0;
}
public static <T> int compareAsIterable(@Nonnull Comparator<? super T> comparator,
@Nonnull Iterable<? extends T> it1,
@Nonnull Iterable<? extends T> it2) {
Iterator<? extends T> elements2 = it2.iterator();
for (T element1: it1) {
int res = comparator.compare(element1, elements2.next());
if (res != 0) return res;
}
return 0;
}
public static <T extends Comparable<? super T>> int compareAsIterable(@Nonnull Iterable<? extends T> it1,
@Nonnull Iterable<? extends T> it2) {
Iterator<? extends T> elements2 = it2.iterator();
for (T element1: it1) {
int res = element1.compareTo(elements2.next());
if (res != 0) return res;
}
return 0;
}
public static <T> int compareAsList(@Nonnull Comparator<? super T> elementComparator,
@Nonnull Collection<? extends T> list1,
@Nonnull Collection<? extends T> list2) {
@ -67,6 +106,7 @@ public class CollectionUtils {
return 0;
}
@Nonnull
public static <T> Comparator<Collection<? extends T>> listComparator(
@Nonnull final Comparator<? super T> elementComparator) {
return new Comparator<Collection<? extends T>>() {
@ -77,14 +117,32 @@ public class CollectionUtils {
};
}
public static <T> boolean isNaturalSortedSet(@Nonnull Iterable<? extends T> it) {
if (it instanceof SortedSet) {
SortedSet<? extends T> sortedSet = (SortedSet<? extends T>)it;
Comparator<?> comparator = sortedSet.comparator();
return (comparator == null) || comparator.equals(Ordering.natural());
}
return false;
}
public static <T> boolean isSortedSet(@Nonnull Comparator<? extends T> elementComparator,
@Nonnull Iterable<? extends T> it) {
if (it instanceof SortedSet) {
SortedSet<? extends T> sortedSet = (SortedSet<? extends T>)it;
Comparator<?> comparator = sortedSet.comparator();
if (comparator == null) {
return elementComparator.equals(Ordering.natural());
}
return elementComparator.equals(comparator);
}
return false;
}
@Nonnull
private static <T> SortedSet<? extends T> toNaturalSortedSet(@Nonnull Collection<? extends T> collection) {
if (collection instanceof SortedSet) {
SortedSet<? extends T> sortedSet = (SortedSet<? extends T>)collection;
Comparator<?> comparator = sortedSet.comparator();
if (comparator == null || comparator.equals(Ordering.natural())) {
return sortedSet;
}
if (isNaturalSortedSet(collection)) {
return (SortedSet<? extends T>)collection;
}
return ImmutableSortedSet.copyOf(collection);
}
@ -102,6 +160,17 @@ public class CollectionUtils {
return ImmutableSortedSet.copyOf(elementComparator, collection);
}
@Nonnull
public static <T> Comparator<Collection<? extends T>> setComparator(
@Nonnull final Comparator<? super T> elementComparator) {
return new Comparator<Collection<? extends T>>() {
@Override
public int compare(Collection<? extends T> list1, Collection<? extends T> list2) {
return compareAsSet(elementComparator, list1, list2);
}
};
}
public static <T extends Comparable<T>> int compareAsSet(@Nonnull Collection<? extends T> set1,
@Nonnull Collection<? extends T> set2) {
int res = Ints.compare(set1.size(), set2.size());