001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   https://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.bcel.classfile;
020
021import java.io.DataInput;
022import java.io.DataInputStream;
023import java.io.DataOutputStream;
024import java.io.IOException;
025import java.util.Arrays;
026
027import org.apache.bcel.util.Args;
028
029/**
030 * Abstract superclass for fields and methods.
031 */
032public abstract class FieldOrMethod extends AccessFlags implements Cloneable, Node {
033
034    /**
035     * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter.
036     */
037    @java.lang.Deprecated
038    protected int name_index; // Points to field name in constant pool
039
040    /**
041     * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter.
042     */
043    @java.lang.Deprecated
044    protected int signature_index; // Points to encoded signature
045
046    /**
047     * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter.
048     */
049    @java.lang.Deprecated
050    protected Attribute[] attributes; // Collection of attributes
051
052    /**
053     * @deprecated (since 6.0) will be removed (not needed)
054     */
055    @java.lang.Deprecated
056    protected int attributes_count; // No. of attributes
057
058    // @since 6.0
059    private AnnotationEntry[] annotationEntries; // annotations defined on the field or method
060
061    /**
062     * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter.
063     */
064    @java.lang.Deprecated
065    protected ConstantPool constant_pool;
066
067    private String signatureAttributeString;
068    private boolean searchedForSignatureAttribute;
069
070    /**
071     * Constructs an empty field or method whose properties must be set before use.
072     */
073    FieldOrMethod() {
074    }
075
076    /**
077     * Constructs a field or method from a data input using standard Code headers.
078     *
079     * @param file Input stream.
080     * @param constantPool The constant pool.
081     * @throws IOException Thrown if an I/O error occurs.
082     * @throws ClassFormatException Thrown if the field or method, or one of its attributes, is malformed.
083     */
084    protected FieldOrMethod(final DataInput file, final ConstantPool constantPool) throws IOException {
085        this(file, constantPool, false);
086    }
087
088    /**
089     * Constructs a field or method from a data input using the selected Code header layout.
090     *
091     * @param file Input stream.
092     * @param constantPool The constant pool.
093     * @param compactCodeHeader Whether to read any Code attributes encountered with compact four-byte headers ({@code true})
094     *        or standard eight-byte headers ({@code false}).
095     * @throws IOException Thrown if an I/O error occurs.
096     * @throws ClassFormatException Thrown if the field or method, or one of its attributes, is malformed.
097     * @see Attribute#readAttribute(DataInput, ConstantPool, boolean)
098     */
099    FieldOrMethod(final DataInput file, final ConstantPool constantPool, final boolean compactCodeHeader) throws IOException {
100        this(file.readUnsignedShort(), file.readUnsignedShort(), file.readUnsignedShort(), null, constantPool);
101        final int attributesCount = file.readUnsignedShort();
102        attributes = new Attribute[attributesCount];
103        for (int i = 0; i < attributesCount; i++) {
104            attributes[i] = Attribute.readAttribute(file, constantPool, compactCodeHeader);
105        }
106        this.attributes_count = attributesCount; // init deprecated field
107    }
108
109    /**
110     * Constructs a field or method from a data input stream using standard Code headers.
111     *
112     * @param file Input stream.
113     * @param constantPool The constant pool.
114     * @throws IOException Thrown if an I/O error occurs.
115     * @throws ClassFormatException Thrown if the field or method, or one of its attributes, is malformed.
116     * @deprecated (6.0) Use {@link #FieldOrMethod(java.io.DataInput, ConstantPool)} instead.
117     */
118    @java.lang.Deprecated
119    protected FieldOrMethod(final DataInputStream file, final ConstantPool constantPool) throws IOException {
120        this((DataInput) file, constantPool);
121    }
122
123    /**
124     * Initialize from another object. Note that both objects use the same references (shallow copy). Use clone() for a
125     * physical copy.
126     *
127     * @param c Source to copy.
128     */
129    protected FieldOrMethod(final FieldOrMethod c) {
130        this(c.getAccessFlags(), c.getNameIndex(), c.getSignatureIndex(), c.getAttributes(), c.getConstantPool());
131    }
132
133    /**
134     * Constructs a FieldOrMethod.
135     *
136     * @param accessFlags Access rights of the field or method.
137     * @param nameIndex Points to the field or method name in the constant pool.
138     * @param signatureIndex Points to encoded signature.
139     * @param attributes Collection of attributes.
140     * @param constantPool Array of constants.
141     */
142    protected FieldOrMethod(final int accessFlags, final int nameIndex, final int signatureIndex, final Attribute[] attributes,
143        final ConstantPool constantPool) {
144        super(accessFlags);
145        this.name_index = nameIndex;
146        this.signature_index = signatureIndex;
147        this.constant_pool = constantPool;
148        setAttributes(attributes);
149    }
150
151    /**
152     * Creates a deep copy of this field.
153     *
154     * @param constantPool The constant pool.
155     * @return deep copy of this field.
156     */
157    protected FieldOrMethod copy_(final ConstantPool constantPool) {
158        try {
159            final FieldOrMethod c = (FieldOrMethod) clone();
160            c.constant_pool = constantPool;
161            c.attributes = new Attribute[attributes.length];
162            c.attributes_count = attributes_count; // init deprecated field
163            Arrays.setAll(c.attributes, i -> attributes[i].copy(constantPool));
164            return c;
165        } catch (final CloneNotSupportedException e) {
166            throw new UnsupportedOperationException(e);
167        }
168    }
169
170    /**
171     * Dumps object to file stream on binary format.
172     *
173     * @param file Output file stream.
174     * @throws IOException Thrown if an I/O error occurs.
175     */
176    public final void dump(final DataOutputStream file) throws IOException {
177        dump(file, attributes);
178    }
179
180    private void dump(final DataOutputStream file, final Attribute[] outputAttributes) throws IOException {
181        file.writeShort(super.getAccessFlags());
182        file.writeShort(name_index);
183        file.writeShort(signature_index);
184        file.writeShort(Args.requireU2(attributes_count, "attributes_count"));
185        for (final Attribute attribute : outputAttributes) {
186            attribute.dump(file);
187        }
188    }
189
190    /**
191     * Dumps a member with the Code encoding selected by its enclosing class, without modifying its attributes.
192     *
193     * @param file Output stream.
194     * @param compactCodeHeader Whether to write Code attributes with compact four-byte headers ({@code true})
195     *        or standard eight-byte headers ({@code false}).
196     * @throws IOException Thrown if an I/O error occurs.
197     * @throws ClassFormatException Thrown if an attribute fails validation.
198     */
199    final void dump(final DataOutputStream file, final boolean compactCodeHeader) throws IOException {
200        final Attribute[] sourceAttributes = getAttributes();
201        Attribute[] outputAttributes = sourceAttributes;
202        for (int i = 0; i < sourceAttributes.length; i++) {
203            if (sourceAttributes[i] instanceof Code && ((Code) sourceAttributes[i]).isCompactCodeHeader() != compactCodeHeader) {
204                if (outputAttributes == sourceAttributes) {
205                    outputAttributes = sourceAttributes.clone();
206                }
207                final Code code = new Code((Code) sourceAttributes[i]);
208                code.setCompactCodeHeader(compactCodeHeader);
209                outputAttributes[i] = code;
210            }
211        }
212        dump(file, outputAttributes);
213    }
214
215    /**
216     * Gets annotations on the field or method.
217     *
218     * @return Annotations on the field or method.
219     * @since 6.0
220     */
221    public AnnotationEntry[] getAnnotationEntries() {
222        if (annotationEntries == null) {
223            annotationEntries = AnnotationEntry.createAnnotationEntries(getAttributes());
224        }
225
226        return annotationEntries;
227    }
228
229    /**
230     * Gets attribute for given tag.
231     *
232     * @param <T> The attribute type.
233     * @param tag The attribute tag.
234     * @return Attribute for given tag, null if not found.
235     * Refer to {@link org.apache.bcel.Const#ATTR_UNKNOWN} constants named ATTR_* for possible values.
236     * @since 6.10.0
237     */
238    @SuppressWarnings("unchecked")
239    public final <T extends Attribute> T getAttribute(final byte tag) {
240        for (final Attribute attribute : getAttributes()) {
241            if (attribute.getTag() == tag) {
242                return (T) attribute;
243            }
244        }
245        return null;
246    }
247
248    /**
249     * Gets the collection of object attributes.
250     *
251     * @return Collection of object attributes.
252     */
253    public final Attribute[] getAttributes() {
254        return attributes;
255    }
256
257    /**
258     * Gets the constant pool used by this object.
259     *
260     * @return Constant pool used by this object.
261     */
262    public final ConstantPool getConstantPool() {
263        return constant_pool;
264    }
265
266    /**
267     * Gets the contents of the signature attribute on this member. So where the 'regular' signature may be
268     * (Ljava/util/Vector;)V the signature attribute may in fact say 'Ljava/lang/Vector&lt;Ljava/lang/String&gt;;' Coded for
269     * performance - searches for the attribute only when requested - only searches for it once.
270     *
271     * @return The generic signature.
272     * @since 6.0
273     */
274    public final String getGenericSignature() {
275        if (!searchedForSignatureAttribute) {
276            boolean found = false;
277            for (int i = 0; !found && i < attributes.length; i++) {
278                if (attributes[i] instanceof Signature) {
279                    signatureAttributeString = ((Signature) attributes[i]).getSignature();
280                    found = true;
281                }
282            }
283            searchedForSignatureAttribute = true;
284        }
285        return signatureAttributeString;
286    }
287
288    /**
289     * Gets the name of object.
290     *
291     * @return Name of object, that is, method name or field name.
292     */
293    public final String getName() {
294        return constant_pool.getConstantUtf8(name_index).getBytes();
295    }
296
297    /**
298     * Gets the index in constant pool of object's name.
299     *
300     * @return Index in constant pool of object's name.
301     */
302    public final int getNameIndex() {
303        return name_index;
304    }
305
306    /**
307     * Gets the string representation of object's type signature.
308     *
309     * @return String representation of object's type signature (Java style).
310     */
311    public final String getSignature() {
312        return constant_pool.getConstantUtf8(signature_index).getBytes();
313    }
314
315    /**
316     * Gets the index in constant pool of field signature.
317     *
318     * @return Index in constant pool of field signature.
319     */
320    public final int getSignatureIndex() {
321        return signature_index;
322    }
323
324    /**
325     * Sets the collection of object attributes.
326     *
327     * @param attributes Collection of object attributes.
328     */
329    public final void setAttributes(final Attribute[] attributes) {
330        this.attributes = attributes != null ? attributes : Attribute.EMPTY_ARRAY;
331        this.attributes_count = this.attributes.length; // init deprecated field
332    }
333
334    /**
335     * Sets the constant pool to be used for this object.
336     *
337     * @param constantPool Constant pool to be used for this object.
338     */
339    public final void setConstantPool(final ConstantPool constantPool) {
340        this.constant_pool = constantPool;
341    }
342
343    /**
344     * Sets the index in constant pool of object's name.
345     *
346     * @param nameIndex Index in constant pool of object's name.
347     */
348    public final void setNameIndex(final int nameIndex) {
349        this.name_index = nameIndex;
350    }
351
352    /**
353     * Sets the index in constant pool of field signature.
354     *
355     * @param signatureIndex Index in constant pool of field signature.
356     */
357    public final void setSignatureIndex(final int signatureIndex) {
358        this.signature_index = signatureIndex;
359    }
360}