/*
* 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.collection;
import net.bytebuddy.build.
HashCodeAndEqualsPlugin;
import net.bytebuddy.description.type.
TypeDefinition;
import net.bytebuddy.description.type.
TypeDescription;
import net.bytebuddy.implementation.
Implementation;
import net.bytebuddy.implementation.bytecode.
StackManipulation;
import net.bytebuddy.implementation.bytecode.
StackSize;
import net.bytebuddy.implementation.bytecode.constant.
IntegerConstant;
import net.bytebuddy.jar.asm.
MethodVisitor;
import net.bytebuddy.jar.asm.
Opcodes;
import java.util.
List;
/**
* A {@link net.bytebuddy.implementation.bytecode.collection.CollectionFactory} that is capable of
* creating an array of a given type with any number of given values.
*/
@
HashCodeAndEqualsPlugin.
Enhance
public class
ArrayFactory implements
CollectionFactory {
/**
* The component type of the array this array factory is creating.
*/
private final
TypeDescription.
Generic componentType;
/**
* The array creator delegate that supplies suitable opcodes for the creation of an array and the storage of
* values inside it.
*/
private final
ArrayCreator arrayCreator;
/**
* The decrease of stack size after each value storage operation.
*/
@
HashCodeAndEqualsPlugin.
ValueHandling(
HashCodeAndEqualsPlugin.
ValueHandling.
Sort.
IGNORE)
private final
StackManipulation.
Size sizeDecrease;
/**
* Creates a new array factory with a given
* {@link net.bytebuddy.implementation.bytecode.collection.ArrayFactory.ArrayCreator}
* without inferring the type from the component type. Normally,
* {@link net.bytebuddy.implementation.bytecode.collection.ArrayFactory#forType(net.bytebuddy.description.type.TypeDescription.Generic)}
* should be used.
*
* @param componentType The component type of the array factory.
* @param arrayCreator The array creator responsible for providing the correct byte code instructions.
*/
protected
ArrayFactory(
TypeDescription.
Generic componentType,
ArrayCreator arrayCreator) {
this.
componentType =
componentType;
this.
arrayCreator =
arrayCreator;
// Size decreases by index and array reference (2) and array element (1, 2) after each element storage.
sizeDecrease =
StackSize.
DOUBLE.
toDecreasingSize().
aggregate(
componentType.
getStackSize().
toDecreasingSize());
}
/**
* Creates a new array factory for a given component type.
*
* @param componentType The component type of the array that is to be build.
* @return A new array factory for the given type.
*/
public static
ArrayFactory forType(
TypeDescription.
Generic componentType) {
return new
ArrayFactory(
componentType,
makeArrayCreatorFor(
componentType));
}
/**
* Creates a suitable array creator for the given component type.
*
* @param componentType The component type of the array to be created.
* @return A suitable array creator.
*/
private static
ArrayCreator makeArrayCreatorFor(
TypeDefinition componentType) {
if (!
componentType.
isPrimitive()) {
return new
ArrayCreator.
ForReferenceType(
componentType.
asErasure());
} else if (
componentType.
represents(boolean.class)) {
return
ArrayCreator.
ForPrimitiveType.
BOOLEAN;
} else if (
componentType.
represents(byte.class)) {
return
ArrayCreator.
ForPrimitiveType.
BYTE;
} else if (
componentType.
represents(short.class)) {
return
ArrayCreator.
ForPrimitiveType.
SHORT;
} else if (
componentType.
represents(char.class)) {
return
ArrayCreator.
ForPrimitiveType.
CHARACTER;
} else if (
componentType.
represents(int.class)) {
return
ArrayCreator.
ForPrimitiveType.
INTEGER;
} else if (
componentType.
represents(long.class)) {
return
ArrayCreator.
ForPrimitiveType.
LONG;
} else if (
componentType.
represents(float.class)) {
return
ArrayCreator.
ForPrimitiveType.
FLOAT;
} else if (
componentType.
represents(double.class)) {
return
ArrayCreator.
ForPrimitiveType.
DOUBLE;
} else {
throw new
IllegalArgumentException("Cannot create array of type " +
componentType);
}
}
/**
* {@inheritDoc}
*/
public
StackManipulation withValues(
List<? extends
StackManipulation>
stackManipulations) {
return new
ArrayStackManipulation(
stackManipulations);
}
/**
* {@inheritDoc}
*/
public
TypeDescription.
Generic getComponentType() {
return
componentType;
}
/**
* An array creator is responsible for providing correct byte code instructions for creating an array
* and for storing values into it.
*/
protected interface
ArrayCreator extends
StackManipulation {
/**
* The creation of an array consumes one slot on the operand stack and adds a new value to it.
* Therefore, the operand stack's size is not altered.
*/
StackManipulation.
Size ARRAY_CREATION_SIZE_CHANGE =
StackSize.
ZERO.
toDecreasingSize();
/**
* The opcode instruction for storing a value of the component type inside an array.
*
* @return The correct storage opcode for the represented type.
*/
int
getStorageOpcode();
/**
* An array creator implementation for primitive types.
*/
enum
ForPrimitiveType implements
ArrayCreator {
/**
* An array creator for creating {@code boolean[]} arrays.
*/
BOOLEAN(
Opcodes.
T_BOOLEAN,
Opcodes.
BASTORE),
/**
* An array creator for creating {@code byte[]} arrays.
*/
BYTE(
Opcodes.
T_BYTE,
Opcodes.
BASTORE),
/**
* An array creator for creating {@code short[]} arrays.
*/
SHORT(
Opcodes.
T_SHORT,
Opcodes.
SASTORE),
/**
* An array creator for creating {@code char[]} arrays.
*/
CHARACTER(
Opcodes.
T_CHAR,
Opcodes.
CASTORE),
/**
* An array creator for creating {@code int[]} arrays.
*/
INTEGER(
Opcodes.
T_INT,
Opcodes.
IASTORE),
/**
* An array creator for creating {@code long[]} arrays.
*/
LONG(
Opcodes.
T_LONG,
Opcodes.
LASTORE),
/**
* An array creator for creating {@code float[]} arrays.
*/
FLOAT(
Opcodes.
T_FLOAT,
Opcodes.
FASTORE),
/**
* An array creator for creating {@code double[]} arrays.
*/
DOUBLE(
Opcodes.
T_DOUBLE,
Opcodes.
DASTORE);
/**
* The opcode for creating an array of this type.
*/
private final int
creationOpcode;
/**
* The opcode for storing a value in an array of this type.
*/
private final int
storageOpcode;
/**
* Creates a new primitive array creator.
*
* @param creationOpcode The opcode for creating an array of this type.
* @param storageOpcode The opcode for storing a value in an array of this type.
*/
ForPrimitiveType(int
creationOpcode, int
storageOpcode) {
this.
creationOpcode =
creationOpcode;
this.
storageOpcode =
storageOpcode;
}
/**
* {@inheritDoc}
*/
public boolean
isValid() {
return true;
}
/**
* {@inheritDoc}
*/
public
Size apply(
MethodVisitor methodVisitor,
Implementation.
Context implementationContext) {
methodVisitor.
visitIntInsn(
Opcodes.
NEWARRAY,
creationOpcode);
return
ARRAY_CREATION_SIZE_CHANGE;
}
/**
* {@inheritDoc}
*/
public int
getStorageOpcode() {
return
storageOpcode;
}
}
/**
* An array creator implementation for reference types.
*/
@
HashCodeAndEqualsPlugin.
Enhance
class
ForReferenceType implements
ArrayCreator {
/**
* The internal name of this array's non-primitive component type.
*/
private final
String internalTypeName;
/**
* Creates a new array creator for a reference type.
*
* @param referenceType The internal name of this array's non-primitive component type.
*/
protected
ForReferenceType(
TypeDescription referenceType) {
this.
internalTypeName =
referenceType.
getInternalName();
}
/**
* {@inheritDoc}
*/
public boolean
isValid() {
return true;
}
/**
* {@inheritDoc}
*/
public
Size apply(
MethodVisitor methodVisitor,
Implementation.
Context implementationContext) {
methodVisitor.
visitTypeInsn(
Opcodes.
ANEWARRAY,
internalTypeName);
return
ARRAY_CREATION_SIZE_CHANGE;
}
/**
* {@inheritDoc}
*/
public int
getStorageOpcode() {
return
Opcodes.
AASTORE;
}
}
}
/**
* A stack manipulation for creating an array as defined by the enclosing array factory.
*/
@
HashCodeAndEqualsPlugin.
Enhance(includeSyntheticFields = true)
protected class
ArrayStackManipulation implements
StackManipulation {
/**
* A list of value load instructions that are to be stored inside the created array.
*/
private final
List<? extends
StackManipulation>
stackManipulations;
/**
* Creates a new array loading instruction.
*
* @param stackManipulations A list of value load instructions that are to be stored inside the created array.
*/
protected
ArrayStackManipulation(
List<? extends
StackManipulation>
stackManipulations) {
this.
stackManipulations =
stackManipulations;
}
/**
* {@inheritDoc}
*/
public boolean
isValid() {
for (
StackManipulation stackManipulation :
stackManipulations) {
if (!
stackManipulation.
isValid()) {
return false;
}
}
return
arrayCreator.
isValid();
}
/**
* {@inheritDoc}
*/
public
Size apply(
MethodVisitor methodVisitor,
Implementation.
Context implementationContext) {
Size size =
IntegerConstant.
forValue(
stackManipulations.
size()).
apply(
methodVisitor,
implementationContext);
// The array's construction does not alter the stack's size.
size =
size.
aggregate(
arrayCreator.
apply(
methodVisitor,
implementationContext));
int
index = 0;
for (
StackManipulation stackManipulation :
stackManipulations) {
methodVisitor.
visitInsn(
Opcodes.
DUP);
size =
size.
aggregate(
StackSize.
SINGLE.
toIncreasingSize());
size =
size.
aggregate(
IntegerConstant.
forValue(
index++).
apply(
methodVisitor,
implementationContext));
size =
size.
aggregate(
stackManipulation.
apply(
methodVisitor,
implementationContext));
methodVisitor.
visitInsn(
arrayCreator.
getStorageOpcode());
size =
size.
aggregate(
sizeDecrease);
}
return
size;
}
}
}