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.base.Predicate;
36import com.google.common.collect.*;
37import org.jf.dexlib2.DebugItemType;
38import org.jf.dexlib2.builder.MutableMethodImplementation;
39import org.jf.dexlib2.iface.ExceptionHandler;
40import org.jf.dexlib2.iface.Field;
41import org.jf.dexlib2.iface.MethodImplementation;
42import org.jf.dexlib2.iface.TryBlock;
43import org.jf.dexlib2.iface.debug.*;
44import org.jf.dexlib2.iface.instruction.Instruction;
45import org.jf.dexlib2.iface.reference.StringReference;
46import org.jf.dexlib2.iface.reference.TypeReference;
47import org.jf.dexlib2.iface.value.EncodedValue;
48import org.jf.dexlib2.util.EncodedValueUtils;
49import org.jf.dexlib2.writer.ClassSection;
50import org.jf.dexlib2.writer.DebugWriter;
51import org.jf.dexlib2.writer.builder.BuilderEncodedValues.BuilderEncodedValue;
52import org.jf.util.AbstractForwardSequentialList;
53import org.jf.util.CollectionUtils;
54import org.jf.util.ExceptionWithContext;
55
56import javax.annotation.Nonnull;
57import javax.annotation.Nullable;
58import java.io.IOException;
59import java.util.*;
60import java.util.Map.Entry;
61import java.util.concurrent.ConcurrentMap;
62
63public 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