net.bytebuddy.implementation.bytecode.collection.ArrayFactory Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2014 - Present 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 extends StackManipulation.AbstractBase 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 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;
}
}
}