Home | History | Annotate | Download | only in builder
      1 /*
      2  * Copyright 2013, Google Inc.
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are
      7  * met:
      8  *
      9  *     * Redistributions of source code must retain the above copyright
     10  * notice, this list of conditions and the following disclaimer.
     11  *     * Redistributions in binary form must reproduce the above
     12  * copyright notice, this list of conditions and the following disclaimer
     13  * in the documentation and/or other materials provided with the
     14  * distribution.
     15  *     * Neither the name of Google Inc. nor the names of its
     16  * contributors may be used to endorse or promote products derived from
     17  * this software without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 package org.jf.dexlib2.writer.builder;
     33 
     34 import com.google.common.base.Function;
     35 import com.google.common.base.Predicate;
     36 import com.google.common.collect.*;
     37 import org.jf.dexlib2.DebugItemType;
     38 import org.jf.dexlib2.builder.MutableMethodImplementation;
     39 import org.jf.dexlib2.iface.ExceptionHandler;
     40 import org.jf.dexlib2.iface.Field;
     41 import org.jf.dexlib2.iface.MethodImplementation;
     42 import org.jf.dexlib2.iface.TryBlock;
     43 import org.jf.dexlib2.iface.debug.*;
     44 import org.jf.dexlib2.iface.instruction.Instruction;
     45 import org.jf.dexlib2.iface.reference.StringReference;
     46 import org.jf.dexlib2.iface.reference.TypeReference;
     47 import org.jf.dexlib2.iface.value.EncodedValue;
     48 import org.jf.dexlib2.util.EncodedValueUtils;
     49 import org.jf.dexlib2.writer.ClassSection;
     50 import org.jf.dexlib2.writer.DebugWriter;
     51 import org.jf.dexlib2.writer.builder.BuilderEncodedValues.BuilderEncodedValue;
     52 import org.jf.util.AbstractForwardSequentialList;
     53 import org.jf.util.CollectionUtils;
     54 import org.jf.util.ExceptionWithContext;
     55 
     56 import javax.annotation.Nonnull;
     57 import javax.annotation.Nullable;
     58 import java.io.IOException;
     59 import java.util.*;
     60 import java.util.Map.Entry;
     61 import java.util.concurrent.ConcurrentMap;
     62 
     63 public class BuilderClassPool extends BaseBuilderPool implements ClassSection<BuilderStringReference,
     64         BuilderTypeReference, BuilderTypeList, BuilderClassDef, BuilderField, BuilderMethod, BuilderAnnotationSet,
     65         BuilderEncodedValue> {
     66     @Nonnull private final ConcurrentMap<String, BuilderClassDef> internedItems =
     67             Maps.newConcurrentMap();
     68 
     69     public BuilderClassPool(@Nonnull DexBuilder dexBuilder) {
     70         super(dexBuilder);
     71     }
     72 
     73     @Nonnull BuilderClassDef internClass(@Nonnull BuilderClassDef classDef) {
     74         BuilderClassDef prev = internedItems.put(classDef.getType(), classDef);
     75         if (prev != null) {
     76             throw new ExceptionWithContext("Class %s has already been interned", classDef.getType());
     77         }
     78         return classDef;
     79     }
     80 
     81     private ImmutableList<BuilderClassDef> sortedClasses = null;
     82     @Nonnull @Override public Collection<? extends BuilderClassDef> getSortedClasses() {
     83         if (sortedClasses == null) {
     84             sortedClasses = Ordering.natural().immutableSortedCopy(internedItems.values());
     85         }
     86         return sortedClasses;
     87     }
     88 
     89     @Nullable @Override
     90     public Entry<? extends BuilderClassDef, Integer> getClassEntryByType(@Nullable BuilderTypeReference type) {
     91         if (type == null) {
     92             return null;
     93         }
     94 
     95         final BuilderClassDef classDef = internedItems.get(type.getType());
     96         if (classDef == null) {
     97             return null;
     98         }
     99 
    100         return new Map.Entry<BuilderClassDef, Integer>() {
    101             @Override public BuilderClassDef getKey() {
    102                 return classDef;
    103             }
    104 
    105             @Override public Integer getValue() {
    106                 return classDef.classDefIndex;
    107             }
    108 
    109             @Override public Integer setValue(Integer value) {
    110                 return classDef.classDefIndex = value;
    111             }
    112         };
    113     }
    114 
    115     @Nonnull @Override public BuilderTypeReference getType(@Nonnull BuilderClassDef builderClassDef) {
    116         return builderClassDef.type;
    117     }
    118 
    119     @Override public int getAccessFlags(@Nonnull BuilderClassDef builderClassDef) {
    120         return builderClassDef.accessFlags;
    121     }
    122 
    123     @Nullable @Override public BuilderTypeReference getSuperclass(@Nonnull BuilderClassDef builderClassDef) {
    124         return builderClassDef.superclass;
    125     }
    126 
    127     @Nullable @Override public BuilderTypeList getInterfaces(@Nonnull BuilderClassDef builderClassDef) {
    128         return builderClassDef.interfaces;
    129     }
    130 
    131     @Nullable @Override public BuilderStringReference getSourceFile(@Nonnull BuilderClassDef builderClassDef) {
    132         return builderClassDef.sourceFile;
    133     }
    134 
    135     private static final Predicate<Field> HAS_INITIALIZER = new Predicate<Field>() {
    136         @Override
    137         public boolean apply(Field input) {
    138             EncodedValue encodedValue = input.getInitialValue();
    139             return encodedValue != null && !EncodedValueUtils.isDefaultValue(encodedValue);
    140         }
    141     };
    142 
    143     private static final Function<BuilderField, BuilderEncodedValue> GET_INITIAL_VALUE =
    144             new Function<BuilderField, BuilderEncodedValue>() {
    145                 @Override
    146                 public BuilderEncodedValue apply(BuilderField input) {
    147                     BuilderEncodedValue initialValue = input.getInitialValue();
    148                     if (initialValue == null) {
    149                         return BuilderEncodedValues.defaultValueForType(input.getType());
    150                     }
    151                     return initialValue;
    152                 }
    153             };
    154 
    155     @Nullable @Override
    156     public Collection<? extends BuilderEncodedValue> getStaticInitializers(@Nonnull BuilderClassDef classDef) {
    157         final SortedSet<BuilderField> sortedStaticFields = classDef.getStaticFields();
    158 
    159         final int lastIndex = CollectionUtils.lastIndexOf(sortedStaticFields, HAS_INITIALIZER);
    160         if (lastIndex > -1) {
    161             return new AbstractCollection<BuilderEncodedValue>() {
    162                 @Nonnull @Override public Iterator<BuilderEncodedValue> iterator() {
    163                     Iterable<BuilderField> fields = Iterables.limit(sortedStaticFields, lastIndex + 1);
    164                     return Iterables.transform(fields, GET_INITIAL_VALUE).iterator();
    165                 }
    166 
    167                 @Override public int size() {
    168                     return lastIndex+1;
    169                 }
    170             };
    171         }
    172         return null;
    173     }
    174 
    175     @Nonnull @Override
    176     public Collection<? extends BuilderField> getSortedStaticFields(@Nonnull BuilderClassDef builderClassDef) {
    177         return builderClassDef.getStaticFields();
    178     }
    179 
    180     @Nonnull @Override
    181     public Collection<? extends BuilderField> getSortedInstanceFields(@Nonnull BuilderClassDef builderClassDef) {
    182         return builderClassDef.getInstanceFields();
    183     }
    184 
    185     @Nonnull @Override
    186     public Collection<? extends BuilderField> getSortedFields(@Nonnull BuilderClassDef builderClassDef) {
    187         return builderClassDef.getFields();
    188     }
    189 
    190     @Nonnull @Override
    191     public Collection<? extends BuilderMethod> getSortedDirectMethods(@Nonnull BuilderClassDef builderClassDef) {
    192         return builderClassDef.getDirectMethods();
    193     }
    194 
    195     @Nonnull @Override
    196     public Collection<? extends BuilderMethod> getSortedVirtualMethods(@Nonnull BuilderClassDef builderClassDef) {
    197         return builderClassDef.getVirtualMethods();
    198     }
    199 
    200     @Nonnull @Override
    201     public Collection<? extends BuilderMethod> getSortedMethods(@Nonnull BuilderClassDef builderClassDef) {
    202         return builderClassDef.getMethods();
    203     }
    204 
    205     @Override public int getFieldAccessFlags(@Nonnull BuilderField builderField) {
    206         return builderField.accessFlags;
    207     }
    208 
    209     @Override public int getMethodAccessFlags(@Nonnull BuilderMethod builderMethod) {
    210         return builderMethod.accessFlags;
    211     }
    212 
    213     @Nullable @Override public BuilderAnnotationSet getClassAnnotations(@Nonnull BuilderClassDef builderClassDef) {
    214         if (builderClassDef.annotations.isEmpty()) {
    215             return null;
    216         }
    217         return builderClassDef.annotations;
    218     }
    219 
    220     @Nullable @Override public BuilderAnnotationSet getFieldAnnotations(@Nonnull BuilderField builderField) {
    221         if (builderField.annotations.isEmpty()) {
    222             return null;
    223         }
    224         return builderField.annotations;
    225     }
    226 
    227     @Nullable @Override public BuilderAnnotationSet getMethodAnnotations(@Nonnull BuilderMethod builderMethod) {
    228         if (builderMethod.annotations.isEmpty()) {
    229             return null;
    230         }
    231         return builderMethod.annotations;
    232     }
    233 
    234     private static final Predicate<BuilderMethodParameter> HAS_PARAMETER_ANNOTATIONS =
    235             new Predicate<BuilderMethodParameter>() {
    236                 @Override
    237                 public boolean apply(BuilderMethodParameter input) {
    238                     return input.getAnnotations().size() > 0;
    239                 }
    240             };
    241 
    242     private static final Function<BuilderMethodParameter, BuilderAnnotationSet> PARAMETER_ANNOTATIONS =
    243             new Function<BuilderMethodParameter, BuilderAnnotationSet>() {
    244                 @Override
    245                 public BuilderAnnotationSet apply(BuilderMethodParameter input) {
    246                     return input.getAnnotations();
    247                 }
    248             };
    249 
    250     @Nullable @Override public List<? extends BuilderAnnotationSet> getParameterAnnotations(
    251             @Nonnull final BuilderMethod method) {
    252         final List<? extends BuilderMethodParameter> parameters = method.getParameters();
    253         boolean hasParameterAnnotations = Iterables.any(parameters, HAS_PARAMETER_ANNOTATIONS);
    254 
    255         if (hasParameterAnnotations) {
    256             return new AbstractForwardSequentialList<BuilderAnnotationSet>() {
    257                 @Nonnull @Override public Iterator<BuilderAnnotationSet> iterator() {
    258                     return Iterables.transform(parameters, PARAMETER_ANNOTATIONS).iterator();
    259                 }
    260 
    261                 @Override public int size() {
    262                     return parameters.size();
    263                 }
    264             };
    265         }
    266         return null;
    267     }
    268 
    269     @Nullable @Override
    270     public Iterable<? extends DebugItem> getDebugItems(@Nonnull BuilderMethod builderMethod) {
    271         MethodImplementation impl = builderMethod.getImplementation();
    272         if (impl == null) {
    273             return null;
    274         }
    275         return impl.getDebugItems();
    276     }
    277 
    278     @Nullable @Override
    279     public Iterable<? extends BuilderStringReference> getParameterNames(@Nonnull BuilderMethod method) {
    280         return Iterables.transform(method.getParameters(), new Function<BuilderMethodParameter, BuilderStringReference>() {
    281             @Nullable @Override public BuilderStringReference apply(BuilderMethodParameter input) {
    282                 return input.name;
    283             }
    284         });
    285     }
    286 
    287     @Override public int getRegisterCount(@Nonnull BuilderMethod builderMethod) {
    288         MethodImplementation impl = builderMethod.getImplementation();
    289         if (impl == null) {
    290             return 0;
    291         }
    292         return impl.getRegisterCount();
    293     }
    294 
    295     @Nullable @Override
    296     public Iterable<? extends Instruction> getInstructions(@Nonnull BuilderMethod builderMethod) {
    297         MethodImplementation impl = builderMethod.getImplementation();
    298         if (impl == null) {
    299             return null;
    300         }
    301         return impl.getInstructions();
    302     }
    303 
    304     @Nonnull @Override
    305     public List<? extends TryBlock<? extends ExceptionHandler>> getTryBlocks(@Nonnull BuilderMethod builderMethod) {
    306         MethodImplementation impl = builderMethod.getImplementation();
    307         if (impl == null) {
    308             return ImmutableList.of();
    309         }
    310         return impl.getTryBlocks();
    311     }
    312 
    313     @Nullable @Override public BuilderTypeReference getExceptionType(@Nonnull ExceptionHandler handler) {
    314         return checkTypeReference(handler.getExceptionTypeReference());
    315     }
    316 
    317     @Nonnull @Override
    318     public MutableMethodImplementation makeMutableMethodImplementation(@Nonnull BuilderMethod builderMethod) {
    319         MethodImplementation impl = builderMethod.getImplementation();
    320         if (impl instanceof MutableMethodImplementation) {
    321             return (MutableMethodImplementation)impl;
    322         }
    323         return new MutableMethodImplementation(impl);
    324     }
    325 
    326     @Override public void setEncodedArrayOffset(@Nonnull BuilderClassDef builderClassDef, int offset) {
    327         builderClassDef.encodedArrayOffset = offset;
    328     }
    329 
    330     @Override public int getEncodedArrayOffset(@Nonnull BuilderClassDef builderClassDef) {
    331         return builderClassDef.encodedArrayOffset;
    332     }
    333 
    334     @Override public void setAnnotationDirectoryOffset(@Nonnull BuilderClassDef builderClassDef, int offset) {
    335         builderClassDef.annotationDirectoryOffset = offset;
    336     }
    337 
    338     @Override public int getAnnotationDirectoryOffset(@Nonnull BuilderClassDef builderClassDef) {
    339         return builderClassDef.annotationDirectoryOffset;
    340     }
    341 
    342     @Override public void setAnnotationSetRefListOffset(@Nonnull BuilderMethod builderMethod, int offset) {
    343         builderMethod.annotationSetRefListOffset = offset;
    344     }
    345 
    346     @Override public int getAnnotationSetRefListOffset(@Nonnull BuilderMethod builderMethod) {
    347         return builderMethod.annotationSetRefListOffset;
    348     }
    349 
    350     @Override public void setCodeItemOffset(@Nonnull BuilderMethod builderMethod, int offset) {
    351         builderMethod.codeItemOffset = offset;
    352     }
    353 
    354     @Override public int getCodeItemOffset(@Nonnull BuilderMethod builderMethod) {
    355         return builderMethod.codeItemOffset;
    356     }
    357 
    358     @Nullable private BuilderStringReference checkStringReference(@Nullable StringReference stringReference) {
    359         if (stringReference == null) {
    360             return null;
    361         }
    362         try {
    363             return (BuilderStringReference)stringReference;
    364         } catch (ClassCastException ex) {
    365             throw new IllegalStateException("Only StringReference instances returned by " +
    366                     "DexBuilder.internStringReference or DexBuilder.internNullableStringReference may be used.");
    367         }
    368     }
    369 
    370     @Nullable private BuilderTypeReference checkTypeReference(@Nullable TypeReference typeReference) {
    371         if (typeReference == null) {
    372             return null;
    373         }
    374         try {
    375             return (BuilderTypeReference)typeReference;
    376         } catch (ClassCastException ex) {
    377             throw new IllegalStateException("Only TypeReference instances returned by " +
    378                     "DexBuilder.internTypeReference or DexBuilder.internNullableTypeReference may be used.");
    379         }
    380     }
    381 
    382     @Override
    383     public void writeDebugItem(@Nonnull DebugWriter<BuilderStringReference, BuilderTypeReference> writer,
    384                                DebugItem debugItem) throws IOException {
    385         switch (debugItem.getDebugItemType()) {
    386             case DebugItemType.START_LOCAL: {
    387                 StartLocal startLocal = (StartLocal)debugItem;
    388                 writer.writeStartLocal(startLocal.getCodeAddress(),
    389                         startLocal.getRegister(),
    390                         checkStringReference(startLocal.getNameReference()),
    391                         checkTypeReference(startLocal.getTypeReference()),
    392                         checkStringReference(startLocal.getSignatureReference()));
    393                 break;
    394             }
    395             case DebugItemType.END_LOCAL: {
    396                 EndLocal endLocal = (EndLocal)debugItem;
    397                 writer.writeEndLocal(endLocal.getCodeAddress(), endLocal.getRegister());
    398                 break;
    399             }
    400             case DebugItemType.RESTART_LOCAL: {
    401                 RestartLocal restartLocal = (RestartLocal)debugItem;
    402                 writer.writeRestartLocal(restartLocal.getCodeAddress(), restartLocal.getRegister());
    403                 break;
    404             }
    405             case DebugItemType.PROLOGUE_END: {
    406                 writer.writePrologueEnd(debugItem.getCodeAddress());
    407                 break;
    408             }
    409             case DebugItemType.EPILOGUE_BEGIN: {
    410                 writer.writeEpilogueBegin(debugItem.getCodeAddress());
    411                 break;
    412             }
    413             case DebugItemType.LINE_NUMBER: {
    414                 LineNumber lineNumber = (LineNumber)debugItem;
    415                 writer.writeLineNumber(lineNumber.getCodeAddress(), lineNumber.getLineNumber());
    416                 break;
    417             }
    418             case DebugItemType.SET_SOURCE_FILE: {
    419                 SetSourceFile setSourceFile = (SetSourceFile)debugItem;
    420                 writer.writeSetSourceFile(setSourceFile.getCodeAddress(),
    421                         checkStringReference(setSourceFile.getSourceFileReference()));
    422                 break;
    423             }
    424             default:
    425                 throw new ExceptionWithContext("Unexpected debug item type: %d", debugItem.getDebugItemType());
    426         }
    427     }
    428 
    429     @Override public int getItemIndex(@Nonnull BuilderClassDef builderClassDef) {
    430         return builderClassDef.classDefIndex;
    431     }
    432 
    433     @Nonnull @Override public Collection<? extends Entry<? extends BuilderClassDef, Integer>> getItems() {
    434         return new BuilderMapEntryCollection<BuilderClassDef>(internedItems.values()) {
    435             @Override protected int getValue(@Nonnull BuilderClassDef key) {
    436                 return key.classDefIndex;
    437             }
    438 
    439             @Override protected int setValue(@Nonnull BuilderClassDef key, int value) {
    440                 int prev = key.classDefIndex;
    441                 key.classDefIndex = value;
    442                 return prev;
    443             }
    444         };
    445     }
    446 
    447     @Override public int getItemCount() {
    448         return internedItems.size();
    449     }
    450 }
    451