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
32package org.jf.dexlib2.writer.builder;
33
34import com.google.common.base.Function;
35import com.google.common.collect.ImmutableList;
36import com.google.common.collect.Iterators;
37import com.google.common.collect.Lists;
38import org.jf.dexlib2.ValueType;
39import org.jf.dexlib2.iface.Annotation;
40import org.jf.dexlib2.iface.MethodImplementation;
41import org.jf.dexlib2.iface.MethodParameter;
42import org.jf.dexlib2.iface.reference.*;
43import org.jf.dexlib2.iface.value.*;
44import org.jf.dexlib2.writer.DexWriter;
45import org.jf.dexlib2.writer.builder.BuilderEncodedValues.*;
46import org.jf.util.ExceptionWithContext;
47
48import javax.annotation.Nonnull;
49import javax.annotation.Nullable;
50import java.io.IOException;
51import java.util.Collections;
52import java.util.Iterator;
53import java.util.List;
54import java.util.Set;
55
56public 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