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.file; 18 19 import com.android.dex.Leb128; 20 import com.android.dx.dex.code.DalvCode; 21 import com.android.dx.rop.code.AccessFlags; 22 import com.android.dx.rop.cst.CstMethodRef; 23 import com.android.dx.rop.cst.CstString; 24 import com.android.dx.rop.type.TypeList; 25 import com.android.dx.util.AnnotatedOutput; 26 import com.android.dx.util.Hex; 27 import java.io.PrintWriter; 28 29 /** 30 * Class that representats a method of a class. 31 */ 32 public final class EncodedMethod extends EncodedMember 33 implements Comparable<EncodedMethod> { 34 /** {@code non-null;} constant for the method */ 35 private final CstMethodRef method; 36 37 /** 38 * {@code null-ok;} code for the method, if the method is neither 39 * {@code abstract} nor {@code native} 40 */ 41 private final CodeItem code; 42 43 /** 44 * Constructs an instance. 45 * 46 * @param method {@code non-null;} constant for the method 47 * @param accessFlags access flags 48 * @param code {@code null-ok;} code for the method, if it is neither 49 * {@code abstract} nor {@code native} 50 * @param throwsList {@code non-null;} list of possibly-thrown exceptions, 51 * just used in generating debugging output (listings) 52 */ EncodedMethod(CstMethodRef method, int accessFlags, DalvCode code, TypeList throwsList)53 public EncodedMethod(CstMethodRef method, int accessFlags, 54 DalvCode code, TypeList throwsList) { 55 super(accessFlags); 56 57 if (method == null) { 58 throw new NullPointerException("method == null"); 59 } 60 61 this.method = method; 62 63 if (code == null) { 64 this.code = null; 65 } else { 66 boolean isStatic = (accessFlags & AccessFlags.ACC_STATIC) != 0; 67 this.code = new CodeItem(method, code, isStatic, throwsList); 68 } 69 } 70 71 /** {@inheritDoc} */ equals(Object other)72 public boolean equals(Object other) { 73 if (! (other instanceof EncodedMethod)) { 74 return false; 75 } 76 77 return compareTo((EncodedMethod) other) == 0; 78 } 79 80 /** 81 * {@inheritDoc} 82 * 83 * <p><b>Note:</b> This compares the method constants only, 84 * ignoring any associated code, because it should never be the 85 * case that two different items with the same method constant 86 * ever appear in the same list (or same file, even).</p> 87 */ compareTo(EncodedMethod other)88 public int compareTo(EncodedMethod other) { 89 return method.compareTo(other.method); 90 } 91 92 /** {@inheritDoc} */ 93 @Override toString()94 public String toString() { 95 StringBuffer sb = new StringBuffer(100); 96 97 sb.append(getClass().getName()); 98 sb.append('{'); 99 sb.append(Hex.u2(getAccessFlags())); 100 sb.append(' '); 101 sb.append(method); 102 103 if (code != null) { 104 sb.append(' '); 105 sb.append(code); 106 } 107 108 sb.append('}'); 109 110 return sb.toString(); 111 } 112 113 /** {@inheritDoc} */ 114 @Override addContents(DexFile file)115 public void addContents(DexFile file) { 116 MethodIdsSection methodIds = file.getMethodIds(); 117 MixedItemSection wordData = file.getWordData(); 118 119 methodIds.intern(method); 120 121 if (code != null) { 122 wordData.add(code); 123 } 124 } 125 126 /** {@inheritDoc} */ toHuman()127 public final String toHuman() { 128 return method.toHuman(); 129 } 130 131 /** {@inheritDoc} */ 132 @Override getName()133 public final CstString getName() { 134 return method.getNat().getName(); 135 } 136 137 /** {@inheritDoc} */ 138 @Override debugPrint(PrintWriter out, boolean verbose)139 public void debugPrint(PrintWriter out, boolean verbose) { 140 if (code == null) { 141 out.println(getRef().toHuman() + ": abstract or native"); 142 } else { 143 code.debugPrint(out, " ", verbose); 144 } 145 } 146 147 /** 148 * Gets the constant for the method. 149 * 150 * @return {@code non-null;} the constant 151 */ getRef()152 public final CstMethodRef getRef() { 153 return method; 154 } 155 156 /** {@inheritDoc} */ 157 @Override encode(DexFile file, AnnotatedOutput out, int lastIndex, int dumpSeq)158 public int encode(DexFile file, AnnotatedOutput out, 159 int lastIndex, int dumpSeq) { 160 int methodIdx = file.getMethodIds().indexOf(method); 161 int diff = methodIdx - lastIndex; 162 int accessFlags = getAccessFlags(); 163 int codeOff = OffsettedItem.getAbsoluteOffsetOr0(code); 164 boolean hasCode = (codeOff != 0); 165 boolean shouldHaveCode = (accessFlags & 166 (AccessFlags.ACC_ABSTRACT | AccessFlags.ACC_NATIVE)) == 0; 167 168 /* 169 * Verify that code appears if and only if a method is 170 * declared to have it. 171 */ 172 if (hasCode != shouldHaveCode) { 173 throw new UnsupportedOperationException( 174 "code vs. access_flags mismatch"); 175 } 176 177 if (out.annotates()) { 178 out.annotate(0, String.format(" [%x] %s", dumpSeq, 179 method.toHuman())); 180 out.annotate(Leb128.unsignedLeb128Size(diff), 181 " method_idx: " + Hex.u4(methodIdx)); 182 out.annotate(Leb128.unsignedLeb128Size(accessFlags), 183 " access_flags: " + 184 AccessFlags.methodString(accessFlags)); 185 out.annotate(Leb128.unsignedLeb128Size(codeOff), 186 " code_off: " + Hex.u4(codeOff)); 187 } 188 189 out.writeUleb128(diff); 190 out.writeUleb128(accessFlags); 191 out.writeUleb128(codeOff); 192 193 return methodIdx; 194 } 195 } 196