OSDN Git Service

d4cda2a9a28496432d24397ed3204320371c7f53
[android-x86/dalvik.git] / dx / src / com / android / dx / dex / cf / CfTranslator.java
1 /*
2  * Copyright (C) 2007 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.android.dx.dex.cf;
18
19 import com.android.dex.util.ExceptionWithContext;
20 import com.android.dx.cf.code.ConcreteMethod;
21 import com.android.dx.cf.code.Ropper;
22 import com.android.dx.cf.direct.DirectClassFile;
23 import com.android.dx.cf.direct.StdAttributeFactory;
24 import com.android.dx.cf.iface.Field;
25 import com.android.dx.cf.iface.FieldList;
26 import com.android.dx.cf.iface.Method;
27 import com.android.dx.cf.iface.MethodList;
28 import com.android.dx.dex.DexOptions;
29 import com.android.dx.dex.code.DalvCode;
30 import com.android.dx.dex.code.PositionList;
31 import com.android.dx.dex.code.RopTranslator;
32 import com.android.dx.dex.file.ClassDefItem;
33 import com.android.dx.dex.file.EncodedField;
34 import com.android.dx.dex.file.EncodedMethod;
35 import com.android.dx.rop.annotation.Annotations;
36 import com.android.dx.rop.annotation.AnnotationsList;
37 import com.android.dx.rop.code.AccessFlags;
38 import com.android.dx.rop.code.DexTranslationAdvice;
39 import com.android.dx.rop.code.LocalVariableExtractor;
40 import com.android.dx.rop.code.LocalVariableInfo;
41 import com.android.dx.rop.code.RopMethod;
42 import com.android.dx.rop.code.TranslationAdvice;
43 import com.android.dx.rop.cst.Constant;
44 import com.android.dx.rop.cst.CstBoolean;
45 import com.android.dx.rop.cst.CstByte;
46 import com.android.dx.rop.cst.CstChar;
47 import com.android.dx.rop.cst.CstFieldRef;
48 import com.android.dx.rop.cst.CstInteger;
49 import com.android.dx.rop.cst.CstMethodRef;
50 import com.android.dx.rop.cst.CstShort;
51 import com.android.dx.rop.cst.CstString;
52 import com.android.dx.rop.cst.CstType;
53 import com.android.dx.rop.cst.TypedConstant;
54 import com.android.dx.rop.type.Type;
55 import com.android.dx.rop.type.TypeList;
56 import com.android.dx.ssa.Optimizer;
57
58 /**
59  * Static method that turns {@code byte[]}s containing Java
60  * classfiles into {@link ClassDefItem} instances.
61  */
62 public class CfTranslator {
63     /** set to {@code true} to enable development-time debugging code */
64     private static final boolean DEBUG = false;
65
66     /**
67      * This class is uninstantiable.
68      */
69     private CfTranslator() {
70         // This space intentionally left blank.
71     }
72
73     /**
74      * Takes a {@code byte[]}, interprets it as a Java classfile, and
75      * translates it into a {@link ClassDefItem}.
76      *
77      * @param filePath {@code non-null;} the file path for the class,
78      * excluding any base directory specification
79      * @param bytes {@code non-null;} contents of the file
80      * @param cfOptions options for class translation
81      * @param dexOptions options for dex output
82      * @return {@code non-null;} the translated class
83      */
84     public static ClassDefItem translate(String filePath, byte[] bytes,
85             CfOptions cfOptions, DexOptions dexOptions) {
86         try {
87             return translate0(filePath, bytes, cfOptions, dexOptions);
88         } catch (RuntimeException ex) {
89             String msg = "...while processing " + filePath;
90             throw ExceptionWithContext.withContext(ex, msg);
91         }
92     }
93
94     /**
95      * Performs the main act of translation. This method is separated
96      * from {@link #translate} just to keep things a bit simpler in
97      * terms of exception handling.
98      *
99      * @param filePath {@code non-null;} the file path for the class,
100      * excluding any base directory specification
101      * @param bytes {@code non-null;} contents of the file
102      * @param cfOptions options for class translation
103      * @param dexOptions options for dex output
104      * @return {@code non-null;} the translated class
105      */
106     private static ClassDefItem translate0(String filePath, byte[] bytes,
107             CfOptions cfOptions, DexOptions dexOptions) {
108         DirectClassFile cf =
109             new DirectClassFile(bytes, filePath, cfOptions.strictNameCheck);
110
111         cf.setAttributeFactory(StdAttributeFactory.THE_ONE);
112         cf.getMagic();
113
114         OptimizerOptions.loadOptimizeLists(cfOptions.optimizeListFile,
115                 cfOptions.dontOptimizeListFile);
116
117         // Build up a class to output.
118
119         CstType thisClass = cf.getThisClass();
120         int classAccessFlags = cf.getAccessFlags() & ~AccessFlags.ACC_SUPER;
121         CstString sourceFile = (cfOptions.positionInfo == PositionList.NONE) ? null :
122             cf.getSourceFile();
123         ClassDefItem out =
124             new ClassDefItem(thisClass, classAccessFlags,
125                     cf.getSuperclass(), cf.getInterfaces(), sourceFile);
126
127         Annotations classAnnotations =
128             AttributeTranslator.getClassAnnotations(cf, cfOptions);
129         if (classAnnotations.size() != 0) {
130             out.setClassAnnotations(classAnnotations);
131         }
132
133         processFields(cf, out);
134         processMethods(cf, cfOptions, dexOptions, out);
135
136         return out;
137     }
138
139     /**
140      * Processes the fields of the given class.
141      *
142      * @param cf {@code non-null;} class being translated
143      * @param out {@code non-null;} output class
144      */
145     private static void processFields(DirectClassFile cf, ClassDefItem out) {
146         CstType thisClass = cf.getThisClass();
147         FieldList fields = cf.getFields();
148         int sz = fields.size();
149
150         for (int i = 0; i < sz; i++) {
151             Field one = fields.get(i);
152             try {
153                 CstFieldRef field = new CstFieldRef(thisClass, one.getNat());
154                 int accessFlags = one.getAccessFlags();
155
156                 if (AccessFlags.isStatic(accessFlags)) {
157                     TypedConstant constVal = one.getConstantValue();
158                     EncodedField fi = new EncodedField(field, accessFlags);
159                     if (constVal != null) {
160                         constVal = coerceConstant(constVal, field.getType());
161                     }
162                     out.addStaticField(fi, constVal);
163                 } else {
164                     EncodedField fi = new EncodedField(field, accessFlags);
165                     out.addInstanceField(fi);
166                 }
167
168                 Annotations annotations =
169                     AttributeTranslator.getAnnotations(one.getAttributes());
170                 if (annotations.size() != 0) {
171                     out.addFieldAnnotations(field, annotations);
172                 }
173             } catch (RuntimeException ex) {
174                 String msg = "...while processing " + one.getName().toHuman() +
175                     " " + one.getDescriptor().toHuman();
176                 throw ExceptionWithContext.withContext(ex, msg);
177             }
178         }
179     }
180
181     /**
182      * Helper for {@link #processFields}, which translates constants into
183      * more specific types if necessary.
184      *
185      * @param constant {@code non-null;} the constant in question
186      * @param type {@code non-null;} the desired type
187      */
188     private static TypedConstant coerceConstant(TypedConstant constant,
189             Type type) {
190         Type constantType = constant.getType();
191
192         if (constantType.equals(type)) {
193             return constant;
194         }
195
196         switch (type.getBasicType()) {
197             case Type.BT_BOOLEAN: {
198                 return CstBoolean.make(((CstInteger) constant).getValue());
199             }
200             case Type.BT_BYTE: {
201                 return CstByte.make(((CstInteger) constant).getValue());
202             }
203             case Type.BT_CHAR: {
204                 return CstChar.make(((CstInteger) constant).getValue());
205             }
206             case Type.BT_SHORT: {
207                 return CstShort.make(((CstInteger) constant).getValue());
208             }
209             default: {
210                 throw new UnsupportedOperationException("can't coerce " +
211                         constant + " to " + type);
212             }
213         }
214     }
215
216     /**
217      * Processes the methods of the given class.
218      *
219      * @param cf {@code non-null;} class being translated
220      * @param cfOptions {@code non-null;} options for class translation
221      * @param dexOptions {@code non-null;} options for dex output
222      * @param out {@code non-null;} output class
223      */
224     private static void processMethods(DirectClassFile cf, CfOptions cfOptions,
225             DexOptions dexOptions, ClassDefItem out) {
226         CstType thisClass = cf.getThisClass();
227         MethodList methods = cf.getMethods();
228         int sz = methods.size();
229
230         for (int i = 0; i < sz; i++) {
231             Method one = methods.get(i);
232             try {
233                 CstMethodRef meth = new CstMethodRef(thisClass, one.getNat());
234                 int accessFlags = one.getAccessFlags();
235                 boolean isStatic = AccessFlags.isStatic(accessFlags);
236                 boolean isPrivate = AccessFlags.isPrivate(accessFlags);
237                 boolean isNative = AccessFlags.isNative(accessFlags);
238                 boolean isAbstract = AccessFlags.isAbstract(accessFlags);
239                 boolean isConstructor = meth.isInstanceInit() ||
240                     meth.isClassInit();
241                 DalvCode code;
242
243                 if (isNative || isAbstract) {
244                     // There's no code for native or abstract methods.
245                     code = null;
246                 } else {
247                     ConcreteMethod concrete =
248                         new ConcreteMethod(one, cf,
249                                 (cfOptions.positionInfo != PositionList.NONE),
250                                 cfOptions.localInfo);
251
252                     TranslationAdvice advice;
253
254                     advice = DexTranslationAdvice.THE_ONE;
255
256                     RopMethod rmeth = Ropper.convert(concrete, advice);
257                     RopMethod nonOptRmeth = null;
258                     int paramSize;
259
260                     paramSize = meth.getParameterWordCount(isStatic);
261
262                     String canonicalName
263                             = thisClass.getClassType().getDescriptor()
264                                 + "." + one.getName().getString();
265
266                     if (cfOptions.optimize &&
267                             OptimizerOptions.shouldOptimize(canonicalName)) {
268                         if (DEBUG) {
269                             System.err.println("Optimizing " + canonicalName);
270                         }
271
272                         nonOptRmeth = rmeth;
273                         rmeth = Optimizer.optimize(rmeth,
274                                 paramSize, isStatic, cfOptions.localInfo, advice);
275
276                         if (DEBUG) {
277                             OptimizerOptions.compareOptimizerStep(nonOptRmeth,
278                                     paramSize, isStatic, cfOptions, advice, rmeth);
279                         }
280
281                         if (cfOptions.statistics) {
282                             CodeStatistics.updateRopStatistics(
283                                     nonOptRmeth, rmeth);
284                         }
285                     }
286
287                     LocalVariableInfo locals = null;
288
289                     if (cfOptions.localInfo) {
290                         locals = LocalVariableExtractor.extract(rmeth);
291                     }
292
293                     code = RopTranslator.translate(rmeth, cfOptions.positionInfo,
294                             locals, paramSize, dexOptions);
295
296                     if (cfOptions.statistics && nonOptRmeth != null) {
297                         updateDexStatistics(cfOptions, dexOptions, rmeth, nonOptRmeth, locals,
298                                 paramSize, concrete.getCode().size());
299                     }
300                 }
301
302                 // Preserve the synchronized flag as its "declared" variant...
303                 if (AccessFlags.isSynchronized(accessFlags)) {
304                     accessFlags |= AccessFlags.ACC_DECLARED_SYNCHRONIZED;
305
306                     /*
307                      * ...but only native methods are actually allowed to be
308                      * synchronized.
309                      */
310                     if (!isNative) {
311                         accessFlags &= ~AccessFlags.ACC_SYNCHRONIZED;
312                     }
313                 }
314
315                 if (isConstructor) {
316                     accessFlags |= AccessFlags.ACC_CONSTRUCTOR;
317                 }
318
319                 TypeList exceptions = AttributeTranslator.getExceptions(one);
320                 EncodedMethod mi =
321                     new EncodedMethod(meth, accessFlags, code, exceptions);
322
323                 if (meth.isInstanceInit() || meth.isClassInit() ||
324                     isStatic || isPrivate) {
325                     out.addDirectMethod(mi);
326                 } else {
327                     out.addVirtualMethod(mi);
328                 }
329
330                 Annotations annotations =
331                     AttributeTranslator.getMethodAnnotations(one);
332                 if (annotations.size() != 0) {
333                     out.addMethodAnnotations(meth, annotations);
334                 }
335
336                 AnnotationsList list =
337                     AttributeTranslator.getParameterAnnotations(one);
338                 if (list.size() != 0) {
339                     out.addParameterAnnotations(meth, list);
340                 }
341             } catch (RuntimeException ex) {
342                 String msg = "...while processing " + one.getName().toHuman() +
343                     " " + one.getDescriptor().toHuman();
344                 throw ExceptionWithContext.withContext(ex, msg);
345             }
346         }
347     }
348
349     /**
350      * Helper that updates the dex statistics.
351      */
352     private static void updateDexStatistics(CfOptions cfOptions, DexOptions dexOptions,
353             RopMethod optRmeth, RopMethod nonOptRmeth,
354             LocalVariableInfo locals, int paramSize, int originalByteCount) {
355         /*
356          * Run rop->dex again on optimized vs. non-optimized method to
357          * collect statistics. We have to totally convert both ways,
358          * since converting the "real" method getting added to the
359          * file would corrupt it (by messing with its constant pool
360          * indices).
361          */
362
363         DalvCode optCode = RopTranslator.translate(optRmeth,
364                 cfOptions.positionInfo, locals, paramSize, dexOptions);
365         DalvCode nonOptCode = RopTranslator.translate(nonOptRmeth,
366                 cfOptions.positionInfo, locals, paramSize, dexOptions);
367
368         /*
369          * Fake out the indices, so code.getInsns() can work well enough
370          * for the current purpose.
371          */
372
373         DalvCode.AssignIndicesCallback callback =
374             new DalvCode.AssignIndicesCallback() {
375                 public int getIndex(Constant cst) {
376                     // Everything is at index 0!
377                     return 0;
378                 }
379             };
380
381         optCode.assignIndices(callback);
382         nonOptCode.assignIndices(callback);
383
384         CodeStatistics.updateDexStatistics(nonOptCode, optCode);
385         CodeStatistics.updateOriginalByteCount(originalByteCount);
386     }
387 }