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.Iterators;
     37 import com.google.common.collect.Lists;
     38 import org.jf.dexlib2.ValueType;
     39 import org.jf.dexlib2.iface.Annotation;
     40 import org.jf.dexlib2.iface.MethodImplementation;
     41 import org.jf.dexlib2.iface.MethodParameter;
     42 import org.jf.dexlib2.iface.reference.*;
     43 import org.jf.dexlib2.iface.value.*;
     44 import org.jf.dexlib2.writer.DexWriter;
     45 import org.jf.dexlib2.writer.builder.BuilderEncodedValues.*;
     46 import org.jf.util.ExceptionWithContext;
     47 
     48 import javax.annotation.Nonnull;
     49 import javax.annotation.Nullable;
     50 import java.io.IOException;
     51 import java.util.Collections;
     52 import java.util.Iterator;
     53 import java.util.List;
     54 import java.util.Set;
     55 
     56 public class DexBuilder extends DexWriter<BuilderStringReference, BuilderStringReference, BuilderTypeReference,
     57         BuilderTypeReference, BuilderProtoReference, BuilderFieldReference, BuilderMethodReference,
     58         BuilderClassDef, BuilderAnnotation, BuilderAnnotationSet, BuilderTypeList, BuilderField, BuilderMethod,
     59         BuilderEncodedValue, BuilderAnnotationElement> {
     60 
     61     private final BuilderContext context;
     62 
     63     public static DexBuilder makeDexBuilder() {
     64         BuilderContext context = new BuilderContext();
     65         return new DexBuilder(15, context);
     66     }
     67 
     68     public static DexBuilder makeDexBuilder(int api) {
     69         BuilderContext context = new BuilderContext();
     70         return new DexBuilder(api, context);
     71     }
     72 
     73     private DexBuilder(int api, @Nonnull BuilderContext context) {
     74         super(api, context.stringPool, context.typePool, context.protoPool,
     75                 context.fieldPool, context.methodPool, context.classPool, context.typeListPool, context.annotationPool,
     76                 context.annotationSetPool);
     77         this.context = context;
     78     }
     79 
     80     @Nonnull public BuilderField internField(@Nonnull String definingClass,
     81                                              @Nonnull String name,
     82                                              @Nonnull String type,
     83                                              int accessFlags,
     84                                              @Nullable EncodedValue initialValue,
     85                                              @Nonnull Set<? extends Annotation> annotations) {
     86         return new BuilderField(context.fieldPool.internField(definingClass, name, type),
     87                 accessFlags,
     88                 context.internNullableEncodedValue(initialValue),
     89                 context.annotationSetPool.internAnnotationSet(annotations));
     90     }
     91 
     92     @Nonnull public BuilderMethod internMethod(@Nonnull String definingClass,
     93                                                @Nonnull String name,
     94                                                @Nullable List<? extends MethodParameter> parameters,
     95                                                @Nonnull String returnType,
     96                                                int accessFlags,
     97                                                @Nonnull Set<? extends Annotation> annotations,
     98                                                @Nullable MethodImplementation methodImplementation) {
     99         if (parameters == null) {
    100             parameters = ImmutableList.of();
    101         }
    102         return new BuilderMethod(context.methodPool.internMethod(definingClass, name, parameters, returnType),
    103                 internMethodParameters(parameters),
    104                 accessFlags,
    105                 context.annotationSetPool.internAnnotationSet(annotations),
    106                 methodImplementation);
    107     }
    108 
    109     @Nonnull public BuilderClassDef internClassDef(@Nonnull String type,
    110                                                    int accessFlags,
    111                                                    @Nullable String superclass,
    112                                                    @Nullable List<String> interfaces,
    113                                                    @Nullable String sourceFile,
    114                                                    @Nonnull Set<? extends Annotation> annotations,
    115                                                    @Nullable Iterable<? extends BuilderField> fields,
    116                                                    @Nullable Iterable<? extends BuilderMethod> methods) {
    117         if (interfaces == null) {
    118             interfaces = ImmutableList.of();
    119         } else {
    120             interfaces = Lists.newArrayList(interfaces);
    121             Collections.sort(interfaces);
    122             String prev = null;
    123             Iterator<String> interfaceIterator = interfaces.iterator();
    124             while (interfaceIterator.hasNext()) {
    125                 String iface = interfaceIterator.next();
    126                 if (prev != null && iface.equals(prev)) {
    127                     interfaceIterator.remove();
    128                 }
    129                 prev = iface;
    130             }
    131         }
    132 
    133         return context.classPool.internClass(new BuilderClassDef(context.typePool.internType(type),
    134                 accessFlags,
    135                 context.typePool.internNullableType(superclass),
    136                 context.typeListPool.internTypeList(interfaces),
    137                 context.stringPool.internNullableString(sourceFile),
    138                 context.annotationSetPool.internAnnotationSet(annotations),
    139                 fields,
    140                 methods));
    141     }
    142 
    143     @Nonnull public BuilderStringReference internStringReference(@Nonnull String string) {
    144         return context.stringPool.internString(string);
    145     }
    146 
    147     @Nullable public BuilderStringReference internNullableStringReference(@Nullable String string) {
    148         if (string != null) {
    149             return internStringReference(string);
    150         }
    151         return null;
    152     }
    153 
    154     @Nonnull public BuilderTypeReference internTypeReference(@Nonnull String type) {
    155         return context.typePool.internType(type);
    156     }
    157 
    158     @Nullable public BuilderTypeReference internNullableTypeReference(@Nullable String type) {
    159         if (type != null) {
    160             return internTypeReference(type);
    161         }
    162         return null;
    163     }
    164 
    165     @Nonnull public BuilderFieldReference internFieldReference(@Nonnull FieldReference field) {
    166         return context.fieldPool.internField(field);
    167     }
    168 
    169     @Nonnull public BuilderMethodReference internMethodReference(@Nonnull MethodReference method) {
    170         return context.methodPool.internMethod(method);
    171     }
    172 
    173     @Nonnull public BuilderReference internReference(@Nonnull Reference reference) {
    174         if (reference instanceof StringReference) {
    175             return internStringReference(((StringReference)reference).getString());
    176         }
    177         if (reference instanceof TypeReference) {
    178             return internTypeReference(((TypeReference)reference).getType());
    179         }
    180         if (reference instanceof MethodReference) {
    181             return internMethodReference((MethodReference)reference);
    182         }
    183         if (reference instanceof FieldReference) {
    184             return internFieldReference((FieldReference)reference);
    185         }
    186         throw new IllegalArgumentException("Could not determine type of reference");
    187     }
    188 
    189     @Nonnull private List<BuilderMethodParameter> internMethodParameters(
    190             @Nullable List<? extends MethodParameter> methodParameters) {
    191         if (methodParameters == null) {
    192             return ImmutableList.of();
    193         }
    194         return ImmutableList.copyOf(Iterators.transform(methodParameters.iterator(),
    195                 new Function<MethodParameter, BuilderMethodParameter>() {
    196                     @Nullable @Override public BuilderMethodParameter apply(MethodParameter input) {
    197                         return internMethodParameter(input);
    198                     }
    199                 }));
    200     }
    201 
    202     @Nonnull private BuilderMethodParameter internMethodParameter(@Nonnull MethodParameter methodParameter) {
    203         return new BuilderMethodParameter(
    204                 context.typePool.internType(methodParameter.getType()),
    205                 context.stringPool.internNullableString(methodParameter.getName()),
    206                 context.annotationSetPool.internAnnotationSet(methodParameter.getAnnotations()));
    207     }
    208 
    209     @Override protected void writeEncodedValue(@Nonnull InternalEncodedValueWriter writer,
    210                                                @Nonnull BuilderEncodedValue encodedValue) throws IOException {
    211         switch (encodedValue.getValueType()) {
    212             case ValueType.ANNOTATION:
    213                 BuilderAnnotationEncodedValue annotationEncodedValue = (BuilderAnnotationEncodedValue)encodedValue;
    214                 writer.writeAnnotation(annotationEncodedValue.typeReference, annotationEncodedValue.elements);
    215                 break;
    216             case ValueType.ARRAY:
    217                 BuilderArrayEncodedValue arrayEncodedValue = (BuilderArrayEncodedValue)encodedValue;
    218                 writer.writeArray(arrayEncodedValue.elements);
    219                 break;
    220             case ValueType.BOOLEAN:
    221                 writer.writeBoolean(((BooleanEncodedValue)encodedValue).getValue());
    222                 break;
    223             case ValueType.BYTE:
    224                 writer.writeByte(((ByteEncodedValue)encodedValue).getValue());
    225                 break;
    226             case ValueType.CHAR:
    227                 writer.writeChar(((CharEncodedValue)encodedValue).getValue());
    228                 break;
    229             case ValueType.DOUBLE:
    230                 writer.writeDouble(((DoubleEncodedValue)encodedValue).getValue());
    231                 break;
    232             case ValueType.ENUM:
    233                 writer.writeEnum(((BuilderEnumEncodedValue)encodedValue).getValue());
    234                 break;
    235             case ValueType.FIELD:
    236                 writer.writeField(((BuilderFieldEncodedValue)encodedValue).fieldReference);
    237                 break;
    238             case ValueType.FLOAT:
    239                 writer.writeFloat(((FloatEncodedValue)encodedValue).getValue());
    240                 break;
    241             case ValueType.INT:
    242                 writer.writeInt(((IntEncodedValue)encodedValue).getValue());
    243                 break;
    244             case ValueType.LONG:
    245                 writer.writeLong(((LongEncodedValue)encodedValue).getValue());
    246                 break;
    247             case ValueType.METHOD:
    248                 writer.writeMethod(((BuilderMethodEncodedValue)encodedValue).methodReference);
    249                 break;
    250             case ValueType.NULL:
    251                 writer.writeNull();
    252                 break;
    253             case ValueType.SHORT:
    254                 writer.writeShort(((ShortEncodedValue)encodedValue).getValue());
    255                 break;
    256             case ValueType.STRING:
    257                 writer.writeString(((BuilderStringEncodedValue)encodedValue).stringReference);
    258                 break;
    259             case ValueType.TYPE:
    260                 writer.writeType(((BuilderTypeEncodedValue)encodedValue).typeReference);
    261                 break;
    262             default:
    263                 throw new ExceptionWithContext("Unrecognized value type: %d", encodedValue.getValueType());
    264         }
    265     }
    266 }
    267