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