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.cf.direct; 18 19 import com.android.dx.cf.iface.AttributeList; 20 import com.android.dx.cf.iface.Member; 21 import com.android.dx.cf.iface.ParseException; 22 import com.android.dx.cf.iface.ParseObserver; 23 import com.android.dx.cf.iface.StdAttributeList; 24 import com.android.dx.rop.cst.ConstantPool; 25 import com.android.dx.rop.cst.CstNat; 26 import com.android.dx.rop.cst.CstString; 27 import com.android.dx.rop.cst.CstType; 28 import com.android.dx.util.ByteArray; 29 import com.android.dx.util.Hex; 30 31 /** 32 * Parser for lists of class file members (that is, fields and methods). 33 */ 34 abstract /*package*/ class MemberListParser { 35 /** {@code non-null;} the class file to parse from */ 36 private final DirectClassFile cf; 37 38 /** {@code non-null;} class being defined */ 39 private final CstType definer; 40 41 /** offset in the byte array of the classfile to the start of the list */ 42 private final int offset; 43 44 /** {@code non-null;} attribute factory to use */ 45 private final AttributeFactory attributeFactory; 46 47 /** {@code >= -1;} the end offset of this list in the byte array of the 48 * classfile, or {@code -1} if not yet parsed */ 49 private int endOffset; 50 51 /** {@code null-ok;} parse observer, if any */ 52 private ParseObserver observer; 53 54 /** 55 * Constructs an instance. 56 * 57 * @param cf {@code non-null;} the class file to parse from 58 * @param definer {@code non-null;} class being defined 59 * @param offset offset in {@code bytes} to the start of the list 60 * @param attributeFactory {@code non-null;} attribute factory to use 61 */ MemberListParser(DirectClassFile cf, CstType definer, int offset, AttributeFactory attributeFactory)62 public MemberListParser(DirectClassFile cf, CstType definer, 63 int offset, AttributeFactory attributeFactory) { 64 if (cf == null) { 65 throw new NullPointerException("cf == null"); 66 } 67 68 if (offset < 0) { 69 throw new IllegalArgumentException("offset < 0"); 70 } 71 72 if (attributeFactory == null) { 73 throw new NullPointerException("attributeFactory == null"); 74 } 75 76 this.cf = cf; 77 this.definer = definer; 78 this.offset = offset; 79 this.attributeFactory = attributeFactory; 80 this.endOffset = -1; 81 } 82 83 /** 84 * Gets the end offset of this constant pool in the {@code byte[]} 85 * which it came from. 86 * 87 * @return {@code >= 0;} the end offset 88 */ getEndOffset()89 public int getEndOffset() { 90 parseIfNecessary(); 91 return endOffset; 92 } 93 94 /** 95 * Sets the parse observer for this instance. 96 * 97 * @param observer {@code null-ok;} the observer 98 */ setObserver(ParseObserver observer)99 public final void setObserver(ParseObserver observer) { 100 this.observer = observer; 101 } 102 103 /** 104 * Runs {@link #parse} if it has not yet been run successfully. 105 */ parseIfNecessary()106 protected final void parseIfNecessary() { 107 if (endOffset < 0) { 108 parse(); 109 } 110 } 111 112 /** 113 * Gets the count of elements in the list. 114 * 115 * @return the count 116 */ getCount()117 protected final int getCount() { 118 ByteArray bytes = cf.getBytes(); 119 return bytes.getUnsignedShort(offset); 120 } 121 122 /** 123 * Gets the class file being defined. 124 * 125 * @return {@code non-null;} the class 126 */ getDefiner()127 protected final CstType getDefiner() { 128 return definer; 129 } 130 131 /** 132 * Gets the human-oriented name for what this instance is parsing. 133 * Subclasses must override this method. 134 * 135 * @return {@code non-null;} the human oriented name 136 */ humanName()137 protected abstract String humanName(); 138 139 /** 140 * Gets the human-oriented string for the given access flags. 141 * Subclasses must override this method. 142 * 143 * @param accessFlags the flags 144 * @return {@code non-null;} the string form 145 */ humanAccessFlags(int accessFlags)146 protected abstract String humanAccessFlags(int accessFlags); 147 148 /** 149 * Gets the {@code CTX_*} constant to use when parsing attributes. 150 * Subclasses must override this method. 151 * 152 * @return {@code non-null;} the human oriented name 153 */ getAttributeContext()154 protected abstract int getAttributeContext(); 155 156 /** 157 * Sets an element in the list. Subclasses must override this method. 158 * 159 * @param n which element 160 * @param accessFlags the {@code access_flags} 161 * @param nat the interpreted name and type (based on the two 162 * {@code *_index} fields) 163 * @param attributes list of parsed attributes 164 * @return {@code non-null;} the constructed member 165 */ set(int n, int accessFlags, CstNat nat, AttributeList attributes)166 protected abstract Member set(int n, int accessFlags, CstNat nat, 167 AttributeList attributes); 168 169 /** 170 * Does the actual parsing. 171 */ parse()172 private void parse() { 173 int attributeContext = getAttributeContext(); 174 int count = getCount(); 175 int at = offset + 2; // Skip the count. 176 177 ByteArray bytes = cf.getBytes(); 178 ConstantPool pool = cf.getConstantPool(); 179 180 if (observer != null) { 181 observer.parsed(bytes, offset, 2, 182 humanName() + "s_count: " + Hex.u2(count)); 183 } 184 185 for (int i = 0; i < count; i++) { 186 try { 187 int accessFlags = bytes.getUnsignedShort(at); 188 int nameIdx = bytes.getUnsignedShort(at + 2); 189 int descIdx = bytes.getUnsignedShort(at + 4); 190 CstString name = (CstString) pool.get(nameIdx); 191 CstString desc = (CstString) pool.get(descIdx); 192 193 if (observer != null) { 194 observer.startParsingMember(bytes, at, name.getString(), 195 desc.getString()); 196 observer.parsed(bytes, at, 0, "\n" + humanName() + 197 "s[" + i + "]:\n"); 198 observer.changeIndent(1); 199 observer.parsed(bytes, at, 2, 200 "access_flags: " + 201 humanAccessFlags(accessFlags)); 202 observer.parsed(bytes, at + 2, 2, 203 "name: " + name.toHuman()); 204 observer.parsed(bytes, at + 4, 2, 205 "descriptor: " + desc.toHuman()); 206 } 207 208 at += 6; 209 AttributeListParser parser = 210 new AttributeListParser(cf, attributeContext, at, 211 attributeFactory); 212 parser.setObserver(observer); 213 at = parser.getEndOffset(); 214 StdAttributeList attributes = parser.getList(); 215 attributes.setImmutable(); 216 CstNat nat = new CstNat(name, desc); 217 Member member = set(i, accessFlags, nat, attributes); 218 219 if (observer != null) { 220 observer.changeIndent(-1); 221 observer.parsed(bytes, at, 0, "end " + humanName() + 222 "s[" + i + "]\n"); 223 observer.endParsingMember(bytes, at, name.getString(), 224 desc.getString(), member); 225 } 226 } catch (ParseException ex) { 227 ex.addContext("...while parsing " + humanName() + "s[" + i + 228 "]"); 229 throw ex; 230 } catch (RuntimeException ex) { 231 ParseException pe = new ParseException(ex); 232 pe.addContext("...while parsing " + humanName() + "s[" + i + 233 "]"); 234 throw pe; 235 } 236 } 237 238 endOffset = at; 239 } 240 } 241