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.dexgen.dex.file; 18 19 import com.android.dexgen.rop.annotation.Annotations; 20 import com.android.dexgen.rop.annotation.AnnotationsList; 21 import com.android.dexgen.rop.code.AccessFlags; 22 import com.android.dexgen.rop.cst.Constant; 23 import com.android.dexgen.rop.cst.CstArray; 24 import com.android.dexgen.rop.cst.CstFieldRef; 25 import com.android.dexgen.rop.cst.CstMethodRef; 26 import com.android.dexgen.rop.cst.CstType; 27 import com.android.dexgen.rop.cst.CstUtf8; 28 import com.android.dexgen.rop.type.StdTypeList; 29 import com.android.dexgen.rop.type.TypeList; 30 import com.android.dexgen.util.AnnotatedOutput; 31 import com.android.dexgen.util.Hex; 32 import com.android.dexgen.util.Writers; 33 34 import java.io.PrintWriter; 35 import java.io.Writer; 36 import java.util.ArrayList; 37 import java.util.TreeSet; 38 39 /** 40 * Representation of a Dalvik class, which is basically a set of 41 * members (fields or methods) along with a few more pieces of 42 * information. 43 */ 44 public final class ClassDefItem extends IndexedItem { 45 /** size of instances when written out to a file, in bytes */ 46 public static final int WRITE_SIZE = 32; 47 48 /** {@code non-null;} type constant for this class */ 49 private final CstType thisClass; 50 51 /** access flags */ 52 private final int accessFlags; 53 54 /** 55 * {@code null-ok;} superclass or {@code null} if this class is a/the 56 * root class 57 */ 58 private final CstType superclass; 59 60 /** {@code null-ok;} list of implemented interfaces */ 61 private TypeListItem interfaces; 62 63 /** {@code null-ok;} source file name or {@code null} if unknown */ 64 private final CstUtf8 sourceFile; 65 66 /** {@code non-null;} associated class data object */ 67 private final ClassDataItem classData; 68 69 /** 70 * {@code null-ok;} item wrapper for the static values, initialized 71 * in {@link #addContents} 72 */ 73 private EncodedArrayItem staticValuesItem; 74 75 /** {@code non-null;} annotations directory */ 76 private AnnotationsDirectoryItem annotationsDirectory; 77 78 /** 79 * Constructs an instance. Its sets of members and annotations are 80 * initially empty. 81 * 82 * @param thisClass {@code non-null;} type constant for this class 83 * @param accessFlags access flags 84 * @param superclass {@code null-ok;} superclass or {@code null} if 85 * this class is a/the root class 86 * @param interfaces {@code non-null;} list of implemented interfaces 87 * @param sourceFile {@code null-ok;} source file name or 88 * {@code null} if unknown 89 */ ClassDefItem(CstType thisClass, int accessFlags, CstType superclass, TypeList interfaces, CstUtf8 sourceFile)90 public ClassDefItem(CstType thisClass, int accessFlags, 91 CstType superclass, TypeList interfaces, CstUtf8 sourceFile) { 92 if (thisClass == null) { 93 throw new NullPointerException("thisClass == null"); 94 } 95 96 /* 97 * TODO: Maybe check accessFlags and superclass, at 98 * least for easily-checked stuff? 99 */ 100 101 if (interfaces == null) { 102 throw new NullPointerException("interfaces == null"); 103 } 104 105 this.thisClass = thisClass; 106 this.accessFlags = accessFlags; 107 this.superclass = superclass; 108 this.interfaces = 109 (interfaces.size() == 0) ? null : new TypeListItem(interfaces); 110 this.sourceFile = sourceFile; 111 this.classData = new ClassDataItem(thisClass); 112 this.staticValuesItem = null; 113 this.annotationsDirectory = new AnnotationsDirectoryItem(); 114 } 115 116 /** {@inheritDoc} */ 117 @Override itemType()118 public ItemType itemType() { 119 return ItemType.TYPE_CLASS_DEF_ITEM; 120 } 121 122 /** {@inheritDoc} */ 123 @Override writeSize()124 public int writeSize() { 125 return WRITE_SIZE; 126 } 127 128 /** {@inheritDoc} */ 129 @Override addContents(DexFile file)130 public void addContents(DexFile file) { 131 TypeIdsSection typeIds = file.getTypeIds(); 132 MixedItemSection byteData = file.getByteData(); 133 MixedItemSection wordData = file.getWordData(); 134 MixedItemSection typeLists = file.getTypeLists(); 135 StringIdsSection stringIds = file.getStringIds(); 136 137 typeIds.intern(thisClass); 138 139 if (!classData.isEmpty()) { 140 MixedItemSection classDataSection = file.getClassData(); 141 classDataSection.add(classData); 142 143 CstArray staticValues = classData.getStaticValuesConstant(); 144 if (staticValues != null) { 145 staticValuesItem = 146 byteData.intern(new EncodedArrayItem(staticValues)); 147 } 148 } 149 150 if (superclass != null) { 151 typeIds.intern(superclass); 152 } 153 154 if (interfaces != null) { 155 interfaces = typeLists.intern(interfaces); 156 } 157 158 if (sourceFile != null) { 159 stringIds.intern(sourceFile); 160 } 161 162 if (! annotationsDirectory.isEmpty()) { 163 if (annotationsDirectory.isInternable()) { 164 annotationsDirectory = wordData.intern(annotationsDirectory); 165 } else { 166 wordData.add(annotationsDirectory); 167 } 168 } 169 } 170 171 /** {@inheritDoc} */ 172 @Override writeTo(DexFile file, AnnotatedOutput out)173 public void writeTo(DexFile file, AnnotatedOutput out) { 174 boolean annotates = out.annotates(); 175 TypeIdsSection typeIds = file.getTypeIds(); 176 int classIdx = typeIds.indexOf(thisClass); 177 int superIdx = (superclass == null) ? -1 : 178 typeIds.indexOf(superclass); 179 int interOff = OffsettedItem.getAbsoluteOffsetOr0(interfaces); 180 int annoOff = annotationsDirectory.isEmpty() ? 0 : 181 annotationsDirectory.getAbsoluteOffset(); 182 int sourceFileIdx = (sourceFile == null) ? -1 : 183 file.getStringIds().indexOf(sourceFile); 184 int dataOff = classData.isEmpty()? 0 : classData.getAbsoluteOffset(); 185 int staticValuesOff = 186 OffsettedItem.getAbsoluteOffsetOr0(staticValuesItem); 187 188 if (annotates) { 189 out.annotate(0, indexString() + ' ' + thisClass.toHuman()); 190 out.annotate(4, " class_idx: " + Hex.u4(classIdx)); 191 out.annotate(4, " access_flags: " + 192 AccessFlags.classString(accessFlags)); 193 out.annotate(4, " superclass_idx: " + Hex.u4(superIdx) + 194 " // " + ((superclass == null) ? "<none>" : 195 superclass.toHuman())); 196 out.annotate(4, " interfaces_off: " + Hex.u4(interOff)); 197 if (interOff != 0) { 198 TypeList list = interfaces.getList(); 199 int sz = list.size(); 200 for (int i = 0; i < sz; i++) { 201 out.annotate(0, " " + list.getType(i).toHuman()); 202 } 203 } 204 out.annotate(4, " source_file_idx: " + Hex.u4(sourceFileIdx) + 205 " // " + ((sourceFile == null) ? "<none>" : 206 sourceFile.toHuman())); 207 out.annotate(4, " annotations_off: " + Hex.u4(annoOff)); 208 out.annotate(4, " class_data_off: " + Hex.u4(dataOff)); 209 out.annotate(4, " static_values_off: " + 210 Hex.u4(staticValuesOff)); 211 } 212 213 out.writeInt(classIdx); 214 out.writeInt(accessFlags); 215 out.writeInt(superIdx); 216 out.writeInt(interOff); 217 out.writeInt(sourceFileIdx); 218 out.writeInt(annoOff); 219 out.writeInt(dataOff); 220 out.writeInt(staticValuesOff); 221 } 222 223 /** 224 * Gets the constant corresponding to this class. 225 * 226 * @return {@code non-null;} the constant 227 */ getThisClass()228 public CstType getThisClass() { 229 return thisClass; 230 } 231 232 /** 233 * Gets the access flags. 234 * 235 * @return the access flags 236 */ getAccessFlags()237 public int getAccessFlags() { 238 return accessFlags; 239 } 240 241 /** 242 * Gets the superclass. 243 * 244 * @return {@code null-ok;} the superclass or {@code null} if 245 * this class is a/the root class 246 */ getSuperclass()247 public CstType getSuperclass() { 248 return superclass; 249 } 250 251 /** 252 * Gets the list of interfaces implemented. 253 * 254 * @return {@code non-null;} the interfaces list 255 */ getInterfaces()256 public TypeList getInterfaces() { 257 if (interfaces == null) { 258 return StdTypeList.EMPTY; 259 } 260 261 return interfaces.getList(); 262 } 263 264 /** 265 * Gets the source file name. 266 * 267 * @return {@code null-ok;} the source file name or {@code null} if unknown 268 */ getSourceFile()269 public CstUtf8 getSourceFile() { 270 return sourceFile; 271 } 272 273 /** 274 * Adds a static field. 275 * 276 * @param field {@code non-null;} the field to add 277 * @param value {@code null-ok;} initial value for the field, if any 278 */ addStaticField(EncodedField field, Constant value)279 public void addStaticField(EncodedField field, Constant value) { 280 classData.addStaticField(field, value); 281 } 282 283 /** 284 * Adds an instance field. 285 * 286 * @param field {@code non-null;} the field to add 287 */ addInstanceField(EncodedField field)288 public void addInstanceField(EncodedField field) { 289 classData.addInstanceField(field); 290 } 291 292 /** 293 * Adds a direct ({@code static} and/or {@code private}) method. 294 * 295 * @param method {@code non-null;} the method to add 296 */ addDirectMethod(EncodedMethod method)297 public void addDirectMethod(EncodedMethod method) { 298 classData.addDirectMethod(method); 299 } 300 301 /** 302 * Adds a virtual method. 303 * 304 * @param method {@code non-null;} the method to add 305 */ addVirtualMethod(EncodedMethod method)306 public void addVirtualMethod(EncodedMethod method) { 307 classData.addVirtualMethod(method); 308 } 309 310 /** 311 * Gets all the methods in this class. The returned list is not linked 312 * in any way to the underlying lists contained in this instance, but 313 * the objects contained in the list are shared. 314 * 315 * @return {@code non-null;} list of all methods 316 */ getMethods()317 public ArrayList<EncodedMethod> getMethods() { 318 return classData.getMethods(); 319 } 320 321 /** 322 * Sets the direct annotations on this class. These are annotations 323 * made on the class, per se, as opposed to on one of its members. 324 * It is only valid to call this method at most once per instance. 325 * 326 * @param annotations {@code non-null;} annotations to set for this class 327 */ setClassAnnotations(Annotations annotations)328 public void setClassAnnotations(Annotations annotations) { 329 annotationsDirectory.setClassAnnotations(annotations); 330 } 331 332 /** 333 * Adds a field annotations item to this class. 334 * 335 * @param field {@code non-null;} field in question 336 * @param annotations {@code non-null;} associated annotations to add 337 */ addFieldAnnotations(CstFieldRef field, Annotations annotations)338 public void addFieldAnnotations(CstFieldRef field, 339 Annotations annotations) { 340 annotationsDirectory.addFieldAnnotations(field, annotations); 341 } 342 343 /** 344 * Adds a method annotations item to this class. 345 * 346 * @param method {@code non-null;} method in question 347 * @param annotations {@code non-null;} associated annotations to add 348 */ addMethodAnnotations(CstMethodRef method, Annotations annotations)349 public void addMethodAnnotations(CstMethodRef method, 350 Annotations annotations) { 351 annotationsDirectory.addMethodAnnotations(method, annotations); 352 } 353 354 /** 355 * Adds a parameter annotations item to this class. 356 * 357 * @param method {@code non-null;} method in question 358 * @param list {@code non-null;} associated list of annotation sets to add 359 */ addParameterAnnotations(CstMethodRef method, AnnotationsList list)360 public void addParameterAnnotations(CstMethodRef method, 361 AnnotationsList list) { 362 annotationsDirectory.addParameterAnnotations(method, list); 363 } 364 365 /** 366 * Gets the method annotations for a given method, if any. This is 367 * meant for use by debugging / dumping code. 368 * 369 * @param method {@code non-null;} the method 370 * @return {@code null-ok;} the method annotations, if any 371 */ getMethodAnnotations(CstMethodRef method)372 public Annotations getMethodAnnotations(CstMethodRef method) { 373 return annotationsDirectory.getMethodAnnotations(method); 374 } 375 376 /** 377 * Gets the parameter annotations for a given method, if any. This is 378 * meant for use by debugging / dumping code. 379 * 380 * @param method {@code non-null;} the method 381 * @return {@code null-ok;} the parameter annotations, if any 382 */ getParameterAnnotations(CstMethodRef method)383 public AnnotationsList getParameterAnnotations(CstMethodRef method) { 384 return annotationsDirectory.getParameterAnnotations(method); 385 } 386 387 /** 388 * Prints out the contents of this instance, in a debugging-friendly 389 * way. 390 * 391 * @param out {@code non-null;} where to output to 392 * @param verbose whether to be verbose with the output 393 */ debugPrint(Writer out, boolean verbose)394 public void debugPrint(Writer out, boolean verbose) { 395 PrintWriter pw = Writers.printWriterFor(out); 396 397 pw.println(getClass().getName() + " {"); 398 pw.println(" accessFlags: " + Hex.u2(accessFlags)); 399 pw.println(" superclass: " + superclass); 400 pw.println(" interfaces: " + 401 ((interfaces == null) ? "<none>" : interfaces)); 402 pw.println(" sourceFile: " + 403 ((sourceFile == null) ? "<none>" : sourceFile.toQuoted())); 404 405 classData.debugPrint(out, verbose); 406 annotationsDirectory.debugPrint(pw); 407 408 pw.println("}"); 409 } 410 } 411