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 implements ClassSection<BuilderStringReference, BuilderTypeReference, BuilderTypeList,
64        BuilderClassDef, BuilderField, BuilderMethod, BuilderAnnotationSet, BuilderEncodedValue> {
65    @Nonnull private final ConcurrentMap<String, BuilderClassDef> internedItems =
66            Maps.newConcurrentMap();
67
68    BuilderClassPool() {
69    }
70
71    @Nonnull BuilderClassDef internClass(@Nonnull BuilderClassDef classDef) {
72        BuilderClassDef prev = internedItems.put(classDef.getType(), classDef);
73        if (prev != null) {
74            throw new ExceptionWithContext("Class %s has already been interned", classDef.getType());
75        }
76        return classDef;
77    }
78
79    private ImmutableList<BuilderClassDef> sortedClasses = null;
80    @Nonnull @Override public Collection<? extends BuilderClassDef> getSortedClasses() {
81        if (sortedClasses == null) {
82            sortedClasses = Ordering.natural().immutableSortedCopy(internedItems.values());
83        }
84        return sortedClasses;
85    }
86
87    @Nullable @Override
88    public Entry<? extends BuilderClassDef, Integer> getClassEntryByType(@Nullable BuilderTypeReference type) {
89        if (type == null) {
90            return null;
91        }
92
93        final BuilderClassDef classDef = internedItems.get(type.getType());
94        if (classDef == null) {
95            return null;
96        }
97
98        return new Map.Entry<BuilderClassDef, Integer>() {
99            @Override public BuilderClassDef getKey() {
100                return classDef;
101            }
102
103            @Override public Integer getValue() {
104                return classDef.classDefIndex;
105            }
106
107            @Override public Integer setValue(Integer value) {
108                return classDef.classDefIndex = value;
109            }
110        };
111    }
112
113    @Nonnull @Override public BuilderTypeReference getType(@Nonnull BuilderClassDef builderClassDef) {
114        return builderClassDef.type;
115    }
116
117    @Override public int getAccessFlags(@Nonnull BuilderClassDef builderClassDef) {
118        return builderClassDef.accessFlags;
119    }
120
121    @Nullable @Override public BuilderTypeReference getSuperclass(@Nonnull BuilderClassDef builderClassDef) {
122        return builderClassDef.superclass;
123    }
124
125    @Nullable @Override public BuilderTypeList getSortedInterfaces(@Nonnull BuilderClassDef builderClassDef) {
126        return builderClassDef.interfaces;
127    }
128
129    @Nullable @Override public BuilderStringReference getSourceFile(@Nonnull BuilderClassDef builderClassDef) {
130        return builderClassDef.sourceFile;
131    }
132
133    private static final Predicate<Field> HAS_INITIALIZER = new Predicate<Field>() {
134        @Override
135        public boolean apply(Field input) {
136            EncodedValue encodedValue = input.getInitialValue();
137            return encodedValue != null && !EncodedValueUtils.isDefaultValue(encodedValue);
138        }
139    };
140
141    private static final Function<BuilderField, BuilderEncodedValue> GET_INITIAL_VALUE =
142            new Function<BuilderField, BuilderEncodedValue>() {
143                @Override
144                public BuilderEncodedValue apply(BuilderField input) {
145                    BuilderEncodedValue initialValue = input.getInitialValue();
146                    if (initialValue == null) {
147                        return BuilderEncodedValues.defaultValueForType(input.getType());
148                    }
149                    return initialValue;
150                }
151            };
152
153    @Nullable @Override
154    public Collection<? extends BuilderEncodedValue> getStaticInitializers(@Nonnull BuilderClassDef classDef) {
155        final SortedSet<BuilderField> sortedStaticFields = classDef.getStaticFields();
156
157        final int lastIndex = CollectionUtils.lastIndexOf(sortedStaticFields, HAS_INITIALIZER);
158        if (lastIndex > -1) {
159            return new AbstractCollection<BuilderEncodedValue>() {
160                @Nonnull @Override public Iterator<BuilderEncodedValue> iterator() {
161                    Iterable<BuilderField> fields = Iterables.limit(sortedStaticFields, lastIndex + 1);
162                    return Iterables.transform(fields, GET_INITIAL_VALUE).iterator();
163                }
164
165                @Override public int size() {
166                    return lastIndex+1;
167                }
168            };
169        }
170        return null;
171    }
172
173    @Nonnull @Override
174    public Collection<? extends BuilderField> getSortedStaticFields(@Nonnull BuilderClassDef builderClassDef) {
175        return builderClassDef.getStaticFields();
176    }
177
178    @Nonnull @Override
179    public Collection<? extends BuilderField> getSortedInstanceFields(@Nonnull BuilderClassDef builderClassDef) {
180        return builderClassDef.getInstanceFields();
181    }
182
183    @Nonnull @Override
184    public Collection<? extends BuilderField> getSortedFields(@Nonnull BuilderClassDef builderClassDef) {
185        return builderClassDef.getFields();
186    }
187
188    @Nonnull @Override
189    public Collection<? extends BuilderMethod> getSortedDirectMethods(@Nonnull BuilderClassDef builderClassDef) {
190        return builderClassDef.getDirectMethods();
191    }
192
193    @Nonnull @Override
194    public Collection<? extends BuilderMethod> getSortedVirtualMethods(@Nonnull BuilderClassDef builderClassDef) {
195        return builderClassDef.getVirtualMethods();
196    }
197
198    @Nonnull @Override
199    public Collection<? extends BuilderMethod> getSortedMethods(@Nonnull BuilderClassDef builderClassDef) {
200        return builderClassDef.getMethods();
201    }
202
203    @Override public int getFieldAccessFlags(@Nonnull BuilderField builderField) {
204        return builderField.accessFlags;
205    }
206
207    @Override public int getMethodAccessFlags(@Nonnull BuilderMethod builderMethod) {
208        return builderMethod.accessFlags;
209    }
210
211    @Nullable @Override public BuilderAnnotationSet getClassAnnotations(@Nonnull BuilderClassDef builderClassDef) {
212        if (builderClassDef.annotations.isEmpty()) {
213            return null;
214        }
215        return builderClassDef.annotations;
216    }
217
218    @Nullable @Override public BuilderAnnotationSet getFieldAnnotations(@Nonnull BuilderField builderField) {
219        if (builderField.annotations.isEmpty()) {
220            return null;
221        }
222        return builderField.annotations;
223    }
224
225    @Nullable @Override public BuilderAnnotationSet getMethodAnnotations(@Nonnull BuilderMethod builderMethod) {
226        if (builderMethod.annotations.isEmpty()) {
227            return null;
228        }
229        return builderMethod.annotations;
230    }
231
232    private static final Predicate<BuilderMethodParameter> HAS_PARAMETER_ANNOTATIONS =
233            new Predicate<BuilderMethodParameter>() {
234                @Override
235                public boolean apply(BuilderMethodParameter input) {
236                    return input.getAnnotations().size() > 0;
237                }
238            };
239
240    private static final Function<BuilderMethodParameter, BuilderAnnotationSet> PARAMETER_ANNOTATIONS =
241            new Function<BuilderMethodParameter, BuilderAnnotationSet>() {
242                @Override
243                public BuilderAnnotationSet apply(BuilderMethodParameter input) {
244                    return input.getAnnotations();
245                }
246            };
247
248    @Nullable @Override public List<? extends BuilderAnnotationSet> getParameterAnnotations(
249            @Nonnull final BuilderMethod method) {
250        final List<? extends BuilderMethodParameter> parameters = method.getParameters();
251        boolean hasParameterAnnotations = Iterables.any(parameters, HAS_PARAMETER_ANNOTATIONS);
252
253        if (hasParameterAnnotations) {
254            return new AbstractForwardSequentialList<BuilderAnnotationSet>() {
255                @Nonnull @Override public Iterator<BuilderAnnotationSet> iterator() {
256                    return Iterables.transform(parameters, PARAMETER_ANNOTATIONS).iterator();
257                }
258
259                @Override public int size() {
260                    return parameters.size();
261                }
262            };
263        }
264        return null;
265    }
266
267    @Nullable @Override
268    public Iterable<? extends DebugItem> getDebugItems(@Nonnull BuilderMethod builderMethod) {
269        MethodImplementation impl = builderMethod.getImplementation();
270        if (impl == null) {
271            return null;
272        }
273        return impl.getDebugItems();
274    }
275
276    @Nullable @Override
277    public Iterable<? extends BuilderStringReference> getParameterNames(@Nonnull BuilderMethod method) {
278        return Iterables.transform(method.getParameters(), new Function<BuilderMethodParameter, BuilderStringReference>() {
279            @Nullable @Override public BuilderStringReference apply(BuilderMethodParameter input) {
280                return input.name;
281            }
282        });
283    }
284
285    @Override public int getRegisterCount(@Nonnull BuilderMethod builderMethod) {
286        MethodImplementation impl = builderMethod.getImplementation();
287        if (impl == null) {
288            return 0;
289        }
290        return impl.getRegisterCount();
291    }
292
293    @Nullable @Override
294    public Iterable<? extends Instruction> getInstructions(@Nonnull BuilderMethod builderMethod) {
295        MethodImplementation impl = builderMethod.getImplementation();
296        if (impl == null) {
297            return null;
298        }
299        return impl.getInstructions();
300    }
301
302    @Nonnull @Override
303    public List<? extends TryBlock<? extends ExceptionHandler>> getTryBlocks(@Nonnull BuilderMethod builderMethod) {
304        MethodImplementation impl = builderMethod.getImplementation();
305        if (impl == null) {
306            return ImmutableList.of();
307        }
308        return impl.getTryBlocks();
309    }
310
311    @Nullable @Override public BuilderTypeReference getExceptionType(@Nonnull ExceptionHandler handler) {
312        return checkTypeReference(handler.getExceptionTypeReference());
313    }
314
315    @Nonnull @Override
316    public MutableMethodImplementation makeMutableMethodImplementation(@Nonnull BuilderMethod builderMethod) {
317        MethodImplementation impl = builderMethod.getImplementation();
318        if (impl instanceof MutableMethodImplementation) {
319            return (MutableMethodImplementation)impl;
320        }
321        return new MutableMethodImplementation(impl);
322    }
323
324    @Override public void setEncodedArrayOffset(@Nonnull BuilderClassDef builderClassDef, int offset) {
325        builderClassDef.encodedArrayOffset = offset;
326    }
327
328    @Override public int getEncodedArrayOffset(@Nonnull BuilderClassDef builderClassDef) {
329        return builderClassDef.encodedArrayOffset;
330    }
331
332    @Override public void setAnnotationDirectoryOffset(@Nonnull BuilderClassDef builderClassDef, int offset) {
333        builderClassDef.annotationDirectoryOffset = offset;
334    }
335
336    @Override public int getAnnotationDirectoryOffset(@Nonnull BuilderClassDef builderClassDef) {
337        return builderClassDef.annotationDirectoryOffset;
338    }
339
340    @Override public void setAnnotationSetRefListOffset(@Nonnull BuilderMethod builderMethod, int offset) {
341        builderMethod.annotationSetRefListOffset = offset;
342    }
343
344    @Override public int getAnnotationSetRefListOffset(@Nonnull BuilderMethod builderMethod) {
345        return builderMethod.annotationSetRefListOffset;
346    }
347
348    @Override public void setCodeItemOffset(@Nonnull BuilderMethod builderMethod, int offset) {
349        builderMethod.codeItemOffset = offset;
350    }
351
352    @Override public int getCodeItemOffset(@Nonnull BuilderMethod builderMethod) {
353        return builderMethod.codeItemOffset;
354    }
355
356    @Nullable private BuilderStringReference checkStringReference(@Nullable StringReference stringReference) {
357        if (stringReference == null) {
358            return null;
359        }
360        try {
361            return (BuilderStringReference)stringReference;
362        } catch (ClassCastException ex) {
363            throw new IllegalStateException("Only StringReference instances returned by " +
364                    "DexBuilder.internStringReference or DexBuilder.internNullableStringReference may be used.");
365        }
366    }
367
368    @Nullable private BuilderTypeReference checkTypeReference(@Nullable TypeReference typeReference) {
369        if (typeReference == null) {
370            return null;
371        }
372        try {
373            return (BuilderTypeReference)typeReference;
374        } catch (ClassCastException ex) {
375            throw new IllegalStateException("Only TypeReference instances returned by " +
376                    "DexBuilder.internTypeReference or DexBuilder.internNullableTypeReference may be used.");
377        }
378    }
379
380    @Override
381    public void writeDebugItem(@Nonnull DebugWriter<BuilderStringReference, BuilderTypeReference> writer,
382                               DebugItem debugItem) throws IOException {
383        switch (debugItem.getDebugItemType()) {
384            case DebugItemType.START_LOCAL: {
385                StartLocal startLocal = (StartLocal)debugItem;
386                writer.writeStartLocal(startLocal.getCodeAddress(),
387                        startLocal.getRegister(),
388                        checkStringReference(startLocal.getNameReference()),
389                        checkTypeReference(startLocal.getTypeReference()),
390                        checkStringReference(startLocal.getSignatureReference()));
391                break;
392            }
393            case DebugItemType.END_LOCAL: {
394                EndLocal endLocal = (EndLocal)debugItem;
395                writer.writeEndLocal(endLocal.getCodeAddress(), endLocal.getRegister());
396                break;
397            }
398            case DebugItemType.RESTART_LOCAL: {
399                RestartLocal restartLocal = (RestartLocal)debugItem;
400                writer.writeRestartLocal(restartLocal.getCodeAddress(), restartLocal.getRegister());
401                break;
402            }
403            case DebugItemType.PROLOGUE_END: {
404                writer.writePrologueEnd(debugItem.getCodeAddress());
405                break;
406            }
407            case DebugItemType.EPILOGUE_BEGIN: {
408                writer.writeEpilogueBegin(debugItem.getCodeAddress());
409                break;
410            }
411            case DebugItemType.LINE_NUMBER: {
412                LineNumber lineNumber = (LineNumber)debugItem;
413                writer.writeLineNumber(lineNumber.getCodeAddress(), lineNumber.getLineNumber());
414                break;
415            }
416            case DebugItemType.SET_SOURCE_FILE: {
417                SetSourceFile setSourceFile = (SetSourceFile)debugItem;
418                writer.writeSetSourceFile(setSourceFile.getCodeAddress(),
419                        checkStringReference(setSourceFile.getSourceFileReference()));
420            }
421            default:
422                throw new ExceptionWithContext("Unexpected debug item type: %d", debugItem.getDebugItemType());
423        }
424    }
425
426    @Override public int getItemIndex(@Nonnull BuilderClassDef builderClassDef) {
427        return builderClassDef.classDefIndex;
428    }
429
430    @Nonnull @Override public Collection<? extends Entry<? extends BuilderClassDef, Integer>> getItems() {
431        return new BuilderMapEntryCollection<BuilderClassDef>(internedItems.values()) {
432            @Override protected int getValue(@Nonnull BuilderClassDef key) {
433                return key.classDefIndex;
434            }
435
436            @Override protected int setValue(@Nonnull BuilderClassDef key, int value) {
437                int prev = key.classDefIndex;
438                key.classDefIndex = value;
439                return prev;
440            }
441        };
442    }
443}
444