blob: 605bab89629c38eb1f492386e7bb413f4acfc37a [file] [log] [blame]
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.dx.cf.direct;
import com.android.dx.cf.iface.AttributeList;
import com.android.dx.cf.iface.Member;
import com.android.dx.cf.iface.ParseException;
import com.android.dx.cf.iface.ParseObserver;
import com.android.dx.cf.iface.StdAttributeList;
import com.android.dx.rop.cst.ConstantPool;
import com.android.dx.rop.cst.CstNat;
import com.android.dx.rop.cst.CstString;
import com.android.dx.rop.cst.CstType;
import com.android.dx.util.ByteArray;
import com.android.dx.util.Hex;
/**
* Parser for lists of class file members (that is, fields and methods).
*/
abstract /*package*/ class MemberListParser {
/** {@code non-null;} the class file to parse from */
private final DirectClassFile cf;
/** {@code non-null;} class being defined */
private final CstType definer;
/** offset in the byte array of the classfile to the start of the list */
private final int offset;
/** {@code non-null;} attribute factory to use */
private final AttributeFactory attributeFactory;
/** {@code >= -1;} the end offset of this list in the byte array of the
* classfile, or {@code -1} if not yet parsed */
private int endOffset;
/** {@code null-ok;} parse observer, if any */
private ParseObserver observer;
/**
* Constructs an instance.
*
* @param cf {@code non-null;} the class file to parse from
* @param definer {@code non-null;} class being defined
* @param offset offset in {@code bytes} to the start of the list
* @param attributeFactory {@code non-null;} attribute factory to use
*/
public MemberListParser(DirectClassFile cf, CstType definer,
int offset, AttributeFactory attributeFactory) {
if (cf == null) {
throw new NullPointerException("cf == null");
}
if (offset < 0) {
throw new IllegalArgumentException("offset < 0");
}
if (attributeFactory == null) {
throw new NullPointerException("attributeFactory == null");
}
this.cf = cf;
this.definer = definer;
this.offset = offset;
this.attributeFactory = attributeFactory;
this.endOffset = -1;
}
/**
* Gets the end offset of this constant pool in the {@code byte[]}
* which it came from.
*
* @return {@code >= 0;} the end offset
*/
public int getEndOffset() {
parseIfNecessary();
return endOffset;
}
/**
* Sets the parse observer for this instance.
*
* @param observer {@code null-ok;} the observer
*/
public final void setObserver(ParseObserver observer) {
this.observer = observer;
}
/**
* Runs {@link #parse} if it has not yet been run successfully.
*/
protected final void parseIfNecessary() {
if (endOffset < 0) {
parse();
}
}
/**
* Gets the count of elements in the list.
*
* @return the count
*/
protected final int getCount() {
ByteArray bytes = cf.getBytes();
return bytes.getUnsignedShort(offset);
}
/**
* Gets the class file being defined.
*
* @return {@code non-null;} the class
*/
protected final CstType getDefiner() {
return definer;
}
/**
* Gets the human-oriented name for what this instance is parsing.
* Subclasses must override this method.
*
* @return {@code non-null;} the human oriented name
*/
protected abstract String humanName();
/**
* Gets the human-oriented string for the given access flags.
* Subclasses must override this method.
*
* @param accessFlags the flags
* @return {@code non-null;} the string form
*/
protected abstract String humanAccessFlags(int accessFlags);
/**
* Gets the {@code CTX_*} constant to use when parsing attributes.
* Subclasses must override this method.
*
* @return {@code non-null;} the human oriented name
*/
protected abstract int getAttributeContext();
/**
* Sets an element in the list. Subclasses must override this method.
*
* @param n which element
* @param accessFlags the {@code access_flags}
* @param nat the interpreted name and type (based on the two
* {@code *_index} fields)
* @param attributes list of parsed attributes
* @return {@code non-null;} the constructed member
*/
protected abstract Member set(int n, int accessFlags, CstNat nat,
AttributeList attributes);
/**
* Does the actual parsing.
*/
private void parse() {
int attributeContext = getAttributeContext();
int count = getCount();
int at = offset + 2; // Skip the count.
ByteArray bytes = cf.getBytes();
ConstantPool pool = cf.getConstantPool();
if (observer != null) {
observer.parsed(bytes, offset, 2,
humanName() + "s_count: " + Hex.u2(count));
}
for (int i = 0; i < count; i++) {
try {
int accessFlags = bytes.getUnsignedShort(at);
int nameIdx = bytes.getUnsignedShort(at + 2);
int descIdx = bytes.getUnsignedShort(at + 4);
CstString name = (CstString) pool.get(nameIdx);
CstString desc = (CstString) pool.get(descIdx);
if (observer != null) {
observer.startParsingMember(bytes, at, name.getString(),
desc.getString());
observer.parsed(bytes, at, 0, "\n" + humanName() +
"s[" + i + "]:\n");
observer.changeIndent(1);
observer.parsed(bytes, at, 2,
"access_flags: " +
humanAccessFlags(accessFlags));
observer.parsed(bytes, at + 2, 2,
"name: " + name.toHuman());
observer.parsed(bytes, at + 4, 2,
"descriptor: " + desc.toHuman());
}
at += 6;
AttributeListParser parser =
new AttributeListParser(cf, attributeContext, at,
attributeFactory);
parser.setObserver(observer);
at = parser.getEndOffset();
StdAttributeList attributes = parser.getList();
attributes.setImmutable();
CstNat nat = new CstNat(name, desc);
Member member = set(i, accessFlags, nat, attributes);
if (observer != null) {
observer.changeIndent(-1);
observer.parsed(bytes, at, 0, "end " + humanName() +
"s[" + i + "]\n");
observer.endParsingMember(bytes, at, name.getString(),
desc.getString(), member);
}
} catch (ParseException ex) {
ex.addContext("...while parsing " + humanName() + "s[" + i +
"]");
throw ex;
} catch (RuntimeException ex) {
ParseException pe = new ParseException(ex);
pe.addContext("...while parsing " + humanName() + "s[" + i +
"]");
throw pe;
}
}
endOffset = at;
}
}