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.collect.ImmutableList;
     36 import com.google.common.collect.ImmutableSet;
     37 import com.google.common.collect.Iterators;
     38 import com.google.common.collect.Sets;
     39 import org.jf.dexlib2.Opcodes;
     40 import org.jf.dexlib2.ValueType;
     41 import org.jf.dexlib2.iface.Annotation;
     42 import org.jf.dexlib2.iface.AnnotationElement;
     43 import org.jf.dexlib2.iface.MethodImplementation;
     44 import org.jf.dexlib2.iface.MethodParameter;
     45 import org.jf.dexlib2.iface.reference.*;
     46 import org.jf.dexlib2.iface.value.*;
     47 import org.jf.dexlib2.writer.DexWriter;
     48 import org.jf.dexlib2.writer.builder.BuilderEncodedValues.*;
     49 import org.jf.util.ExceptionWithContext;
     50 
     51 import javax.annotation.Nonnull;
     52 import javax.annotation.Nullable;
     53 import java.io.IOException;
     54 import java.util.Iterator;
     55 import java.util.List;
     56 import java.util.Set;
     57 
     58 public class DexBuilder extends DexWriter<BuilderStringReference, BuilderStringReference, BuilderTypeReference,
     59         BuilderTypeReference, BuilderMethodProtoReference, BuilderFieldReference, BuilderMethodReference,
     60         BuilderClassDef, BuilderAnnotation, BuilderAnnotationSet, BuilderTypeList, BuilderField, BuilderMethod,
     61         BuilderEncodedValue, BuilderAnnotationElement, BuilderStringPool, BuilderTypePool, BuilderProtoPool,
     62         BuilderFieldPool, BuilderMethodPool, BuilderClassPool, BuilderTypeListPool, BuilderAnnotationPool,
     63         BuilderAnnotationSetPool> {
     64 
     65     public DexBuilder(@Nonnull Opcodes opcodes) {
     66         super(opcodes);
     67     }
     68 
     69     @Nonnull @Override protected SectionProvider getSectionProvider() {
     70         return new DexBuilderSectionProvider();
     71     }
     72 
     73     @Nonnull public BuilderField internField(@Nonnull String definingClass,
     74                                              @Nonnull String name,
     75                                              @Nonnull String type,
     76                                              int accessFlags,
     77                                              @Nullable EncodedValue initialValue,
     78                                              @Nonnull Set<? extends Annotation> annotations) {
     79         return new BuilderField(fieldSection.internField(definingClass, name, type),
     80                 accessFlags,
     81                 internNullableEncodedValue(initialValue),
     82                 annotationSetSection.internAnnotationSet(annotations));
     83     }
     84 
     85     @Nonnull public BuilderMethod internMethod(@Nonnull String definingClass,
     86                                                @Nonnull String name,
     87                                                @Nullable List<? extends MethodParameter> parameters,
     88                                                @Nonnull String returnType,
     89                                                int accessFlags,
     90                                                @Nonnull Set<? extends Annotation> annotations,
     91                                                @Nullable MethodImplementation methodImplementation) {
     92         if (parameters == null) {
     93             parameters = ImmutableList.of();
     94         }
     95         return new BuilderMethod(methodSection.internMethod(definingClass, name, parameters, returnType),
     96                 internMethodParameters(parameters),
     97                 accessFlags,
     98                 annotationSetSection.internAnnotationSet(annotations),
     99                 methodImplementation);
    100     }
    101 
    102     @Nonnull public BuilderClassDef internClassDef(@Nonnull String type,
    103                                                    int accessFlags,
    104                                                    @Nullable String superclass,
    105                                                    @Nullable List<String> interfaces,
    106                                                    @Nullable String sourceFile,
    107                                                    @Nonnull Set<? extends Annotation> annotations,
    108                                                    @Nullable Iterable<? extends BuilderField> fields,
    109                                                    @Nullable Iterable<? extends BuilderMethod> methods) {
    110         if (interfaces == null) {
    111             interfaces = ImmutableList.of();
    112         } else {
    113             Set<String> interfaces_copy = Sets.newHashSet(interfaces);
    114             Iterator<String> interfaceIterator = interfaces.iterator();
    115             while (interfaceIterator.hasNext()) {
    116                 String iface = interfaceIterator.next();
    117                 if (!interfaces_copy.contains(iface)) {
    118                     interfaceIterator.remove();
    119                 } else {
    120                     interfaces_copy.remove(iface);
    121                 }
    122             }
    123         }
    124 
    125         return classSection.internClass(new BuilderClassDef(typeSection.internType(type),
    126                 accessFlags,
    127                 typeSection.internNullableType(superclass),
    128                 typeListSection.internTypeList(interfaces),
    129                 stringSection.internNullableString(sourceFile),
    130                 annotationSetSection.internAnnotationSet(annotations),
    131                 fields,
    132                 methods));
    133     }
    134 
    135     @Nonnull public BuilderStringReference internStringReference(@Nonnull String string) {
    136         return stringSection.internString(string);
    137     }
    138 
    139     @Nullable public BuilderStringReference internNullableStringReference(@Nullable String string) {
    140         if (string != null) {
    141             return internStringReference(string);
    142         }
    143         return null;
    144     }
    145 
    146     @Nonnull public BuilderTypeReference internTypeReference(@Nonnull String type) {
    147         return typeSection.internType(type);
    148     }
    149 
    150     @Nullable public BuilderTypeReference internNullableTypeReference(@Nullable String type) {
    151         if (type != null) {
    152             return internTypeReference(type);
    153         }
    154         return null;
    155     }
    156 
    157     @Nonnull public BuilderFieldReference internFieldReference(@Nonnull FieldReference field) {
    158         return fieldSection.internField(field);
    159     }
    160 
    161     @Nonnull public BuilderMethodReference internMethodReference(@Nonnull MethodReference method) {
    162         return methodSection.internMethod(method);
    163     }
    164 
    165     @Nonnull public BuilderMethodProtoReference internMethodProtoReference(@Nonnull MethodProtoReference methodProto) {
    166         return protoSection.internMethodProto(methodProto);
    167     }
    168 
    169     @Nonnull public BuilderReference internReference(@Nonnull Reference reference) {
    170         if (reference instanceof StringReference) {
    171             return internStringReference(((StringReference)reference).getString());
    172         }
    173         if (reference instanceof TypeReference) {
    174             return internTypeReference(((TypeReference)reference).getType());
    175         }
    176         if (reference instanceof MethodReference) {
    177             return internMethodReference((MethodReference)reference);
    178         }
    179         if (reference instanceof FieldReference) {
    180             return internFieldReference((FieldReference)reference);
    181         }
    182         if (reference instanceof MethodProtoReference) {
    183             return internMethodProtoReference((MethodProtoReference) reference);
    184         }
    185         throw new IllegalArgumentException("Could not determine type of reference");
    186     }
    187 
    188     @Nonnull private List<BuilderMethodParameter> internMethodParameters(
    189             @Nullable List<? extends MethodParameter> methodParameters) {
    190         if (methodParameters == null) {
    191             return ImmutableList.of();
    192         }
    193         return ImmutableList.copyOf(Iterators.transform(methodParameters.iterator(),
    194                 new Function<MethodParameter, BuilderMethodParameter>() {
    195                     @Nullable @Override public BuilderMethodParameter apply(MethodParameter input) {
    196                         return internMethodParameter(input);
    197                     }
    198                 }));
    199     }
    200 
    201     @Nonnull private BuilderMethodParameter internMethodParameter(@Nonnull MethodParameter methodParameter) {
    202         return new BuilderMethodParameter(
    203                 typeSection.internType(methodParameter.getType()),
    204                 stringSection.internNullableString(methodParameter.getName()),
    205                 annotationSetSection.internAnnotationSet(methodParameter.getAnnotations()));
    206     }
    207 
    208     @Override protected void writeEncodedValue(@Nonnull InternalEncodedValueWriter writer,
    209                                                @Nonnull BuilderEncodedValue encodedValue) throws IOException {
    210         switch (encodedValue.getValueType()) {
    211             case ValueType.ANNOTATION:
    212                 BuilderAnnotationEncodedValue annotationEncodedValue = (BuilderAnnotationEncodedValue)encodedValue;
    213                 writer.writeAnnotation(annotationEncodedValue.typeReference, annotationEncodedValue.elements);
    214                 break;
    215             case ValueType.ARRAY:
    216                 BuilderArrayEncodedValue arrayEncodedValue = (BuilderArrayEncodedValue)encodedValue;
    217                 writer.writeArray(arrayEncodedValue.elements);
    218                 break;
    219             case ValueType.BOOLEAN:
    220                 writer.writeBoolean(((BooleanEncodedValue)encodedValue).getValue());
    221                 break;
    222             case ValueType.BYTE:
    223                 writer.writeByte(((ByteEncodedValue)encodedValue).getValue());
    224                 break;
    225             case ValueType.CHAR:
    226                 writer.writeChar(((CharEncodedValue)encodedValue).getValue());
    227                 break;
    228             case ValueType.DOUBLE:
    229                 writer.writeDouble(((DoubleEncodedValue)encodedValue).getValue());
    230                 break;
    231             case ValueType.ENUM:
    232                 writer.writeEnum(((BuilderEnumEncodedValue)encodedValue).getValue());
    233                 break;
    234             case ValueType.FIELD:
    235                 writer.writeField(((BuilderFieldEncodedValue)encodedValue).fieldReference);
    236                 break;
    237             case ValueType.FLOAT:
    238                 writer.writeFloat(((FloatEncodedValue)encodedValue).getValue());
    239                 break;
    240             case ValueType.INT:
    241                 writer.writeInt(((IntEncodedValue)encodedValue).getValue());
    242                 break;
    243             case ValueType.LONG:
    244                 writer.writeLong(((LongEncodedValue)encodedValue).getValue());
    245                 break;
    246             case ValueType.METHOD:
    247                 writer.writeMethod(((BuilderMethodEncodedValue)encodedValue).methodReference);
    248                 break;
    249             case ValueType.NULL:
    250                 writer.writeNull();
    251                 break;
    252             case ValueType.SHORT:
    253                 writer.writeShort(((ShortEncodedValue)encodedValue).getValue());
    254                 break;
    255             case ValueType.STRING:
    256                 writer.writeString(((BuilderStringEncodedValue)encodedValue).stringReference);
    257                 break;
    258             case ValueType.TYPE:
    259                 writer.writeType(((BuilderTypeEncodedValue)encodedValue).typeReference);
    260                 break;
    261             default:
    262                 throw new ExceptionWithContext("Unrecognized value type: %d", encodedValue.getValueType());
    263         }
    264     }
    265 
    266     @Nonnull Set<? extends BuilderAnnotationElement> internAnnotationElements(
    267             @Nonnull Set<? extends AnnotationElement> elements) {
    268         return ImmutableSet.copyOf(
    269                 Iterators.transform(elements.iterator(),
    270                         new Function<AnnotationElement, BuilderAnnotationElement>() {
    271                             @Nullable @Override
    272                             public BuilderAnnotationElement apply(AnnotationElement input) {
    273                                 return internAnnotationElement(input);
    274                             }
    275                         }));
    276     }
    277 
    278     @Nonnull private BuilderAnnotationElement internAnnotationElement(@Nonnull AnnotationElement annotationElement) {
    279         return new BuilderAnnotationElement(stringSection.internString(annotationElement.getName()),
    280                 internEncodedValue(annotationElement.getValue()));
    281     }
    282 
    283     @Nullable BuilderEncodedValue internNullableEncodedValue(@Nullable EncodedValue encodedValue) {
    284         if (encodedValue == null) {
    285             return null;
    286         }
    287         return internEncodedValue(encodedValue);
    288     }
    289 
    290     @Nonnull private BuilderEncodedValue internEncodedValue(@Nonnull EncodedValue encodedValue) {
    291         switch (encodedValue.getValueType()) {
    292             case ValueType.ANNOTATION:
    293                 return internAnnotationEncodedValue((AnnotationEncodedValue)encodedValue);
    294             case ValueType.ARRAY:
    295                 return internArrayEncodedValue((ArrayEncodedValue)encodedValue);
    296             case ValueType.BOOLEAN:
    297                 boolean value = ((BooleanEncodedValue)encodedValue).getValue();
    298                 return value?BuilderBooleanEncodedValue.TRUE_VALUE:BuilderBooleanEncodedValue.FALSE_VALUE;
    299             case ValueType.BYTE:
    300                 return new BuilderByteEncodedValue(((ByteEncodedValue)encodedValue).getValue());
    301             case ValueType.CHAR:
    302                 return new BuilderCharEncodedValue(((CharEncodedValue)encodedValue).getValue());
    303             case ValueType.DOUBLE:
    304                 return new BuilderDoubleEncodedValue(((DoubleEncodedValue)encodedValue).getValue());
    305             case ValueType.ENUM:
    306                 return internEnumEncodedValue((EnumEncodedValue)encodedValue);
    307             case ValueType.FIELD:
    308                 return internFieldEncodedValue((FieldEncodedValue)encodedValue);
    309             case ValueType.FLOAT:
    310                 return new BuilderFloatEncodedValue(((FloatEncodedValue)encodedValue).getValue());
    311             case ValueType.INT:
    312                 return new BuilderIntEncodedValue(((IntEncodedValue)encodedValue).getValue());
    313             case ValueType.LONG:
    314                 return new BuilderLongEncodedValue(((LongEncodedValue)encodedValue).getValue());
    315             case ValueType.METHOD:
    316                 return internMethodEncodedValue((MethodEncodedValue)encodedValue);
    317             case ValueType.NULL:
    318                 return BuilderNullEncodedValue.INSTANCE;
    319             case ValueType.SHORT:
    320                 return new BuilderShortEncodedValue(((ShortEncodedValue)encodedValue).getValue());
    321             case ValueType.STRING:
    322                 return internStringEncodedValue((StringEncodedValue)encodedValue);
    323             case ValueType.TYPE:
    324                 return internTypeEncodedValue((TypeEncodedValue)encodedValue);
    325             default:
    326                 throw new ExceptionWithContext("Unexpected encoded value type: %d", encodedValue.getValueType());
    327         }
    328     }
    329 
    330     @Nonnull private BuilderAnnotationEncodedValue internAnnotationEncodedValue(@Nonnull AnnotationEncodedValue value) {
    331         return new BuilderAnnotationEncodedValue(
    332                 typeSection.internType(value.getType()),
    333                 internAnnotationElements(value.getElements()));
    334     }
    335 
    336     @Nonnull private BuilderArrayEncodedValue internArrayEncodedValue(@Nonnull ArrayEncodedValue value) {
    337         return new BuilderArrayEncodedValue(
    338                 ImmutableList.copyOf(
    339                         Iterators.transform(value.getValue().iterator(),
    340                                 new Function<EncodedValue, BuilderEncodedValue>() {
    341                                     @Nullable @Override public BuilderEncodedValue apply(EncodedValue input) {
    342                                         return internEncodedValue(input);
    343                                     }
    344                                 })));
    345     }
    346 
    347     @Nonnull private BuilderEnumEncodedValue internEnumEncodedValue(@Nonnull EnumEncodedValue value) {
    348         return new BuilderEnumEncodedValue(fieldSection.internField(value.getValue()));
    349     }
    350 
    351     @Nonnull private BuilderFieldEncodedValue internFieldEncodedValue(@Nonnull FieldEncodedValue value) {
    352         return new BuilderFieldEncodedValue(fieldSection.internField(value.getValue()));
    353     }
    354 
    355     @Nonnull private BuilderMethodEncodedValue internMethodEncodedValue(@Nonnull MethodEncodedValue value) {
    356         return new BuilderMethodEncodedValue(methodSection.internMethod(value.getValue()));
    357     }
    358 
    359     @Nonnull private BuilderStringEncodedValue internStringEncodedValue(@Nonnull StringEncodedValue string) {
    360         return new BuilderStringEncodedValue(stringSection.internString(string.getValue()));
    361     }
    362 
    363     @Nonnull private BuilderTypeEncodedValue internTypeEncodedValue(@Nonnull TypeEncodedValue type) {
    364         return new BuilderTypeEncodedValue(typeSection.internType(type.getValue()));
    365     }
    366 
    367     protected class DexBuilderSectionProvider extends SectionProvider {
    368         @Nonnull @Override public BuilderStringPool getStringSection() {
    369             return new BuilderStringPool();
    370         }
    371 
    372         @Nonnull @Override public BuilderTypePool getTypeSection() {
    373             return new BuilderTypePool(DexBuilder.this);
    374         }
    375 
    376         @Nonnull @Override public BuilderProtoPool getProtoSection() {
    377             return new BuilderProtoPool(DexBuilder.this);
    378         }
    379 
    380         @Nonnull @Override public BuilderFieldPool getFieldSection() {
    381             return new BuilderFieldPool(DexBuilder.this);
    382         }
    383 
    384         @Nonnull @Override public BuilderMethodPool getMethodSection() {
    385             return new BuilderMethodPool(DexBuilder.this);
    386         }
    387 
    388         @Nonnull @Override public BuilderClassPool getClassSection() {
    389             return new BuilderClassPool(DexBuilder.this);
    390         }
    391 
    392         @Nonnull @Override public BuilderTypeListPool getTypeListSection() {
    393             return new BuilderTypeListPool(DexBuilder.this);
    394         }
    395 
    396         @Nonnull @Override public BuilderAnnotationPool getAnnotationSection() {
    397             return new BuilderAnnotationPool(DexBuilder.this);
    398         }
    399 
    400         @Nonnull @Override public BuilderAnnotationSetPool getAnnotationSetSection() {
    401             return new BuilderAnnotationSetPool(DexBuilder.this);
    402         }
    403     }
    404 }
    405