/* * 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.CstType; import com.android.dx.rop.cst.CstUtf8; 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); CstUtf8 name = (CstUtf8) pool.get(nameIdx); CstUtf8 desc = (CstUtf8) 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; } }