mirror of
https://github.com/revanced/smali.git
synced 2025-05-02 23:54:38 +02:00
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:
parent
22c3185bb7
commit
4ffbfa2e71
@ -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() {}
|
||||
}
|
||||
|
@ -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());
|
||||
|
@ -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());
|
||||
|
@ -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) {
|
||||
|
45
dexlib2/src/main/java/org/jf/dexlib2/util/FieldUtil.java
Normal file
45
dexlib2/src/main/java/org/jf/dexlib2/util/FieldUtil.java
Normal 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() {}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
314
dexlib2/src/main/java/org/jf/dexlib2/writer/DexFile.java
Normal file
314
dexlib2/src/main/java/org/jf/dexlib2/writer/DexFile.java
Normal 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);
|
||||
}
|
||||
}
|
53
dexlib2/src/main/java/org/jf/dexlib2/writer/DexItemType.java
Normal file
53
dexlib2/src/main/java/org/jf/dexlib2/writer/DexItemType.java
Normal 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;
|
||||
}
|
342
dexlib2/src/main/java/org/jf/dexlib2/writer/DexWriter.java
Normal file
342
dexlib2/src/main/java/org/jf/dexlib2/writer/DexWriter.java
Normal 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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
88
dexlib2/src/main/java/org/jf/dexlib2/writer/FieldPool.java
Normal file
88
dexlib2/src/main/java/org/jf/dexlib2/writer/FieldPool.java
Normal 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()));
|
||||
}
|
||||
}
|
||||
}
|
86
dexlib2/src/main/java/org/jf/dexlib2/writer/MethodPool.java
Normal file
86
dexlib2/src/main/java/org/jf/dexlib2/writer/MethodPool.java
Normal 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()));
|
||||
}
|
||||
}
|
||||
}
|
160
dexlib2/src/main/java/org/jf/dexlib2/writer/ProtoPool.java
Normal file
160
dexlib2/src/main/java/org/jf/dexlib2/writer/ProtoPool.java
Normal 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());
|
||||
}
|
||||
}
|
||||
}
|
93
dexlib2/src/main/java/org/jf/dexlib2/writer/StringPool.java
Normal file
93
dexlib2/src/main/java/org/jf/dexlib2/writer/StringPool.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
150
dexlib2/src/main/java/org/jf/dexlib2/writer/TypeListPool.java
Normal file
150
dexlib2/src/main/java/org/jf/dexlib2/writer/TypeListPool.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
97
dexlib2/src/main/java/org/jf/dexlib2/writer/TypePool.java
Normal file
97
dexlib2/src/main/java/org/jf/dexlib2/writer/TypePool.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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() {}
|
||||
}
|
@ -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());
|
||||
|
Loading…
x
Reference in New Issue
Block a user