2 * Copyright (C) 2007 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.dx.dex.cf;
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;
59 * Static method that turns {@code byte[]}s containing Java
60 * classfiles into {@link ClassDefItem} instances.
62 public class CfTranslator {
63 /** set to {@code true} to enable development-time debugging code */
64 private static final boolean DEBUG = false;
67 * This class is uninstantiable.
69 private CfTranslator() {
70 // This space intentionally left blank.
74 * Takes a {@code byte[]}, interprets it as a Java classfile, and
75 * translates it into a {@link ClassDefItem}.
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
84 public static ClassDefItem translate(String filePath, byte[] bytes,
85 CfOptions cfOptions, DexOptions dexOptions) {
87 return translate0(filePath, bytes, cfOptions, dexOptions);
88 } catch (RuntimeException ex) {
89 String msg = "...while processing " + filePath;
90 throw ExceptionWithContext.withContext(ex, msg);
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.
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
106 private static ClassDefItem translate0(String filePath, byte[] bytes,
107 CfOptions cfOptions, DexOptions dexOptions) {
109 new DirectClassFile(bytes, filePath, cfOptions.strictNameCheck);
111 cf.setAttributeFactory(StdAttributeFactory.THE_ONE);
114 OptimizerOptions.loadOptimizeLists(cfOptions.optimizeListFile,
115 cfOptions.dontOptimizeListFile);
117 // Build up a class to output.
119 CstType thisClass = cf.getThisClass();
120 int classAccessFlags = cf.getAccessFlags() & ~AccessFlags.ACC_SUPER;
121 CstString sourceFile = (cfOptions.positionInfo == PositionList.NONE) ? null :
124 new ClassDefItem(thisClass, classAccessFlags,
125 cf.getSuperclass(), cf.getInterfaces(), sourceFile);
127 Annotations classAnnotations =
128 AttributeTranslator.getClassAnnotations(cf, cfOptions);
129 if (classAnnotations.size() != 0) {
130 out.setClassAnnotations(classAnnotations);
133 processFields(cf, out);
134 processMethods(cf, cfOptions, dexOptions, out);
140 * Processes the fields of the given class.
142 * @param cf {@code non-null;} class being translated
143 * @param out {@code non-null;} output class
145 private static void processFields(DirectClassFile cf, ClassDefItem out) {
146 CstType thisClass = cf.getThisClass();
147 FieldList fields = cf.getFields();
148 int sz = fields.size();
150 for (int i = 0; i < sz; i++) {
151 Field one = fields.get(i);
153 CstFieldRef field = new CstFieldRef(thisClass, one.getNat());
154 int accessFlags = one.getAccessFlags();
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());
162 out.addStaticField(fi, constVal);
164 EncodedField fi = new EncodedField(field, accessFlags);
165 out.addInstanceField(fi);
168 Annotations annotations =
169 AttributeTranslator.getAnnotations(one.getAttributes());
170 if (annotations.size() != 0) {
171 out.addFieldAnnotations(field, annotations);
173 } catch (RuntimeException ex) {
174 String msg = "...while processing " + one.getName().toHuman() +
175 " " + one.getDescriptor().toHuman();
176 throw ExceptionWithContext.withContext(ex, msg);
182 * Helper for {@link #processFields}, which translates constants into
183 * more specific types if necessary.
185 * @param constant {@code non-null;} the constant in question
186 * @param type {@code non-null;} the desired type
188 private static TypedConstant coerceConstant(TypedConstant constant,
190 Type constantType = constant.getType();
192 if (constantType.equals(type)) {
196 switch (type.getBasicType()) {
197 case Type.BT_BOOLEAN: {
198 return CstBoolean.make(((CstInteger) constant).getValue());
201 return CstByte.make(((CstInteger) constant).getValue());
204 return CstChar.make(((CstInteger) constant).getValue());
206 case Type.BT_SHORT: {
207 return CstShort.make(((CstInteger) constant).getValue());
210 throw new UnsupportedOperationException("can't coerce " +
211 constant + " to " + type);
217 * Processes the methods of the given class.
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
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();
230 for (int i = 0; i < sz; i++) {
231 Method one = methods.get(i);
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() ||
243 if (isNative || isAbstract) {
244 // There's no code for native or abstract methods.
247 ConcreteMethod concrete =
248 new ConcreteMethod(one, cf,
249 (cfOptions.positionInfo != PositionList.NONE),
250 cfOptions.localInfo);
252 TranslationAdvice advice;
254 advice = DexTranslationAdvice.THE_ONE;
256 RopMethod rmeth = Ropper.convert(concrete, advice);
257 RopMethod nonOptRmeth = null;
260 paramSize = meth.getParameterWordCount(isStatic);
263 = thisClass.getClassType().getDescriptor()
264 + "." + one.getName().getString();
266 if (cfOptions.optimize &&
267 OptimizerOptions.shouldOptimize(canonicalName)) {
269 System.err.println("Optimizing " + canonicalName);
273 rmeth = Optimizer.optimize(rmeth,
274 paramSize, isStatic, cfOptions.localInfo, advice);
277 OptimizerOptions.compareOptimizerStep(nonOptRmeth,
278 paramSize, isStatic, cfOptions, advice, rmeth);
281 if (cfOptions.statistics) {
282 CodeStatistics.updateRopStatistics(
287 LocalVariableInfo locals = null;
289 if (cfOptions.localInfo) {
290 locals = LocalVariableExtractor.extract(rmeth);
293 code = RopTranslator.translate(rmeth, cfOptions.positionInfo,
294 locals, paramSize, dexOptions);
296 if (cfOptions.statistics && nonOptRmeth != null) {
297 updateDexStatistics(cfOptions, dexOptions, rmeth, nonOptRmeth, locals,
298 paramSize, concrete.getCode().size());
302 // Preserve the synchronized flag as its "declared" variant...
303 if (AccessFlags.isSynchronized(accessFlags)) {
304 accessFlags |= AccessFlags.ACC_DECLARED_SYNCHRONIZED;
307 * ...but only native methods are actually allowed to be
311 accessFlags &= ~AccessFlags.ACC_SYNCHRONIZED;
316 accessFlags |= AccessFlags.ACC_CONSTRUCTOR;
319 TypeList exceptions = AttributeTranslator.getExceptions(one);
321 new EncodedMethod(meth, accessFlags, code, exceptions);
323 if (meth.isInstanceInit() || meth.isClassInit() ||
324 isStatic || isPrivate) {
325 out.addDirectMethod(mi);
327 out.addVirtualMethod(mi);
330 Annotations annotations =
331 AttributeTranslator.getMethodAnnotations(one);
332 if (annotations.size() != 0) {
333 out.addMethodAnnotations(meth, annotations);
336 AnnotationsList list =
337 AttributeTranslator.getParameterAnnotations(one);
338 if (list.size() != 0) {
339 out.addParameterAnnotations(meth, list);
341 } catch (RuntimeException ex) {
342 String msg = "...while processing " + one.getName().toHuman() +
343 " " + one.getDescriptor().toHuman();
344 throw ExceptionWithContext.withContext(ex, msg);
350 * Helper that updates the dex statistics.
352 private static void updateDexStatistics(CfOptions cfOptions, DexOptions dexOptions,
353 RopMethod optRmeth, RopMethod nonOptRmeth,
354 LocalVariableInfo locals, int paramSize, int originalByteCount) {
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
363 DalvCode optCode = RopTranslator.translate(optRmeth,
364 cfOptions.positionInfo, locals, paramSize, dexOptions);
365 DalvCode nonOptCode = RopTranslator.translate(nonOptRmeth,
366 cfOptions.positionInfo, locals, paramSize, dexOptions);
369 * Fake out the indices, so code.getInsns() can work well enough
370 * for the current purpose.
373 DalvCode.AssignIndicesCallback callback =
374 new DalvCode.AssignIndicesCallback() {
375 public int getIndex(Constant cst) {
376 // Everything is at index 0!
381 optCode.assignIndices(callback);
382 nonOptCode.assignIndices(callback);
384 CodeStatistics.updateDexStatistics(nonOptCode, optCode);
385 CodeStatistics.updateOriginalByteCount(originalByteCount);