/*
* Copyright 2014 - 2018 Rafael Winterhalter
*
* 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 net.bytebuddy.implementation.bytecode;
import net.bytebuddy.description.type.
TypeDefinition;
import net.bytebuddy.implementation.
Implementation;
import net.bytebuddy.jar.asm.
MethodVisitor;
import net.bytebuddy.jar.asm.
Opcodes;
/**
* Duplicates a value that is lying on top of the stack.
*/
public enum
Duplication implements
StackManipulation {
/**
* A duplication of no values. This corresponds a no-op instruction.
*/
ZERO(
StackSize.
ZERO,
Opcodes.
NOP) {
@
Override
public
Size apply(
MethodVisitor methodVisitor,
Implementation.
Context implementationContext) {
return
size;
}
@
Override
public
StackManipulation flipOver(
TypeDefinition typeDefinition) {
throw new
IllegalStateException("Cannot flip zero value");
}
},
/**
* A duplication of a single-sized stack values.
*/
SINGLE(
StackSize.
SINGLE,
Opcodes.
DUP) {
@
Override
public
StackManipulation flipOver(
TypeDefinition typeDefinition) {
switch (
typeDefinition.
getStackSize()) {
case
SINGLE:
return
WithFlip.
SINGLE_SINGLE;
case
DOUBLE:
return
WithFlip.
SINGLE_DOUBLE;
default:
throw new
IllegalArgumentException("Cannot flip: " +
typeDefinition);
}
}
},
/**
* A duplication of a double-sized stack value.
*/
DOUBLE(
StackSize.
DOUBLE,
Opcodes.
DUP2) {
@
Override
public
StackManipulation flipOver(
TypeDefinition typeDefinition) {
switch (
typeDefinition.
getStackSize()) {
case
SINGLE:
return
WithFlip.
DOUBLE_SINGLE;
case
DOUBLE:
return
WithFlip.
DOUBLE_DOUBLE;
default:
throw new
IllegalArgumentException("Cannot flip: " +
typeDefinition);
}
}
};
/**
* The size representing the impact of applying the duplication onto the operand stack.
*/
protected final
Size size;
/**
* The opcode that represents the manipulation.
*/
private final int
opcode;
/**
* Creates a new duplication.
*
* @param stackSize The size representing the impact of applying the duplication onto the operand stack.
* @param opcode The opcode that represents the manipulation.
*/
Duplication(
StackSize stackSize, int
opcode) {
size =
stackSize.
toIncreasingSize();
this.
opcode =
opcode;
}
/**
* Duplicates a value given its type.
*
* @param typeDefinition The type to be duplicated.
* @return A stack manipulation that duplicates the given type.
*/
public static
Duplication of(
TypeDefinition typeDefinition) {
switch (
typeDefinition.
getStackSize()) {
case
SINGLE:
return
SINGLE;
case
DOUBLE:
return
DOUBLE;
case
ZERO:
return
ZERO;
default:
throw new
AssertionError("Unexpected type: " +
typeDefinition);
}
}
/**
* Creates a duplication that flips the stack's top value over the second stack element.
*
* @param typeDefinition The type of the second element on the operand stack.
* @return A stack manipulation that represents such a duplication flip.
*/
public abstract
StackManipulation flipOver(
TypeDefinition typeDefinition);
/**
* {@inheritDoc}
*/
public boolean
isValid() {
return true;
}
/**
* {@inheritDoc}
*/
public
Size apply(
MethodVisitor methodVisitor,
Implementation.
Context implementationContext) {
methodVisitor.
visitInsn(
opcode);
return
size;
}
/**
* A duplication that flips a value over the second value on the operand stack.
*/
protected enum
WithFlip implements
StackManipulation {
/**
* A flip instruction that flips a single-sized element over another single-size element.
*/
SINGLE_SINGLE(
Opcodes.
DUP_X1,
StackSize.
SINGLE),
/**
* A flip instruction that flips a double-sized element over a single-size element.
*/
SINGLE_DOUBLE(
Opcodes.
DUP_X2,
StackSize.
SINGLE),
/**
* A flip instruction that flips a single-sized element over a double-size element.
*/
DOUBLE_SINGLE(
Opcodes.
DUP2_X1,
StackSize.
DOUBLE),
/**
* A flip instruction that flips a double-sized element over another double-size element.
*/
DOUBLE_DOUBLE(
Opcodes.
DUP2_X2,
StackSize.
DOUBLE);
/**
* The opcode to apply.
*/
private final int
opcode;
/**
* The size that is added to the operand stack.
*/
private final
StackSize stackSize;
/**
* Creates a flip duplication.
*
* @param opcode The opcode to apply.
* @param stackSize The size that is added to the operand stack.
*/
WithFlip(int
opcode,
StackSize stackSize) {
this.
opcode =
opcode;
this.
stackSize =
stackSize;
}
/**
* {@inheritDoc}
*/
public boolean
isValid() {
return true;
}
/**
* {@inheritDoc}
*/
public
Size apply(
MethodVisitor methodVisitor,
Implementation.
Context implementationContext) {
methodVisitor.
visitInsn(
opcode);
return
stackSize.
toIncreasingSize();
}
}
}