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.generic;
020
021import java.util.Objects;
022
023import org.apache.bcel.Const;
024
025/**
026 * Wrapper class for push operations, which are implemented either as BIPUSH, LDC or xCONST_n instructions.
027 */
028public final class PUSH implements CompoundInstruction, VariableLengthInstruction, InstructionConstants {
029
030    private final Instruction instruction;
031
032    /**
033     * Pushes an array type constant, for example {@code int[].class}, {@code String[].class}, and so on.
034     *
035     * @param cp generated constant pool.
036     * @param value to push.
037     * @since 6.7.0
038     */
039    public PUSH(final ConstantPoolGen cp, final ArrayType value) {
040        if (value == null) {
041            instruction = InstructionConst.ACONST_NULL;
042        } else {
043            instruction = new LDC(cp.addArrayClass(value));
044        }
045    }
046
047   /**
048     * Constructs a PUSH for a boolean value.
049     *
050     * @param cp Constant pool.
051     * @param value to push.
052     */
053    public PUSH(final ConstantPoolGen cp, final boolean value) {
054        Objects.requireNonNull(cp, "cp");
055        instruction = InstructionConst.getInstruction(Const.ICONST_0 + (value ? 1 : 0));
056    }
057
058    /**
059     * Constructs a PUSH for a Boolean value.
060     *
061     * @param cp Constant pool.
062     * @param value to push.
063     */
064    public PUSH(final ConstantPoolGen cp, final Boolean value) {
065        this(cp, value.booleanValue());
066    }
067
068    /**
069     * creates a push object from a Character value. Warning: Make sure not to attempt to allow autoboxing to create this
070     * value parameter, as an alternative constructor will be called
071     *
072     * @param cp Constant pool.
073     * @param value to push.
074     */
075    public PUSH(final ConstantPoolGen cp, final Character value) {
076        this(cp, value.charValue());
077    }
078
079    /**
080     * Constructs a PUSH for a double value.
081     *
082     * @param cp Constant pool.
083     * @param value to push.
084     */
085    public PUSH(final ConstantPoolGen cp, final double value) {
086        if (value == 0.0) {
087            instruction = InstructionConst.DCONST_0;
088        } else if (value == 1.0) {
089            instruction = InstructionConst.DCONST_1;
090        } else {
091            instruction = new LDC2_W(cp.addDouble(value));
092        }
093    }
094
095    /**
096     * Constructs a PUSH for a float value.
097     *
098     * @param cp Constant pool.
099     * @param value to push.
100     */
101    public PUSH(final ConstantPoolGen cp, final float value) {
102        if (value == 0.0) {
103            instruction = InstructionConst.FCONST_0;
104        } else if (value == 1.0) {
105            instruction = InstructionConst.FCONST_1;
106        } else if (value == 2.0) {
107            instruction = InstructionConst.FCONST_2;
108        } else {
109            instruction = new LDC(cp.addFloat(value));
110        }
111    }
112
113    /**
114     * This constructor also applies for values of type short, char, byte
115     *
116     * @param cp Constant pool.
117     * @param value to push.
118     */
119    public PUSH(final ConstantPoolGen cp, final int value) {
120        if (value >= -1 && value <= 5) {
121            instruction = InstructionConst.getInstruction(Const.ICONST_0 + value);
122        } else if (Instruction.isValidByte(value)) {
123            instruction = new BIPUSH((byte) value);
124        } else if (Instruction.isValidShort(value)) {
125            instruction = new SIPUSH((short) value);
126        } else {
127            instruction = new LDC(cp.addInteger(value));
128        }
129    }
130
131    /**
132     * Constructs a PUSH for a long value.
133     *
134     * @param cp Constant pool.
135     * @param value to push.
136     */
137    public PUSH(final ConstantPoolGen cp, final long value) {
138        if (value == 0) {
139            instruction = InstructionConst.LCONST_0;
140        } else if (value == 1) {
141            instruction = InstructionConst.LCONST_1;
142        } else {
143            instruction = new LDC2_W(cp.addLong(value));
144        }
145    }
146
147    /**
148     * Constructs a PUSH for a Number value.
149     *
150     * @param cp Constant pool.
151     * @param value to push.
152     */
153    public PUSH(final ConstantPoolGen cp, final Number value) {
154        if (value instanceof Integer || value instanceof Short || value instanceof Byte) {
155            instruction = new PUSH(cp, value.intValue()).instruction;
156        } else if (value instanceof Double) {
157            instruction = new PUSH(cp, value.doubleValue()).instruction;
158        } else if (value instanceof Float) {
159            instruction = new PUSH(cp, value.floatValue()).instruction;
160        } else if (value instanceof Long) {
161            instruction = new PUSH(cp, value.longValue()).instruction;
162        } else {
163            throw new ClassGenException("What's this: " + value);
164        }
165    }
166
167    /**
168     * Constructs a PUSH for an ObjectType value.
169     *
170     * @param cp The constant pool.
171     * @param value to push.
172     * @since 6.0
173     */
174    public PUSH(final ConstantPoolGen cp, final ObjectType value) {
175        if (value == null) {
176            instruction = InstructionConst.ACONST_NULL;
177        } else {
178            instruction = new LDC(cp.addClass(value));
179        }
180    }
181
182    /**
183     * Constructs a PUSH for a String value.
184     *
185     * @param cp Constant pool.
186     * @param value to push.
187     */
188    public PUSH(final ConstantPoolGen cp, final String value) {
189        if (value == null) {
190            instruction = InstructionConst.ACONST_NULL;
191        } else {
192            instruction = new LDC(cp.addString(value));
193        }
194    }
195
196    /**
197     * Gets the instruction.
198     *
199     * @return The instruction.
200     */
201    public Instruction getInstruction() {
202        return instruction;
203    }
204
205    @Override
206    public InstructionList getInstructionList() {
207        return new InstructionList(instruction);
208    }
209
210    /**
211     * @return mnemonic for instruction.
212     */
213    @Override
214    public String toString() {
215        return instruction + " (PUSH)";
216    }
217}