| /* |
| * Copyright 2013, 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.builder; |
| |
| import com.google.common.base.Function; |
| import com.google.common.base.Predicate; |
| import com.google.common.collect.*; |
| import org.jf.dexlib2.DebugItemType; |
| import org.jf.dexlib2.builder.MutableMethodImplementation; |
| import org.jf.dexlib2.iface.ExceptionHandler; |
| import org.jf.dexlib2.iface.Field; |
| import org.jf.dexlib2.iface.MethodImplementation; |
| import org.jf.dexlib2.iface.TryBlock; |
| import org.jf.dexlib2.iface.debug.*; |
| import org.jf.dexlib2.iface.instruction.Instruction; |
| import org.jf.dexlib2.iface.reference.StringReference; |
| import org.jf.dexlib2.iface.reference.TypeReference; |
| import org.jf.dexlib2.iface.value.EncodedValue; |
| import org.jf.dexlib2.util.EncodedValueUtils; |
| import org.jf.dexlib2.writer.ClassSection; |
| import org.jf.dexlib2.writer.DebugWriter; |
| import org.jf.dexlib2.writer.builder.BuilderEncodedValues.BuilderEncodedValue; |
| import org.jf.util.AbstractForwardSequentialList; |
| 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.*; |
| import java.util.Map.Entry; |
| import java.util.concurrent.ConcurrentMap; |
| |
| public class BuilderClassPool extends BaseBuilderPool implements ClassSection<BuilderStringReference, |
| BuilderTypeReference, BuilderTypeList, BuilderClassDef, BuilderField, BuilderMethod, BuilderAnnotationSet, |
| BuilderEncodedValue> { |
| @Nonnull private final ConcurrentMap<String, BuilderClassDef> internedItems = |
| Maps.newConcurrentMap(); |
| |
| public BuilderClassPool(@Nonnull DexBuilder dexBuilder) { |
| super(dexBuilder); |
| } |
| |
| @Nonnull BuilderClassDef internClass(@Nonnull BuilderClassDef classDef) { |
| BuilderClassDef prev = internedItems.put(classDef.getType(), classDef); |
| if (prev != null) { |
| throw new ExceptionWithContext("Class %s has already been interned", classDef.getType()); |
| } |
| return classDef; |
| } |
| |
| private ImmutableList<BuilderClassDef> sortedClasses = null; |
| @Nonnull @Override public Collection<? extends BuilderClassDef> getSortedClasses() { |
| if (sortedClasses == null) { |
| sortedClasses = Ordering.natural().immutableSortedCopy(internedItems.values()); |
| } |
| return sortedClasses; |
| } |
| |
| @Nullable @Override |
| public Entry<? extends BuilderClassDef, Integer> getClassEntryByType(@Nullable BuilderTypeReference type) { |
| if (type == null) { |
| return null; |
| } |
| |
| final BuilderClassDef classDef = internedItems.get(type.getType()); |
| if (classDef == null) { |
| return null; |
| } |
| |
| return new Map.Entry<BuilderClassDef, Integer>() { |
| @Override public BuilderClassDef getKey() { |
| return classDef; |
| } |
| |
| @Override public Integer getValue() { |
| return classDef.classDefIndex; |
| } |
| |
| @Override public Integer setValue(Integer value) { |
| return classDef.classDefIndex = value; |
| } |
| }; |
| } |
| |
| @Nonnull @Override public BuilderTypeReference getType(@Nonnull BuilderClassDef builderClassDef) { |
| return builderClassDef.type; |
| } |
| |
| @Override public int getAccessFlags(@Nonnull BuilderClassDef builderClassDef) { |
| return builderClassDef.accessFlags; |
| } |
| |
| @Nullable @Override public BuilderTypeReference getSuperclass(@Nonnull BuilderClassDef builderClassDef) { |
| return builderClassDef.superclass; |
| } |
| |
| @Nullable @Override public BuilderTypeList getInterfaces(@Nonnull BuilderClassDef builderClassDef) { |
| return builderClassDef.interfaces; |
| } |
| |
| @Nullable @Override public BuilderStringReference getSourceFile(@Nonnull BuilderClassDef builderClassDef) { |
| return builderClassDef.sourceFile; |
| } |
| |
| 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 Function<BuilderField, BuilderEncodedValue> GET_INITIAL_VALUE = |
| new Function<BuilderField, BuilderEncodedValue>() { |
| @Override |
| public BuilderEncodedValue apply(BuilderField input) { |
| BuilderEncodedValue initialValue = input.getInitialValue(); |
| if (initialValue == null) { |
| return BuilderEncodedValues.defaultValueForType(input.getType()); |
| } |
| return initialValue; |
| } |
| }; |
| |
| @Nullable @Override |
| public Collection<? extends BuilderEncodedValue> getStaticInitializers(@Nonnull BuilderClassDef classDef) { |
| final SortedSet<BuilderField> sortedStaticFields = classDef.getStaticFields(); |
| |
| final int lastIndex = CollectionUtils.lastIndexOf(sortedStaticFields, HAS_INITIALIZER); |
| if (lastIndex > -1) { |
| return new AbstractCollection<BuilderEncodedValue>() { |
| @Nonnull @Override public Iterator<BuilderEncodedValue> iterator() { |
| return FluentIterable.from(sortedStaticFields) |
| .limit(lastIndex+1) |
| .transform(GET_INITIAL_VALUE).iterator(); |
| } |
| |
| @Override public int size() { |
| return lastIndex+1; |
| } |
| }; |
| } |
| return null; |
| } |
| |
| @Nonnull @Override |
| public Collection<? extends BuilderField> getSortedStaticFields(@Nonnull BuilderClassDef builderClassDef) { |
| return builderClassDef.getStaticFields(); |
| } |
| |
| @Nonnull @Override |
| public Collection<? extends BuilderField> getSortedInstanceFields(@Nonnull BuilderClassDef builderClassDef) { |
| return builderClassDef.getInstanceFields(); |
| } |
| |
| @Nonnull @Override |
| public Collection<? extends BuilderField> getSortedFields(@Nonnull BuilderClassDef builderClassDef) { |
| return builderClassDef.getFields(); |
| } |
| |
| @Nonnull @Override |
| public Collection<? extends BuilderMethod> getSortedDirectMethods(@Nonnull BuilderClassDef builderClassDef) { |
| return builderClassDef.getDirectMethods(); |
| } |
| |
| @Nonnull @Override |
| public Collection<? extends BuilderMethod> getSortedVirtualMethods(@Nonnull BuilderClassDef builderClassDef) { |
| return builderClassDef.getVirtualMethods(); |
| } |
| |
| @Nonnull @Override |
| public Collection<? extends BuilderMethod> getSortedMethods(@Nonnull BuilderClassDef builderClassDef) { |
| return builderClassDef.getMethods(); |
| } |
| |
| @Override public int getFieldAccessFlags(@Nonnull BuilderField builderField) { |
| return builderField.accessFlags; |
| } |
| |
| @Override public int getMethodAccessFlags(@Nonnull BuilderMethod builderMethod) { |
| return builderMethod.accessFlags; |
| } |
| |
| @Nullable @Override public BuilderAnnotationSet getClassAnnotations(@Nonnull BuilderClassDef builderClassDef) { |
| if (builderClassDef.annotations.isEmpty()) { |
| return null; |
| } |
| return builderClassDef.annotations; |
| } |
| |
| @Nullable @Override public BuilderAnnotationSet getFieldAnnotations(@Nonnull BuilderField builderField) { |
| if (builderField.annotations.isEmpty()) { |
| return null; |
| } |
| return builderField.annotations; |
| } |
| |
| @Nullable @Override public BuilderAnnotationSet getMethodAnnotations(@Nonnull BuilderMethod builderMethod) { |
| if (builderMethod.annotations.isEmpty()) { |
| return null; |
| } |
| return builderMethod.annotations; |
| } |
| |
| private static final Predicate<BuilderMethodParameter> HAS_PARAMETER_ANNOTATIONS = |
| new Predicate<BuilderMethodParameter>() { |
| @Override |
| public boolean apply(BuilderMethodParameter input) { |
| return input.getAnnotations().size() > 0; |
| } |
| }; |
| |
| private static final Function<BuilderMethodParameter, BuilderAnnotationSet> PARAMETER_ANNOTATIONS = |
| new Function<BuilderMethodParameter, BuilderAnnotationSet>() { |
| @Override |
| public BuilderAnnotationSet apply(BuilderMethodParameter input) { |
| return input.getAnnotations(); |
| } |
| }; |
| |
| @Nullable @Override public List<? extends BuilderAnnotationSet> getParameterAnnotations( |
| @Nonnull final BuilderMethod method) { |
| final List<? extends BuilderMethodParameter> parameters = method.getParameters(); |
| boolean hasParameterAnnotations = Iterables.any(parameters, HAS_PARAMETER_ANNOTATIONS); |
| |
| if (hasParameterAnnotations) { |
| return new AbstractForwardSequentialList<BuilderAnnotationSet>() { |
| @Nonnull @Override public Iterator<BuilderAnnotationSet> iterator() { |
| return FluentIterable.from(parameters) |
| .transform(PARAMETER_ANNOTATIONS).iterator(); |
| } |
| |
| @Override public int size() { |
| return parameters.size(); |
| } |
| }; |
| } |
| return null; |
| } |
| |
| @Nullable @Override |
| public Iterable<? extends DebugItem> getDebugItems(@Nonnull BuilderMethod builderMethod) { |
| MethodImplementation impl = builderMethod.getImplementation(); |
| if (impl == null) { |
| return null; |
| } |
| return impl.getDebugItems(); |
| } |
| |
| @Nullable @Override |
| public Iterable<? extends BuilderStringReference> getParameterNames(@Nonnull BuilderMethod method) { |
| return Iterables.transform(method.getParameters(), new Function<BuilderMethodParameter, BuilderStringReference>() { |
| @Nullable @Override public BuilderStringReference apply(BuilderMethodParameter input) { |
| return input.name; |
| } |
| }); |
| } |
| |
| @Override public int getRegisterCount(@Nonnull BuilderMethod builderMethod) { |
| MethodImplementation impl = builderMethod.getImplementation(); |
| if (impl == null) { |
| return 0; |
| } |
| return impl.getRegisterCount(); |
| } |
| |
| @Nullable @Override |
| public Iterable<? extends Instruction> getInstructions(@Nonnull BuilderMethod builderMethod) { |
| MethodImplementation impl = builderMethod.getImplementation(); |
| if (impl == null) { |
| return null; |
| } |
| return impl.getInstructions(); |
| } |
| |
| @Nonnull @Override |
| public List<? extends TryBlock<? extends ExceptionHandler>> getTryBlocks(@Nonnull BuilderMethod builderMethod) { |
| MethodImplementation impl = builderMethod.getImplementation(); |
| if (impl == null) { |
| return ImmutableList.of(); |
| } |
| return impl.getTryBlocks(); |
| } |
| |
| @Nullable @Override public BuilderTypeReference getExceptionType(@Nonnull ExceptionHandler handler) { |
| return checkTypeReference(handler.getExceptionTypeReference()); |
| } |
| |
| @Nonnull @Override |
| public MutableMethodImplementation makeMutableMethodImplementation(@Nonnull BuilderMethod builderMethod) { |
| MethodImplementation impl = builderMethod.getImplementation(); |
| if (impl instanceof MutableMethodImplementation) { |
| return (MutableMethodImplementation)impl; |
| } |
| return new MutableMethodImplementation(impl); |
| } |
| |
| @Override public void setEncodedArrayOffset(@Nonnull BuilderClassDef builderClassDef, int offset) { |
| builderClassDef.encodedArrayOffset = offset; |
| } |
| |
| @Override public int getEncodedArrayOffset(@Nonnull BuilderClassDef builderClassDef) { |
| return builderClassDef.encodedArrayOffset; |
| } |
| |
| @Override public void setAnnotationDirectoryOffset(@Nonnull BuilderClassDef builderClassDef, int offset) { |
| builderClassDef.annotationDirectoryOffset = offset; |
| } |
| |
| @Override public int getAnnotationDirectoryOffset(@Nonnull BuilderClassDef builderClassDef) { |
| return builderClassDef.annotationDirectoryOffset; |
| } |
| |
| @Override public void setAnnotationSetRefListOffset(@Nonnull BuilderMethod builderMethod, int offset) { |
| builderMethod.annotationSetRefListOffset = offset; |
| } |
| |
| @Override public int getAnnotationSetRefListOffset(@Nonnull BuilderMethod builderMethod) { |
| return builderMethod.annotationSetRefListOffset; |
| } |
| |
| @Override public void setCodeItemOffset(@Nonnull BuilderMethod builderMethod, int offset) { |
| builderMethod.codeItemOffset = offset; |
| } |
| |
| @Override public int getCodeItemOffset(@Nonnull BuilderMethod builderMethod) { |
| return builderMethod.codeItemOffset; |
| } |
| |
| @Nullable private BuilderStringReference checkStringReference(@Nullable StringReference stringReference) { |
| if (stringReference == null) { |
| return null; |
| } |
| try { |
| return (BuilderStringReference)stringReference; |
| } catch (ClassCastException ex) { |
| throw new IllegalStateException("Only StringReference instances returned by " + |
| "DexBuilder.internStringReference or DexBuilder.internNullableStringReference may be used."); |
| } |
| } |
| |
| @Nullable private BuilderTypeReference checkTypeReference(@Nullable TypeReference typeReference) { |
| if (typeReference == null) { |
| return null; |
| } |
| try { |
| return (BuilderTypeReference)typeReference; |
| } catch (ClassCastException ex) { |
| throw new IllegalStateException("Only TypeReference instances returned by " + |
| "DexBuilder.internTypeReference or DexBuilder.internNullableTypeReference may be used."); |
| } |
| } |
| |
| @Override |
| public void writeDebugItem(@Nonnull DebugWriter<BuilderStringReference, BuilderTypeReference> writer, |
| DebugItem debugItem) throws IOException { |
| switch (debugItem.getDebugItemType()) { |
| case DebugItemType.START_LOCAL: { |
| StartLocal startLocal = (StartLocal)debugItem; |
| writer.writeStartLocal(startLocal.getCodeAddress(), |
| startLocal.getRegister(), |
| checkStringReference(startLocal.getNameReference()), |
| checkTypeReference(startLocal.getTypeReference()), |
| checkStringReference(startLocal.getSignatureReference())); |
| break; |
| } |
| case DebugItemType.END_LOCAL: { |
| EndLocal endLocal = (EndLocal)debugItem; |
| writer.writeEndLocal(endLocal.getCodeAddress(), endLocal.getRegister()); |
| break; |
| } |
| case DebugItemType.RESTART_LOCAL: { |
| RestartLocal restartLocal = (RestartLocal)debugItem; |
| writer.writeRestartLocal(restartLocal.getCodeAddress(), restartLocal.getRegister()); |
| break; |
| } |
| case DebugItemType.PROLOGUE_END: { |
| writer.writePrologueEnd(debugItem.getCodeAddress()); |
| break; |
| } |
| case DebugItemType.EPILOGUE_BEGIN: { |
| writer.writeEpilogueBegin(debugItem.getCodeAddress()); |
| break; |
| } |
| case DebugItemType.LINE_NUMBER: { |
| LineNumber lineNumber = (LineNumber)debugItem; |
| writer.writeLineNumber(lineNumber.getCodeAddress(), lineNumber.getLineNumber()); |
| break; |
| } |
| case DebugItemType.SET_SOURCE_FILE: { |
| SetSourceFile setSourceFile = (SetSourceFile)debugItem; |
| writer.writeSetSourceFile(setSourceFile.getCodeAddress(), |
| checkStringReference(setSourceFile.getSourceFileReference())); |
| break; |
| } |
| default: |
| throw new ExceptionWithContext("Unexpected debug item type: %d", debugItem.getDebugItemType()); |
| } |
| } |
| |
| @Override public int getItemIndex(@Nonnull BuilderClassDef builderClassDef) { |
| return builderClassDef.classDefIndex; |
| } |
| |
| @Nonnull @Override public Collection<? extends Entry<? extends BuilderClassDef, Integer>> getItems() { |
| return new BuilderMapEntryCollection<BuilderClassDef>(internedItems.values()) { |
| @Override protected int getValue(@Nonnull BuilderClassDef key) { |
| return key.classDefIndex; |
| } |
| |
| @Override protected int setValue(@Nonnull BuilderClassDef key, int value) { |
| int prev = key.classDefIndex; |
| key.classDefIndex = value; |
| return prev; |
| } |
| }; |
| } |
| |
| @Override public int getItemCount() { |
| return internedItems.size(); |
| } |
| } |