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.base.Predicate;
36 import com.google.common.collect.*;
37 import org.jf.dexlib2.DebugItemType;
38 import org.jf.dexlib2.builder.MutableMethodImplementation;
39 import org.jf.dexlib2.iface.ExceptionHandler;
40 import org.jf.dexlib2.iface.Field;
41 import org.jf.dexlib2.iface.MethodImplementation;
42 import org.jf.dexlib2.iface.TryBlock;
43 import org.jf.dexlib2.iface.debug.*;
44 import org.jf.dexlib2.iface.instruction.Instruction;
45 import org.jf.dexlib2.iface.reference.StringReference;
46 import org.jf.dexlib2.iface.reference.TypeReference;
47 import org.jf.dexlib2.iface.value.EncodedValue;
48 import org.jf.dexlib2.util.EncodedValueUtils;
49 import org.jf.dexlib2.writer.ClassSection;
50 import org.jf.dexlib2.writer.DebugWriter;
51 import org.jf.dexlib2.writer.builder.BuilderEncodedValues.BuilderEncodedValue;
52 import org.jf.util.AbstractForwardSequentialList;
53 import org.jf.util.CollectionUtils;
54 import org.jf.util.ExceptionWithContext;
55 
56 import javax.annotation.Nonnull;
57 import javax.annotation.Nullable;
58 import java.io.IOException;
59 import java.util.*;
60 import java.util.Map.Entry;
61 import java.util.concurrent.ConcurrentMap;
62 
63 public 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 
BuilderClassPool()68     BuilderClassPool() {
69     }
70 
internClass(@onnull BuilderClassDef classDef)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;
getSortedClasses()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
getClassEntryByType(@ullable BuilderTypeReference type)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 getInterfaces(@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                 break;
421             }
422             default:
423                 throw new ExceptionWithContext("Unexpected debug item type: %d", debugItem.getDebugItemType());
424         }
425     }
426 
427     @Override public int getItemIndex(@Nonnull BuilderClassDef builderClassDef) {
428         return builderClassDef.classDefIndex;
429     }
430 
431     @Nonnull @Override public Collection<? extends Entry<? extends BuilderClassDef, Integer>> getItems() {
432         return new BuilderMapEntryCollection<BuilderClassDef>(internedItems.values()) {
433             @Override protected int getValue(@Nonnull BuilderClassDef key) {
434                 return key.classDefIndex;
435             }
436 
437             @Override protected int setValue(@Nonnull BuilderClassDef key, int value) {
438                 int prev = key.classDefIndex;
439                 key.classDefIndex = value;
440                 return prev;
441             }
442         };
443     }
444 }
445