001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 *  Unless required by applicable law or agreed to in writing, software
012 *  distributed under the License is distributed on an "AS IS" BASIS,
013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 *  See the License for the specific language governing permissions and
015 *  limitations under the License.
016 */
017
018package org.apache.bcel.classfile;
019
020import java.io.DataInput;
021import java.io.DataOutputStream;
022import java.io.IOException;
023import java.util.Arrays;
024
025import org.apache.bcel.Const;
026import org.apache.commons.lang3.ArrayUtils;
027
028/**
029 * This class represents a bootstrap method attribute, i.e., the bootstrap method ref, the number of bootstrap arguments
030 * and an array of the bootstrap arguments.
031 *
032 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.23"> The class File Format :
033 *      The BootstrapMethods Attribute</a>
034 * @since 6.0
035 */
036public class BootstrapMethod implements Cloneable {
037
038    static final BootstrapMethod[] EMPTY_ARRAY = {};
039
040    /** Index of the CONSTANT_MethodHandle_info structure in the constant_pool table */
041    private int bootstrapMethodRef;
042
043    /** Array of references to the constant_pool table */
044    private int[] bootstrapArguments;
045
046    /**
047     * Initialize from another object.
048     *
049     * @param c Source to copy.
050     */
051    public BootstrapMethod(final BootstrapMethod c) {
052        this(c.getBootstrapMethodRef(), c.getBootstrapArguments());
053    }
054
055    /**
056     * Constructs object from input stream.
057     *
058     * @param input Input stream
059     * @throws IOException if an I/O error occurs.
060     */
061    BootstrapMethod(final DataInput input) throws IOException {
062        this(input.readUnsignedShort(), input.readUnsignedShort());
063
064        for (int i = 0; i < bootstrapArguments.length; i++) {
065            bootstrapArguments[i] = input.readUnsignedShort();
066        }
067    }
068
069    // helper method
070    private BootstrapMethod(final int bootstrapMethodRef, final int numBootstrapArguments) {
071        this(bootstrapMethodRef, new int[numBootstrapArguments]);
072    }
073
074    /**
075     * @param bootstrapMethodRef int index into constant_pool of CONSTANT_MethodHandle
076     * @param bootstrapArguments int[] indices into constant_pool of CONSTANT_[type]_info
077     */
078    public BootstrapMethod(final int bootstrapMethodRef, final int[] bootstrapArguments) {
079        this.bootstrapMethodRef = bootstrapMethodRef;
080        setBootstrapArguments(bootstrapArguments);
081    }
082
083    /**
084     * @return deep copy of this object
085     */
086    public BootstrapMethod copy() {
087        try {
088            return (BootstrapMethod) clone();
089        } catch (final CloneNotSupportedException ignore) {
090            // TODO should this throw?
091        }
092        return null;
093    }
094
095    /**
096     * Dump object to file stream in binary format.
097     *
098     * @param file Output file stream
099     * @throws IOException if an I/O error occurs.
100     */
101    public final void dump(final DataOutputStream file) throws IOException {
102        file.writeShort(bootstrapMethodRef);
103        file.writeShort(bootstrapArguments.length);
104        for (final int bootstrapArgument : bootstrapArguments) {
105            file.writeShort(bootstrapArgument);
106        }
107    }
108
109    /**
110     * @return int[] of bootstrap_method indices into constant_pool of CONSTANT_[type]_info
111     */
112    public int[] getBootstrapArguments() {
113        return bootstrapArguments;
114    }
115
116    /**
117     * @return index into constant_pool of bootstrap_method
118     */
119    public int getBootstrapMethodRef() {
120        return bootstrapMethodRef;
121    }
122
123    /**
124     * @return count of number of boostrap arguments
125     */
126    public int getNumBootstrapArguments() {
127        return bootstrapArguments.length;
128    }
129
130    /**
131     * @param bootstrapArguments int[] indices into constant_pool of CONSTANT_[type]_info
132     */
133    public void setBootstrapArguments(final int[] bootstrapArguments) {
134        this.bootstrapArguments = ArrayUtils.nullToEmpty(bootstrapArguments);
135    }
136
137    /**
138     * @param bootstrapMethodRef int index into constant_pool of CONSTANT_MethodHandle
139     */
140    public void setBootstrapMethodRef(final int bootstrapMethodRef) {
141        this.bootstrapMethodRef = bootstrapMethodRef;
142    }
143
144    /**
145     * @return String representation.
146     */
147    @Override
148    public final String toString() {
149        return "BootstrapMethod(" + bootstrapMethodRef + ", " + bootstrapArguments.length + ", " + Arrays.toString(bootstrapArguments) + ")";
150    }
151
152    /**
153     * @return Resolved string representation
154     */
155    public final String toString(final ConstantPool constantPool) {
156        final StringBuilder buf = new StringBuilder();
157        final String bootstrapMethodName = constantPool.constantToString(bootstrapMethodRef, Const.CONSTANT_MethodHandle);
158        buf.append(Utility.compactClassName(bootstrapMethodName, false));
159        final int bootstrapArgumentsLen = bootstrapArguments.length;
160        if (bootstrapArgumentsLen > 0) {
161            buf.append("\nMethod Arguments:");
162            for (int i = 0; i < bootstrapArgumentsLen; i++) {
163                buf.append("\n  ").append(i).append(": ");
164                buf.append(constantPool.constantToString(constantPool.getConstant(bootstrapArguments[i])));
165            }
166        }
167        return buf.toString();
168    }
169}